import hxdiscord.DiscordClient; import hxdiscord.types.*; import hxdiscord.endpoints.Endpoints; import haxe.MainLoop; import haxe.Json; import sys.io.File; import sys.FileSystem; using StringTools; class Onequestionmark { // Initialize vars static var Bot:DiscordClient; static var settings:Dynamic; static var botInfo:Dynamic; static var iceRegex:EReg = ~/\bicc?ed?\b/i; static var hugDB:Array; static var motdDB:haxe.DynamicAccess = {}; static var yesnoDB:Dynamic; static var echoboxDB:haxe.DynamicAccess = {}; static var saveTimer = Date.now().getMinutes(); static var saveQueue:Array = []; static function main() { Sys.println('[${timestamp()}] Starting onequestionmark-chat'); // Load files if (FileSystem.exists("settings.json")) { Sys.println('[${timestamp()}] Found settings.json, loading'); settings = Json.parse(File.getContent("settings.json")); } else { Sys.println('[${timestamp()}] Did not find settings.json, exiting'); Sys.exit(0); } if (FileSystem.exists("hug-db.json")) { Sys.println('[${timestamp()}] Found hug-db.json, loading'); hugDB = Json.parse(File.getContent("hug-db.json")); } else { Sys.println('[${timestamp()}] Did not find hug-db.json, skipping'); hugDB = ["https://cdn.discordapp.com/attachments/270113422232911883/445026464623099907/brohugbump.gif"]; // Fallback } if (FileSystem.exists("motd-db.json")) { Sys.println('[${timestamp()}] Found motd-db.json, loading'); motdDB = Json.parse(File.getContent("motd-db.json")); } else { Sys.println('[${timestamp()}] Did not find motd-db.json, skipping'); motdDB = Json.parse('{"sunday": [],"monday": [],"tuesday": [],"wednesday": [],"thursday": [],"friday": [],"saturday": []}'); // Fallback } if (FileSystem.exists("yesno-db.json")) { Sys.println('[${timestamp()}] Found yesno-db.json, loading'); yesnoDB = Json.parse(File.getContent("yesno-db.json")); } else { Sys.println('[${timestamp()}] Did not find yesno-db.json, skipping'); yesnoDB = {"yes": [],"no": []}; // Fallback } if (FileSystem.exists("echobox-db.json")) { Sys.println('[${timestamp()}] Found echobox-db.json, loading'); yesnoDB = Json.parse(File.getContent("echobox-db.json")); } else { Sys.println('[${timestamp()}] Did not find echobox-db.json, generating'); File.saveContent("echobox-db.json", Json.stringify(echoboxDB, "\t")); } // Start the bot Bot = new DiscordClient(settings.token, [3276799], settings.debug); Bot.onReady = onReady; Bot.onMessageCreate = onMessageCreate; Bot.connect(); } public static function onReady() { Sys.println('[${timestamp()}] Bot is online'); //Endpoints.sendMessage(settings.devchannel, {content:"onequestionmark-chat alpha test is now running"}, null, false); botInfo = Endpoints.getCurrentUser(); MainLoop.add(motd); MainLoop.add(saveSystem); } public static function onMessageCreate(m:Message) { // DevMode check if ((!settings.devmode) || (m.guild_id == settings.devserver)) { var msg = m.content; // Command processor if (msg.charAt(0) == "?") { var command = ""; if (msg.indexOf(" ") != -1) { // Separate command from rest of message, if necessary command = msg.substring(1, msg.indexOf(" ")).toLowerCase(); msg = msg.substring(msg.indexOf(" ")+1); } else { // Otherwise, remove msg command = msg.substring(1).toLowerCase(); msg = ""; } switch (command) { // Commands that require authorization case "quit": if (m.author.id == settings.botowner) { m.react('✅'); Sys.println('[${timestamp()}] Shutdown command received from botowner, exiting'); shutdown(); } case "devmode": if (m.author.id == settings.botowner) { if (!settings.devmode) { settings.devmode = true; m.reply({content:"DevMode enabled."}, false); } else { settings.devmode = false; m.reply({content:"DevMode disabled."}, false); } } case "motd": if (m.author.id == settings.botowner) { if (!settings.motd.channels.contains(m.channel_id)) { settings.motd.channels.push(m.channel_id); m.reply({content:'MOTD has been enabled for <#${m.channel_id}>'}, false); } else { settings.motd.channels.remove(m.channel_id); m.reply({content:'MOTD has been disabled for <#${m.channel_id}>'}, false); } saveQueue.push("settings"); } // System for WIP commands that only work in testing server case "wipcommand": if (m.guild_id == settings.devserver) { m.reply({content:"Dev Server response."}, true); } // Basic response commands case "help": m.reply({content:' **onequestionmark bot commands**\n`?chk`: ack\n`?slap `: The classic mIRC troutslap.\n`?hug `: Posts randomized hug image.\n`?yes` and `?no`: Posts randomized yes/no.\n`?angery`\n`?subway`\n`?motd`: Enables MOTD for current channel (requires auth)\n`?devmode`: Toggles Dev Mode (requires auth)\n`?quit`: Shutdown command (requires auth)\n**Non-command bot functions:**\n*MOTD*: Bot will post a randomized *Meme of the Day* in enabled channels.\n*Icce*: Bot provides users with ice cuboids.\n*Meteor*: Bot reacts to falling rocks in the chat.'}, false); case "chk": m.reply({content:'<@${m.author.id}>: ack'}, false); case "mrkrabs": m.reply({content:'<@${m.author.id}>: ack ack ack ack ack'}, false); case "slap": if (msg.length != 0) {m.reply({content:'*onequestionmark slaps ${msg} around a bit with a large trout*'}, false);} // Basic aliases case "angery": m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1079518504413311108/angery.jpg"}, false); case "subway": m.reply({content:"https://www.youtube.com/watch?v=y3VRXVvr6XU"}, false); case "illuminati": m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1147204253039984671/illuminati.gif"}, false); case "cube": m.reply({content:"https://cdn.discordapp.com/attachments/270113422232911883/502690458779123722/the_cube.jpg"}, false); case "coolsville": m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1147583765212835921/coolsville.gif"}, false); case "tufac": m.reply({content:"*Not Teh Face, but better,*\n*Tufac to the letter!*\n*Always two faces, never one,*\n*Tufac has you on the run!*"}, false); case "communism": m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1147590229960691742/communism.gif"}, false); // Image database commands case "hug": m.reply({content:'🫂 *hugs ${msg}*\n${hugDB[randInt(0, hugDB.length-1)]}'}, false); case "yes": var yes = yesnoDB.yes; m.reply({content:'${yes[randInt(0, yes.length-1)]}'}, false); case "no": var no = yesnoDB.no; m.reply({content:'${no[randInt(0, no.length-1)]}'}, false); // Echobox case "echobox": // Create Echobox array for the server if missing if (!echoboxDB.exists('server${m.guild_id}')) { echoboxDB.set('server${m.guild_id}', []); } var echobox:Array = echoboxDB.get('server${m.guild_id}'); var quote:String = ""; // Echobox response if (!m.mention_everyone) { if (echobox.length > 0) { var ebchoice = randInt(0, echobox.length-1); //m.reply({content:'**Echobox, quote #${ebchoice+1}:** ${echobox[ebchoice]}'}, false); quote = '**Echobox, quote #${ebchoice+1}:** ${echobox[ebchoice]}'; } else { //m.reply({content:'**Echobox:** There are no quotes in this server\'s Echobox yet.'}, false); quote = '**Echobox:** There are no quotes in this server\'s Echobox yet.'; } // Add quote to Echobox if (msg.length != 0) { if (!echobox.contains(msg)) { echobox.push(msg); echoboxDB.set('server${m.guild_id}', echobox); saveQueue.push("echobox"); m.reply({content:quote}, false); } else { m.reply({content:'**Echobox:** This quote is already in the database.'}, false); } } else { m.reply({content:quote}, false); } } else { m.reply({content:'**Echobox:** You cannot add `@everyone` to the Echobox.'}, false); } } } // Non-command responses if ((msg.charAt(0) == ".") && (msg.length == 1)) {m.reply({content:"omg a meteor"}, true);} if ((iceRegex.match(msg)) && (m.author.id != botInfo.id)) {m.reply({content:"Did some carbon-based lifeform just say **I C E**?"});} if ((m.mention_everyone == true) && (msg.charAt(0) != "?")) {m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1147598637241741343/at_everyone.jpg"}, true);} } } // Time-related functions public static function timestamp() { return '${StringTools.lpad(Std.string(Date.now().getHours()), "0", 2)}:${StringTools.lpad(Std.string(Date.now().getMinutes()), "0", 2)}'; } public static function datestamp() { return '${StringTools.lpad(Std.string(Date.now().getMonth()+1), "0", 2)}-${StringTools.lpad(Std.string(Date.now().getDate()), "0", 2)}'; } // Clean print day of week public static function checkDay() { var day = ""; switch (Date.now().getDay()+1) { case 1: day = "sun"; case 2: day = "mon"; case 3: day = "tues"; case 4: day = "wed"; case 5: day = "thurs"; case 6: day = "fri"; case 7: day = "sat"; } return day; } // Clean print month public static function checkMonth() { var month = ""; switch (Date.now().getMonth()+1) { case 1: month = "jan"; case 2: month = "feb"; case 3: month = "mar"; case 4: month = "apr"; case 5: month = "may"; case 6: month = "jun"; case 7: month = "jul"; case 8: month = "aug"; case 9: month = "sep"; case 10: month = "oct"; case 11: month = "nov"; case 12: month = "dec"; } return month; } //Get current day of month, padded public static function checkDate() { return StringTools.lpad(Std.string(Date.now().getDate()), "0", 2); } public static function monthdate() { return '${checkMonth()}${checkDate()}'; } public static function weekdate() { return '${checkDay()}${checkDate()}'; } public static function motd() { if (settings.motd.date != datestamp()) { settings.motd.date = datestamp(); settings.motd.posted = false; } if ((settings.motd.posted == false) && (Date.now().getHours() >= settings.motd.time)) { settings.motd.posted = true; saveQueue.push("settings"); var msg = ""; var day:Array = []; // Check for exact date if (motdDB.exists('${monthdate()}-${Date.now().getFullYear()}')) { day = motdDB.get('${monthdate()}-${Date.now().getFullYear()}'); } // Else, check for special date else if (motdDB.exists(monthdate())) { day = motdDB.get(monthdate()); } // Else, check for special day/date combo else if (motdDB.exists(weekdate())) { day = motdDB.get(weekdate()); } // Otherwise, post daily meme else if (motdDB.exists(checkDay())) { day = motdDB.get(checkDay()); } if (day.length > 0) {msg = day[randInt(0, day.length-1)];} var channels:Array = settings.motd.channels; // Because it won't let me do this directly if (channels.length > 0) {for (i in channels) {if (msg.length > 0) {Endpoints.sendMessage(i, {content:msg}, null, false);}}} } } // Filesystem operations public static function saveSystem() { if (saveTimer != Date.now().getMinutes()) { // We only want to run this once a minute if (saveQueue.length > 0) { // See if there's anything in the queue switch (saveQueue.shift()) { case "settings": saveSettings(); Sys.println('[${timestamp()}] Saved settings.json'); case "echobox": saveEchoboxDB(); Sys.println('[${timestamp()}] Saved echobox-db.json'); } } saveTimer = Date.now().getMinutes(); // Update timer } } public static function saveSettings() { File.saveContent("settings.json", Json.stringify(settings, "\t")); } public static function saveEchoboxDB() { File.saveContent("echobox-db.json", Json.stringify(echoboxDB, "\t")); } // General functions public static function randInt(x, y) { // Return a random integer between x and y, both inclusive return Std.random((y+1)-x)+x; } public static function shutdown() { saveSettings(); saveEchoboxDB(); Sys.exit(0); } }