|
|
|
@@ -6,6 +6,7 @@ import haxe.Timer;
|
|
|
|
|
import haxe.Json;
|
|
|
|
|
import sys.io.File;
|
|
|
|
|
import sys.FileSystem;
|
|
|
|
|
import BigInt;
|
|
|
|
|
import izzcomlib.IzzComLib.*;
|
|
|
|
|
using StringTools;
|
|
|
|
|
|
|
|
|
@@ -14,7 +15,7 @@ class Onequestionmark {
|
|
|
|
|
static var Bot:DiscordClient;
|
|
|
|
|
static var settings: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 motdDB:haxe.DynamicAccess<Dynamic> = {};
|
|
|
|
@@ -173,6 +174,15 @@ class Onequestionmark {
|
|
|
|
|
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
|
|
|
|
|
case "wipcommand":
|
|
|
|
|
if (m.guild_id == settings.devserver) {
|
|
|
|
@@ -195,22 +205,27 @@ class Onequestionmark {
|
|
|
|
|
"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:",
|
|
|
|
|
"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:",
|
|
|
|
|
"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`?quit`: Shutdown command",
|
|
|
|
|
"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.\n**Icce**: Bot provides users with ice cuboids. :ice_cube:",
|
|
|
|
|
"value": "**MOTD**: *Meme of the Day* is posted in enabled channels.",
|
|
|
|
|
"inline": true
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
@@ -236,13 +251,6 @@ class Onequestionmark {
|
|
|
|
|
Sys.println('[${timestamp()}] ytdebug: No result to debug');
|
|
|
|
|
m.reply({content:"ytdebug: No result to debug"}, false);
|
|
|
|
|
}
|
|
|
|
|
case "trace":
|
|
|
|
|
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('✅');
|
|
|
|
|
case "pirate":
|
|
|
|
|
if (msg.length > 0) {pvn("p",msg,m);}
|
|
|
|
|
case "ninja":
|
|
|
|
@@ -391,7 +399,7 @@ class Onequestionmark {
|
|
|
|
|
|
|
|
|
|
// 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 ((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);}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -497,50 +505,153 @@ class Onequestionmark {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This function scrapes PVN and replies with the appropriate results.
|
|
|
|
|
* 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 string to search.
|
|
|
|
|
*/
|
|
|
|
|
* @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(" | ")) {
|
|
|
|
|
var splitQuery = query.split(" | ");
|
|
|
|
|
query = '${splitQuery[0].urlEncode()}&n2=${splitQuery[1].urlEncode()}';
|
|
|
|
|
splitQuery = query.split(" | ");
|
|
|
|
|
} else {
|
|
|
|
|
m.reply({content:'$choice: Must choose two combatants.'}, false);
|
|
|
|
|
return;
|
|
|
|
|
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 {
|
|
|
|
|
query = query.urlEncode();
|
|
|
|
|
if (choice == "p") {printPirate(getPirate(query),m);}
|
|
|
|
|
if (choice == "n") {printNinja(getNinja(query),m);}
|
|
|
|
|
}
|
|
|
|
|
var req = new haxe.Http('https://majcher.com/project/pvn/pvn.cgi?a=${choice.urlEncode()}&n1=${query}');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.onData = function (request) {
|
|
|
|
|
var data = new HtmlDocument(request);
|
|
|
|
|
var search = data.find("table>tr>td");
|
|
|
|
|
public static function getPirate(name) {
|
|
|
|
|
var pirate:Dynamic = {};
|
|
|
|
|
pirate.name = name;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
m.reply({content:'## ${search[0].innerText}\n${search[2].innerText} ${search[3].innerText}\n${search[4].innerText} ${search[5].innerText}\n${search[6].innerText} ${search[7].innerText}\n${search[8].innerText} ${search[9].innerText}\n**${search[10].innerText} ${search[11].innerText}**'}, false);
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
if (choice.length > 1) {
|
|
|
|
|
var result = data.find('div#result');
|
|
|
|
|
|
|
|
|
|
m.reply({content:'## ${search[12].innerText}\n${search[14].innerText} ${search[15].innerText}\n${search[16].innerText} ${search[17].innerText}\n${search[18].innerText} ${search[19].innerText}\n${search[20].innerText} ${search[21].innerText}\n**${search[22].innerText} ${search[23].innerText}**'}, false);
|
|
|
|
|
|
|
|
|
|
m.reply({content:'## ${result[0].innerText}'}, false);
|
|
|
|
|
}
|
|
|
|
|
} catch(e) {
|
|
|
|
|
m.reply({content:'$choice: Error - $e'}, false);
|
|
|
|
|
Sys.println('[${timestamp()}] $choice: Error - $e');
|
|
|
|
|
}
|
|
|
|
|
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--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.onError = function (error) {
|
|
|
|
|
m.reply({content:'Error in PVN: $error'}, false);
|
|
|
|
|
Sys.println('[${timestamp()}] $choice: Error - $error, request was $query');
|
|
|
|
|
pirate.weapons = "";
|
|
|
|
|
for (i in 0...weapons.length) {
|
|
|
|
|
if (Std.string(pirate.weapons).length != 0) {pirate.weapons += ", ";}
|
|
|
|
|
pirate.weapons = pirate.weapons + weapons[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.request();
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|