import { MatrixClient, SimpleFsStorageProvider, AutojoinRoomsMixin, } from "matrix-bot-sdk"; const http = require("http"); // probably the worst code you have seen in your life const bannedKeywords = [ ['Sus', 'thigh high'], ['Amogus!!', 'amogus', 'among us'], ['LGBTQQIAAP++ propaganda', 'transgender', 'trans rights', [ 'thigh', 'sock', 'computer' ], 'transhumanist evolution', 'pride parade', 'pride flag', 'gay pride' ], ['Chinese cartoons', 'anime', 'manga', 'loli', 'azur lane', 'mihoyo', 'kancolle', 'neko', 'touhou', 'foxgirl', 'wolf girl', 'ayanamikodon', 'hatsune miku', 'vocaloid', 'mikudayo', 'kyoto animation'], ['Furries', 'fursona', 'fursuit', 'e621', 'furry fandom', 'furry art'], ['Gore', ' gore ', ' guro ', ' gory '], ]; // This will be the URL where clients can reach your homeserver. Note that this might be different // from where the web/chat interface is hosted. The server must support password registration without // captcha or terms of service (public servers typically won't work). const homeserverUrl = "https://trygve.me"; // Use the access token you got from login or registration above. const accessToken = "your token"; // In order to make sure the bot doesn't lose its state between restarts, we'll give it a place to cache // any information it needs to. You can implement your own storage provider if you like, but a JSON file // will work fine for this example. const storage = new SimpleFsStorageProvider("hello-bot.json"); // Finally, let's create the client and set it to autojoin rooms. Autojoining is typical of bots to ensure // they can be easily added to any room. const client = new MatrixClient(homeserverUrl, accessToken, storage); AutojoinRoomsMixin.setupOnClient(client); // Before we start the bot, register our command handler client.on("room.event", handleCommand); // Now that everything is set up, start the bot. This will start the sync loop and run until killed. client.start().then(() => console.log("Bot started!")); // This is the command handler we registered a few lines up async function handleCommand(roomId: string, event: any) { let expression = ''; let image_url = ''; if ( event['content']?.['msgtype'] == 'm.image' ) { image_url = homeserverUrl + "/_matrix/media/r0/download/" + (event['content']?.['url']).slice(6); expression = 'image'; } if ( event["content"]["avatar_url"] ) { image_url = homeserverUrl + "/_matrix/media/r0/download/" + (event['content']?.['avatar_url']).slice(6); expression = 'pfp'; } if (event['sender'] === await client.getUserId()) return; if (image_url == '') return; console.log('New image received, analyzing...'); analyze_image(image_url, function(degenerate, degenerateKeywords, fullDegenerateKeywords, response) { if (degenerate) { console.log('====Degeneracy Detected!===='); console.log('User: ' + event['sender'] + ' sent image with url: ' + image_url); console.log("Full keywords: " + fullDegenerateKeywords); console.log("Sent keywords: " + degenerateKeywords); console.log(response); console.log('https://matrix.to/#/' + roomId + '/' + event['event_id'] + '?via=willy.club&via=trygve.me&via=matrix.org'); console.log('============================'); // determine what type of event it was if (expression == 'image') { client.redactEvent(roomId, event['event_id']); client.sendText(roomId, 'Woah! Please refrain from posting images containing: "' + degenerateKeywords + '".'); } else { client.kickUser(event['sender'], roomId, 'Please change your profile picture'); } } else { console.log('=======Nothing found========'); console.log('User: ' + event['sender'] + ' sent image with url: ' + image_url); console.log(response); console.log('https://matrix.to/#/' + roomId + '/' + event['event_id'] + '?via=willy.club&via=trygve.me&via=matrix.org'); console.log('============================'); } }); } function analyze_image(image_url, callback) { http.get(`http://localhost:5000/?image_url=` + image_url, resp => { let data = ""; // A chunk of data has been recieved. resp.on("data", chunk => { data += chunk; }); // The whole response has been received. Print out the result. resp.on("end", () => { let response = JSON.parse(data).message; let responseWithoutComma = response.replace(',', ' '); let degenerate = false; let degenerateKeywords = ''; let fullDegenerateKeywords = ''; bannedKeywords.forEach(keyword => { if (Array.isArray(keyword)) { let alreadyAddedKeywordForThisGroup = false; let skippedFirstIterationBecauseItsTheTitle = false; keyword.forEach(subKeyword => { if (!skippedFirstIterationBecauseItsTheTitle) { skippedFirstIterationBecauseItsTheTitle = true; return; } if (Array.isArray(subKeyword)) { let allOfThemMatch = true; subKeyword.forEach(subsubKeyword => { if(responseWithoutComma.includes(subsubKeyword)) { fullDegenerateKeywords += subsubKeyword + ','; } else { allOfThemMatch = false; } }); if (allOfThemMatch) { degenerate = true; if (!alreadyAddedKeywordForThisGroup) { degenerateKeywords += keyword[0] + ','; alreadyAddedKeywordForThisGroup = true; } } } else if (responseWithoutComma.includes(subKeyword)) { degenerate = true; if (!alreadyAddedKeywordForThisGroup) { degenerateKeywords += keyword[0] + ','; fullDegenerateKeywords += subKeyword + ','; alreadyAddedKeywordForThisGroup = true; } } }); } else if (responseWithoutComma.includes(keyword)) { degenerate = true; degenerateKeywords += keyword + ','; fullDegenerateKeywords += keyword + ','; } }); degenerateKeywords = degenerateKeywords.substr(0, degenerateKeywords.length -1); fullDegenerateKeywords = fullDegenerateKeywords.substr(0, fullDegenerateKeywords.length -1); if (degenerate) { callback(true, degenerateKeywords, fullDegenerateKeywords, response); } else { callback(false, '', '', response); } }); }); } // sometimes we dont have permission to redact messages, lets just ignore that process.on('uncaughtException', function (exception) { console.log('An error occured but it was probably not that important'); //console.log(exception); });