|
|
@@ -1,10 +1,13 @@
|
|
|
|
import hxdiscord.DiscordClient;
|
|
|
|
import hxdiscord.DiscordClient;
|
|
|
|
import hxdiscord.types.*;
|
|
|
|
import hxdiscord.types.*;
|
|
|
|
import hxdiscord.endpoints.Endpoints;
|
|
|
|
import hxdiscord.endpoints.Endpoints;
|
|
|
|
import haxe.MainLoop;
|
|
|
|
import htmlparser.HtmlDocument;
|
|
|
|
|
|
|
|
import haxe.Timer;
|
|
|
|
import haxe.Json;
|
|
|
|
import haxe.Json;
|
|
|
|
import sys.io.File;
|
|
|
|
import sys.io.File;
|
|
|
|
import sys.FileSystem;
|
|
|
|
import sys.FileSystem;
|
|
|
|
|
|
|
|
import BigInt;
|
|
|
|
|
|
|
|
import izzcomlib.IzzComLib.*;
|
|
|
|
using StringTools;
|
|
|
|
using StringTools;
|
|
|
|
|
|
|
|
|
|
|
|
class Onequestionmark {
|
|
|
|
class Onequestionmark {
|
|
|
@@ -12,15 +15,17 @@ class Onequestionmark {
|
|
|
|
static var Bot:DiscordClient;
|
|
|
|
static var Bot:DiscordClient;
|
|
|
|
static var settings:Dynamic;
|
|
|
|
static var settings:Dynamic;
|
|
|
|
static var botInfo:Dynamic;
|
|
|
|
static var botInfo:Dynamic;
|
|
|
|
static var iceRegex:EReg = ~/\bicc?ed?\b/i;
|
|
|
|
//static var iceRegex:EReg = ~/\bicc?ed?\b/i;
|
|
|
|
|
|
|
|
|
|
|
|
static var hugDB:Array<String>;
|
|
|
|
static var hugDB:Array<String>;
|
|
|
|
static var motdDB:haxe.DynamicAccess<Dynamic> = {};
|
|
|
|
static var motdDB:haxe.DynamicAccess<Dynamic> = {};
|
|
|
|
static var yesnoDB:Dynamic;
|
|
|
|
static var yesnoDB:Dynamic;
|
|
|
|
static var echoboxDB:haxe.DynamicAccess<Dynamic> = {};
|
|
|
|
static var echoboxDB:haxe.DynamicAccess<Dynamic> = {};
|
|
|
|
|
|
|
|
|
|
|
|
static var saveTimer = Date.now().getMinutes();
|
|
|
|
static var motdTimer:Timer;
|
|
|
|
|
|
|
|
static var saveTimer:Timer;
|
|
|
|
static var saveQueue:Array<String> = [];
|
|
|
|
static var saveQueue:Array<String> = [];
|
|
|
|
|
|
|
|
static var timerInit:Bool = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static function main() {
|
|
|
|
static function main() {
|
|
|
@@ -41,7 +46,7 @@ class Onequestionmark {
|
|
|
|
hugDB = Json.parse(File.getContent("hug-db.json"));
|
|
|
|
hugDB = Json.parse(File.getContent("hug-db.json"));
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
Sys.println('[${timestamp()}] Did not find hug-db.json, skipping');
|
|
|
|
Sys.println('[${timestamp()}] Did not find hug-db.json, skipping');
|
|
|
|
hugDB = ["https://cdn.discordapp.com/attachments/270113422232911883/445026464623099907/brohugbump.gif"]; // Fallback
|
|
|
|
hugDB = []; // Fallback
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (FileSystem.exists("motd-db.json")) {
|
|
|
|
if (FileSystem.exists("motd-db.json")) {
|
|
|
@@ -49,7 +54,7 @@ class Onequestionmark {
|
|
|
|
motdDB = Json.parse(File.getContent("motd-db.json"));
|
|
|
|
motdDB = Json.parse(File.getContent("motd-db.json"));
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
Sys.println('[${timestamp()}] Did not find motd-db.json, skipping');
|
|
|
|
Sys.println('[${timestamp()}] Did not find motd-db.json, skipping');
|
|
|
|
motdDB = Json.parse('{"sunday": [],"monday": [],"tuesday": [],"wednesday": [],"thursday": [],"friday": [],"saturday": []}'); // Fallback
|
|
|
|
motdDB = Json.parse('{}'); // Fallback
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (FileSystem.exists("yesno-db.json")) {
|
|
|
|
if (FileSystem.exists("yesno-db.json")) {
|
|
|
@@ -67,6 +72,11 @@ class Onequestionmark {
|
|
|
|
Sys.println('[${timestamp()}] Did not find echobox-db.json, generating');
|
|
|
|
Sys.println('[${timestamp()}] Did not find echobox-db.json, generating');
|
|
|
|
File.saveContent("echobox-db.json", Json.stringify(echoboxDB, "\t"));
|
|
|
|
File.saveContent("echobox-db.json", Json.stringify(echoboxDB, "\t"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!FileSystem.exists("export")) {
|
|
|
|
|
|
|
|
FileSystem.createDirectory("export");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
saveSystem();
|
|
|
|
|
|
|
|
|
|
|
|
// Start the bot
|
|
|
|
// Start the bot
|
|
|
|
Bot = new DiscordClient(settings.token, [3276799], settings.debug);
|
|
|
|
Bot = new DiscordClient(settings.token, [3276799], settings.debug);
|
|
|
@@ -75,24 +85,43 @@ class Onequestionmark {
|
|
|
|
Bot.connect();
|
|
|
|
Bot.connect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* The `onReady()` function is run upon a successful `Bot.connect()`
|
|
|
|
|
|
|
|
*/
|
|
|
|
public static function onReady() {
|
|
|
|
public static function onReady() {
|
|
|
|
Sys.sleep(1);
|
|
|
|
Sys.sleep(1);
|
|
|
|
Sys.println('[${timestamp()}] Bot is online');
|
|
|
|
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);
|
|
|
|
// Join message from program args
|
|
|
|
MainLoop.add(saveSystem);
|
|
|
|
if (Sys.args().length > 0) {
|
|
|
|
|
|
|
|
Endpoints.sendMessage(settings.devchannel, {content:Sys.args().join(" ")}, null, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
botInfo = Endpoints.getCurrentUser();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (timerInit == false) {
|
|
|
|
|
|
|
|
timerInit = true;
|
|
|
|
|
|
|
|
startMotd();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* The `onMessageCreate()` event provides the bulk of onequestionmark's functionality.
|
|
|
|
|
|
|
|
* This is what triggers when a new Discord message is received, and contains the bot's command processor.
|
|
|
|
|
|
|
|
*/
|
|
|
|
public static function onMessageCreate(m:Message) {
|
|
|
|
public static function onMessageCreate(m:Message) {
|
|
|
|
// DevMode check
|
|
|
|
// DevMode check
|
|
|
|
if ((!settings.devmode) || (m.guild_id == settings.devserver)) {
|
|
|
|
if ((!settings.devmode) || (m.guild_id == settings.devserver)) {
|
|
|
|
var msg = m.content;
|
|
|
|
var msg = m.content;
|
|
|
|
|
|
|
|
var sender:String;
|
|
|
|
|
|
|
|
if (m.getMember().nick == null) {sender = m.author.global_name;} else {sender = m.getMember().nick;}
|
|
|
|
|
|
|
|
|
|
|
|
// Command processor
|
|
|
|
// Command processor
|
|
|
|
if (msg.charAt(0) == "?") {
|
|
|
|
if (msg.charAt(0) == "?") {
|
|
|
|
var command = "";
|
|
|
|
var command = "";
|
|
|
|
|
|
|
|
var args:Array<String> = [];
|
|
|
|
|
|
|
|
|
|
|
|
if (msg.indexOf(" ") != -1) {
|
|
|
|
if (msg.indexOf(" ") != -1) {
|
|
|
|
// Separate command from rest of message, if necessary
|
|
|
|
// Separate command from rest of message, if necessary
|
|
|
@@ -104,6 +133,12 @@ class Onequestionmark {
|
|
|
|
msg = "";
|
|
|
|
msg = "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Separate arguments from command
|
|
|
|
|
|
|
|
if (command.indexOf(".") != -1) {
|
|
|
|
|
|
|
|
args = command.substring(command.indexOf(".")+1).toLowerCase().split(".");
|
|
|
|
|
|
|
|
command = command.substring(0, command.indexOf(".")).toLowerCase();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (command) {
|
|
|
|
switch (command) {
|
|
|
|
// Commands that require authorization
|
|
|
|
// Commands that require authorization
|
|
|
|
case "quit":
|
|
|
|
case "quit":
|
|
|
@@ -124,6 +159,11 @@ class Onequestionmark {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case "motd":
|
|
|
|
case "motd":
|
|
|
|
if (m.author.id == settings.botowner) {
|
|
|
|
if (m.author.id == settings.botowner) {
|
|
|
|
|
|
|
|
if (args.length != 0) {
|
|
|
|
|
|
|
|
if (args[0] == "force") {
|
|
|
|
|
|
|
|
m.reply({content:'${getMotd(true)}'}, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (!settings.motd.channels.contains(m.channel_id)) {
|
|
|
|
if (!settings.motd.channels.contains(m.channel_id)) {
|
|
|
|
settings.motd.channels.push(m.channel_id);
|
|
|
|
settings.motd.channels.push(m.channel_id);
|
|
|
|
m.reply({content:'MOTD has been enabled for <#${m.channel_id}>'}, false);
|
|
|
|
m.reply({content:'MOTD has been enabled for <#${m.channel_id}>'}, false);
|
|
|
@@ -133,6 +173,16 @@ class Onequestionmark {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
saveQueue.push("settings");
|
|
|
|
saveQueue.push("settings");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
case "trace":
|
|
|
|
|
|
|
|
if (m.author.id == settings.botowner) {
|
|
|
|
|
|
|
|
if (m.referenced_message == null) {
|
|
|
|
|
|
|
|
Sys.println('[${timestamp()}] trace: ${Json.stringify(getMessage(m.channel_id, m.id))}');
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
Sys.println('[${timestamp()}] trace: ${Json.stringify(getMessage(m.referenced_message.channel_id, m.referenced_message.id))}');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
m.react('✅');
|
|
|
|
|
|
|
|
}
|
|
|
|
// System for WIP commands that only work in testing server
|
|
|
|
// System for WIP commands that only work in testing server
|
|
|
|
case "wipcommand":
|
|
|
|
case "wipcommand":
|
|
|
|
if (m.guild_id == settings.devserver) {
|
|
|
|
if (m.guild_id == settings.devserver) {
|
|
|
@@ -140,14 +190,135 @@ class Onequestionmark {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Basic response commands
|
|
|
|
// Basic response commands
|
|
|
|
case "help":
|
|
|
|
case "help":
|
|
|
|
m.reply({content:'
|
|
|
|
m.reply({embeds: [
|
|
|
|
**onequestionmark bot commands**\n`?chk`: ack\n`?slap <target>`: The classic mIRC troutslap.\n`?hug <target (optional)>`: 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);
|
|
|
|
{
|
|
|
|
|
|
|
|
"color": 13733022,
|
|
|
|
|
|
|
|
"url": "https://discord.com",
|
|
|
|
|
|
|
|
"author": {
|
|
|
|
|
|
|
|
"name": "onequestionmark help",
|
|
|
|
|
|
|
|
"url": "https://discord.com",
|
|
|
|
|
|
|
|
"icon_url": "https://cdn.discordapp.com/avatars/1071524991084015756/d623b45d33e8119599b29c5cf5ed532e.png"
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
"thumbnail": {
|
|
|
|
|
|
|
|
"url": "https://cdn.discordapp.com/emojis/1071560243051511808.png"
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
"fields": [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"name": "**Bot commands:**",
|
|
|
|
|
|
|
|
"value": "`?help`: Posts this help document. :book:\n`?chk`: Replies with \"ack\". :speaking_head:\n`?slap <target>`: The classic mIRC troutslap. :fish:\n`?hug <target (optional)>`: Posts randomized hug image. :people_hugging:\n`?yes`: Posts randomized \"yes\" image or video. :thumbsup:\n`?no`: Posts randomized \"no\" image or video. :no_entry_sign:\n`?mrkrabs`: Similar to `?chk`. :crab:\n`?tufac`: The official Tufac Theme Song. :busts_in_silhouette:\n`?coin <count (optional)>`: Flips between 1 and 100 coins. :coin:\n`?dice.<arg> <num>`: Rolls <arg>-sided die <num> times :game_die:\n`?8ball`: Answers your yes or no questions. :8ball:\n`?echobox`: Posts randomized quote from the quote database. :loudspeaker:\n`?echobox <quote>`: Adds provided quote to the database. :writing_hand:\n`?youtube <query>`: Return first result of a YouTube search. :cinema:",
|
|
|
|
|
|
|
|
"inline": false
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"name": "**Pirate vs. Ninja:**",
|
|
|
|
|
|
|
|
"value": "`?pirate <name>`: Look up stats for a pirate. :pirate_flag:\n`?ninja <name>`: Look up stats for a ninja. :ninja:\n`?pvn <pirate> | <ninja>`: Let them fight! :crossed_swords:\n`?nvp <ninja> | <pirate>`: Same as above but reversed order.",
|
|
|
|
|
|
|
|
"inline": false
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"name": "**Basic Response Aliases:**",
|
|
|
|
|
|
|
|
"value": "`?angery`: *I taste a vegetal.* :rage:\n`?subway`: Arin's infamous Subway rant. :sandwich:\n`?illuminati`: Always watching. :eye:\n`?cube`: *The Cube*\n`?coolsville`: Population: Us :sunglasses:\n`?communism`: The old 3D rotating gif.\n`?opinions`: Here they come. :scream:\n`?fineart`: The great masterpiece. :art:\n`?artishard`: Art *is* hard. :corn:\n`?florida`: :carpentry_saw::rabbit:\n`?stop`: :stop_sign: :smiley:\n`?indeed`",
|
|
|
|
|
|
|
|
"inline": false
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"name": "**Auth-Requiring Commands:**",
|
|
|
|
|
|
|
|
"value": "`?motd`: Enables MOTD for current channel.\n`?devmode`: Toggles Dev Mode.\n`?trace`: Debugging tool.\n`?quit`: Shutdown command.",
|
|
|
|
|
|
|
|
"inline": true
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
"name": "**Non-command bot functions:**",
|
|
|
|
|
|
|
|
"value": "**MOTD**: *Meme of the Day* is posted in enabled channels.",
|
|
|
|
|
|
|
|
"inline": true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
]});
|
|
|
|
case "chk":
|
|
|
|
case "chk":
|
|
|
|
m.reply({content:'<@${m.author.id}>: ack'}, false);
|
|
|
|
m.reply({content:'<@${m.author.id}>: ack'}, false);
|
|
|
|
case "mrkrabs":
|
|
|
|
case "mrkrabs":
|
|
|
|
m.reply({content:'<@${m.author.id}>: ack ack ack ack ack'}, false);
|
|
|
|
m.reply({content:'<@${m.author.id}>: ack ack ack ack ack'}, false);
|
|
|
|
case "slap":
|
|
|
|
case "slap":
|
|
|
|
if (msg.length != 0) {m.reply({content:'*onequestionmark slaps ${msg} around a bit with a large trout*'}, false);}
|
|
|
|
if (msg.length != 0) {m.reply({content:'*onequestionmark slaps ${msg} around a bit with a large trout*'}, 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 "youtube":
|
|
|
|
|
|
|
|
if (msg.length > 0) {ytlookup(m,msg);}
|
|
|
|
|
|
|
|
case "ytdebug":
|
|
|
|
|
|
|
|
if (FileSystem.exists("export/ytlookup_result.txt")) {
|
|
|
|
|
|
|
|
var debugRename = 'export/${datestamp()}_${StringTools.lpad(Std.string(Date.now().getHours()), "0", 2)}-${StringTools.lpad(Std.string(Date.now().getMinutes()), "0", 2)}-${StringTools.lpad(Std.string(Date.now().getSeconds()), "0", 2)}_ytdebug.txt';
|
|
|
|
|
|
|
|
FileSystem.rename("export/ytlookup_result.txt", debugRename);
|
|
|
|
|
|
|
|
Sys.println('[${timestamp()}] ytdebug: Created debug file ${debugRename}');
|
|
|
|
|
|
|
|
m.react('✅');
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
Sys.println('[${timestamp()}] ytdebug: No result to debug');
|
|
|
|
|
|
|
|
m.reply({content:"ytdebug: No result to debug"}, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
case "pirate":
|
|
|
|
|
|
|
|
if (msg.length > 0) {pvn("p",msg,m);}
|
|
|
|
|
|
|
|
case "ninja":
|
|
|
|
|
|
|
|
if (msg.length > 0) {pvn("n",msg,m);}
|
|
|
|
|
|
|
|
case "pvn":
|
|
|
|
|
|
|
|
if (msg.length > 0) {pvn("pvn",msg,m);}
|
|
|
|
|
|
|
|
case "nvp":
|
|
|
|
|
|
|
|
if (msg.length > 0) {pvn("nvp",msg,m);}
|
|
|
|
|
|
|
|
// Gaming functions
|
|
|
|
|
|
|
|
case "coin":
|
|
|
|
|
|
|
|
if ((msg.length == 0) || (Std.parseInt(msg) == null) || (Std.parseInt(msg) == 1)) {
|
|
|
|
|
|
|
|
var coin = randInt(0,1);
|
|
|
|
|
|
|
|
if (coin == 1) {
|
|
|
|
|
|
|
|
m.reply({content:'${sender} flipped a coin and got heads.'}, false);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
m.reply({content:'${sender} flipped a coin and got tails.'}, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if ((Std.parseInt(msg) > 1) && (Std.parseInt(msg) < 101)) {
|
|
|
|
|
|
|
|
var heads:Int = 0;
|
|
|
|
|
|
|
|
for (i in 1...(Std.parseInt(msg)+1)) {
|
|
|
|
|
|
|
|
heads += randInt(0,1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
m.reply({content:'${sender} flipped ${Std.parseInt(msg)} coins.\n**Heads:** ${heads}\n**Tails:** ${Std.parseInt(msg)-heads}'}, false);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
m.reply({content:'Please enter a value between 1 and 100.'}, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
case "dice":
|
|
|
|
|
|
|
|
var sides:Int = 6;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set number of sides
|
|
|
|
|
|
|
|
if (args.length != 0) {
|
|
|
|
|
|
|
|
if (Std.parseInt(args[0]) != null) {
|
|
|
|
|
|
|
|
sides = Std.parseInt(args[0]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Validate sides
|
|
|
|
|
|
|
|
if (sides == 1) {
|
|
|
|
|
|
|
|
m.reply({content:'${sender} rolled a ball or something, idk.'}, false);
|
|
|
|
|
|
|
|
} else if (sides == 2) {
|
|
|
|
|
|
|
|
m.reply({content:'${sender} tried to roll a 2-sided die (this is called a coin btw).'}, false);
|
|
|
|
|
|
|
|
} else if ((sides < 1) || (sides > 100)) {
|
|
|
|
|
|
|
|
m.reply({content:'Please enter a value between 1 and 100.'}, false);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// Roll a single die
|
|
|
|
|
|
|
|
if ((msg.length == 0) || (Std.parseInt(msg) == null) || (Std.parseInt(msg) == 1)) {
|
|
|
|
|
|
|
|
m.reply({content:'${sender} rolled a ${sides}-sided die and got ${randInt(1,sides)}.'}, false);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// Roll multiple dice
|
|
|
|
|
|
|
|
if ((Std.parseInt(msg) > 1) && (Std.parseInt(msg) < 101)) {
|
|
|
|
|
|
|
|
var results:Array<Int> = [];
|
|
|
|
|
|
|
|
for (i in 1...(Std.parseInt(msg)+1)) {
|
|
|
|
|
|
|
|
results.push(randInt(1,sides));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort results high-to-low
|
|
|
|
|
|
|
|
results.sort((a, b) -> b - a);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m.reply({content:'${sender} rolled a ${sides}-sided die ${Std.parseInt(msg)} times.\n**Results:** ${results.toString()}'}, false);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
m.reply({content:'Please enter a value between 1 and 100.'}, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
case "8ball":
|
|
|
|
|
|
|
|
var response:Array<String> = ["It is certain.","It is decidedly so.","Without a doubt.","Yes definitely.","You may rely on it.","As I see it, yes.","Most likely.","Outlook good.","Yes.","Signs point to yes.","Reply hazy, try again.","Ask again later.","Better not tell you now.","Cannot predict now.","Concentrate and ask again.","Don't count on it.","My reply is no.","My sources say no.","Outlook not so good.","Very doubtful."];
|
|
|
|
|
|
|
|
m.reply({content:'🎱 ${response[randInt(0,19)]} 🎱'}, false);
|
|
|
|
// Basic aliases
|
|
|
|
// Basic aliases
|
|
|
|
case "angery":
|
|
|
|
case "angery":
|
|
|
|
m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1079518504413311108/angery.jpg"}, false);
|
|
|
|
m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1079518504413311108/angery.jpg"}, false);
|
|
|
@@ -159,19 +330,35 @@ class Onequestionmark {
|
|
|
|
m.reply({content:"https://cdn.discordapp.com/attachments/270113422232911883/502690458779123722/the_cube.jpg"}, false);
|
|
|
|
m.reply({content:"https://cdn.discordapp.com/attachments/270113422232911883/502690458779123722/the_cube.jpg"}, false);
|
|
|
|
case "coolsville":
|
|
|
|
case "coolsville":
|
|
|
|
m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1147583765212835921/coolsville.gif"}, false);
|
|
|
|
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":
|
|
|
|
case "communism":
|
|
|
|
m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1147590229960691742/communism.gif"}, false);
|
|
|
|
m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1147590229960691742/communism.gif"}, false);
|
|
|
|
|
|
|
|
case "opinions":
|
|
|
|
|
|
|
|
m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1147983375701921892/opinions.jpg"}, false);
|
|
|
|
|
|
|
|
case "fineart":
|
|
|
|
|
|
|
|
m.reply({content:"https://cdn.discordapp.com/attachments/270113422232911883/423323629116325898/unknown.png"}, false);
|
|
|
|
|
|
|
|
case "artishard":
|
|
|
|
|
|
|
|
m.reply({content:"https://cdn.discordapp.com/attachments/270113422232911883/356638565834424330/tumblr_nrxhzoF0KM1t75ioqo2_250.jpg"}, false);
|
|
|
|
|
|
|
|
case "indeed":
|
|
|
|
|
|
|
|
m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1305252892491649145/indeed.gif"}, false);
|
|
|
|
|
|
|
|
case "florida":
|
|
|
|
|
|
|
|
m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1305253566348529696/florida.gif"}, false);
|
|
|
|
|
|
|
|
case "stop":
|
|
|
|
|
|
|
|
m.reply({content:"https://cdn.discordapp.com/attachments/1071547517847732305/1305253985036275712/stop.jpg"}, false);
|
|
|
|
// Image database commands
|
|
|
|
// Image database commands
|
|
|
|
case "hug":
|
|
|
|
case "hug":
|
|
|
|
|
|
|
|
if (hugDB.length > 0) {
|
|
|
|
m.reply({content:'🫂 *hugs ${msg}*\n${hugDB[randInt(0, hugDB.length-1)]}'}, false);
|
|
|
|
m.reply({content:'🫂 *hugs ${msg}*\n${hugDB[randInt(0, hugDB.length-1)]}'}, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
case "yes":
|
|
|
|
case "yes":
|
|
|
|
var yes = yesnoDB.yes;
|
|
|
|
var yes = yesnoDB.yes;
|
|
|
|
|
|
|
|
if (yes.length > 0) {
|
|
|
|
m.reply({content:'${yes[randInt(0, yes.length-1)]}'}, false);
|
|
|
|
m.reply({content:'${yes[randInt(0, yes.length-1)]}'}, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
case "no":
|
|
|
|
case "no":
|
|
|
|
var no = yesnoDB.no;
|
|
|
|
var no = yesnoDB.no;
|
|
|
|
|
|
|
|
if (no.length > 0) {
|
|
|
|
m.reply({content:'${no[randInt(0, no.length-1)]}'}, false);
|
|
|
|
m.reply({content:'${no[randInt(0, no.length-1)]}'}, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
// Echobox
|
|
|
|
// Echobox
|
|
|
|
case "echobox":
|
|
|
|
case "echobox":
|
|
|
|
// Create Echobox array for the server if missing
|
|
|
|
// Create Echobox array for the server if missing
|
|
|
@@ -183,13 +370,11 @@ class Onequestionmark {
|
|
|
|
var quote:String = "";
|
|
|
|
var quote:String = "";
|
|
|
|
|
|
|
|
|
|
|
|
// Echobox response
|
|
|
|
// Echobox response
|
|
|
|
if (!m.mention_everyone) {
|
|
|
|
if ((!m.mention_everyone) && (m.mentions.length == 0) && (m.mention_roles.length == 0)) {
|
|
|
|
if (echobox.length > 0) {
|
|
|
|
if (echobox.length > 0) {
|
|
|
|
var ebchoice = randInt(0, echobox.length-1);
|
|
|
|
var ebchoice = randInt(0, echobox.length-1);
|
|
|
|
//m.reply({content:'**Echobox, quote #${ebchoice+1}:** ${echobox[ebchoice]}'}, false);
|
|
|
|
|
|
|
|
quote = '**Echobox, quote #${ebchoice+1}:** ${echobox[ebchoice]}';
|
|
|
|
quote = '**Echobox, quote #${ebchoice+1}:** ${echobox[ebchoice]}';
|
|
|
|
} else {
|
|
|
|
} 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.';
|
|
|
|
quote = '**Echobox:** There are no quotes in this server\'s Echobox yet.';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -207,136 +392,356 @@ class Onequestionmark {
|
|
|
|
m.reply({content:quote}, false);
|
|
|
|
m.reply({content:quote}, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
m.reply({content:'**Echobox:** You cannot add `@everyone` to the Echobox.'}, false);
|
|
|
|
m.reply({content:'**Echobox:** You cannot add mentions to the Echobox.'}, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Non-command responses
|
|
|
|
// Non-command responses
|
|
|
|
if ((msg.charAt(0) == ".") && (msg.length == 1)) {m.reply({content:"omg a meteor"}, true);}
|
|
|
|
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 ((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);}
|
|
|
|
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)}';
|
|
|
|
* This function starts the "Meme of the Day" system.
|
|
|
|
}
|
|
|
|
* A temporary timer is set that counts down to the next MOTD event, at which point the main timer is started on a 24 hour loop.
|
|
|
|
|
|
|
|
*/
|
|
|
|
// Clean print day of week
|
|
|
|
public static function startMotd() {
|
|
|
|
public static function checkDay() {
|
|
|
|
var now = Date.now();
|
|
|
|
var day = "";
|
|
|
|
var nextPost = new Date(now.getFullYear(), now.getMonth(), now.getDate(), settings.motd.time, 0, 0);
|
|
|
|
switch (Date.now().getDay()+1) {
|
|
|
|
if (now.getHours() >= settings.motd.time) {
|
|
|
|
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()) {
|
|
|
|
if (settings.motd.date != datestamp()) {
|
|
|
|
settings.motd.date = datestamp();
|
|
|
|
settings.motd.date = datestamp();
|
|
|
|
settings.motd.posted = false;
|
|
|
|
postMotd();
|
|
|
|
|
|
|
|
saveQueue.push("settings");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
nextPost = DateTools.delta(nextPost, 86400000); // Add one day in milliseconds
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((settings.motd.posted == false) && (Date.now().getHours() >= settings.motd.time)) {
|
|
|
|
// Calculate delay for timers
|
|
|
|
settings.motd.posted = true;
|
|
|
|
var delay = Std.int(nextPost.getTime() - now.getTime());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Timer.delay(function(){
|
|
|
|
|
|
|
|
settings.motd.date = datestamp();
|
|
|
|
|
|
|
|
postMotd();
|
|
|
|
saveQueue.push("settings");
|
|
|
|
saveQueue.push("settings");
|
|
|
|
|
|
|
|
|
|
|
|
var msg = "";
|
|
|
|
// Start the looped MOTD timer
|
|
|
|
var day:Array<String> = [];
|
|
|
|
motdTimer = new Timer(86400000);
|
|
|
|
|
|
|
|
motdTimer.run = function() {
|
|
|
|
// Check for exact date
|
|
|
|
settings.motd.date = datestamp();
|
|
|
|
if (motdDB.exists('${monthdate()}-${Date.now().getFullYear()}')) {
|
|
|
|
postMotd();
|
|
|
|
day = motdDB.get('${monthdate()}-${Date.now().getFullYear()}');
|
|
|
|
saveQueue.push("settings");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Else, check for special date
|
|
|
|
}, delay);
|
|
|
|
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)];}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Sends the MOTD to the channels specified in the bot settings.
|
|
|
|
|
|
|
|
* May just move this code into the places it's needed instead of keeping this function.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static function postMotd() {
|
|
|
|
|
|
|
|
var msg = getMotd(true);
|
|
|
|
var channels:Array<String> = settings.motd.channels; // Because it won't let me do this directly
|
|
|
|
var channels:Array<String> = 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);}}}
|
|
|
|
if (channels.length > 0) {for (i in channels) {if (msg.length > 0) {Endpoints.sendMessage(i, {content:msg}, null, false);}}}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* This function checks the MOTD database to find the entry most relevant to the current date and returns the appropriate data.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param update Whether the result should be stored to exclude from the next run.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static function getMotd(update:Bool = false) {
|
|
|
|
|
|
|
|
var msg = "";
|
|
|
|
|
|
|
|
var day:Array<String> = [];
|
|
|
|
|
|
|
|
var special = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check for exact date
|
|
|
|
|
|
|
|
if (motdDB.exists('${datestamp()}')) {
|
|
|
|
|
|
|
|
day = motdDB.get('${datestamp()}');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Else, check for special date
|
|
|
|
|
|
|
|
else if (motdDB.exists('${printMonth().substr(0,3)}${printDate()}')) {
|
|
|
|
|
|
|
|
day = motdDB.get('${printMonth().substr(0,3)}${printDate()}');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Else, check for special day/date combo
|
|
|
|
|
|
|
|
else if (motdDB.exists('${printDay().substr(0,3)}${printDate()}')) {
|
|
|
|
|
|
|
|
day = motdDB.get('${printDay().substr(0,3)}${printDate()}');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Else, check for just the date
|
|
|
|
|
|
|
|
else if (motdDB.exists('${printDate()}')) {
|
|
|
|
|
|
|
|
day = motdDB.get('${printDate()}');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, post daily meme
|
|
|
|
|
|
|
|
else if (motdDB.exists(printDay().substr(0,3))) {
|
|
|
|
|
|
|
|
day = motdDB.get(printDay().substr(0,3));
|
|
|
|
|
|
|
|
special = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Filesystem operations
|
|
|
|
if (day.length > 0) {
|
|
|
|
public static function saveSystem() {
|
|
|
|
if (update && !special && (day.length > 2)) {
|
|
|
|
if (saveTimer != Date.now().getMinutes()) { // We only want to run this once a minute
|
|
|
|
var today = Date.now().getDay();
|
|
|
|
|
|
|
|
var previous:Array<Int> = settings.motd.previous;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var choice = randIntExclude(0, day.length-1,[previous[today]]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
previous[today] = choice;
|
|
|
|
|
|
|
|
settings.motd.previous = previous;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
msg = day[choice];
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
msg = day[randInt(0, day.length-1)];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* This function performs the *Pirate vs Ninja* stat calculations and replies with the appropriate results.
|
|
|
|
|
|
|
|
* @param choice `p`, `n`, `pvn`, or `nvp`.
|
|
|
|
|
|
|
|
* @param query The name of the character. For dual-character choices, separate names with a pipe.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static function pvn(choice:String,query:String,m:Message) {
|
|
|
|
|
|
|
|
if (choice.length > 1) {
|
|
|
|
|
|
|
|
var splitQuery:Array<String> = [];
|
|
|
|
|
|
|
|
if (query.contains(" | ")) {
|
|
|
|
|
|
|
|
splitQuery = query.split(" | ");
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
splitQuery.push(query);
|
|
|
|
|
|
|
|
splitQuery.push(query);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (choice == "pvn") {
|
|
|
|
|
|
|
|
var pirate = getPirate(splitQuery[0]);
|
|
|
|
|
|
|
|
var ninja = getNinja(splitQuery[1]);
|
|
|
|
|
|
|
|
printPirate(pirate,m);
|
|
|
|
|
|
|
|
printNinja(ninja,m);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pirate.total > ninja.total) {
|
|
|
|
|
|
|
|
m.reply({content:'## ${pirate.rank} ${pirate.name} wins!'}, false);
|
|
|
|
|
|
|
|
} else if (pirate.total < ninja.total) {
|
|
|
|
|
|
|
|
m.reply({content:'## ${ninja.rank} ${ninja.name} wins!'}, false);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
m.reply({content:'## It\'s a TIE!'}, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (choice == "nvp") {
|
|
|
|
|
|
|
|
var ninja = getNinja(splitQuery[0]);
|
|
|
|
|
|
|
|
var pirate = getPirate(splitQuery[1]);
|
|
|
|
|
|
|
|
printNinja(ninja,m);
|
|
|
|
|
|
|
|
printPirate(pirate,m);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (pirate.total > ninja.total) {
|
|
|
|
|
|
|
|
m.reply({content:'## ${pirate.rank} ${pirate.name} wins!'}, false);
|
|
|
|
|
|
|
|
} else if (pirate.total < ninja.total) {
|
|
|
|
|
|
|
|
m.reply({content:'## ${ninja.rank} ${ninja.name} wins!'}, false);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
m.reply({content:'## It\'s a TIE!'}, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if (choice == "p") {printPirate(getPirate(query),m);}
|
|
|
|
|
|
|
|
if (choice == "n") {printNinja(getNinja(query),m);}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function getPirate(name) {
|
|
|
|
|
|
|
|
var pirate:Dynamic = {};
|
|
|
|
|
|
|
|
pirate.name = name;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var pweapons = ['Cutlass', 'Pistol', 'Broken Bottle', 'Cannon', 'Attack Parrot', 'Hook Hand', 'Belaying Pin', 'Dagger', 'Grappling Hook', 'A Mean Streak A Mile Wide'];
|
|
|
|
|
|
|
|
pirate.swash = (hashString(name, "swashbuckling", 20) + 1);
|
|
|
|
|
|
|
|
pirate.drunk = (hashString(name, 'drunkenness', 20) + 1);
|
|
|
|
|
|
|
|
pirate.booty = (hashString(name, 'booty', 20) + 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var weapons:Array<String> = [];
|
|
|
|
|
|
|
|
var wnum = (hashString(name, "pweapons", 5) - 1);
|
|
|
|
|
|
|
|
while (wnum > 0) {
|
|
|
|
|
|
|
|
var wsel = pweapons[hashString(name, '$wnum pweapon', pweapons.length - 1)];
|
|
|
|
|
|
|
|
if (!weapons.contains(wsel)) {weapons.push(wsel);}
|
|
|
|
|
|
|
|
wnum--;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pirate.weapons = "";
|
|
|
|
|
|
|
|
for (i in 0...weapons.length) {
|
|
|
|
|
|
|
|
if (Std.string(pirate.weapons).length != 0) {pirate.weapons += ", ";}
|
|
|
|
|
|
|
|
pirate.weapons = pirate.weapons + weapons[i];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pirate.total = pirate.swash + pirate.drunk + pirate.booty + weapons.length;
|
|
|
|
|
|
|
|
pirate.rank = "Cabin Boy";
|
|
|
|
|
|
|
|
if (pirate.total > 15) {pirate.rank = 'Pirate';}
|
|
|
|
|
|
|
|
if (pirate.total > 30) {pirate.rank = 'Dread Pirate';}
|
|
|
|
|
|
|
|
if (pirate.total > 45) {pirate.rank = 'Captain';}
|
|
|
|
|
|
|
|
if (pirate.total >= 60) {pirate.rank = 'Pirate King';}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return(pirate);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function getNinja(name) {
|
|
|
|
|
|
|
|
var ninja:Dynamic = {};
|
|
|
|
|
|
|
|
ninja.name = name;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var nweapons = ['Shuriken', 'Katana', 'Poison Dart', 'Death Touch', 'Ninja-To', 'Smoke Bomb', 'Thousand Blossom Finger', 'A Pointy Stick', 'Jo Stick', 'Nunchaku'];
|
|
|
|
|
|
|
|
ninja.sneak = (hashString(name, "sneakiness", 20) + 1);
|
|
|
|
|
|
|
|
ninja.peejs = (hashString(name, 'pajamas', 20) + 1);
|
|
|
|
|
|
|
|
ninja.point = (hashString(name, 'pointy things', 20) + 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var weapons:Array<String> = [];
|
|
|
|
|
|
|
|
var wnum = (hashString(name, "nweapons", 5) - 1);
|
|
|
|
|
|
|
|
while (wnum > 0) {
|
|
|
|
|
|
|
|
var wsel = nweapons[hashString(name, '$wnum nweapon', nweapons.length - 1)];
|
|
|
|
|
|
|
|
if (!weapons.contains(wsel)) {weapons.push(wsel);}
|
|
|
|
|
|
|
|
wnum--;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ninja.weapons = "";
|
|
|
|
|
|
|
|
for (i in 0...weapons.length) {
|
|
|
|
|
|
|
|
if (Std.string(ninja.weapons).length != 0) {ninja.weapons += ", ";}
|
|
|
|
|
|
|
|
ninja.weapons = ninja.weapons + weapons[i];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ninja.total = ninja.sneak + ninja.peejs + ninja.point + weapons.length;
|
|
|
|
|
|
|
|
ninja.rank = "Apprentice";
|
|
|
|
|
|
|
|
if (ninja.total > 15) {ninja.rank = 'Ninja';}
|
|
|
|
|
|
|
|
if (ninja.total > 30) {ninja.rank = 'Deadly Ninja';}
|
|
|
|
|
|
|
|
if (ninja.total > 45) {ninja.rank = 'Ninja Master';}
|
|
|
|
|
|
|
|
if (ninja.total >= 60) {ninja.rank = 'Grandmaster';}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return(ninja);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function printPirate(pirate, m:Message) {
|
|
|
|
|
|
|
|
m.reply({content:'## ${pirate.rank} ${pirate.name}\nSwashbuckling: ${pirate.swash}\nDrunkenness: ${pirate.drunk}\nBooty: ${pirate.booty}\nWeapons: ${pirate.weapons}\n**Total: ${pirate.total}**'}, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static function printNinja(ninja, m:Message) {
|
|
|
|
|
|
|
|
m.reply({content:'## ${ninja.rank} ${ninja.name}\nSneakiness: ${ninja.sneak}\nPajamas: ${ninja.peejs}\nPointy Things: ${ninja.point}\nWeapons: ${ninja.weapons}\n**Total: ${ninja.total}**'}, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Marc Majcher's string hasher from *Pirate vs Ninja!*
|
|
|
|
|
|
|
|
* @param input The input string.
|
|
|
|
|
|
|
|
* @param seed An identifying salt to make strings come out differently for different, but same-sized lists.
|
|
|
|
|
|
|
|
* @param num The number of buckets you wish to hash into.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static function hashString(input:String, seed:String, num:BigInt) {
|
|
|
|
|
|
|
|
var count:BigInt = 0;
|
|
|
|
|
|
|
|
var total:BigInt = 0;
|
|
|
|
|
|
|
|
var xorResult = "";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Pad strings until they're equal in length
|
|
|
|
|
|
|
|
input = StringTools.rpad(input, String.fromCharCode(0), seed.length);
|
|
|
|
|
|
|
|
seed = StringTools.rpad(seed, String.fromCharCode(0), input.length);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// XOR the input and seed
|
|
|
|
|
|
|
|
for (i in 0...(input.length)) {
|
|
|
|
|
|
|
|
xorResult = xorResult + String.fromCharCode(input.charCodeAt(i) ^ seed.charCodeAt(i));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calculate stats
|
|
|
|
|
|
|
|
for (i in 0...xorResult.length) {
|
|
|
|
|
|
|
|
total += (xorResult.charCodeAt(i):BigInt) * ((128:BigInt).pow(count));
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return(((total * (total + (523:BigInt))) % num).toInt());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* This function performs a YouTube search and replies with the first result.
|
|
|
|
|
|
|
|
* @param m The message data.
|
|
|
|
|
|
|
|
* @param query The string to search.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static function ytlookup(m:Message,query:String) {
|
|
|
|
|
|
|
|
var req = new haxe.Http('https://www.youtube.com/results?search_query=${query.urlEncode()}');
|
|
|
|
|
|
|
|
Sys.println('[${timestamp()}] ytlookup: URL - https://www.youtube.com/results?search_query=${query.urlEncode()}');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
req.onData = function (request) {
|
|
|
|
|
|
|
|
File.saveContent("export/ytlookup_result.txt", request);
|
|
|
|
|
|
|
|
var data = new HtmlDocument(request);
|
|
|
|
|
|
|
|
var search = data.find("script"); // YouTube obfuscates everything into JS garbage so we have to check every <script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var gotcha = "";
|
|
|
|
|
|
|
|
var result = "Error: Unable to parse YouTube search result.";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (i in search) {
|
|
|
|
|
|
|
|
if (i.toString().contains("\"videoRenderer\":{\"videoId\":\"") || i.toString().contains("\"reelWatchEndpoint\":{\"videoId\":\"")) {
|
|
|
|
|
|
|
|
gotcha = i.toString();
|
|
|
|
|
|
|
|
if ((i.toString().contains("\"reelWatchEndpoint\":{\"videoId\":\"")) && (gotcha.indexOf("\"reelWatchEndpoint\":{\"videoId\":\"") < gotcha.indexOf("\"videoRenderer\":{\"videoId\":\""))) {
|
|
|
|
|
|
|
|
// Top result is a Short
|
|
|
|
|
|
|
|
result = "https://youtu.be/" + gotcha.substring(gotcha.indexOf("\"reelWatchEndpoint\":{\"videoId\":\"")+32, gotcha.indexOf("\"", gotcha.indexOf("\"reelWatchEndpoint\":{\"videoId\":\"")+32));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sys.println('[${timestamp()}] ytlookup: Result - $result (Short)');
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// Top result is a normal video
|
|
|
|
|
|
|
|
result = "https://youtu.be/" + gotcha.substring(gotcha.indexOf("\"videoRenderer\":{\"videoId\":\"")+28, gotcha.indexOf("\"", gotcha.indexOf("\"videoRenderer\":{\"videoId\":\"")+28));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sys.println('[${timestamp()}] ytlookup: Result - $result (Normal)');
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m.reply({content:result}, false);
|
|
|
|
|
|
|
|
m.reply({content:"-# YouTube Lookup is still in beta. If the result seems inaccurate, please use the command `?ytdebug` so the developer can review the data that was received."}, false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
req.onError = function (error) {
|
|
|
|
|
|
|
|
m.reply({content:'Error in YouTube Lookup: $error'}, false);
|
|
|
|
|
|
|
|
Sys.println('[${timestamp()}] ytlookup: Error - $error, request was $query');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
req.request();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Retrieves a specific message in the channel and returns it as a JSON object.
|
|
|
|
|
|
|
|
* @param channel_id Numerical ID of the channel.
|
|
|
|
|
|
|
|
* @param m_id Numerical ID of the message.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static function getMessage(channel_id:String, m_id:String) {
|
|
|
|
|
|
|
|
var req:hxdiscord.utils.Http = new hxdiscord.utils.Http("https://discord.com/api/v"+hxdiscord.gateway.Gateway.API_VERSION+"/channels/"+channel_id+"/messages/"+m_id);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
req.addHeader("User-Agent", "hxdiscord (https://github.com/FurretDev/hxdiscord)");
|
|
|
|
|
|
|
|
req.addHeader("Authorization", DiscordClient.authHeader);
|
|
|
|
|
|
|
|
req.setMethod("GET");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var msg:Dynamic = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
req.onData = function(data:String) {
|
|
|
|
|
|
|
|
msg = haxe.Json.parse(data);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
req.onError = function(error) {
|
|
|
|
|
|
|
|
Sys.println('[${timestamp()}] getMessage: Error - $error');
|
|
|
|
|
|
|
|
Sys.println('[${timestamp()}] getMessage: ${req.responseData}');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
req.send();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* The `saveSystem()` function handles all filesystem writes. When the bot needs to save a file, it pushes data to the `saveQueue` array.
|
|
|
|
|
|
|
|
* The save system checks the queue regularly and operates on the first entry provided.
|
|
|
|
|
|
|
|
* This throttles filesystem access, to prevent multiple commands from writing to the drive at the same time.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static function saveSystem() {
|
|
|
|
|
|
|
|
Timer.delay(function() {
|
|
|
|
|
|
|
|
saveTimer = new Timer(60*1000);
|
|
|
|
|
|
|
|
saveTimer.run = function() {
|
|
|
|
if (saveQueue.length > 0) { // See if there's anything in the queue
|
|
|
|
if (saveQueue.length > 0) { // See if there's anything in the queue
|
|
|
|
switch (saveQueue.shift()) {
|
|
|
|
switch (saveQueue.shift()) {
|
|
|
|
case "settings":
|
|
|
|
case "settings":
|
|
|
@@ -347,25 +752,30 @@ class Onequestionmark {
|
|
|
|
Sys.println('[${timestamp()}] Saved echobox-db.json');
|
|
|
|
Sys.println('[${timestamp()}] Saved echobox-db.json');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
saveTimer = Date.now().getMinutes(); // Update timer
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}, (60-Date.now().getSeconds())*1000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Writes the modified `settings.json` from memory back to the drive.
|
|
|
|
|
|
|
|
*/
|
|
|
|
public static function saveSettings() {
|
|
|
|
public static function saveSettings() {
|
|
|
|
File.saveContent("settings.json", Json.stringify(settings, "\t"));
|
|
|
|
File.saveContent("settings.json", Json.stringify(settings, "\t"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Writes the modified `echobox-db.json` from memory back to the drive.
|
|
|
|
|
|
|
|
*/
|
|
|
|
public static function saveEchoboxDB() {
|
|
|
|
public static function saveEchoboxDB() {
|
|
|
|
File.saveContent("echobox-db.json", Json.stringify(echoboxDB, "\t"));
|
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Performs a clean shutdown of the bot, after saving relevant files to the disk.
|
|
|
|
|
|
|
|
*/
|
|
|
|
public static function shutdown() {
|
|
|
|
public static function shutdown() {
|
|
|
|
saveSettings();
|
|
|
|
saveSettings();
|
|
|
|
saveEchoboxDB();
|
|
|
|
saveEchoboxDB();
|
|
|
|