2021-12-03 13:05:21 +01:00
const { inspect } = require ( 'util' ) ;
2021-06-18 15:41:57 +02:00
const { Client } = require ( 'discord.js' ) ;
2021-06-19 15:06:20 +02:00
// eslint-disable-next-line no-unused-vars
const { TextChannel , GuildMember } = require ( './extensions' ) ;
2021-06-18 15:41:57 +02:00
const { Logger } = require ( '../logger' ) ;
const Modmail = require ( './Modmail' ) ;
const Registry = require ( './Registry' ) ;
2021-06-19 15:06:20 +02:00
const Resolver = require ( './Resolver' ) ;
2022-01-12 17:29:52 +01:00
const Cache = require ( './JsonCache' ) ;
2021-06-18 15:41:57 +02:00
class ModmailClient extends Client {
2021-10-22 09:35:04 +02:00
constructor ( options ) {
2021-06-18 15:41:57 +02:00
super ( options . clientOptions ) ;
this . _options = options ;
this . _ready = false ;
2021-06-19 15:06:20 +02:00
this . prefix = options . prefix ;
2021-06-18 15:41:57 +02:00
this . logger = new Logger ( this , options . loggerOptions ) ;
this . registry = new Registry ( this ) ;
2021-06-19 15:06:20 +02:00
this . resolver = new Resolver ( this ) ;
2021-06-20 13:12:01 +02:00
this . cache = new Cache ( this ) ;
this . modmail = new Modmail ( this ) ;
2021-06-18 15:41:57 +02:00
this . on ( 'ready' , ( ) => {
this . logger . info ( ` Client ready, logged in as ${ this . user . tag } ` ) ;
} ) ;
}
2021-10-22 09:35:04 +02:00
async init ( ) {
2021-06-18 15:41:57 +02:00
2021-06-19 15:06:20 +02:00
this . registry . loadCommands ( ) ;
this . on ( 'message' , this . handleMessage . bind ( this ) ) ;
2021-06-20 13:12:01 +02:00
this . cache . load ( ) ;
2021-06-19 15:06:20 +02:00
2021-06-18 15:41:57 +02:00
this . logger . info ( ` Logging in ` ) ;
2021-06-20 16:43:05 +02:00
const promise = this . ready ( ) ;
2021-06-18 15:41:57 +02:00
await this . login ( this . _options . discordToken ) ;
2021-06-20 16:43:05 +02:00
await promise ;
2021-06-19 15:06:20 +02:00
this . mainServer = this . guilds . cache . get ( this . _options . mainGuild ) ;
this . bansServer = this . guilds . cache . get ( this . _options . bansGuild ) || null ;
this . logger . info ( ` Starting up modmail handler ` ) ;
2021-07-19 21:30:31 +02:00
await this . modmail . init ( ) ;
2021-06-18 15:41:57 +02:00
2021-06-20 00:02:27 +02:00
process . on ( 'exit' , ( ) => {
2021-06-20 00:21:27 +02:00
this . logger . warn ( 'process exiting' ) ;
2021-10-22 09:35:04 +02:00
this . cache . savePersistentCache ( ) ;
2021-06-20 13:12:01 +02:00
this . cache . saveModmailHistory ( this . modmail ) ;
2021-06-20 00:02:27 +02:00
} ) ;
2021-06-19 20:25:42 +02:00
process . on ( 'SIGINT' , ( ) => {
2021-06-20 00:21:27 +02:00
this . logger . warn ( 'received sigint' ) ;
2021-10-22 09:35:04 +02:00
// this.cache.save();
// this.cache.saveModmailHistory(this.modmail);
2021-06-19 20:25:42 +02:00
// eslint-disable-next-line no-process-exit
process . exit ( ) ;
} ) ;
2021-06-18 15:41:57 +02:00
2021-11-29 11:25:38 +01:00
process . on ( 'unhandledRejection' , ( reason , prom ) => {
2022-01-12 17:46:06 +01:00
this . logger . error ( ` Unhandled promise rejection at: ${ inspect ( prom ) } \n Reason: ${ reason } ` ) ;
2021-11-29 11:25:38 +01:00
} ) ;
2021-06-18 15:41:57 +02:00
this . _ready = true ;
2021-11-29 11:48:45 +01:00
await this . modmail . reminderChannel . send ( ` Modmail bot booted and ready. ` ) ;
2021-06-18 15:41:57 +02:00
2021-06-19 15:06:20 +02:00
}
2021-10-22 09:35:04 +02:00
ready ( ) {
2021-06-19 15:06:20 +02:00
return new Promise ( ( resolve ) => {
2022-03-23 13:39:31 +01:00
if ( this . _ready ) return resolve ( ) ;
2021-06-19 15:06:20 +02:00
this . once ( 'ready' , resolve ) ;
} ) ;
2021-06-18 15:41:57 +02:00
}
2021-10-22 09:35:04 +02:00
async handleMessage ( message ) {
2021-06-18 15:41:57 +02:00
2021-06-19 15:06:20 +02:00
if ( ! this . _ready ) return ;
if ( message . author . bot ) return ;
// No command handling in dms, at least for now
2021-06-20 00:21:27 +02:00
if ( ! message . guild ) try {
return this . modmail . handleUser ( message ) ;
} catch ( err ) {
this . logger . error ( ` Error during user handle: \n ${ err . stack } ` ) ;
return ;
}
2021-06-18 15:41:57 +02:00
2021-06-19 15:06:20 +02:00
const { prefix } = this ;
const { channel , guild , content , member } = message ;
2021-10-22 09:35:04 +02:00
if ( ! [ this . mainServer . id , this . bansServer ? . id || '0' ] . includes ( guild . id ) ) return ;
2021-06-19 15:06:20 +02:00
if ( ! content || ! content . startsWith ( prefix ) ) return ;
const roles = member . roles . cache . map ( ( r ) => r . id ) ;
2021-10-22 09:35:04 +02:00
if ( ! roles . some ( ( r ) => this . _options . staffRoles . includes ( r ) ) && ! member . hasPermission ( 'ADMINISTRATOR' ) ) return ;
2021-06-19 15:06:20 +02:00
2021-10-22 09:35:04 +02:00
const [ rawCommand , ... args ] = content . split ( ' ' ) ;
2021-06-20 13:12:01 +02:00
const commandName = rawCommand . substring ( prefix . length ) ;
2021-06-19 15:06:20 +02:00
const command = this . registry . find ( commandName ) ;
if ( ! command ) return ;
2021-12-22 10:30:50 +01:00
message . _caller = commandName . toLowerCase ( ) ;
2021-06-19 15:06:20 +02:00
if ( command . showUsage && ! args . length ) {
let helpStr = ` ** ${ command . name } ** \n Usage: ${ this . prefix } ${ command . name } ${ command . usage } ` ;
if ( command . aliases ) helpStr += ` \n Aliases: ${ command . aliases . join ( ', ' ) } ` ;
2021-12-02 18:20:30 +01:00
return channel . send ( helpStr ) . catch ( err => this . logger . error ( ` Client.handleMessage errored at channel.send: \n ${ err . stack } ` ) ) ;
2021-06-19 15:06:20 +02:00
}
2021-06-19 22:46:44 +02:00
this . logger . debug ( ` ${ message . author . tag } is executing command ${ command . name } ` ) ;
2021-11-29 11:25:38 +01:00
const clean = message . content . replace ( ` ${ this . prefix } ${ commandName } ` , '' ) . trim ( ) ;
2021-11-29 14:38:37 +01:00
const result = await command . execute ( message , { args : [ ... args ] , clean } ) . catch ( ( err ) => {
this . logger . error ( ` Command ${ command . name } errored during execution: \n ARGS: [ " ${ args . join ( '", "' ) } " ] \n ${ err . stack } ` ) ;
2021-06-19 15:06:20 +02:00
return {
error : true ,
msg : ` Command ${ command . name } ran into an error during execution. This has been logged. `
} ;
} ) ;
if ( ! result ) return ;
2021-12-03 13:05:21 +01:00
if ( result . error ) return channel . send ( result . msg ) . catch ( err => this . logger . error ( ` Client.load errored at channel.send: \n ${ err . stack } \n ${ inspect ( result ) } ` ) ) ;
else if ( result . response ) return channel . send ( result . response ) . catch ( err => this . logger . error ( ` Client.load errored at channel.send: \n ${ err . stack } \n ${ inspect ( result ) } ` ) ) ;
else if ( typeof result === 'string' ) return channel . send ( result ) . catch ( err => this . logger . error ( ` Client.load errored at channel.send: \n ${ err . stack } \n ${ inspect ( result ) } ` ) ) ;
2021-06-19 15:06:20 +02:00
}
2021-06-18 15:41:57 +02:00
2021-10-22 09:35:04 +02:00
resolveUser ( ... args ) {
2021-06-20 13:12:01 +02:00
return this . resolver . resolveUser ( ... args ) ;
}
2021-10-22 09:35:04 +02:00
resolveUsers ( ... args ) {
2021-06-20 13:12:01 +02:00
return this . resolver . resolveUsers ( ... args ) ;
}
2021-10-22 09:35:04 +02:00
resolveChannels ( ... args ) {
2021-06-20 13:12:01 +02:00
return this . resolver . resolveChannels ( ... args ) ;
2021-06-18 15:41:57 +02:00
}
2021-10-22 09:35:04 +02:00
resolveChannel ( ... args ) {
2021-06-20 13:12:01 +02:00
return this . resolver . resolveChannel ( ... args ) ;
2021-06-19 23:57:12 +02:00
}
2021-10-22 09:35:04 +02:00
async prompt ( str , { author , channel , time } ) {
2021-06-19 20:05:32 +02:00
if ( ! channel && author ) channel = await author . createDM ( ) ;
if ( ! channel ) throw new Error ( ` Missing channel for prompt, must pass at least author. ` ) ;
2021-12-02 18:20:30 +01:00
await channel . send ( str ) . catch ( err => this . logger . error ( ` Client.prompt errored at channel.send: \n ${ err . stack } ` ) ) ;
2021-10-22 09:35:04 +02:00
return channel . awaitMessages ( ( m ) => m . author . id === author . id , { max : 1 , time : time || 30000 , errors : [ 'time' ] } )
2021-06-19 20:05:32 +02:00
. then ( ( collected ) => {
return collected . first ( ) ;
} )
2021-10-22 09:35:04 +02:00
. catch ( ( error ) => { // eslint-disable-line no-unused-vars, handle-callback-err
2021-06-19 20:05:32 +02:00
return null ;
} ) ;
}
2022-03-23 13:39:31 +01:00
getUserFromChannel ( channel ) {
const chCache = this . cache . channels ;
const result = Object . entries ( chCache ) . find ( ( [ , val ] ) => {
return val === channel . id ;
} ) ;
if ( ! result ) return {
error : true ,
msg : ` This doesn't seem to be a valid modmail channel. Cache might be out of sync. **[MISSING TARGET]** `
} ;
return result ;
}
2021-06-18 15:41:57 +02:00
}
module . exports = ModmailClient ;