2021-03-19 21:23:38 -04:00
/* eslint-disable no-bitwise */
2021-03-28 23:24:56 -04:00
import axios from 'axios' ;
2021-03-19 21:23:38 -04:00
import nodemailer from 'nodemailer' ;
import childProcess from 'child_process' ;
import { promisify } from 'util' ;
import signale from 'signale' ;
import { Member , Message , Guild , PrivateChannel , GroupChannel , Role , AnyGuildChannel , WebhookPayload } from 'eris' ;
import { Client , Command , Moderation , PBX , Report , RichEmbed } from '.' ;
import { statusMessages as emotes } from '../configs/emotes.json' ;
export default class Util {
public client : Client ;
public moderation : Moderation ;
public signale : signale.Signale ;
public transporter : nodemailer.Transporter ;
public pbx : PBX ;
public report : Report ;
constructor ( client : Client ) {
this . client = client ;
this . moderation = new Moderation ( this . client ) ;
this . signale = signale ;
this . signale . config ( {
displayDate : true ,
displayTimestamp : true ,
displayFilename : true ,
} ) ;
this . transporter = nodemailer . createTransport ( {
host : 'staff.libraryofcode.org' ,
port : 587 ,
auth : { user : 'internal' , pass : this.client.config.emailPass } ,
} ) ;
this . pbx = new PBX ( this . client ) ;
this . report = new Report ( this . client ) ;
}
get emojis() {
return {
SUCCESS : emotes.success ,
LOADING : emotes.loading ,
ERROR : emotes.error ,
} ;
}
2021-03-28 23:24:56 -04:00
public async addUserToMailingList ( email : string , list : string ) {
try {
const { data } = await axios ( {
method : 'POST' ,
url : ` localhost:2161/list/ ${ list } / ` ,
params : { auth : this.client.config.internalKey , email } ,
} ) ;
return true ;
} catch {
return false ;
}
}
public async removeUserFromMailingList ( email : string , list : string ) {
try {
const { data } = await axios ( {
method : 'DELETE' ,
url : ` localhost:2161/list/ ${ list } / ` ,
params : { auth : this.client.config.internalKey , email } ,
} ) ;
return true ;
} catch {
return false ;
}
}
2021-03-19 21:23:38 -04:00
public hrn ( number : any , fixed : number , formatter : any | any [ ] ) {
const builtInFormatters = {
en : [ 'KMGTPEZY' . split ( '' ) , 1 e3 ] ,
zh_CN : [ '百千万亿兆京垓秭' . split ( '' ) , [ 100 , 10 , 10 , 1 e4 , 1 e4 , 1 e4 , 1 e4 , 1 e4 ] ] ,
} ;
number = Math . abs ( number ) ;
if ( ! fixed && fixed !== 0 ) fixed = 1 ;
if ( typeof formatter === 'object' ) {
const name = ` ${ new Date ( ) . getTime ( ) } ` ;
builtInFormatters [ name ] = formatter ;
formatter = name ;
}
if ( ! builtInFormatters [ formatter ] ) formatter = 'en' ;
formatter = builtInFormatters [ formatter ] ;
let power = 1 ;
const texts = formatter [ 0 ] ;
const powers = formatter [ 1 ] ;
let loop = 0 ;
let is_array = false ;
if ( typeof powers === 'object' ) is_array = true ;
// eslint-disable-next-line no-constant-condition
while ( 1 ) {
if ( is_array ) power = powers [ loop ] ;
else power = powers ;
if ( number >= power && loop < texts . length ) number /= power ;
else {
number = number . toFixed ( fixed ) ;
return loop === 0 ? number : ` ${ number } ${ texts [ loop - 1 ] } ` ;
}
// eslint-disable-next-line no-plusplus
++ loop ;
}
}
/ * *
* Resolves a command
* @param query Command input
* @param message Only used to check for errors
* /
/ * p u b l i c r e s o l v e C o m m a n d ( q u e r y : s t r i n g | s t r i n g [ ] ) : P r o m i s e < { c m d : C o m m a n d , a r g s : s t r i n g [ ] } > {
try {
if ( typeof query === 'string' ) query = query . split ( ' ' ) ;
const commands = this . client . commands . toArray ( ) ;
const resolvedCommand = commands . find ( ( c ) = > c . name === query [ 0 ] . toLowerCase ( ) || c . aliases . includes ( query [ 0 ] . toLowerCase ( ) ) ) ;
if ( ! resolvedCommand ) return Promise . resolve ( null ) ;
query . shift ( ) ;
return Promise . resolve ( { cmd : resolvedCommand , args : query } ) ;
} catch ( error ) {
return Promise . reject ( error ) ;
}
}
* /
public dataConversion ( bytes : number ) : string {
const i = Math . floor ( Math . log ( bytes ) / Math . log ( 1024 ) ) ;
const sizes = [ 'B' , 'KB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ] ;
if ( bytes === 0 ) {
return '0 KB' ;
}
return ` ${ ( bytes / 1024 * * i ) . toFixed ( 2 ) } ${ sizes [ i ] } ` ;
}
public async exec ( command : string , _options : childProcess.ExecOptions = { } ) : Promise < string > {
const ex = promisify ( childProcess . exec ) ;
try {
return ( await ex ( command ) ) . stdout ;
} catch ( err ) {
return err ;
}
/ * r e t u r n n e w P r o m i s e ( ( r e s , r e j ) = > {
let output = '' ;
const writeFunction = ( data : string | Buffer | Error ) = > {
output += ` ${ data } ` ;
} ;
const cmd = childProcess . exec ( command , options ) ;
cmd . stdout . on ( 'data' , writeFunction ) ;
cmd . stderr . on ( 'data' , writeFunction ) ;
cmd . on ( 'error' , writeFunction ) ;
cmd . once ( 'close' , ( code , signal ) = > {
cmd . stdout . off ( 'data' , writeFunction ) ;
cmd . stderr . off ( 'data' , writeFunction ) ;
cmd . off ( 'error' , writeFunction ) ;
setTimeout ( ( ) = > { } , 1000 ) ;
if ( code !== 0 ) rej ( new Error ( ` Command failed: ${ command } \ n ${ output } ` ) ) ;
res ( output ) ;
} ) ;
} ) ; * /
}
/ * *
* Resolves a command
* @param query Command input
* @param message Only used to check for errors
* /
public resolveCommand ( query : string | string [ ] , message? : Message ) : Promise < { cmd : Command , args : string [ ] } > {
try {
let resolvedCommand : Command ;
if ( typeof query === 'string' ) query = query . split ( ' ' ) ;
const commands = this . client . commands . toArray ( ) ;
resolvedCommand = commands . find ( ( c ) = > c . name === query [ 0 ] . toLowerCase ( ) || c . aliases . includes ( query [ 0 ] . toLowerCase ( ) ) ) ;
if ( ! resolvedCommand ) return Promise . resolve ( null ) ;
query . shift ( ) ;
while ( resolvedCommand . subcommands . size && query . length ) {
const subCommands = resolvedCommand . subcommands . toArray ( ) ;
const found = subCommands . find ( ( c ) = > c . name === query [ 0 ] . toLowerCase ( ) || c . aliases . includes ( query [ 0 ] . toLowerCase ( ) ) ) ;
if ( ! found ) break ;
resolvedCommand = found ;
query . shift ( ) ;
}
return Promise . resolve ( { cmd : resolvedCommand , args : query } ) ;
} catch ( error ) {
if ( message ) this . handleError ( error , message ) ;
else this . handleError ( error ) ;
return Promise . reject ( error ) ;
}
}
public resolveGuildChannel ( query : string , { channels } : Guild , categories = false ) : AnyGuildChannel | undefined {
const ch : AnyGuildChannel [ ] = channels . filter ( ( c ) = > ( ! categories ? c . type !== 4 : true ) ) ;
return ch . find ( ( c ) = > c . id === query . replace ( /[<#>]/g , '' ) || c . name === query )
|| ch . find ( ( c ) = > c . name . toLowerCase ( ) === query . toLowerCase ( ) )
|| ch . find ( ( c ) = > c . name . toLowerCase ( ) . startsWith ( query . toLowerCase ( ) ) ) ;
}
public resolveRole ( query : string , { roles } : Guild ) : Role | undefined {
return roles . find ( ( r ) = > r . id === query . replace ( /[<@&>]/g , '' ) || r . name === query )
|| roles . find ( ( r ) = > r . name . toLowerCase ( ) === query . toLowerCase ( ) )
|| roles . find ( ( r ) = > r . name . toLowerCase ( ) . startsWith ( query . toLowerCase ( ) ) ) ;
}
public resolveMember ( query : string , { members } : Guild ) : Member | undefined {
return members . find ( ( m ) = > ` ${ m . username } # ${ m . discriminator } ` === query || m . username === query || m . id === query . replace ( /[<@!>]/g , '' ) || m . nick === query ) // Exact match for mention, username+discrim, username and user ID
|| members . find ( ( m ) = > ` ${ m . username . toLowerCase ( ) } # ${ m . discriminator } ` === query . toLowerCase ( ) || m . username . toLowerCase ( ) === query . toLowerCase ( ) || ( m . nick && m . nick . toLowerCase ( ) === query . toLowerCase ( ) ) ) // Case insensitive match for username+discrim, username
|| members . find ( ( m ) = > m . username . toLowerCase ( ) . startsWith ( query . toLowerCase ( ) ) || ( m . nick && m . nick . toLowerCase ( ) . startsWith ( query . toLowerCase ( ) ) ) ) ;
}
public async handleError ( error : Error , message? : Message , command? : Command , disable = true ) : Promise < void > {
try {
this . signale . error ( error ) ;
const info : WebhookPayload = { content : ` \` \` \` js \ n ${ error . stack || error } \ n \` \` \` ` , embeds : [ ] } ;
if ( message ) {
const embed = new RichEmbed ( ) ;
embed . setColor ( 'FF0000' ) ;
embed . setAuthor ( ` Error caused by ${ message . author . username } # ${ message . author . discriminator } ` , message . author . avatarURL ) ;
embed . setTitle ( 'Message content' ) ;
embed . setDescription ( message . content ) ;
embed . addField ( 'User' , ` ${ message . author . mention } ( \` ${ message . author . id } \` ) ` , true ) ;
embed . addField ( 'Channel' , message . channel . mention , true ) ;
let guild : string ;
if ( message . channel instanceof PrivateChannel || message . channel instanceof GroupChannel ) guild = '@me' ;
else guild = message . channel . guild . id ;
embed . addField ( 'Message link' , ` [Click here](https://discordapp.com/channels/ ${ guild } / ${ message . channel . id } / ${ message . id } ) ` , true ) ;
embed . setTimestamp ( new Date ( message . timestamp ) ) ;
info . embeds . push ( embed ) ;
}
await this . client . executeWebhook ( this . client . config . webhookID , this . client . config . webhookToken , info ) ;
const msg = message ? message . content . slice ( this . client . config . prefix . length ) . trim ( ) . split ( / +/g ) : [ ] ;
if ( command && disable ) this . resolveCommand ( msg ) . then ( ( c ) = > { c . cmd . enabled = false ; } ) ;
if ( message ) message . channel . createMessage ( ` *** ${ this . emojis . ERROR } An unexpected error has occured - please contact a Staff member. ${ command && disable ? ' This command has been disabled.' : '' } *** ` ) ;
} catch ( err ) {
this . signale . error ( err ) ;
}
}
public splitString ( string : string , length : number ) : string [ ] {
if ( ! string ) return [ ] ;
if ( Array . isArray ( string ) ) string = string . join ( '\n' ) ;
if ( string . length <= length ) return [ string ] ;
const arrayString : string [ ] = [ ] ;
let str : string = '' ;
let pos : number ;
while ( string . length > 0 ) {
pos = string . length > length ? string . lastIndexOf ( '\n' , length ) : string . length ;
if ( pos > length ) pos = length ;
str = string . substr ( 0 , pos ) ;
string = string . substr ( pos ) ;
arrayString . push ( str ) ;
}
return arrayString ;
}
public splitFields ( fields : { name : string , value : string , inline? : boolean } [ ] ) : { name : string , value : string , inline? : boolean } [ ] [ ] {
let index = 0 ;
const array : { name : string , value : string , inline? : boolean } [ ] [ ] = [ [ ] ] ;
while ( fields . length ) {
if ( array [ index ] . length >= 25 ) { index += 1 ; array [ index ] = [ ] ; }
array [ index ] . push ( fields [ 0 ] ) ; fields . shift ( ) ;
}
return array ;
}
public splitArray < T > ( array : T [ ] , count : number ) {
const finalArray : T [ ] [ ] = [ ] ;
while ( array . length ) {
finalArray . push ( array . splice ( 0 , count ) ) ;
}
return finalArray ;
}
public decimalToHex ( int : number ) : string {
const hex = int . toString ( 16 ) ;
return '#000000' . substring ( 0 , 7 - hex . length ) + hex ;
}
public randomNumber ( min : number , max : number ) : number {
return Math . round ( Math . random ( ) * ( max - min ) + min ) ;
}
public encode ( arg : string ) {
return arg . split ( '' ) . map ( ( x ) = > x . charCodeAt ( 0 ) / 400 ) ;
}
public normalize ( string ) {
const input = [ ] ;
// eslint-disable-next-line no-plusplus
for ( let i = 0 ; i < string . length ; i ++ ) {
input . push ( string . charCodeAt ( i ) / 1000 ) ;
}
return input ;
}
public convert_ascii ( ascii : [ ] ) {
let string = '' ;
// eslint-disable-next-line no-plusplus
for ( let i = 0 ; i < ascii . length ; i ++ ) {
string += String . fromCharCode ( ascii [ i ] * 4000 ) ;
}
return string ;
}
public percentile ( arr : number [ ] , val : number ) {
return ( 100 * arr . reduce ( ( acc , v ) = > acc + ( v < val ? 1 : 0 ) + ( v === val ? 0.5 : 0 ) , 0 ) ) / arr . length ;
}
public ordinal ( i : number ) {
const j = i % 10 ;
const k = i % 100 ;
if ( j === 1 && k !== 11 ) {
return ` ${ i } st ` ;
}
if ( j === 2 && k !== 12 ) {
return ` ${ i } nd ` ;
}
if ( j === 3 && k !== 13 ) {
return ` ${ i } rd ` ;
}
return ` ${ i } th ` ;
}
public capsFirstLetter ( string ? : string ) : string | void {
if ( typeof string !== 'string' ) return undefined ;
return string . substring ( 0 , 1 ) . toUpperCase ( ) + string . substring ( 1 ) ;
}
}