2021-01-07 05:34:14 -08:00
/ * T h e A r t i f i c e r w a s b u i l t i n m e m o r y o f B a b k a
* With love , Ean
*
* December 21 , 2020
* /
2021-01-07 11:00:46 -08:00
// DEVMODE is to prevent users from accessing parts of the bot that are currently broken
2021-01-07 05:34:14 -08:00
const DEVMODE = false ;
2021-01-07 11:00:46 -08:00
// DEBUG is used to toggle the cmdPrompt
const DEBUG = true ;
2021-01-07 05:34:14 -08:00
import {
startBot , editBotsStatus ,
Intents , StatusTypes , ActivityType ,
Message , Guild , sendMessage , sendDirectMessage ,
cache
} from "https://deno.land/x/discordeno@10.0.0/mod.ts" ;
import utils from "./src/utils.ts" ;
import solver from "./src/solver.ts" ;
import config from "./config.ts" ;
startBot ( {
token : config.token ,
intents : [ Intents . GUILD_MESSAGES , Intents . DIRECT_MESSAGES , Intents . GUILDS ] ,
eventHandlers : {
ready : ( ) = > {
console . log ( "Logged in!" ) ;
editBotsStatus ( StatusTypes . Online , ` ${ config . prefix } help for commands ` , ActivityType . Game ) ;
2021-01-07 11:00:46 -08:00
// setTimeout added to make sure the startup message does not error out
2021-01-07 05:34:14 -08:00
setTimeout ( ( ) = > {
sendMessage ( config . logChannel , ` ${ config . name } has started, running version ${ config . version } . ` ) . catch ( ( ) = > {
console . error ( "Failed to send message 00" ) ;
} ) ;
} , 1000 ) ;
} ,
guildCreate : ( guild : Guild ) = > {
sendMessage ( config . logChannel , ` New guild joined: ${ guild . name } (id: ${ guild . id } ). This guild has ${ guild . memberCount } members! ` ) . catch ( ( ) = > {
console . error ( "Failed to send message 01" ) ;
} ) ;
} ,
guildDelete : ( guild : Guild ) = > {
sendMessage ( config . logChannel , ` I have been removed from: ${ guild . name } (id: ${ guild . id } ) ` ) . catch ( ( ) = > {
console . error ( "Failed to send message 02" ) ;
} ) ;
} ,
debug : ( DEVMODE ? console . error : ( ) = > { } ) ,
messageCreate : async ( message : Message ) = > {
// Ignore all other bots
if ( message . author . bot ) return ;
// Ignore all messages that are not commands
if ( message . content . indexOf ( config . prefix ) !== 0 ) return ;
// Split into standard command + args format
const args = message . content . slice ( config . prefix . length ) . trim ( ) . split ( / +/g ) ;
const command = args . shift ( ) ? . toLowerCase ( ) ;
// All commands below here
// [[ping
// Its a ping test, what else do you want.
if ( command === "ping" ) {
// Calculates ping between sending a message and editing it, giving a nice round-trip latency.
try {
const m = await utils . sendIndirectMessage ( message , "Ping?" , sendMessage , sendDirectMessage ) ;
m . edit ( ` Pong! Latency is ${ m . timestamp - message . timestamp } ms. ` ) ;
} catch ( err ) {
console . error ( "Failed to send message 10" , message , err ) ;
}
}
2021-01-07 22:02:38 -08:00
// [[rip [[memory
// Displays a short message I wanted to include
else if ( command === "rip" || command === "memory" ) {
utils . sendIndirectMessage ( message , "The Artificer was built in memory of my Grandmother, Babka\nWith much love, Ean\n\nDecember 21, 2020" , sendMessage , sendDirectMessage ) . catch ( err = > {
console . error ( "Failed to send message 11" , message , err ) ;
} ) ;
}
2021-01-07 05:34:14 -08:00
// [[help or [[h or [[?
// Help command, prints from help file
else if ( command === "help" || command === "h" || command === "?" ) {
utils . sendIndirectMessage ( message , config . help . join ( "\n" ) , sendMessage , sendDirectMessage ) . catch ( err = > {
console . error ( "Failed to send message 20" , message , err ) ;
} ) ;
}
2021-01-07 11:00:46 -08:00
// [[version or [[v
2021-01-07 05:34:14 -08:00
// Returns version of the bot
else if ( command === "version" || command === "v" ) {
utils . sendIndirectMessage ( message , ` My current version is ${ config . version } . ` , sendMessage , sendDirectMessage ) . catch ( err = > {
console . error ( "Failed to send message 30" , message , err ) ;
} ) ;
}
2021-01-07 11:00:46 -08:00
// [[popcat or [[pop or [[p
2021-01-07 05:34:14 -08:00
// popcat animated emoji
2021-01-07 11:00:46 -08:00
else if ( command === "popcat" || command === "pop" || command === "p" ) {
2021-01-07 05:34:14 -08:00
utils . sendIndirectMessage ( message , ` < ${ config . emojis . popcat . animated ? "a" : "" } : ${ config . emojis . popcat . name } : ${ config . emojis . popcat . id } > ` , sendMessage , sendDirectMessage ) . catch ( err = > {
console . error ( "Failed to send message 40" , message , err ) ;
} ) ;
message . delete ( ) . catch ( err = > {
console . error ( "Failed to delete message 41" , message , err ) ;
} ) ;
}
// [[report or [[r (command that failed)
// Manually report a failed roll
else if ( command === "report" || command === "r" ) {
2021-01-07 11:00:46 -08:00
sendMessage ( config . reportChannel , ( "USER REPORT:\n" + args . join ( " " ) ) ) . catch ( err = > {
2021-01-07 05:34:14 -08:00
console . error ( "Failed to send message 50" , message , err ) ;
} ) ;
utils . sendIndirectMessage ( message , "Failed command has been reported to my developer." , sendMessage , sendDirectMessage ) . catch ( err = > {
console . error ( "Failed to send message 51" , message , err ) ;
} ) ;
}
// [[stats or [[s
// Displays stats on the bot
else if ( command === "stats" || command === "s" ) {
utils . sendIndirectMessage ( message , ` ${ config . name } is rolling dice for ${ cache . members . size } users, in ${ cache . channels . size } channels of ${ cache . guilds . size } servers. ` , sendMessage , sendDirectMessage ) . catch ( err = > {
console . error ( "Failed to send message 60" , message , err ) ;
} ) ;
}
// [[
// Dice rolling commence!
else {
2021-01-07 11:00:46 -08:00
// If DEVMODE is on, only allow this command to be used in the devServer
if ( DEVMODE && message . guildID !== config . devServer ) {
2021-01-07 05:34:14 -08:00
utils . sendIndirectMessage ( message , "Command is in development, please try again later." , sendMessage , sendDirectMessage ) . catch ( err = > {
console . error ( "Failed to send message 70" , message , err ) ;
} ) ;
return ;
}
2021-01-07 11:00:46 -08:00
// Rest of this command is in a try-catch to protect all sends/edits from erroring out
2021-01-07 05:34:14 -08:00
try {
const m = await utils . sendIndirectMessage ( message , "Rolling..." , sendMessage , sendDirectMessage ) ;
const modifiers = {
noDetails : false ,
spoiler : "" ,
maxRoll : false ,
nominalRoll : false ,
gmRoll : false ,
gms : < string [ ] > [ ]
} ;
2021-01-07 11:00:46 -08:00
// Check if any of the args are command flags and pull those out into the modifiers object
2021-01-07 05:34:14 -08:00
for ( let i = 0 ; i < args . length ; i ++ ) {
2021-01-07 11:00:46 -08:00
switch ( args [ i ] . toLowerCase ( ) ) {
2021-01-07 05:34:14 -08:00
case "-nd" :
modifiers . noDetails = true ;
args . splice ( i , 1 ) ;
i -- ;
break ;
case "-s" :
modifiers . spoiler = "||" ;
args . splice ( i , 1 ) ;
i -- ;
break ;
case "-m" :
modifiers . maxRoll = true ;
args . splice ( i , 1 ) ;
i -- ;
break ;
case "-n" :
modifiers . nominalRoll = true ;
args . splice ( i , 1 ) ;
i -- ;
break ;
case "-gm" :
modifiers . gmRoll = true ;
2021-01-07 11:00:46 -08:00
// -gm is a little more complex, as we must get all of the GMs that need to be DMd
2021-01-07 22:02:38 -08:00
while ( ( ( i + 1 ) < args . length ) && args [ i + 1 ] . startsWith ( "<@" ) ) {
2021-01-07 11:00:46 -08:00
// Keep looping thru the rest of the args until one does not start with the discord mention code
2021-01-07 22:02:38 -08:00
modifiers . gms . push ( args [ i + 1 ] . replace ( /[!]/g , "" ) ) ;
2021-01-07 05:34:14 -08:00
args . splice ( ( i + 1 ) , 1 ) ;
}
if ( modifiers . gms . length < 1 ) {
2021-01-07 11:00:46 -08:00
// If -gm is on and none were found, throw an error
2021-01-07 05:34:14 -08:00
m . edit ( "Error: Must specifiy at least one GM by mentioning them" ) ;
return ;
}
args . splice ( i , 1 ) ;
i -- ;
break ;
default :
break ;
}
}
2021-01-07 11:00:46 -08:00
// maxRoll and nominalRoll cannot both be on, throw an error
if ( modifiers . maxRoll && modifiers . nominalRoll ) {
m . edit ( "Error: Cannot maximise and nominise the roll at the same time" ) ;
return ;
}
2021-01-07 05:34:14 -08:00
2021-01-07 11:00:46 -08:00
// Rejoin all of the args and send it into the solver, if solver returns a falsy item, an error object will be substituded in
const rollCmd = command + " " + args . join ( " " ) ;
2021-01-07 05:34:14 -08:00
const returnmsg = solver . parseRoll ( rollCmd , config . prefix , config . postfix , modifiers . maxRoll , modifiers . nominalRoll ) || { error : true , errorMsg : "Error: Empty message" , line1 : "" , line2 : "" , line3 : "" } ;
2021-01-07 11:00:46 -08:00
2021-01-07 05:34:14 -08:00
let returnText = "" ;
2021-01-07 11:00:46 -08:00
// If there was an error, report it to the user in hopes that they can determine what they did wrong
2021-01-07 05:34:14 -08:00
if ( returnmsg . error ) {
returnText = returnmsg . errorMsg ;
2021-01-07 11:00:46 -08:00
m . edit ( returnText ) ;
return ;
2021-01-07 05:34:14 -08:00
} else {
2021-01-07 11:00:46 -08:00
// Else format the output using details from the solver
2021-01-07 05:34:14 -08:00
returnText = "<@" + message . author . id + ">" + returnmsg . line1 + "\n" + returnmsg . line2 ;
if ( modifiers . noDetails ) {
2021-01-07 11:00:46 -08:00
returnText += "\nDetails suppressed by -nd flag." ;
2021-01-07 05:34:14 -08:00
} else {
returnText += "\nDetails:\n" + modifiers . spoiler + returnmsg . line3 + modifiers . spoiler ;
}
}
2021-01-07 11:00:46 -08:00
// If the roll was a GM roll, send DMs to all the GMs
2021-01-07 05:34:14 -08:00
if ( modifiers . gmRoll ) {
2021-01-07 11:00:46 -08:00
// Make a new return line to be sent to the roller
2021-01-07 05:34:14 -08:00
const normalText = "<@" + message . author . id + ">" + returnmsg . line1 + "\nResults have been messaged to the following GMs: " + modifiers . gms . join ( " " ) ;
2021-01-07 11:00:46 -08:00
// And message the full details to each of the GMs, alerting roller of every GM that could not be messaged
2021-01-07 05:34:14 -08:00
modifiers . gms . forEach ( async e = > {
const msgs = utils . split2k ( returnText ) ;
const failedDMs = < string [ ] > [ ] ;
for ( let i = 0 ; ( ( failedDMs . indexOf ( e ) === - 1 ) && ( i < msgs . length ) ) ; i ++ ) {
2021-01-07 22:02:38 -08:00
await sendDirectMessage ( e . substr ( 2 , ( e . length - 3 ) ) , msgs [ i ] ) . catch ( ( ) = > {
2021-01-07 05:34:14 -08:00
failedDMs . push ( e ) ;
utils . sendIndirectMessage ( message , "WARNING: " + e + " could not be messaged. If this issue persists, make sure direct messages are allowed from this server." , sendMessage , sendDirectMessage ) ;
} ) ;
}
} ) ;
m . edit ( normalText ) ;
} else {
2021-01-07 11:00:46 -08:00
// When not a GM roll, make sure the message is not too big
2021-01-07 05:34:14 -08:00
if ( returnText . length > 2000 ) {
2021-01-07 11:00:46 -08:00
// If its too big, attempt to DM details to the roller
2021-01-07 05:34:14 -08:00
const msgs = utils . split2k ( returnText ) ;
let failed = false ;
for ( let i = 0 ; ( ! failed && ( i < msgs . length ) ) ; i ++ ) {
await sendDirectMessage ( message . author . id , msgs [ i ] ) . catch ( ( ) = > {
failed = true ;
} ) ;
}
2021-01-07 11:00:46 -08:00
// If DM fails to send, alert roller of the failure, else handle normally
2021-01-07 05:34:14 -08:00
if ( failed ) {
returnText = "<@" + message . author . id + ">" + returnmsg . line1 + "\n" + returnmsg . line2 + "\nDetails have been ommitted from this message for being over 2000 characters. WARNING: <@" + message . author . id + "> could **NOT** be messaged full details for verification purposes." ;
} else {
returnText = "<@" + message . author . id + ">" + returnmsg . line1 + "\n" + returnmsg . line2 + "\nDetails have been ommitted from this message for being over 2000 characters. Full details have been messaged to <@" + message . author . id + "> for verification purposes." ;
}
}
2021-01-07 11:00:46 -08:00
// Finally send the text
2021-01-07 05:34:14 -08:00
m . edit ( returnText ) ;
}
} catch ( err ) {
console . error ( "Something failed 71" ) ;
}
}
}
}
} ) ;
2021-01-07 11:00:46 -08:00
// Start up the command prompt for debug usage
if ( DEBUG ) {
utils . cmdPrompt ( config . logChannel , config . name , sendMessage ) ;
}