2020-08-09 21:53:09 +02:00
const timestring = require ( 'timestring' ) ;
const moment = require ( 'moment' ) ;
2020-06-04 19:59:09 +02:00
2020-08-19 10:26:29 +02:00
const { InfractionResolves } = require ( '../../util/Constants.js' ) ;
2020-09-20 00:45:23 +02:00
const { Guild , GuildChannel , Role , User } = require ( 'discord.js' ) ;
2020-08-19 10:26:29 +02:00
2020-04-08 18:08:46 +02:00
class Resolver {
constructor ( client ) {
this . client = client ;
}
2020-07-11 22:40:43 +02:00
/ * *
*
*
* @ param { string } [ str = '' ] Keyword to search for
* @ param { string } type Type of the component ( ex . command )
* @ param { boolean } [ exact = true ] Exact matching
* @ returns
* @ memberof Resolver
* /
2020-08-14 08:29:58 +02:00
2020-07-28 20:49:34 +02:00
resolveComponent ( arg , strict = true , type = 'any' ) {
2020-07-28 05:00:24 +02:00
const string = arg . toLowerCase ( ) ;
const components = this . client . registry . components
2020-07-28 20:49:34 +02:00
. filter ( ( c ) => type === 'any' ? [ 'command' , 'setting' ] . includes ( c . type ) : c . type === type )
2020-07-28 05:00:24 +02:00
. filter ( strict ? filterExact ( string ) : filterInexact ( string ) ) ; //eslint-disable-line no-use-before-define
return components . first ( ) ;
}
2020-08-14 08:29:58 +02:00
resolveCases ( str = '' , max = 0 ) {
const cases = [ ] ;
const matches = str . match ( /(\d{1,6})(?:\.\.\.?|-)(\d{1,6})?/iu ) ;
if ( matches ) {
const [ , first , second ] = matches ;
let difference = Math . abs ( ( second ? second : max ) - parseInt ( first ) ) ;
if ( difference + parseInt ( first ) > max ) difference = max ;
new Array ( difference + 1 ) . fill ( 0 )
. forEach ( ( item , i ) => {
const number = i + parseInt ( first ) ;
if ( number <= max ) cases . push ( i + parseInt ( first ) ) ;
} ) ;
} else {
const split = str . split ( ' ' ) ;
for ( const string of split ) {
const number = parseInt ( string ) ;
if ( number <= max && ! cases . includes ( number ) ) cases . push ( number ) ;
}
}
return cases ;
}
2020-05-21 12:47:58 +02:00
components ( str = '' , type , exact = true ) {
2020-04-08 18:08:46 +02:00
const string = str . toLowerCase ( ) ;
const components = this . client . registry . components
2020-07-26 22:09:54 +02:00
. filter ( ( c ) => type === 'any' ? [ 'command' , 'setting' ] . includes ( c . type ) : c . type === type )
2020-05-21 12:47:58 +02:00
. filter ( exact ? filterExact ( string ) : filterInexact ( string ) ) //eslint-disable-line no-use-before-define
2020-04-08 18:08:46 +02:00
. array ( ) ;
return components || [ ] ;
}
2020-05-06 01:40:46 +02:00
2020-08-04 11:35:00 +02:00
componentsByTag ( str , type = 'any' , exact = true ) {
2020-07-26 22:09:54 +02:00
const key = str . toLowerCase ( ) ;
const components = this . client . registry . components
2020-08-04 11:35:00 +02:00
. filter ( ( c ) => type === 'any' || c . type === type )
2020-07-26 22:09:54 +02:00
. filter ( exact ? ( c ) => c . tags . includes ( key ) : filterInexactTags ( key ) )
. array ( ) ;
return components || [ ] ;
}
2020-05-24 23:39:37 +02:00
timeAgo ( diff , extraMin = false , extraHours = false , extraDays = false ) {
diff = parseInt ( diff ) ;
if ( isNaN ( diff ) ) return 'that ain\'t it chief (not a number)' ;
2020-06-09 17:58:09 +02:00
const years = Math . floor ( diff / 60 / 60 / 24 / 365 ) ,
months = Math . floor ( diff / 60 / 60 / 24 / 30.4 ) ,
weeks = extraDays ? Math . floor ( diff / 60 / 60 / 24 / 7 ) : ( diff / 60 / 60 / 24 / 7 ) . toFixed ( ) ,
days = extraHours ? Math . floor ( diff / 60 / 60 / 24 ) : ( diff / 60 / 60 / 24 ) . toFixed ( ) ,
hours = extraMin ? Math . floor ( diff / 60 / 60 ) : ( diff / 60 / 60 ) . toFixed ( ) ,
2020-05-24 23:39:37 +02:00
minutes = ( diff / 60 ) . toFixed ( ) ;
if ( days > 365 ) {
2020-06-09 17:58:09 +02:00
return ` ${ years > 0 ? years : 1 } year ${ years > 1 ? 's' : '' } ${ months % 12 > 0 ? ` ${ months % 12 } month ${ months % 12 > 1 ? 's' : '' } ` : '' } ` ;
2020-05-24 23:39:37 +02:00
} else if ( weeks > 4 ) {
2020-06-09 17:58:09 +02:00
return ` ${ months } month ${ months % 12 > 1 ? 's' : '' } ${ days % 30 > 0 ? ` ${ days % 30 } day ${ days % 30 > 1 ? 's' : '' } ` : '' } ` ;
2020-05-24 23:39:37 +02:00
} else if ( days >= 7 ) {
2020-06-09 17:58:09 +02:00
return ` ${ weeks } week ${ weeks > 1 ? 's' : '' } ${ extraDays && days % 7 > 0 ? ` ${ days % 7 } day ${ days % 7 > 1 ? 's' : '' } ` : '' } ` ;
2020-05-24 23:39:37 +02:00
} else if ( hours >= 24 ) {
2020-06-09 17:58:09 +02:00
return ` ${ days } day ${ days > 1 ? 's' : '' } ${ extraHours && hours % 24 > 0 ? ` ${ hours % 24 } hour ${ hours % 24 > 1 ? 's' : '' } ` : '' } ` ;
2020-05-24 23:39:37 +02:00
} else if ( minutes >= 60 ) {
2020-06-09 17:58:09 +02:00
return ` ${ hours } hour ${ hours > 1 ? 's' : '' } ${ extraMin && minutes % 60 > 0 ? ` ${ minutes % 60 } minute ${ minutes % 60 > 1 ? 's' : '' } ` : '' } ` ;
2020-05-24 23:39:37 +02:00
} else if ( diff >= 60 ) {
return ` ${ minutes } minute ${ minutes > 1 ? 's' : '' } ` ;
}
return diff . toFixed ( ) + ` second ${ diff . toFixed ( ) !== 1 ? 's' : '' } ` ;
}
2020-05-06 01:40:46 +02:00
resolveBoolean ( input ) {
input = input . toLowerCase ( ) ;
2020-05-25 13:13:34 +02:00
const truthy = [ 'on' , 'true' , 'yes' , 'enable' , 'y' , 't' ] ;
const falsey = [ 'off' , 'false' , 'no' , 'disable' , 'n' , 'f' ] ;
2020-05-06 01:40:46 +02:00
if ( truthy . includes ( input ) ) {
return true ;
} else if ( falsey . includes ( input ) ) {
return false ;
2020-05-21 12:47:58 +02:00
}
return null ;
2020-08-19 10:26:29 +02:00
}
async list ( list , options , args , resolver = null , strict = false , guild = null ) {
let [ method , ... changes ] = args ;
method = method . toLowerCase ( ) ;
const methods = {
list : [ 'view' , 'list' , '?' , 'show' ] ,
add : [ 'add' , '+' ] ,
remove : [ 'remove' , 'delete' , '-' ] ,
set : [ 'set' , '=' ]
} ;
if ( resolver ) {
let results = resolver ( changes , strict , guild ) ;
if ( results instanceof Promise ) results = await results ;
changes = results . map ( ( r ) => r . id || r ) ;
}
if ( methods . list . includes ( method ) ) {
return { list , method : 'list' } ;
} else if ( methods . add . includes ( method ) ) {
const added = [ ] ;
for ( const change of changes ) {
if ( ! options . includes ( change ) ) continue ;
if ( list . includes ( change ) ) continue ;
list . push ( change ) ;
added . push ( change ) ;
}
return { list , changed : added , method : 'add' } ;
} else if ( methods . remove . includes ( method ) ) {
const removed = [ ] ;
for ( const change of changes ) {
if ( ! list . includes ( change ) ) continue ;
list . splice ( list . indexOf ( change ) , 1 ) ;
removed . push ( change ) ;
}
return { list , changed : removed , method : 'remove' } ;
} else if ( methods . set . includes ( method ) ) {
return { list : changes , changed : changes , method : 'set' } ;
}
return null ;
}
2020-05-06 01:40:46 +02:00
2020-05-24 00:10:01 +02:00
/ * *
* Resolves methods used primarily for settings , also deals with appending the arguments into existing lists
2020-09-20 00:45:23 +02:00
* TODO : Refactor to use an options object instead of an ungodly amount of args
2020-05-24 00:10:01 +02:00
*
* @ param { Array < String > } args The incoming arguments with the first element being the method ex . [ 'add' , 'ban' , 'kick' ]
* @ param { Array < String > } valid An array of items to compare to , if an argument doesn 't exist in this array it' ll be skipped over
* @ param { Array < String > } [ existing = [ ] ] Existing values in the array , valid elements will be appended to this
2020-06-10 16:48:26 +02:00
* @ param { Function } resolver One of the resolver functions used to resolve the passed values into objects ( should always be one of the mass resolvers due to the nature of this method , might break otherwise ) NOTE : REMEMBER TO BIND 'this' ARG !
* @ param { Guild } guild The guild for the resolver to use when resolving
2020-08-25 16:10:52 +02:00
* @ param { Boolean } caseSensitive Whether or not to preserve case
2020-09-20 00:45:23 +02:00
* @ param { Array || String } [ allowedMethods = 'all' ] Which methods to allow to be resolved
2020-05-24 00:10:01 +02:00
* @ returns { Object }
* @ memberof Resolver
* /
2020-09-20 00:45:23 +02:00
async resolveMethod ( args , valid , existing = [ ] , resolver , guild , caseSensitive = false , allowedMethods = 'all' ) {
2020-05-24 00:10:01 +02:00
const methods = {
2020-05-24 23:39:37 +02:00
list : [ 'view' , 'list' , '?' , 'show' ] ,
2020-05-24 00:10:01 +02:00
add : [ 'add' , '+' ] ,
2020-06-10 16:48:26 +02:00
set : [ 'set' , '=' ] ,
2020-05-24 23:39:37 +02:00
remove : [ 'remove' , 'delete' , '-' ] ,
2020-06-20 22:26:02 +02:00
reset : [ 'clear' , 'reset' , 'default' ] ,
2020-06-09 17:58:09 +02:00
off : [ 'off' , 'disable' , 'false' , 'no' , 'n' , 'f' ] ,
on : [ 'on' , 'enable' , 'true' , 'yes' , 'y' , 't' ]
2020-05-24 00:10:01 +02:00
} ;
2020-09-20 00:45:23 +02:00
if ( allowedMethods === 'all' ) allowedMethods = Object . keys ( methods ) ;
2020-05-24 00:10:01 +02:00
if ( ! args . length ) return false ;
// eslint-disable-next-line prefer-const
let [ method , ... rest ] = args ;
method = method . toLowerCase ( ) ;
2020-05-24 23:39:37 +02:00
let resolved = [ ] ;
2020-06-09 17:58:09 +02:00
2020-09-20 00:45:23 +02:00
if ( methods . reset . includes ( method ) && allowedMethods . includes ( 'reset' ) ) return { method : 'reset' , result : [ ] , changed : [ ] , resolved } ;
if ( methods . off . includes ( method ) && allowedMethods . includes ( 'off' ) ) return { method : 'off' } ;
if ( methods . on . includes ( method ) && allowedMethods . includes ( 'on' ) ) return { method : 'on' } ;
2020-05-24 00:10:01 +02:00
if ( ! rest . length ) {
2020-09-20 00:45:23 +02:00
if ( methods . add . includes ( method ) && allowedMethods . includes ( 'add' ) ) return { method : 'add' } ;
if ( methods . remove . includes ( method ) && allowedMethods . includes ( 'remove' ) ) return { method : 'remove' } ;
if ( methods . set . includes ( method ) && allowedMethods . includes ( 'set' ) ) return { method : 'set' } ;
2020-05-24 00:10:01 +02:00
}
2020-09-20 00:45:23 +02:00
if ( methods . list . includes ( method ) && allowedMethods . includes ( 'list' ) ) {
2020-05-24 23:39:37 +02:00
2020-06-10 16:48:26 +02:00
if ( resolver ) resolved = await resolver ( existing , false , guild ) ;
2020-05-24 23:39:37 +02:00
return { method : 'list' , resolved } ;
2020-09-20 00:45:23 +02:00
} else if ( methods . add . includes ( method ) && allowedMethods . includes ( 'add' ) ) {
2020-05-24 23:39:37 +02:00
2020-05-24 00:10:01 +02:00
const added = [ ] ;
for ( let elem of rest ) {
2020-05-24 23:39:37 +02:00
if ( resolver ) {
2020-06-10 16:48:26 +02:00
const _resolved = await resolver ( elem , false , guild ) ;
2020-05-24 23:39:37 +02:00
if ( _resolved ) {
if ( ! resolved . includes ( _resolved [ 0 ] ) ) resolved . push ( _resolved [ 0 ] ) ;
elem = _resolved [ 0 ] . id ;
2020-08-17 22:24:43 +02:00
} else continue ;
2020-05-24 23:39:37 +02:00
}
2020-07-11 22:40:43 +02:00
if ( ! caseSensitive ) elem = elem . toLowerCase ( ) ;
2020-05-24 00:10:01 +02:00
if ( existing . includes ( elem ) || valid && ! valid . includes ( elem ) ) continue ;
2020-05-24 23:39:37 +02:00
2020-05-24 00:10:01 +02:00
added . push ( elem ) ;
existing . push ( elem ) ;
2020-05-24 23:39:37 +02:00
2020-05-24 00:10:01 +02:00
}
2020-05-24 23:39:37 +02:00
return { rest , method : 'add' , changed : added , result : existing , resolved } ;
2020-09-20 00:45:23 +02:00
} else if ( methods . remove . includes ( method ) && allowedMethods . includes ( 'remove' ) ) {
2020-05-24 23:39:37 +02:00
2020-05-24 00:10:01 +02:00
const removed = [ ] ;
for ( let elem of rest ) {
2020-05-24 23:39:37 +02:00
if ( resolver ) {
2020-06-10 16:48:26 +02:00
const _resolved = await resolver ( elem , false , guild ) ;
2020-05-24 23:39:37 +02:00
if ( _resolved ) {
if ( ! resolved . includes ( _resolved [ 0 ] ) ) resolved . push ( _resolved [ 0 ] ) ;
elem = _resolved [ 0 ] . id ;
2020-08-17 22:24:43 +02:00
} else continue ;
2020-05-24 23:39:37 +02:00
}
2020-07-11 22:40:43 +02:00
if ( ! caseSensitive ) elem = elem . toLowerCase ( ) ;
2020-05-24 00:10:01 +02:00
if ( ! existing . includes ( elem ) || removed . includes ( elem ) || valid && ! valid . includes ( elem ) ) continue ;
removed . push ( elem ) ;
existing . splice ( existing . indexOf ( elem ) , 1 ) ;
2020-05-24 23:39:37 +02:00
2020-05-24 00:10:01 +02:00
}
2020-05-24 23:39:37 +02:00
return { rest , method : 'remove' , changed : removed , result : existing , resolved } ;
2020-09-20 00:45:23 +02:00
} else if ( methods . set . includes ( method ) && allowedMethods . includes ( 'set' ) ) {
2020-06-10 16:48:26 +02:00
const set = [ ] ;
for ( let elem of rest ) {
if ( resolver ) {
const _resolved = await resolver ( elem , false , guild ) ;
if ( _resolved ) {
if ( ! resolved . includes ( _resolved [ 0 ] ) ) resolved . push ( _resolved [ 0 ] ) ;
elem = _resolved [ 0 ] . id ;
2020-08-17 22:24:43 +02:00
} else continue ;
2020-06-10 16:48:26 +02:00
}
2020-07-11 22:40:43 +02:00
if ( ! caseSensitive ) elem = elem . toLowerCase ( ) ;
if ( valid && ! valid . includes ( elem ) ) continue ;
set . push ( elem ) ;
2020-06-10 16:48:26 +02:00
}
return { rest , method : 'set' , changed : set , result : set , resolved } ;
2020-05-24 00:10:01 +02:00
}
return false ;
}
2020-04-08 18:08:46 +02:00
2020-04-11 10:06:39 +02:00
/ * *
* Resolve several user resolveables
*
2020-09-20 00:45:23 +02:00
* @ param { Array < String > } [ resolveables = [ ] ] an array of user resolveables ( name , id , tag )
* @ param { Boolean } [ strict = false ] whether or not to attempt resolving by partial usernames
* @ returns { Promise < Array < User >> || boolean } Array of resolved users or false if none were resolved
2020-04-11 10:06:39 +02:00
* @ memberof Resolver
* /
2020-04-09 11:18:14 +02:00
async resolveUsers ( resolveables = [ ] , strict = false ) {
2020-04-08 18:08:46 +02:00
if ( typeof resolveables === 'string' ) resolveables = [ resolveables ] ;
if ( resolveables . length === 0 ) return false ;
2020-05-21 12:47:58 +02:00
const { users } = this . client ;
const resolved = [ ] ;
2020-04-08 18:08:46 +02:00
2020-05-21 12:47:58 +02:00
for ( const resolveable of resolveables ) {
2020-04-08 18:08:46 +02:00
2020-05-21 12:47:58 +02:00
if ( ( /<@!?([0-9]{17,21})>/u ) . test ( resolveable ) ) {
2020-04-08 18:08:46 +02:00
2020-05-21 12:47:58 +02:00
const [ , id ] = resolveable . match ( /<@!?([0-9]{17,21})>/u ) ;
const user = await users . fetch ( id ) . catch ( ( err ) => {
if ( err . code === 10013 ) return false ;
2020-07-23 22:40:20 +02:00
// this.client.logger.warn(err); return false;
2020-05-21 12:47:58 +02:00
} ) ;
2020-04-08 18:08:46 +02:00
if ( user ) resolved . push ( user ) ;
2020-05-21 12:47:58 +02:00
} else if ( ( /(id:)?([0-9]{17,21})/u ) . test ( resolveable ) ) {
2020-04-08 18:08:46 +02:00
2020-05-21 12:47:58 +02:00
const [ , , id ] = resolveable . match ( /(id:)?([0-9]{17,21})/u ) ;
const user = await users . fetch ( id ) . catch ( ( err ) => {
if ( err . code === 10013 ) return false ;
2020-07-23 22:40:20 +02:00
// this.client.logger.warn(err); return false;
2020-05-21 12:47:58 +02:00
} ) ;
2020-04-08 18:08:46 +02:00
if ( user ) resolved . push ( user ) ;
2020-05-21 12:47:58 +02:00
} else if ( ( /^@?([\S\s]{1,32})#([0-9]{4})/u ) . test ( resolveable ) ) {
2020-04-08 18:08:46 +02:00
2020-05-21 12:47:58 +02:00
const m = resolveable . match ( /^@?([\S\s]{1,32})#([0-9]{4})/u ) ;
const username = m [ 1 ] . toLowerCase ( ) ;
const discrim = m [ 2 ] . toLowerCase ( ) ;
2020-08-10 02:00:49 +02:00
const user = users . cache . sort ( ( a , b ) => a . username . length - b . username . length ) . filter ( ( u ) => u . username . toLowerCase ( ) === username && u . discriminator === discrim ) . first ( ) ;
2020-04-08 18:08:46 +02:00
if ( user ) resolved . push ( user ) ;
2020-04-09 11:18:14 +02:00
} else if ( ! strict ) {
2020-05-21 12:47:58 +02:00
const name = resolveable . toLowerCase ( ) ;
2020-08-10 02:00:49 +02:00
const user = users . cache . sort ( ( a , b ) => a . username . length - b . username . length ) . filter ( ( u ) => u . username . toLowerCase ( ) . includes ( name ) ) . first ( ) ;
2020-04-09 11:18:14 +02:00
if ( user ) resolved . push ( user ) ;
2020-04-08 18:08:46 +02:00
}
}
2020-05-08 08:50:54 +02:00
return resolved . length ? resolved : false ;
2020-04-08 18:08:46 +02:00
}
2020-04-17 17:23:13 +02:00
async resolveUser ( resolveable , strict ) {
2020-05-22 22:13:47 +02:00
if ( ! resolveable ) return false ;
2020-05-24 23:39:37 +02:00
if ( resolveable instanceof Array ) throw new Error ( 'Resolveable cannot be of type Array, use resolveUsers for resolving arrays of users' ) ;
2020-05-21 12:47:58 +02:00
const result = await this . resolveUsers ( [ resolveable ] , strict ) ;
2020-05-17 13:04:58 +02:00
return result ? result [ 0 ] : false ;
2020-05-22 22:13:47 +02:00
2020-04-17 17:23:13 +02:00
}
2020-04-11 10:06:39 +02:00
/ * *
* Resolve multiple member resolveables
*
2020-09-20 00:45:23 +02:00
* @ param { Array < String > } [ resolveables = [ ] ] an array of member resolveables ( name , nickname , tag , id )
* @ param { Boolean } [ strict = false ] whether or not to attempt resolving by partial matches
2020-04-11 10:06:39 +02:00
* @ param { Guild } guild the guild in which to look for members
2020-09-20 00:45:23 +02:00
* @ returns { Promise < Array < GuildMember >> || Promise < Boolean > } an array of resolved members or false if none were resolved
2020-04-11 10:06:39 +02:00
* @ memberof Resolver
* /
2020-06-02 12:09:28 +02:00
async resolveMembers ( resolveables = [ ] , strict = false , guild = null ) {
2020-04-08 18:08:46 +02:00
if ( typeof resolveables === 'string' ) resolveables = [ resolveables ] ;
if ( resolveables . length === 0 ) return false ;
2020-06-02 12:09:28 +02:00
if ( ! guild ) return false ;
2020-05-21 12:47:58 +02:00
const { members } = guild ;
const resolved = [ ] ;
2020-04-08 18:08:46 +02:00
2020-05-21 12:47:58 +02:00
for ( const resolveable of resolveables ) {
2020-04-08 18:08:46 +02:00
2020-05-21 12:47:58 +02:00
if ( ( /<@!?([0-9]{17,21})>/u ) . test ( resolveable ) ) {
2020-04-08 18:08:46 +02:00
2020-05-21 12:47:58 +02:00
const [ , id ] = resolveable . match ( /<@!?([0-9]{17,21})>/u ) ;
const member = await members . fetch ( id ) . catch ( ( err ) => {
if ( err . code === 10007 ) return false ;
2020-07-23 22:40:20 +02:00
// this.client.logger.warn(err); return false;
2020-05-21 12:47:58 +02:00
} ) ;
2020-04-08 18:08:46 +02:00
if ( member ) resolved . push ( member ) ;
2020-05-21 12:47:58 +02:00
} else if ( ( /(id:)?([0-9]{17,21})/u ) . test ( resolveable ) ) {
2020-04-09 11:18:14 +02:00
2020-05-21 12:47:58 +02:00
const [ , , id ] = resolveable . match ( /(id:)?([0-9]{17,21})/u ) ;
const member = await members . fetch ( id ) . catch ( ( err ) => {
if ( err . code === 10007 ) return false ;
2020-07-23 22:40:20 +02:00
// this.client.logger.warn(err); return false;
2020-05-21 12:47:58 +02:00
} ) ;
2020-04-09 11:18:14 +02:00
if ( member ) resolved . push ( member ) ;
2020-05-21 12:47:58 +02:00
} else if ( ( /^@?([\S\s]{1,32})#([0-9]{4})/u ) . test ( resolveable ) ) {
2020-04-09 11:18:14 +02:00
2020-05-21 12:47:58 +02:00
const m = resolveable . match ( /^@?([\S\s]{1,32})#([0-9]{4})/u ) ;
const username = m [ 1 ] . toLowerCase ( ) ;
const discrim = m [ 2 ] . toLowerCase ( ) ;
const member = members . cache . filter ( ( m ) => m . user . username . toLowerCase ( ) === username && m . user . discriminator === discrim ) . first ( ) ;
2020-04-09 11:18:14 +02:00
if ( member ) resolved . push ( member ) ;
2020-05-21 12:47:58 +02:00
} else if ( ( /^@?([\S\s]{1,32})/u ) . test ( resolveable ) && guild && ! strict ) {
2020-04-09 11:18:14 +02:00
2020-05-21 12:47:58 +02:00
const nickname = resolveable . match ( /^@?([\S\s]{1,32})/u ) [ 0 ] . toLowerCase ( ) ;
2020-08-10 02:00:49 +02:00
const member = members . cache . sort ( ( a , b ) => a . user . username . length - b . user . username . length ) . filter ( ( m ) => m && m . user &&
2020-05-21 12:47:58 +02:00
( ( ! m . nickname ? false : m . nickname . toLowerCase ( ) === nickname ) ||
2020-04-09 11:18:14 +02:00
( ! m . nickname ? false : m . nickname . toLowerCase ( ) . includes ( nickname ) ) ||
m . user . username . toLowerCase ( ) . includes ( nickname ) ||
2020-05-21 12:47:58 +02:00
m . user . username . toLowerCase ( ) === nickname ) ) . first ( ) ;
2020-04-09 11:18:14 +02:00
if ( member ) resolved . push ( member ) ;
2020-04-17 17:23:13 +02:00
}
2020-04-08 18:08:46 +02:00
}
2020-04-11 10:06:39 +02:00
return resolved . length > 0 ? resolved : false ;
}
2020-06-02 12:09:28 +02:00
async resolveMember ( resolveable , strict , guild ) {
2020-05-07 01:26:16 +02:00
2020-05-22 22:13:47 +02:00
if ( ! resolveable ) return false ;
2020-05-24 23:39:37 +02:00
if ( resolveable instanceof Array ) throw new Error ( 'Resolveable cannot be of type Array, use resolveMembers for resolving arrays of members' ) ;
2020-06-02 12:09:28 +02:00
const result = await this . resolveMembers ( [ resolveable ] , strict , guild ) ;
2020-05-17 13:04:58 +02:00
return result ? result [ 0 ] : false ;
2020-05-07 01:26:16 +02:00
}
2020-04-11 10:06:39 +02:00
/ * *
2020-04-11 10:11:22 +02:00
* Resolve multiple channels
2020-04-11 10:06:39 +02:00
*
2020-09-20 00:45:23 +02:00
* @ param { Array < String > } [ resolveables = [ ] ] an array of channel resolveables ( name , id )
* @ param { Guild } guild the guild in which to look for channels
* @ param { Boolean } [ strict = false ] whether or not partial names are resolved
* @ param { Function } [ filter = ( ) ] filter the resolving channels
* @ returns { Promise < Array < GuildChannel >> || Promise < Boolean > } an array of guild channels or false if none were resolved
2020-04-11 10:06:39 +02:00
* @ memberof Resolver
* /
2020-06-19 15:38:56 +02:00
async resolveChannels ( resolveables = [ ] , strict = false , guild = null , filter = ( ) => true ) {
2020-04-11 10:06:39 +02:00
if ( typeof resolveables === 'string' ) resolveables = [ resolveables ] ;
if ( resolveables . length === 0 ) return false ;
2020-06-02 12:09:28 +02:00
if ( ! guild ) return false ;
2020-05-25 13:13:34 +02:00
const CM = guild . channels ;
2020-05-21 12:47:58 +02:00
const resolved = [ ] ;
2020-04-11 10:06:39 +02:00
2020-05-21 12:47:58 +02:00
for ( const resolveable of resolveables ) {
2020-04-11 10:06:39 +02:00
2020-05-25 13:13:34 +02:00
const channel = CM . resolve ( resolveable ) ;
if ( channel && filter ( channel ) ) {
2020-04-11 10:06:39 +02:00
resolved . push ( channel ) ;
continue ;
}
2020-05-21 12:47:58 +02:00
const name = /^#?([a-z0-9\-_0]*)/iu ;
2020-07-11 22:40:43 +02:00
const id = /^<?#?([0-9]*)>?/iu ;
2020-04-11 10:06:39 +02:00
2020-05-08 08:50:54 +02:00
if ( id . test ( resolveable ) ) {
2020-05-21 12:47:58 +02:00
const match = resolveable . match ( id ) ;
const [ , ch ] = match ;
2020-05-08 08:50:54 +02:00
2020-07-20 00:42:21 +02:00
const channel = await this . client . channels . fetch ( ch ) . catch ( ( e ) => { } ) ; //eslint-disable-line no-empty, no-empty-function, no-unused-vars
2020-05-08 08:50:54 +02:00
2020-05-25 13:13:34 +02:00
if ( channel && filter ( channel ) ) resolved . push ( channel ) ;
2020-05-08 08:50:54 +02:00
} else if ( name . test ( resolveable ) ) {
2020-04-11 10:06:39 +02:00
2020-05-21 12:47:58 +02:00
const match = resolveable . match ( name ) ;
const ch = match [ 1 ] . toLowerCase ( ) ;
2020-05-25 13:13:34 +02:00
2020-06-10 16:48:26 +02:00
const channel = CM . cache . sort ( ( a , b ) => a . name . length - b . name . length ) . filter ( filter ) . filter ( ( c ) => {
2020-05-25 13:13:34 +02:00
if ( ! strict ) return c . name . toLowerCase ( ) . includes ( ch ) ;
2020-04-11 10:06:39 +02:00
return c . name . toLowerCase ( ) === ch ;
2020-06-04 19:59:09 +02:00
} ) . first ( ) ;
2020-05-25 13:13:34 +02:00
2020-04-11 10:06:39 +02:00
if ( channel ) resolved . push ( channel ) ;
2020-07-23 22:41:52 +02:00
}
2020-04-11 10:06:39 +02:00
}
return resolved . length > 0 ? resolved : false ;
}
2020-05-17 13:04:58 +02:00
2020-06-19 15:38:56 +02:00
async resolveChannel ( resolveable , strict , guild , filter ) {
2020-05-17 13:04:58 +02:00
2020-05-22 22:13:47 +02:00
if ( ! resolveable ) return false ;
2020-05-24 23:39:37 +02:00
if ( resolveable instanceof Array ) throw new Error ( 'Resolveable cannot be of type Array, use resolveChannels for resolving arrays of channels' ) ;
2020-06-19 15:38:56 +02:00
const result = await this . resolveChannels ( [ resolveable ] , strict , guild , filter ) ;
2020-05-17 13:04:58 +02:00
return result ? result [ 0 ] : false ;
}
2020-04-11 10:06:39 +02:00
2020-04-11 10:11:22 +02:00
/ * *
* Resolve multiple roles
*
2020-09-20 00:45:23 +02:00
* @ param { Array < String > } [ resolveables = [ ] ] an array of roles resolveables ( name , id )
2020-04-11 10:11:22 +02:00
* @ param { Guild } guild the guild in which to look for roles
2020-09-20 00:45:23 +02:00
* @ param { Boolean } [ strict = false ] whether or not partial names are resolved
* @ returns { Promise < Array < Role >> || Promise < Boolean > } an array of roles or false if none were resolved
2020-04-11 10:11:22 +02:00
* @ memberof Resolver
* /
2020-06-02 12:09:28 +02:00
async resolveRoles ( resolveables = [ ] , strict = false , guild = null ) {
2020-04-11 10:06:39 +02:00
if ( typeof resolveables === 'string' ) resolveables = [ resolveables ] ;
if ( resolveables . length === 0 ) return false ;
2020-06-02 12:09:28 +02:00
if ( ! guild ) return false ;
2020-05-21 12:47:58 +02:00
const { roles } = guild ;
const resolved = [ ] ;
2020-04-11 10:06:39 +02:00
2020-05-21 12:47:58 +02:00
for ( const resolveable of resolveables ) {
2020-04-11 10:06:39 +02:00
2020-05-21 12:47:58 +02:00
const id = /^(<@&)?([0-9]{16,22})>?/iu ;
2020-04-11 10:06:39 +02:00
if ( id . test ( resolveable ) ) {
2020-05-21 12:47:58 +02:00
const match = resolveable . match ( id ) ;
const [ , , rId ] = match ;
2020-04-11 10:06:39 +02:00
2020-05-21 12:47:58 +02:00
const role = await roles . fetch ( rId ) . catch ( this . client . logger . error ) ;
2020-04-11 10:06:39 +02:00
if ( role ) resolved . push ( role ) ;
} else {
2020-06-10 16:48:26 +02:00
const role = roles . cache . sort ( ( a , b ) => a . name . length - b . name . length ) . filter ( ( r ) => {
2020-04-11 10:06:39 +02:00
if ( ! strict ) return r . name . toLowerCase ( ) . includes ( resolveable . toLowerCase ( ) ) ;
return r . name . toLowerCase ( ) === resolveable . toLowerCase ( ) ;
2020-05-08 08:50:54 +02:00
} ) . first ( ) ;
2020-04-11 10:06:39 +02:00
if ( role ) resolved . push ( role ) ;
}
}
2020-04-17 17:23:13 +02:00
return resolved . length > 0 ? resolved : false ;
2020-04-09 11:18:14 +02:00
2020-04-08 18:08:46 +02:00
}
2020-05-17 13:04:58 +02:00
2020-06-02 12:09:28 +02:00
async resolveRole ( resolveable , strict , guild ) {
2020-05-22 22:13:47 +02:00
if ( ! resolveable ) return false ;
2020-05-24 23:39:37 +02:00
if ( resolveable instanceof Array ) throw new Error ( 'Resolveable cannot be of type Array, use resolveRoles for resolving arrays of roles' ) ;
2020-06-02 12:09:28 +02:00
const result = await this . resolveRoles ( [ resolveable ] , strict , guild ) ;
2020-05-17 13:04:58 +02:00
return result ? result [ 0 ] : false ;
2020-06-02 12:09:28 +02:00
}
2020-06-04 19:59:09 +02:00
resolveTime ( string ) {
let time = null ;
try {
time = timestring ( string ) ;
} catch ( err ) {
return null ;
}
return time ;
}
2020-08-09 21:53:09 +02:00
resolveDate ( string ) {
let date = null ;
const matches = string . match ( /([0-9]{4}(?:\/|-)[0-9]{2}(?:\/|-)[0-9]{2})/gimu ) ; //YYYY-MM-DD is REQUIRED.
if ( matches && matches . length > 0 ) {
try {
const string = matches [ 0 ] . replace ( /\//giu , '-' ) ;
date = moment ( string ) ;
} catch ( error ) {
return null ;
}
}
return date ;
}
2020-08-19 10:26:29 +02:00
resolveInfraction ( string ) {
let infraction = null ;
for ( const [ key , values ] of Object . entries ( InfractionResolves ) ) {
if ( values . includes ( string . toLowerCase ( ) ) ) {
infraction = key ;
break ;
}
}
return infraction ;
}
resolveInfractions ( args = [ ] ) {
const resolved = [ ] ;
for ( const arg of args ) {
const infraction = this . resolveInfraction ( arg ) ;
if ( infraction ) resolved . push ( infraction ) ;
}
return resolved ;
}
2020-08-17 22:24:43 +02:00
/ * *
* Iterate through several arguments attempting to resolve them with the passed resolvers
*
2020-09-20 00:45:23 +02:00
* @ param { Array < String > } [ args = [ ] ]
2020-08-17 22:24:43 +02:00
* @ param { Array < Function > } [ resolvers = [ ] ]
2020-09-20 00:45:23 +02:00
* @ param { Boolean } strict
2020-08-17 22:24:43 +02:00
* @ param { Guild } guild
* @ returns
* /
2020-06-02 12:09:28 +02:00
async infinite ( args = [ ] , resolvers = [ ] , strict , guild ) {
let parsed = [ ] , //eslint-disable-line prefer-const
parameters = [ ] ;
if ( resolvers . length === 0 ) return { parsed , parameters : args } ;
for ( let i = 0 ; i < args . length ; i ++ ) {
const arg = args [ i ] ;
let resolved = null ;
for ( const resolver of resolvers ) {
if ( resolved ) break ;
resolved = resolver ( arg , strict , guild ) ;
if ( resolved instanceof Promise ) resolved = await resolved ;
}
if ( resolved ) {
const ids = parsed . map ( ( p ) => p . id ) ;
if ( ! ids . includes ( resolved . id ) ) parsed . push ( resolved ) ;
continue ;
} else {
2020-07-04 12:23:10 +02:00
parameters = args . splice ( i ) ;
2020-06-02 12:09:28 +02:00
break ;
}
}
return { parsed , parameters } ;
2020-05-17 13:04:58 +02:00
}
2020-09-20 00:45:23 +02:00
// 'wordfilter actions word mute 5 min 1 point force' // time reg: (\d{1,3}\s?[a-z]{1,7} ?){1,2} | points reg: \d{1,3}\s?(points?|pts?|p) -- parse points first
resolveModAction ( input , validActions = null ) {
const points = /(\d{1,3})\s?(points?|pts?|p)/i ;
const time = /(\d{1,3}\s?[a-z]{1,7})\s?(\d{1,3}\s?[a-z]{1,7})?/i ;
const force = /force/i ;
const prune = /prune/i ;
const result = { action : null , duration : null , points : null , force : false , prune : false } ;
const [ action , ... rest ] = input . split ( ' ' ) ;
const inf = this . resolveInfraction ( action ) ;
if ( ! inf || ( validActions && ! validActions . includes ( inf ) ) ) return { error : true , template : 'ERR_INVALID_INFRACTION' } ;
input = rest . join ( ' ' ) ;
result . action = inf ;
if ( force . test ( input ) ) {
result . force = true ;
input = input . replace ( force , '' ) ;
}
if ( prune . test ( input ) ) {
result . prune = true ;
input = input . replace ( prune , '' ) ;
}
if ( points . test ( input ) ) {
const match = input . match ( points ) ;
const pts = parseInt ( match [ 1 ] ) ;
if ( isNaN ( pts ) || pts < 0 || pts > 100 ) return { error : true , template : 'S_AUTOMOD_INVALID_POINTS' } ;
result . points = pts ;
input = input . replace ( match [ 0 ] , '' ) ;
}
if ( time . test ( input ) ) {
const match = input . match ( time ) ;
const tiem = this . resolveTime ( match [ 0 ] ) ;
if ( ! tiem ) return { error : true , template : 'ERR_INVALID_TIMESTRING' } ;
result . duration = tiem ;
}
return result ;
}
2020-04-08 18:08:46 +02:00
}
2020-04-09 16:30:52 +02:00
module . exports = Resolver ;
2020-05-21 12:47:58 +02:00
const filterExact = ( search ) => ( comp ) => comp . id . toLowerCase ( ) === search ||
2020-04-08 18:08:46 +02:00
comp . resolveable . toLowerCase ( ) === search ||
2020-05-21 12:47:58 +02:00
comp . aliases && ( comp . aliases . some ( ( ali ) => ` ${ comp . type } : ${ ali } ` . toLowerCase ( ) === search ) ||
comp . aliases . some ( ( ali ) => ali . toLowerCase ( ) === search ) ) ;
2020-04-08 18:08:46 +02:00
2020-05-21 12:47:58 +02:00
const filterInexact = ( search ) => ( comp ) => comp . id . toLowerCase ( ) . includes ( search ) ||
2020-04-08 18:08:46 +02:00
comp . resolveable . toLowerCase ( ) . includes ( search ) ||
2020-05-21 12:47:58 +02:00
comp . aliases && ( comp . aliases . some ( ( ali ) => ` ${ comp . type } : ${ ali } ` . toLowerCase ( ) . includes ( search ) ) ||
2020-07-26 22:09:54 +02:00
comp . aliases . some ( ( ali ) => ali . toLowerCase ( ) . includes ( search ) ) ) ;
const filterInexactTags = ( search ) => ( comp ) => comp . id . toLowerCase ( ) . includes ( search . toLowerCase ( ) ) ||
comp . resolveable . toLowerCase ( ) . includes ( search . toLowerCase ( ) ) ||
comp . aliases && ( comp . aliases . some ( ( ali ) => ` ${ comp . type } : ${ ali } ` . toLowerCase ( ) . includes ( search . toLowerCase ( ) ) ) ||
comp . aliases . some ( ( ali ) => ali . toLowerCase ( ) . includes ( search . toLowerCase ( ) ) ) ||
comp . tags . some ( ( tag ) => tag . toLowerCase ( ) . includes ( search . toLowerCase ( ) ) ) ) ;