import { ClusterClient , Collections , GatewayClientEvents , ShardClient , Structures } from 'detritus-client' ; import { ActivityActionTypes , AuditLogActions , AuditLogChangeKeys , ClientEvents , MessageFlags , UserFlags as DiscordUserFlags , } from 'detritus-client/lib/constants' ; import { Embed , Markup , Snowflake , intToHex , intToRGB } from 'detritus-client/lib/utils' ; import { Endpoints as DiscordEndpoints , RequestTypes } from 'detritus-client-rest' ; import { EventSubscription , Timers } from 'detritus-utils' ; import GuildSettingsStore from './guildsettings' ; import { Store } from './store' ; import { BooleanEmojis , DateMomentLogFormat , DiscordUserFlagsText , EmbedColors , GuildLoggerFlags , GuildLoggerTypes , PermissionsText , RedisChannels , } from '../constants' ; import { RedisSpewer } from '../redis' ; import { createColorUrl , createTimestampMomentFromGuild , createTimestampStringFromGuild , createUserEmbed , createUserString , permissionsToObject , } from '../utils' ; type ClientStatusType = 'desktop' | 'mobile' | 'web' ; export type GuildLoggingEventItemAudits = [ Structures . AuditLog , ... Array < Structures . AuditLog > ] ; export type GuildLoggingEventItem = { audits ? : GuildLoggingEventItemAudits ; guildId : string ; happened : number ; name : ClientEvents . GUILD_BAN_ADD ; payload : GatewayClientEvents . ClusterEvent & GatewayClientEvents . GuildBanAdd ; } | { audits ? : GuildLoggingEventItemAudits ; guildId : string ; happened : number ; name : ClientEvents . GUILD_BAN_REMOVE ; payload : GatewayClientEvents . ClusterEvent & GatewayClientEvents . GuildBanRemove ; } | { audits ? : GuildLoggingEventItemAudits ; cached : { from ? : ClientStatusType ; memberCount : number ; } ; guildId : string ; happened : number ; name : ClientEvents . GUILD_MEMBER_ADD ; payload : GatewayClientEvents . ClusterEvent & GatewayClientEvents . GuildMemberAdd ; } | { audits ? : GuildLoggingEventItemAudits ; cached : { memberCount : number ; } ; guildId : string ; happened : number ; name : ClientEvents . GUILD_MEMBER_REMOVE ; payload : GatewayClientEvents . ClusterEvent & GatewayClientEvents . GuildMemberRemove ; } | { audits ? : GuildLoggingEventItemAudits ; cached : { member : Structures . Member ; old : Structures . Member ; } ; guildId : string ; happened : number ; name : ClientEvents . GUILD_MEMBER_UPDATE ; payload : GatewayClientEvents . ClusterEvent & GatewayClientEvents . GuildMemberUpdate ; } | { audits ? : GuildLoggingEventItemAudits ; guildId : string ; happened : number ; name : ClientEvents . MESSAGE_DELETE ; payload : GatewayClientEvents . ClusterEvent & GatewayClientEvents . MessageDelete ; } | { audits ? : GuildLoggingEventItemAudits ; guildId : string ; happened : number ; name : ClientEvents . MESSAGE_DELETE_BULK ; payload : GatewayClientEvents . ClusterEvent & GatewayClientEvents . MessageDeleteBulk ; } | { audits ? : GuildLoggingEventItemAudits ; cached : { message : Structures . Message ; old : Structures . Message | null ; } ; guildId : string ; happened : number ; name : ClientEvents . MESSAGE_UPDATE ; payload : GatewayClientEvents . ClusterEvent & GatewayClientEvents . MessageUpdate ; } | { audits ? : GuildLoggingEventItemAudits ; cached : { avatars ? : { current ? : { filename : string ; value : Buffer ; } ; old ? : { filename : string ; value : Buffer ; } ; } ; old : Structures . User ; user : Structures . User ; } ; guildId : string ; happened : number ; name : ClientEvents . USERS_UPDATE ; payload : GatewayClientEvents . ClusterEvent & GatewayClientEvents . UsersUpdate ; } | { audits ? : GuildLoggingEventItemAudits ; cached : { old : Structures . VoiceState | null ; voiceState : Structures . VoiceState ; } ; guildId : string ; happened : number ; name : ClientEvents . VOICE_STATE_UPDATE ; payload : GatewayClientEvents . ClusterEvent & GatewayClientEvents . VoiceStateUpdate ; } | { audits ? : GuildLoggingEventItemAudits ; cached : { role : Structures . Role ; } ; guildId : string ; happened : number ; name : ClientEvents . GUILD_ROLE_CREATE ; payload : GatewayClientEvents . ClusterEvent & GatewayClientEvents . GuildRoleCreate ; } | { audits ? : GuildLoggingEventItemAudits ; guildId : string ; happened : number ; name : ClientEvents . GUILD_ROLE_DELETE ; payload : GatewayClientEvents . ClusterEvent & GatewayClientEvents . GuildRoleDelete ; } | { audits ? : GuildLoggingEventItemAudits ; cached : { icons ? : { current ? : { filename : string ; value : Buffer ; } ; old ? : { filename : string ; value : Buffer ; } ; } ; old : Structures . Role ; role : Structures . Role ; } ; guildId : string ; happened : number ; name : ClientEvents . GUILD_ROLE_UPDATE ; payload : GatewayClientEvents . ClusterEvent & GatewayClientEvents . GuildRoleUpdate ; } ; export interface GuildLogStorage { queues : Collections . BaseCollection < GuildLoggerTypes , Array < GuildLoggingEventItem > > ; shard : ShardClient ; timeout : Timers . Timeout ; } export const AUDITLOG_EVENTS = [ ClientEvents . GUILD_BAN_ADD , ClientEvents . GUILD_BAN_REMOVE , ClientEvents . GUILD_MEMBER_ADD , ClientEvents . GUILD_MEMBER_REMOVE , ClientEvents . GUILD_MEMBER_UPDATE , ClientEvents . GUILD_ROLE_CREATE , ClientEvents . GUILD_ROLE_DELETE , ClientEvents . GUILD_ROLE_UPDATE , ClientEvents . MESSAGE_DELETE , ClientEvents . MESSAGE_DELETE_BULK , ClientEvents . VOICE_STATE_UPDATE , ] ; export const AUDIT_LEEWAY_TIME = 2000 ; export const COLLECTION_TIME = 1000 ; export const MAX_EMBED_SIZE = 6000 ; export const MAX_MENTIONS = 10 ; class GuildLoggingStore extends Store < string , GuildLogStorage > { async add ( guildId , type , shard , event ) { await Timers . sleep ( 250 ) ; const storage = this . getOrCreate ( guildId , shard ) ; let queue ; if ( storage . queues . has ( type ) ) { queue = storage . queues . get ( type ) as Array < GuildLoggingEventItem > ; } else { queue = [ ] ; storage . queues . set ( type , queue ) ; } queue . push ( event ) ; this . startTimeout ( guildId ) ; } getOrCreate ( guildId , shard ) { let storage ; if ( this . has ( guildId ) ) { storage = this . get ( guildId ) as GuildLogStorage ; } else { storage = { queues : new Collections . BaseCollection < GuildLoggerTypes , Array < GuildLoggingEventItem > > ( ) , shard , timeout : new Timers . Timeout ( ) , } ; this . set ( guildId , storage ) ; } return storage ; } startTimeout ( guildId ) { const storage = this . get ( guildId ) ; if ( ! storage ) { return ; } const { queues , shard , timeout } = storage ; timeout . start ( COLLECTION_TIME , async ( ) => { const guild = shard . guilds . get ( guildId ) ; const canViewAuditLogs = ( guild ) ? ! ! ( guild . me && guild . me . canViewAuditLogs ) : false ; let shouldFetchAuditLogs = canViewAuditLogs && queues . some ( ( queue ) => { return queue . some ( ( event ) => { if ( event . audits ) { return false ; } if ( AUDITLOG_EVENTS . includes ( event . name ) ) { switch ( event . name ) { case ClientEvents . GUILD_MEMBER_ADD : { const { member } = event . payload ; return member . bot ; } ; case ClientEvents . GUILD_ROLE_CREATE : { const { role } = event . payload ; return ! role . botId && ! role . integrationId ; } ; case ClientEvents . MESSAGE_DELETE : { const { message } = event . payload ; return ! ! message ; } ; case ClientEvents . MESSAGE_DELETE_BULK : { const { messages } = event . payload ; return messages . every ( ( message ) => ( message ) ? ! ! message . author . id : true ) ; } ; case ClientEvents . VOICE_STATE_UPDATE : { const { differences } = event . payload ; if ( differences ) { return 'deaf' in differences || 'mute' in differences ; } return false ; } ; } return true ; } return false ; } ) ; } ) ; if ( shouldFetchAuditLogs ) { const auditLogs = await shard . rest . fetchGuildAuditLogs ( guildId , { limit : 50 } ) ; for ( let [ auditLogId , auditLog ] of auditLogs ) { let events ; switch ( auditLog . actionType ) { case AuditLogActions . BOT_ADD : { events = findEventsForBotAdd ( auditLog , storage ) ; } ; break ; case AuditLogActions . MEMBER_BAN_ADD : { events = findEventsForMemberBanAdd ( auditLog , storage ) ; } ; break ; case AuditLogActions . MEMBER_BAN_REMOVE : { events = findEventsForMemberBanRemove ( auditLog , storage ) ; } ; break ; case AuditLogActions . MEMBER_KICK : { events = findEventsForMemberKick ( auditLog , storage ) ; } ; break ; case AuditLogActions . MEMBER_ROLE_UPDATE : { events = findEventsForMemberRoleUpdate ( auditLog , storage ) ; } ; break ; case AuditLogActions . MEMBER_UPDATE : { events = findEventsForMemberUpdate ( auditLog , storage ) ; } ; break ; case AuditLogActions . MESSAGE_DELETE : { events = findEventsForMessageDelete ( auditLog , storage ) ; } ; break ; case AuditLogActions . MESSAGE_BULK_DELETE : { events = findEventsForMessageBulkDelete ( auditLog , storage ) ; } ; break ; case AuditLogActions . ROLE_CREATE : { events = findEventsForRoleCreate ( auditLog , storage ) ; } ; break ; case AuditLogActions . ROLE_DELETE : { events = findEventsForRoleDelete ( auditLog , storage ) ; } ; break ; case AuditLogActions . ROLE_UPDATE : { events = findEventsForRoleUpdate ( auditLog , storage ) ; } ; break ; } if ( events ) { for ( let event of events ) { if ( event . audits ) { event . audits . push ( auditLog ) ; } else { event . audits = [ auditLog ] ; } } } } } const settings = await GuildSettingsStore . getOrFetch ( { client : shard } , guildId ) ; if ( settings ) { const promises = [ ] ; for ( let [ loggerType , queue ] of queues ) { const unfilteredPayloads = queue . splice ( 0 , 10 ) . map ( ( event ) => createLogPayload ( event ) ) ; const payloads = [ ] ; while ( unfilteredPayloads . length ) { const embeds = [ ] ; const payload = { embeds } ; let total = 0 ; for ( let { embed , files } of unfilteredPayloads ) { if ( files . length ) { if ( ! embeds . length ) { embeds . push ( embed ) ; payload . files = files ; } break ; } if ( total + embed . length <= MAX_EMBED_SIZE ) { total += embed . length ; embeds . push ( embed ) ; } else { break ; } } unfilteredPayloads . splice ( 0 , embeds . length ) ; payloads . push ( payload ) ; } if ( payloads . length ) { const loggers = settings . loggers . filter ( ( logger ) => logger . type === loggerType ) ; for ( let logger of loggers ) { for ( let payload of payloads ) { promises . push ( logger . execute ( shard , payload ) ) ; } } } else { queues . delete ( loggerType ) ; } } await Promise . all ( promises ) ; if ( queues . some ( ( queue ) => ! ! queue . length ) ) { return this . startTimeout ( guildId ) ; } } this . delete ( guildId ) ; } ) ; } async tryAdd ( shard , event ) { const { guildId } = event ; const settings = await GuildSettingsStore . getOrFetch ( { client : shard } , guildId ) ; if ( ! settings || settings . blocked ) { return ; } let shouldLog = false ; switch ( event . name ) { case ClientEvents . GUILD_BAN_ADD : shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . GUILD_BAN_ADD ) ; break ; case ClientEvents . GUILD_BAN_REMOVE : shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . GUILD_BAN_REMOVE ) ; break ; case ClientEvents . GUILD_MEMBER_ADD : shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . GUILD_MEMBER_ADD ) ; break ; case ClientEvents . GUILD_MEMBER_REMOVE : shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . GUILD_MEMBER_REMOVE ) ; break ; case ClientEvents . GUILD_MEMBER_UPDATE : shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . GUILD_MEMBER_UPDATE ) ; break ; case ClientEvents . GUILD_ROLE_CREATE : shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . GUILD_ROLE_CREATE ) ; break ; case ClientEvents . GUILD_ROLE_DELETE : shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . GUILD_ROLE_DELETE ) ; break ; case ClientEvents . GUILD_ROLE_UPDATE : shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . GUILD_ROLE_UPDATE ) ; break ; case ClientEvents . MESSAGE_DELETE : shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . MESSAGE_DELETE ) ; break ; case ClientEvents . MESSAGE_DELETE_BULK : shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . MESSAGE_DELETE_BULK ) ; break ; case ClientEvents . MESSAGE_UPDATE : shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . MESSAGE_UPDATE ) ; break ; case ClientEvents . USERS_UPDATE : shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . USER_UPDATE ) ; break ; case ClientEvents . VOICE_STATE_UPDATE : { const { differences , leftChannel } = event . payload ; if ( ! differences || leftChannel ) { shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . VOICE_CHANNEL_CONNECTION ) ; } else { if ( differences . channelId ) { shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . VOICE_CHANNEL_MOVE ) ; } else { shouldLog = ! settings . hasDisabledLoggerEventFlag ( GuildLoggerFlags . VOICE_CHANNEL_MODIFY ) ; } } } ; break ; } if ( shouldLog ) { let loggerType = null ; switch ( event . name ) { case ClientEvents . GUILD_BAN_ADD : case ClientEvents . GUILD_BAN_REMOVE : { loggerType = GuildLoggerTypes . BANS ; } ; break ; case ClientEvents . GUILD_MEMBER_ADD : case ClientEvents . GUILD_MEMBER_REMOVE : case ClientEvents . GUILD_MEMBER_UPDATE : { loggerType = GuildLoggerTypes . MEMBERS ; } ; break ; case ClientEvents . GUILD_ROLE_CREATE : case ClientEvents . GUILD_ROLE_DELETE : case ClientEvents . GUILD_ROLE_UPDATE : { loggerType = GuildLoggerTypes . ROLES ; } ; break ; case ClientEvents . MESSAGE_DELETE : case ClientEvents . MESSAGE_DELETE_BULK : case ClientEvents . MESSAGE_UPDATE : { loggerType = GuildLoggerTypes . MESSAGES ; } ; break ; case ClientEvents . USERS_UPDATE : { loggerType = GuildLoggerTypes . USERS ; } ; break ; case ClientEvents . VOICE_STATE_UPDATE : { loggerType = GuildLoggerTypes . VOICE ; } ; break ; } if ( loggerType !== null && settings . loggers . some ( ( logger ) => logger . type === loggerType ) ) { this . add ( guildId , loggerType , shard , event ) ; } } } create ( cluster , redis ) { const subscriptions = [ ] ; { const subscription = cluster . subscribe ( ClientEvents . GUILD_BAN_ADD , async ( payload ) => { const { guildId , shard } = payload ; const happened = Date . now ( ) ; const name = ClientEvents . GUILD_BAN_ADD ; return this . tryAdd ( shard , { guildId , happened , name , payload } ) ; } ) ; subscriptions . push ( subscription ) ; } { const subscription = cluster . subscribe ( ClientEvents . GUILD_BAN_REMOVE , async ( payload ) => { const { guildId , shard } = payload ; const happened = Date . now ( ) ; const name = ClientEvents . GUILD_BAN_REMOVE ; return this . tryAdd ( shard , { guildId , happened , name , payload } ) ; } ) ; subscriptions . push ( subscription ) ; } { const name = ClientEvents . GUILD_MEMBER_ADD ; const subscription = cluster . subscribe ( name , async ( payload ) => { const { guildId , isDuplicate , member , shard , userId } = payload ; if ( ! isDuplicate ) { const { guild } = member ; let from = 'desktop' ; if ( member . presence && member . presence . clientStatus ) { const { clientStatus } = member . presence ; from = ( clientStatus . mobile || clientStatus . desktop || clientStatus . web || from ) as ClientStatusType ; } const happened = Date . now ( ) ; return this . tryAdd ( shard , { cached : { from , memberCount : ( guild ) ? guild . memberCount : 0 , } , guildId , happened , name , payload , } ) ; } } ) ; subscriptions . push ( subscription ) ; } { const name = ClientEvents . GUILD_MEMBER_REMOVE ; const subscription = cluster . subscribe ( name , async ( payload ) => { const { guildId , isDuplicate , shard } = payload ; if ( ! isDuplicate ) { const guild = shard . guilds . get ( guildId ) ; const happened = Date . now ( ) ; return this . tryAdd ( shard , { cached : { memberCount : ( guild ) ? guild . memberCount : 0 , } , guildId , happened , name , payload , } ) ; } } ) ; subscriptions . push ( subscription ) ; } { const keys = [ 'avatar' , 'deaf' , 'hoistedRole' , 'mute' , 'nick' , 'premiumSince' , 'roles' ] ; const name = ClientEvents . GUILD_MEMBER_UPDATE ; const subscription = cluster . subscribe ( name , async ( payload ) => { const { differences , guildId , member , old , shard } = payload ; if ( old && ( differences && keys . some ( ( key ) => key in differences ) ) ) { const happened = Date . now ( ) ; return this . tryAdd ( shard , { cached : { member : member . clone ( ) , old , } , guildId , happened , name , payload , } ) ; } } ) ; subscriptions . push ( subscription ) ; } { const name = ClientEvents . GUILD_ROLE_CREATE ; const subscription = cluster . subscribe ( name , async ( payload ) => { const { guildId , role , shard } = payload ; return this . tryAdd ( shard , { cached : { role : role . clone ( ) , } , guildId , happened : Date . now ( ) , name , payload , } ) ; } ) ; subscriptions . push ( subscription ) ; } { const name = ClientEvents . GUILD_ROLE_DELETE ; const subscription = cluster . subscribe ( name , async ( payload ) => { const { guildId , role , shard } = payload ; return this . tryAdd ( shard , { guildId , happened : Date . now ( ) , name , payload , } ) ; } ) ; subscriptions . push ( subscription ) ; } { const keys = [ 'color' , 'hoist' , 'icon' , 'mentionable' , 'name' , 'permissions' , 'position' , 'unicodeEmoji' ] ; const name = ClientEvents . GUILD_ROLE_UPDATE ; const subscription = cluster . subscribe ( name , async ( payload ) => { const { differences , guildId , old , role , shard } = payload ; if ( guildId === '178313653177548800' ) { console . log ( role , old , differences ) ; } if ( old && ( differences && keys . some ( ( key ) => key in differences ) ) ) { const cached = { old , role : role . clone ( ) } ; const happened = Date . now ( ) ; if ( old . icon !== role . icon ) { cached . icons = { } ; try { if ( old . icon ) { const url = old . iconUrlFormat ( null , { size : 512 } ) ! ; cached . icons . old = { filename : `${ role . id }-${ happened }-${ url . split ( '/' ) . pop ( ) ! . split ( '?' ) . shift ( ) }` , value : await shard . rest . get ( url ) } ; } if ( role . icon ) { const url = role . iconUrlFormat ( null , { size : 512 } ) ! ; cached . icons . current = { filename : `${ role . id }-${ happened }-${ url . split ( '/' ) . pop ( ) ! . split ( '?' ) . shift ( ) }` , value : await shard . rest . get ( url ) } ; } } catch ( error ) { cached . icons = undefined ; } } return this . tryAdd ( shard , { cached , guildId , happened , name , payload , } ) ; } } ) ; subscriptions . push ( subscription ) ; } { const subscription = cluster . subscribe ( ClientEvents . MESSAGE_DELETE , async ( payload ) => { const { guildId , message , shard } = payload ; if ( guildId && ( ! message || ! message . author . bot ) ) { const happened = Date . now ( ) ; const name = ClientEvents . MESSAGE_DELETE ; return this . tryAdd ( shard , { guildId , happened , name , payload } ) ; } } ) ; subscriptions . push ( subscription ) ; } { const subscription = cluster . subscribe ( ClientEvents . MESSAGE_DELETE_BULK , async ( payload ) => { const { guildId , shard } = payload ; if ( guildId ) { const happened = Date . now ( ) ; const name = ClientEvents . MESSAGE_DELETE_BULK ; return this . tryAdd ( shard , { guildId , happened , name , payload } ) ; } } ) ; subscriptions . push ( subscription ) ; } { const name = ClientEvents . MESSAGE_UPDATE ; const subscription = cluster . subscribe ( name , async ( payload ) => { const { guildId , isEmbedUpdate , message , old , shard } = payload ; if ( ! isEmbedUpdate && guildId && message && ! message . author . bot ) { const happened = Date . now ( ) ; return this . tryAdd ( shard , { cached : { message : message . clone ( ) , old , } , guildId , happened , name , payload , } ) ; } } ) ; subscriptions . push ( subscription ) ; } { const name = ClientEvents . USERS_UPDATE ; const subscription = cluster . subscribe ( name , async ( payload ) => { const { shard , old , user } = payload ; if ( old ) { const cached = { old , user : user . clone ( ) } ; const happened = Date . now ( ) ; if ( old . avatar !== user . avatar ) { cached . avatars = { } ; try { if ( old . avatar ) { const url = old . avatarUrlFormat ( null , { size : 512 } ) ; cached . avatars . old = { filename : `${ user . id }-${ happened }-${ url . split ( '/' ) . pop ( ) ! . split ( '?' ) . shift ( ) }` , value : await shard . rest . get ( url ) } ; } if ( user . avatar ) { const url = user . avatarUrlFormat ( null , { size : 512 } ) ; cached . avatars . current = { filename : `${ user . id }-${ happened }-${ url . split ( '/' ) . pop ( ) ! . split ( '?' ) . shift ( ) }` , value : await shard . rest . get ( url ) } ; } } catch ( error ) { cached . avatars = undefined ; } } for ( let [ guildId , guild ] of user . guilds ) { await this . tryAdd ( shard , { cached , guildId , happened , name , payload , } ) ; } } } ) ; subscriptions . push ( subscription ) ; } { const name = ClientEvents . VOICE_STATE_UPDATE ; const subscription = cluster . subscribe ( name , async ( payload ) => { const { old , shard , voiceState } = payload ; const { guildId } = voiceState ; if ( guildId ) { const happened = Date . now ( ) ; return this . tryAdd ( shard , { cached : { old , voiceState : voiceState . clone ( ) , } , guildId , happened , name , payload , } ) ; } } ) ; subscriptions . push ( subscription ) ; } return subscriptions ; } } export default new GuildLoggingStore ( ) ; export function createLogPayload ( event ) { const { audits , guildId , happened } = event ; const embed = new Embed ( ) ; const files = [ ] ; switch ( event . name ) { case ClientEvents . GUILD_BAN_ADD : { const { user } = event . payload ; createUserEmbed ( user , embed ) ; embed . setColor ( EmbedColors . LOG_DELETION ) ; embed . setThumbnail ( user . avatarUrlFormat ( null , { size : 1024 } ) ) ; embed . setDescription ( user . mention ) ; { const timestamp = createTimestampStringFromGuild ( happened , guildId ) ; embed . setFooter ( `User Banned • ${ timestamp }` ) ; } if ( audits ) { const [ audit ] = audits ; const description = [ ] ; description . push ( `**Banned By**: ${ createUserString ( audit . userId , audit . user as Structures . User ) }` ) ; if ( audit . reason ) { description . push ( `**Reason**: ${ Markup . codeblock ( audit . reason ) }` ) ; } embed . addField ( 'Moderation Action' , description . join ( '\n' ) ) ; } } ; break ; case ClientEvents . GUILD_BAN_REMOVE : { const { user } = event . payload ; createUserEmbed ( user , embed ) ; embed . setColor ( EmbedColors . LOG_CREATION ) ; embed . setThumbnail ( user . avatarUrlFormat ( null , { size : 1024 } ) ) ; embed . setDescription ( user . mention ) ; { const timestamp = createTimestampStringFromGuild ( happened , guildId ) ; embed . setFooter ( `User Unbanned • ${ timestamp }` ) ; } if ( audits ) { const [ audit ] = audits ; const description = [ ] ; description . push ( `**Unbanned By**: ${ createUserString ( audit . userId , audit . user as Structures . User ) }` ) ; if ( audit . reason ) { description . push ( `**Reason**: ${ Markup . codeblock ( audit . reason ) }` ) ; } embed . addField ( 'Moderation Action' , description . join ( '\n' ) ) ; } } ; break ; case ClientEvents . GUILD_MEMBER_ADD : { const { from , memberCount } = event . cached ; const { member } = event . payload ; createUserEmbed ( member , embed ) ; embed . setColor ( EmbedColors . LOG_CREATION ) ; embed . setThumbnail ( member . avatarUrlFormat ( null , { size : 1024 } ) ) ; embed . setDescription ( member . mention ) ; { let footer ; if ( audits ) { footer = 'Member Added' ; } else { footer = 'Member Joined' ; } const timestamp = createTimestampStringFromGuild ( member . joinedAtUnix || happened , guildId ) ; embed . setFooter ( `${ footer } • ${ timestamp }` ) ; } if ( audits ) { const [ audit ] = audits ; const description = [ ] ; description . push ( `**Added By**: ${ createUserString ( audit . userId , audit . user as Structures . User ) }` ) ; embed . addField ( 'Moderation Action' , description . join ( '\n' ) ) ; } { const description = [ ] ; description . push ( `**Id**: ${ Markup . codestring ( member . id ) }` ) ; description . push ( `**Bot**: ${ ( member . bot ) ? 'Yes' : 'No' }` ) ; if ( memberCount ) { description . push ( `**Members**: ${ ( memberCount - 1 ) . toLocaleString ( ) } -> ${ memberCount . toLocaleString ( ) }` ) ; } embed . addField ( 'Information' , description . join ( '\n' ) , true ) ; } { const description = [ ] ; { const timestamp = createTimestampMomentFromGuild ( member . createdAtUnix , guildId ) ; description . push ( `**Discord**: ${ timestamp . fromNow ( ) }` ) ; description . push ( `**->** ${ Markup . spoiler ( timestamp . format ( DateMomentLogFormat ) ) }` ) ; } embed . addField ( 'Joined' , description . join ( '\n' ) , true ) ; } } ; break ; case ClientEvents . GUILD_MEMBER_REMOVE : { const { memberCount } = event . cached ; const { member , shard , userId } = event . payload ; const guild = shard . guilds . get ( guildId ) ; let { user } = event . payload ; if ( ! user && audits ) { const [ audit ] = audits ; if ( audit . user ) { user = audit . user ; } } if ( user ) { createUserEmbed ( user , embed ) ; embed . setThumbnail ( user . avatarUrlFormat ( null , { size : 1024 } ) ) ; } else { embed . setAuthor ( 'Unknown User' ) ; } embed . setColor ( EmbedColors . LOG_DELETION ) ; embed . setDescription ( `<@!${ userId }>` ) ; { let text ; if ( audits ) { const [ audit ] = audits ; const description = [ ] ; let userText = createUserString ( audit . userId , audit . user as Structures . User ) ; switch ( audit . actionType ) { case AuditLogActions . MEMBER_BAN_ADD : { description . push ( `**Banned By**: ${ userText }` ) ; text = 'Member Banned' ; } ; break ; case AuditLogActions . MEMBER_KICK : { description . push ( `**Kicked By**: ${ userText }` ) ; text = 'Member Kicked' ; } ; break ; default : { description . push ( `**Unknown Reason**: ${ userText }` ) ; text = `Member Left (Unknown Reason: ${ audit . actionType })` ; } ; } if ( audit . reason ) { description . push ( `**Reason**: ${ Markup . codeblock ( audit . reason ) }` ) ; } embed . addField ( 'Moderation Action' , description . join ( '\n' ) ) ; } else { text = 'Member Left' ; } { const timestamp = createTimestampStringFromGuild ( happened , guildId ) ; embed . setFooter ( `${ text } • ${ timestamp }` ) ; } } { const description = [ ] ; description . push ( `**Id**: ${ Markup . codestring ( userId ) }` ) ; if ( user ) { description . push ( `**Bot**: ${ ( user . bot ) ? 'Yes' : 'No' }` ) ; } if ( memberCount ) { description . push ( `**Members**: ${ ( memberCount + 1 ) . toLocaleString ( ) } -> ${ memberCount . toLocaleString ( ) }` ) ; } embed . addField ( 'Information' , description . join ( '\n' ) , true ) ; } if ( user ) { const description = [ ] ; { const timestamp = createTimestampMomentFromGuild ( user . createdAtUnix , guildId ) ; description . push ( `**Discord**: ${ timestamp . fromNow ( ) }` ) ; description . push ( `**->** ${ Markup . spoiler ( timestamp . format ( DateMomentLogFormat ) ) }` ) ; } if ( member && member . joinedAtUnix ) { const timestamp = createTimestampMomentFromGuild ( member . joinedAtUnix , guildId ) ; description . push ( `**Guild**: ${ timestamp . fromNow ( ) }` ) ; description . push ( `**->** ${ Markup . spoiler ( timestamp . format ( DateMomentLogFormat ) ) }` ) ; if ( guild && guild . isReady ) { } } embed . addField ( 'Joined' , description . join ( '\n' ) , true ) ; } } ; break ; case ClientEvents . GUILD_MEMBER_UPDATE : { const { member , old } = event . cached ; createUserEmbed ( member , embed ) ; embed . setThumbnail ( member . avatarUrlFormat ( null , { size : 1024 } ) ) ; embed . setColor ( EmbedColors . LOG_UPDATE ) ; embed . setDescription ( member . mention ) ; { const timestamp = createTimestampStringFromGuild ( happened , guildId ) ; embed . setFooter ( `Member Updated • ${ timestamp }` ) ; } if ( audits ) { const moderator = audits [ 0 ] . user as Structures . User ; const isSelf = member . id === moderator . id ; const description = [ ] ; if ( ! isSelf ) { description . push ( `By ${ createUserString ( moderator . id , moderator ) }` ) ; } let reason ; for ( let audit of audits ) { for ( let [ x , change ] of audit . changes ) { switch ( change . key ) { case AuditLogChangeKeys . DEAF : { if ( change . newValue ) { description . push ( `- Deafened` ) ; } else { description . push ( `- Undeafened` ) ; } } ; break ; case AuditLogChangeKeys . MUTE : { if ( change . newValue ) { description . push ( '- Microphone Muted' ) ; } else { description . push ( '- Microphone Unmuted' ) ; } } ; break ; case AuditLogChangeKeys . NICK : { description . push ( `- Nickname Change` ) ; if ( change . oldValue ) { if ( change . newValue ) { description . push ( `-> **${ Markup . codestring ( change . oldValue ) }** to **${ Markup . codestring ( change . newValue ) }**` ) ; } else { description . push ( `-> Cleared **${ Markup . codestring ( change . oldValue ) }**` ) ; } } else { description . push ( `-> Set to **${ Markup . codestring ( change . newValue ) }**` ) ; } } ; break ; case AuditLogChangeKeys . ROLES_ADD : { description . push ( `- Added Roles` ) ; const roles = change . newValue . map ( ( raw ) => { return `<@&${ raw . id }> (${ Markup . escape . all ( raw . name ) })` ; } ) ; description . push ( `-> ${ roles . join ( ', ' ) }` ) ; } ; break ; case AuditLogChangeKeys . ROLES_REMOVE : { description . push ( `- Removed Roles` ) ; const roles = change . newValue . map ( ( raw ) => { return `<@&${ raw . id }> (${ Markup . escape . all ( raw . name ) })` ; } ) ; description . push ( `-> ${ roles . join ( ', ' ) }` ) ; } ; break ; default : { description . push ( `- ${ change . key } Changed` ) ; description . push ( `-> ${ Markup . escape . all ( JSON . stringify ( change . newValue ) ) }` ) ; } ; } } reason = audit . reason ; } if ( reason ) { description . push ( `**Reason**: ${ Markup . codeblock ( reason ) }` ) ; } if ( isSelf ) { embed . addField ( 'Self-Action' , description . join ( '\n' ) ) ; } else { embed . addField ( 'Moderation Action' , description . join ( '\n' ) ) ; } } { const description = [ ] ; if ( ! audits ) { if ( member . deaf !== old . deaf || member . mute !== old . deaf ) { const text = [ ] ; if ( member . deaf !== old . deaf ) { text . push ( ( member . deaf ) ? 'Deafened' : 'Undeafened' ) ; } if ( member . mute !== old . mute ) { text . push ( ( member . mute ) ? 'Muted' : 'Unmuted' ) ; } if ( text . length ) { description . push ( `- Server ${ text . join ( ' and ' ) }` ) ; } } if ( member . nick !== old . nick ) { description . push ( `- Nickname Change` ) ; if ( member . nick ) { if ( old . nick ) { description . push ( `-> **${ Markup . codestring ( old . nick ) }** to **${ Markup . codestring ( member . nick ) }**` ) ; } else { description . push ( `-> Set to **${ Markup . codestring ( member . nick || '' ) }**` ) ; } } else { description . push ( `-> Cleared **${ Markup . codestring ( old . nick || '' ) }**` ) ; } } if ( member . hasDifference ( 'roles' , old . _roles ) ) { const additions = [ ] ; const removals = [ ] ; for ( let [ roleId , role ] of member . roles ) { if ( ! old . roles . has ( roleId ) ) { if ( role ) { additions . push ( `<@&${ roleId }> (${ Markup . escape . all ( role . name ) })` ) ; } else { additions . push ( `<@&${ roleId }>` ) ; } } } for ( let [ roleId , role ] of old . roles ) { if ( ! member . roles . has ( roleId ) ) { if ( role ) { removals . push ( `<@&${ roleId }> (${ Markup . escape . all ( role . name ) })` ) ; } else { removals . push ( `<@&${ roleId }>` ) ; } } } if ( removals . length ) { description . push ( `- Removed Roles` ) ; description . push ( `-> ${ removals . join ( ', ' ) }` ) ; } if ( additions . length ) { description . push ( `- Added Roles` ) ; description . push ( `-> ${ additions . join ( ', ' ) }` ) ; } } } if ( member . hoistedRoleId !== old . hoistedRoleId ) { description . push ( '- Hoisted Role Change' ) ; let removedText = '' ; if ( old . hoistedRoleId ) { const role = old . hoistedRole ; if ( role ) { removedText = `<@&${ old . hoistedRoleId }> (${ Markup . escape . all ( role . name ) })` ; } else { removedText = `<@&${ old . hoistedRoleId }>` ; } } if ( member . hoistedRoleId ) { let addedText = '' ; if ( member . hoistedRoleId ) { const role = member . hoistedRole ; if ( role ) { addedText = `<@&${ member . hoistedRoleId }> (${ Markup . escape . all ( role . name ) })` ; } else { addedText = `<@&${ member . hoistedRoleId }>` ; } } if ( old . hoistedRoleId ) { description . push ( `-> ${ removedText } to ${ addedText }` ) ; } else { description . push ( `-> Added ${ addedText }` ) ; } } else { description . push ( `-> Removed ${ removedText }` ) ; } } if ( member . premiumSinceUnix !== old . premiumSinceUnix ) { description . push ( '- Boost Change' ) ; if ( member . premiumSinceUnix ) { description . push ( `-> Boosted` ) ; } else { description . push ( `-> Unboosted` ) ; } } if ( member . avatar !== old . avatar ) { if ( member . avatar && old . avatar ) { description . push ( '- Changed their Server Avatar' ) ; } else if ( member . avatar && ! old . avatar ) { description . push ( '- Set their Server Avatar' ) ; } else if ( ! member . avatar && old . avatar ) { description . push ( '- Removed their Server Avatar' ) ; } } if ( description . length ) { embed . addField ( 'Changes' , description . join ( '\n' ) ) ; } } } ; break ; case ClientEvents . GUILD_ROLE_CREATE : { const { role } = event . cached ; { const timestamp = createTimestampStringFromGuild ( happened , guildId ) ; embed . setFooter ( `Role Created • ${ timestamp }` ) ; } embed . setColor ( EmbedColors . LOG_CREATION ) ; embed . setDescription ( `${ role . mention } ${ Markup . spoiler ( `(${ role . id })` ) }` ) ; embed . setAuthor ( role . name ) ; if ( role . color ) { const url = createColorUrl ( role . color ) ; embed . setAuthor ( role . name , url ) ; } if ( audits ) { const [ audit ] = audits ; const description = [ ] ; description . push ( `**Created By**: ${ createUserString ( audit . userId , audit . user as Structures . User ) }` ) ; embed . addField ( 'Moderation Action' , description . join ( '\n' ) ) ; } { const description = [ ] ; if ( role . color ) { const color = intToRGB ( role . color ) ; const hex = Markup . codestring ( intToHex ( role . color , true ) ) ; const rgb = Markup . codestring ( `(${ color . r }, ${ color . g }, ${ color . b })` ) ; description . push ( `**Color**: ${ hex } ${ rgb }` ) ; } else { description . push ( `**Color**: No Color` ) ; } description . push ( `**Hoisted**: ${ ( role . hoist ) ? 'Yes' : 'No' }` ) ; description . push ( `**Id**: \`${ role . id }\`` ) ; description . push ( `**Managed**: ${ ( role . managed ) ? 'Yes' : 'No' }` ) ; description . push ( `**Mentionable**: ${ ( role . mentionable ) ? 'Yes' : 'No' }` ) ; description . push ( `**Position**: ${ role . position }` ) ; if ( role . tags ) { if ( role . isBoosterRole ) { description . push ( '**Type**: Booster Role' ) ; } else if ( role . botId ) { description . push ( `**Type**: Bot Role (for <@${ role . botId }>)` ) ; } else if ( role . integrationId ) { description . push ( `**Type**: Integration Role (${ role . integrationId })` ) ; } else { description . push ( `**Type**: Unknown (${ JSON . stringify ( role . tags ) })` ) ; } } embed . addField ( 'Information' , description . join ( '\n' ) ) ; } } ; break ; case ClientEvents . GUILD_ROLE_DELETE : { const { role , roleId } = event . payload ; { const timestamp = createTimestampStringFromGuild ( happened , guildId ) ; embed . setFooter ( `Role Deleted • ${ timestamp }` ) ; } embed . setColor ( EmbedColors . LOG_DELETION ) ; embed . setDescription ( `<@&${ roleId }> ${ Markup . spoiler ( `(${ roleId })` ) }` ) ; if ( audits ) { const [ audit ] = audits ; const description = [ ] ; description . push ( `**Deleted By**: ${ createUserString ( audit . userId , audit . user as Structures . User ) }` ) ; embed . addField ( 'Moderation Action' , description . join ( '\n' ) ) ; } if ( role ) { embed . setAuthor ( role . name ) ; if ( role . color ) { const url = createColorUrl ( role . color ) ; embed . setAuthor ( role . name , url ) ; } { const description = [ ] ; if ( role . color ) { const color = intToRGB ( role . color ) ; const hex = Markup . codestring ( intToHex ( role . color , true ) ) ; const rgb = Markup . codestring ( `(${ color . r }, ${ color . g }, ${ color . b })` ) ; description . push ( `**Color**: ${ hex } ${ rgb }` ) ; } else { description . push ( `**Color**: No Color` ) ; } { const timestamp = createTimestampMomentFromGuild ( role . createdAtUnix , guildId ) ; description . push ( `**Created**: ${ timestamp . fromNow ( ) }` ) ; description . push ( `**->** ${ Markup . spoiler ( timestamp . format ( DateMomentLogFormat ) ) }` ) ; } description . push ( `**Hoisted**: ${ ( role . hoist ) ? 'Yes' : 'No' }` ) ; description . push ( `**Id**: \`${ role . id }\`` ) ; description . push ( `**Managed**: ${ ( role . managed ) ? 'Yes' : 'No' }` ) ; description . push ( `**Mentionable**: ${ ( role . mentionable ) ? 'Yes' : 'No' }` ) ; description . push ( `**Position**: ${ role . position }` ) ; if ( role . tags ) { if ( role . isBoosterRole ) { description . push ( '**Type**: Booster Role' ) ; } else if ( role . botId ) { description . push ( `**Type**: Bot Role (for <@${ role . botId }>)` ) ; } else if ( role . integrationId ) { description . push ( `**Type**: Integration Role (${ role . integrationId })` ) ; } else { description . push ( `**Type**: Unknown (${ JSON . stringify ( role . tags ) })` ) ; } } embed . addField ( 'Information' , description . join ( '\n' ) ) ; } } } ; break ; case ClientEvents . GUILD_ROLE_UPDATE : { const { icons , old , role } = event . cached ; { const timestamp = createTimestampStringFromGuild ( happened , guildId ) ; embed . setFooter ( `Role Updated • ${ timestamp }` ) ; } embed . setColor ( EmbedColors . LOG_UPDATE ) ; embed . setDescription ( `${ role . mention } ${ Markup . spoiler ( `(${ role . id })` ) }` ) ; embed . setAuthor ( role . name ) ; if ( role . icon ) { embed . setAuthor ( role . name , role . iconUrl ! ) ; } else if ( role . color ) { const url = createColorUrl ( role . color ) ; embed . setAuthor ( role . name , url ) ; } if ( icons ) { if ( icons . current ) { embed . setAuthor ( role . name , `attachment://${ icons . current . filename }` ) ; files . push ( icons . current ) ; } if ( icons . old ) { embed . setThumbnail ( `attachment://${ icons . old . filename }` ) ; files . push ( icons . old ) ; } } if ( audits ) { const [ audit ] = audits ; const description = [ ] ; description . push ( `**Updated By**: ${ createUserString ( audit . userId , audit . user as Structures . User ) }` ) ; embed . addField ( 'Moderation Action' , description . join ( '\n' ) ) ; } { const description = [ ] ; if ( role . color !== old . color ) { description . push ( '- Color' ) ; let addText = '' ; if ( role . color ) { const color = intToRGB ( role . color ) ; const hex = Markup . codestring ( intToHex ( role . color , true ) ) ; const rgb = Markup . codestring ( `(${ color . r }, ${ color . g }, ${ color . b })` ) ; addText = `**${ hex }** **${ rgb }**` ; } let removeText = '' ; if ( old . color ) { const color = intToRGB ( old . color ) ; const hex = Markup . codestring ( intToHex ( old . color , true ) ) ; const rgb = Markup . codestring ( `(${ color . r }, ${ color . g }, ${ color . b })` ) ; removeText = `**${ hex }** **${ rgb }**` ; } if ( role . color ) { if ( old . color ) { description . push ( `-> ${ removeText } to ${ addText }` ) ; } else { description . push ( `-> Set to ${ addText }` ) ; } } else { description . push ( `-> Cleared ${ removeText }` ) ; } } if ( role . unicodeEmoji !== old . unicodeEmoji ) { description . push ( '- Emoji' ) ; let addText = '' ; if ( role . unicodeEmoji ) { addText = role . unicodeEmoji ; if ( 15 < addText . length ) { addText = addText . slice ( 0 , 12 ) + '...' ; } addText = Markup . codestring ( addText ) ; } let removeText = '' ; if ( old . unicodeEmoji ) { removeText = old . unicodeEmoji ; if ( 15 < removeText . length ) { removeText = removeText . slice ( 0 , 12 ) + '...' ; } removeText = Markup . codestring ( removeText ) ; } if ( role . unicodeEmoji ) { if ( old . unicodeEmoji ) { description . push ( `-> ${ removeText } to ${ addText }` ) ; } else { description . push ( `-> Set to ${ addText }` ) ; } } else { description . push ( `-> Cleared ${ removeText }` ) ; } } if ( role . hoist !== old . hoist ) { if ( role . hoist ) { description . push ( '- Hoisted Role' ) ; } else { description . push ( '- Unhoisted Role' ) ; } } if ( role . icon !== old . icon ) { description . push ( '- Icon' ) ; if ( role . icon && old . icon ) { description . push ( '-> Changed Icons' ) ; } else if ( role . icon ) { description . push ( '-> Set Icon' ) ; } else { description . push ( '-> Removed Icon' ) ; } } if ( role . mentionable !== old . mentionable ) { if ( role . mentionable ) { description . push ( '- Made Mentionable' ) ; } else { description . push ( '- Made Unmentionable' ) ; } } if ( role . position !== old . position ) { description . push ( '- Position' ) ; description . push ( `-> #${ old . position } to #${ role . position }` ) ; } if ( description . length ) { embed . addField ( 'Changes' , description . join ( '\n' ) ) ; } } if ( role . permissions !== old . permissions ) { const added = [ ] ; const removed = [ ] ; const newPermissions = permissionsToObject ( role . permissions ) ; const oldPermissions = permissionsToObject ( old . permissions ) ; for ( let key in newPermissions ) { if ( newPermissions [ key ] !== oldPermissions [ key ] ) { if ( newPermissions [ key ] ) { added . push ( `${ PermissionsText [ key ] }: ${ BooleanEmojis . YES }` ) ; } else { removed . push ( `${ PermissionsText [ key ] }: ${ BooleanEmojis . NO }` ) ; } } } if ( added . length ) { embed . addField ( 'Enabled Permissions' , Markup . codeblock ( added . join ( '\n' ) ) , true ) ; } if ( removed . length ) { embed . addField ( 'Removed Permissions' , Markup . codeblock ( removed . join ( '\n' ) ) , true ) ; } } } ; break ; case ClientEvents . MESSAGE_DELETE : { const { channelId , message , messageId } = event . payload ; { const timestamp = createTimestampStringFromGuild ( happened , guildId ) ; embed . setFooter ( `Message Deleted • ${ timestamp }` ) ; } if ( message ) { createUserEmbed ( message . author , embed ) ; } else { embed . setAuthor ( 'Unknown Author' ) ; } embed . setColor ( EmbedColors . LOG_DELETION ) ; if ( audits ) { const [ audit ] = audits ; const description = [ ] ; description . push ( `By ${ createUserString ( audit . userId , audit . user as Structures . User ) }` ) ; if ( audit . reason ) { description . push ( `**Reason**: ${ Markup . codeblock ( audit . reason ) }` ) ; } embed . addField ( 'Moderation Action' , description . join ( '\n' ) ) ; } if ( message ) { if ( message . fromSystem ) { embed . setDescription ( Markup . codeblock ( message . systemContent ) ) ; } else if ( message . content ) { embed . setDescription ( Markup . codeblock ( message . content ) ) ; } if ( message . activity ) { switch ( message . activity . type ) { case ActivityActionTypes . LISTEN : { } ; break ; } } if ( message . attachments . length ) { const urls = message . attachments . filter ( ( attachment ) => { return ! ! attachment . url ; } ) . map ( ( attachment ) => { return Markup . url ( attachment . filename , attachment . url as string ) ; } ) ; embed . addField ( 'Attachments' , urls . join ( ', ' ) ) ; const imageAttachment = message . attachments . find ( ( attachment ) => attachment . isImage ) ; if ( imageAttachment && imageAttachment . url ) { embed . setThumbnail ( imageAttachment . url ) ; } } if ( message . embeds . length ) { embed . addField ( 'Embeds' , `${ message . embeds . length } Embeds` ) ; } if ( message . mentionEveryone || message . mentions . length ) { const text = [ ] ; if ( message . mentionEveryone ) { text . push ( 'Everyone' ) ; } if ( message . mentions . length ) { if ( MAX_MENTIONS <= message . mentions . length ) { text . push ( `${ message . mentions . length . toLocaleString ( ) } Users` ) ; } else { text . push ( message . mentions . map ( ( user ) => `**${ Markup . codestring ( String ( user ) ) }**` ) . join ( ', ' ) ) ; } } embed . addField ( 'Mentions' , `- ${ text . join ( ', ' ) }` ) ; } if ( message . mentionRoles . length ) { const description = [ ] ; if ( MAX_MENTIONS <= message . mentionRoles . length ) { description . push ( `- ${ message . mentionRoles . length . toLocaleString ( ) } Roles` ) ; } else { description . push ( `- ${ message . mentionRoles . map ( ( role , roleId ) => ( role ) ? `**${ Markup . codestring ( String ( role ) ) }**` : `<@&${ roleId }>` ) . join ( ', ' ) }` ) ; } embed . addField ( 'Role Mentions' , description . join ( '\n' ) ) ; } } else { embed . setDescription ( 'Content not available' ) ; } let timestamp ; if ( message ) { timestamp = createTimestampMomentFromGuild ( message . timestampUnix , guildId ) ; } else { timestamp = createTimestampMomentFromGuild ( Snowflake . timestamp ( messageId ) , guildId ) ; } embed . addField ( 'Information' , [ `**Channel**: <#${ channelId }>` , `**Created**: ${ timestamp . fromNow ( ) }` , `**->** ${ Markup . spoiler ( timestamp . format ( DateMomentLogFormat ) ) }` , `**Link**: ${ Markup . url ( messageId , DiscordEndpoints . Routes . URL + DiscordEndpoints . Routes . MESSAGE ( guildId , channelId , messageId ) ) }` , ] . join ( '\n' ) ) ; } ; break ; case ClientEvents . MESSAGE_DELETE_BULK : { const { amount , channelId , guildId , messages } = event . payload ; { const timestamp = createTimestampStringFromGuild ( happened , guildId ) ; embed . setFooter ( `Message Bulk Deletion • ${ timestamp }` ) ; } embed . setAuthor ( `${ amount . toLocaleString ( ) } Messages Deleted` ) ; embed . setColor ( EmbedColors . LOG_DELETION ) ; { const description = [ `**Amount**: ${ amount . toLocaleString ( ) }` + '\n' , ] ; if ( audits ) { const [ audit ] = audits ; switch ( audit . actionType ) { case AuditLogActions . MEMBER_BAN_ADD : { const target = audit . target as Structures . User ; createUserEmbed ( target , embed ) ; description . push ( `Bulk Deletion of ${ createUserString ( audit . targetId , target ) }'s Messages` ) ; description . push ( `Banned By ${ createUserString ( audit . userId , audit . user as Structures . User ) }` ) ; } ; break ; case AuditLogActions . MESSAGE_BULK_DELETE : { description . push ( `By ${ createUserString ( audit . userId , audit . user as Structures . User ) }` ) ; } ; break ; } if ( audit . reason ) { description . push ( `**Reason**: ${ Markup . codeblock ( audit . reason ) }` ) ; } } embed . setDescription ( description . join ( '\n' ) ) ; } const value = JSON . stringify ( { messages : messages . map ( ( message , id ) => message || { id } ) , } , null , 2 ) ; files . push ( { filename : 'messages.json' , value } ) ; embed . addField ( 'Information' , [ `**Channel**: <#${ channelId }>` , ] . join ( '\n' ) ) ; } ; break ; case ClientEvents . MESSAGE_UPDATE : { const { message , old } = event . cached ; const { channelId , differences , messageId } = event . payload ; { const time = ( message && message . isEdited ) ? message . editedAtUnix : happened ; const timestamp = createTimestampStringFromGuild ( time , guildId ) ; embed . setFooter ( `Message Updated • ${ timestamp }` ) ; } if ( message ) { createUserEmbed ( message . author , embed ) ; } else { embed . setAuthor ( 'Unknown Author' ) ; } embed . setColor ( EmbedColors . LOG_UPDATE ) ; if ( old && differences ) { if ( message . content !== old . content ) { if ( old . content ) { embed . setDescription ( Markup . codeblock ( old . content ) ) ; } else { embed . setDescription ( 'Empty Message' ) ; } if ( message . content ) { if ( 1014 < message . content . length ) { const contentOne = message . content . slice ( 0 , 1014 ) ; const contentTwo = message . content . slice ( 1014 ) ; embed . addField ( 'New Content' , Markup . codeblock ( contentOne ) ) ; embed . addField ( '\u200b' , Markup . codeblock ( contentTwo ) ) ; } else { embed . addField ( 'New Content' , Markup . codeblock ( message . content ) ) ; } } else { embed . addField ( 'New Content' , 'Empty Message' ) ; } } if ( differences . attachments ) { const oldUrls = old . attachments . filter ( ( attachment ) => ! ! attachment . url ) . map ( ( attachment ) => { return Markup . url ( attachment . filename , attachment . url as string ) ; } ) ; const newUrls = message . attachments . filter ( ( attachment ) => ! ! attachment . url ) . map ( ( attachment ) => { return Markup . url ( attachment . filename , attachment . url as string ) ; } ) ; if ( ! newUrls . every ( ( url ) => oldUrls . includes ( url ) ) ) { embed . addField ( 'Attachments' , [ `- ${ oldUrls . join ( ', ' ) || 'None' }` , `+ ${ newUrls . join ( ', ' ) || 'None' }` , ] . join ( '\n' ) ) ; } } if ( differences . embeds ) { embed . addField ( 'Embeds' , `- ${ old . embeds . length } Embeds to ${ message . embeds . length } Embeds` , true ) ; } if ( message . flags !== old . flags ) { let text ; const hadSuppressEmbeds = old . hasFlagSuppressEmbeds ; const hasSuppressEmbeds = message . hasFlagSuppressEmbeds ; if ( hadSuppressEmbeds && ! hasSuppressEmbeds ) { text = '- Unsuppressed Embeds' ; } else if ( ! hadSuppressEmbeds && hasSuppressEmbeds ) { text = '- Suppressed Embeds' ; } else { text = `${ old . flags } -> ${ message . flags }` ; } embed . addField ( 'Flags' , text , true ) ; } if ( message . mentionEveryone !== old . mentionEveryone || differences . mentions ) { const removedText = [ ] ; const addedText = [ ] ; if ( message . mentionEveryone !== old . mentionEveryone ) { if ( message . mentionEveryone ) { addedText . push ( 'Everyone' ) ; } else { removedText . push ( 'Everyone' ) ; } } if ( differences . mentions ) { const removed = old . mentions . filter ( ( memberOrUser ) => ! message . mentions . has ( memberOrUser . id ) ) ; const added = message . mentions . filter ( ( memberOrUser ) => ! old . mentions . has ( memberOrUser . id ) ) ; if ( MAX_MENTIONS <= removed . length ) { removedText . push ( `${ removed . length . toLocaleString ( ) } Users` ) ; } else { removedText . push ( removed . map ( ( memberOrUser ) => `**${ Markup . codestring ( String ( memberOrUser ) ) }**` ) . join ( ', ' ) ) ; } if ( MAX_MENTIONS <= added . length ) { addedText . push ( `${ added . length . toLocaleString ( ) } Users` ) ; } else { addedText . push ( added . map ( ( memberOrUser ) => `**${ Markup . codestring ( String ( memberOrUser ) ) }**` ) . join ( ', ' ) ) ; } } embed . addField ( 'Mentions' , [ `- ${ removedText . join ( ', ' ) || 'None' }` , `+ ${ addedText . join ( ', ' ) || 'None' }` , ] . join ( '\n' ) ) ; } } else { embed . setDescription ( 'Message was too old to get differences for' ) ; } let timestamp ; if ( message ) { timestamp = createTimestampMomentFromGuild ( message . timestampUnix , guildId ) ; } else { timestamp = createTimestampMomentFromGuild ( Snowflake . timestamp ( messageId ) , guildId ) ; } embed . addField ( 'Information' , [ `**Channel**: <#${ channelId }>` , `**Created**: ${ timestamp . fromNow ( ) }` , `**->** ${ Markup . spoiler ( timestamp . format ( DateMomentLogFormat ) ) }` , `**Link**: ${ Markup . url ( messageId , DiscordEndpoints . Routes . URL + DiscordEndpoints . Routes . MESSAGE ( guildId , channelId , messageId ) ) }` , ] . join ( '\n' ) ) ; } ; break ; case ClientEvents . USERS_UPDATE : { const { avatars , old , user } = event . cached ; const { differences } = event . payload ; { const timestamp = createTimestampStringFromGuild ( happened , guildId ) ; embed . setFooter ( `User Updated • ${ timestamp }` ) ; } createUserEmbed ( user , embed ) ; embed . setColor ( EmbedColors . LOG_UPDATE ) ; if ( avatars ) { if ( avatars . current ) { embed . setAuthor ( undefined , `attachment://${ avatars . current . filename }` ) ; files . push ( avatars . current ) ; } if ( avatars . old ) { embed . setThumbnail ( `attachment://${ avatars . old . filename }` ) ; files . push ( avatars . old ) ; } } embed . setDescription ( user . mention ) ; if ( differences ) { const description = [ ] ; if ( differences . avatar !== undefined ) { if ( differences . avatar && user . avatar ) { description . push ( '- Changed Avatars' ) ; } else if ( differences . avatar && ! user . avatar ) { description . push ( '- Removed Avatar' ) ; } else if ( ! differences . avatar && user . avatar ) { description . push ( '- Changed from the default Avatar' ) ; } } if ( differences . username !== undefined || differences . discriminator !== undefined ) { if ( differences . username !== undefined ) { description . push ( '- Username Change' ) ; description . push ( `-> **${ Markup . codestring ( differences . username ) }** to **${ Markup . codestring ( user . username ) }**` ) ; } if ( differences . discriminator !== undefined ) { description . push ( '- Discriminator Change' ) ; description . push ( `-> **${ Markup . codestring ( differences . discriminator ) }** to **${ Markup . codestring ( user . discriminator ) }**` ) ; } } if ( differences . publicFlags !== undefined ) { const added = [ ] ; const removed = [ ] ; for ( let key in DiscordUserFlagsText ) { const flag = parseInt ( key ) as DiscordUserFlags ; const oldHas = old . hasFlag ( flag ) ; const newHas = user . hasFlag ( flag ) ; if ( oldHas && ! newHas ) { removed . push ( `**${ Markup . codestring ( DiscordUserFlagsText [ flag ] ) }**` ) ; } else if ( ! oldHas && newHas ) { added . push ( `**${ Markup . codestring ( DiscordUserFlagsText [ flag ] ) }**` ) ; } } description . push ( '- Badges' ) ; if ( removed . length ) { description . push ( `-> Removed ${ removed . join ( ', ' ) }` ) ; } if ( added . length ) { description . push ( `-> Added ${ added . join ( ', ' ) }` ) ; } } embed . addField ( 'Changes' , description . join ( '\n' ) ) ; } } ; break ; case ClientEvents . VOICE_STATE_UPDATE : { const { old , voiceState } = event . cached ; const { differences , leftChannel , shard } = event . payload ; if ( voiceState . member ) { createUserEmbed ( voiceState . member , embed ) ; embed . setDescription ( voiceState . member . mention ) ; } { let text ; if ( leftChannel ) { embed . setColor ( EmbedColors . LOG_DELETION ) ; text = 'Left Voice' ; } else if ( differences ) { embed . setColor ( EmbedColors . LOG_UPDATE ) ; if ( differences . channelId && differences . sessionId ) { text = 'Changed Channels from a different device' ; } else if ( differences . channelId ) { text = 'Changed Channels' ; } else if ( differences . sessionId ) { text = 'Rejoined Voice from a different device' ; } else { text = 'Voice State Updated' ; } } else { embed . setColor ( EmbedColors . LOG_CREATION ) ; text = 'Joined Voice' ; } const timestamp = createTimestampStringFromGuild ( happened , guildId ) ; embed . setFooter ( `${ text } • ${ timestamp }` ) ; } let shouldLogMuteDeaf = true ; if ( audits ) { const [ audit ] = audits ; const isSelf = voiceState . userId === audit . userId ; const description = [ ] ; if ( ! isSelf ) { description . push ( `By ${ createUserString ( audit . userId , audit . user as Structures . User ) }` ) ; } for ( let [ x , change ] of audit . changes ) { switch ( change . key ) { case AuditLogChangeKeys . DEAF : { shouldLogMuteDeaf = false ; description . push ( `- ${ ( change . newValue ) ? 'Deafened' : 'Undeafened' }` ) ; } ; break ; case AuditLogChangeKeys . MUTE : { shouldLogMuteDeaf = false ; description . push ( `- ${ ( change . newValue ) ? 'Muted' : 'Unmuted' }` ) ; } ; break ; } } if ( audit . reason ) { description . push ( `**Reason**: ${ Markup . codeblock ( audit . reason ) }` ) ; } if ( isSelf ) { embed . addField ( 'Self-Action' , description . join ( '\n' ) ) ; } else { embed . addField ( 'Moderation Action' , description . join ( '\n' ) ) ; } } if ( differences ) { if ( leftChannel && differences . channelId ) { let oldChannelName ; if ( shard . channels . has ( differences . channelId ) ) { const oldChannel = shard . channels . get ( differences . channelId ) as Structures . Channel ; oldChannelName = `**${ Markup . codestring ( oldChannel . toString ( ) ) }**` ; } else { oldChannelName = `<#${ differences . channelId }>` ; } embed . setDescription ( [ ( ( voiceState . member ) ? voiceState . member . mention : voiceState . userId ) + '\n' , `- Disconnected from ${ oldChannelName }` , ] . join ( '\n' ) ) ; } else { const description = [ ] ; if ( differences . channelId ) { let oldChannelName ; if ( shard . channels . has ( differences . channelId ) ) { const oldChannel = shard . channels . get ( differences . channelId ) as Structures . Channel ; oldChannelName = `**${ Markup . codestring ( oldChannel . toString ( ) ) }**` ; } else { oldChannelName = `<#${ differences . channelId }>` ; } if ( voiceState . channelId ) { let channelName ; if ( shard . channels . has ( voiceState . channelId ) ) { const channel = shard . channels . get ( voiceState . channelId ) as Structures . Channel ; channelName = `**${ Markup . codestring ( channel . toString ( ) ) }**` ; } else { channelName = `<#${ voiceState . channelId }>` ; } if ( differences . sessionId ) { description . push ( `- Moved from ${ oldChannelName } to ${ channelName } from a different device` ) ; } else { description . push ( `- Moved from ${ oldChannelName } to ${ channelName }` ) ; } } else { } } else if ( differences . sessionId ) { if ( voiceState . channelId ) { let channelName ; if ( shard . channels . has ( voiceState . channelId ) ) { const channel = shard . channels . get ( voiceState . channelId ) as Structures . Channel ; channelName = `**${ Markup . codestring ( channel . toString ( ) ) }**` ; } else { channelName = `<#${ voiceState . channelId }>` ; } description . push ( `- Rejoined ${ channelName } on a different device` ) ; } else { description . push ( '- Rejoined on a different device' ) ; } } if ( shouldLogMuteDeaf ) { const text = [ ] ; if ( differences . deaf !== undefined ) { text . push ( ( voiceState . deaf ) ? 'Deafened' : 'Undeafened' ) ; } if ( differences . mute !== undefined ) { text . push ( ( voiceState . mute ) ? 'Muted' : 'Unmuted' ) ; } if ( text . length ) { description . push ( `- Server ${ text . join ( ' and ' ) } Themselves` ) ; if ( audits && audits [ 0 ] . reason ) { const [ audit ] = audits ; description . push ( `-> Reason: ${ Markup . codestring ( audit . reason || '' ) }` ) ; } } } { const text = [ ] ; if ( differences . selfDeaf !== undefined ) { text . push ( ( voiceState . selfDeaf ) ? 'Deafened' : 'Undeafened' ) ; } if ( differences . selfMute !== undefined ) { text . push ( ( voiceState . selfMute ) ? 'Muted' : 'Unmuted' ) ; } if ( text . length ) { description . push ( `- ${ text . join ( ' and ' ) } Themselves` ) ; } } { const text = [ ] ; if ( differences . selfStream !== undefined ) { text . push ( `${ ( voiceState . selfStream ) ? 'Started' : 'Stopped' } using Go Live` ) ; } if ( differences . selfVideo !== undefined ) { text . push ( `${ ( voiceState . selfVideo ) ? 'Started' : 'Stopped' } Streaming Video` ) ; } if ( text . length ) { description . push ( `- ${ text . join ( ' and ' ) }` ) ; } } if ( differences . suppress !== undefined ) { if ( differences . suppress ) { description . push ( '- Suppressed' ) ; } else { description . push ( '- Unsuppressed' ) ; } } if ( description . length ) { embed . addField ( 'Changes' , description . join ( '\n' ) ) ; } } } else if ( voiceState . channelId ) { let channelName ; if ( shard . channels . has ( voiceState . channelId ) ) { const channel = shard . channels . get ( voiceState . channelId ) as Structures . Channel ; channelName = `**${ Markup . codestring ( channel . toString ( ) ) }**` ; } else { channelName = `<#${ voiceState . channelId }>` ; } const description = [ ( ( voiceState . member ) ? voiceState . member . mention : voiceState . userId ) + '\n' , `- Joined ${ channelName }` , ] ; { const text = [ ] ; if ( voiceState . selfDeaf ) { text . push ( 'Self Deafened' ) ; } if ( voiceState . selfMute ) { text . push ( 'Self Muted' ) ; } if ( voiceState . selfVideo ) { text . push ( 'Streaming Video' ) ; } if ( text . length ) { description . push ( `- ${ text . join ( ', ' ) }` ) ; } } embed . setDescription ( description . join ( '\n' ) ) ; } } ; break ; } return { embed , files } ; } export function findEventsForBotAdd ( auditLog , storage ) { const queue = storage . queues . get ( GuildLoggerTypes . MEMBERS ) ; if ( queue ) { return queue . filter ( ( event ) => { if ( event . name !== ClientEvents . GUILD_MEMBER_ADD ) { return false ; } if ( auditLog . createdAtUnix <= ( event . happened - AUDIT_LEEWAY_TIME ) ) { return false ; } return event . payload . userId === auditLog . targetId ; } ) ; } return [ ] ; } export function findEventsForMemberBanAdd ( auditLog , storage ) { const queues = [ storage . queues . get ( GuildLoggerTypes . BANS ) , storage . queues . get ( GuildLoggerTypes . MEMBERS ) , storage . queues . get ( GuildLoggerTypes . MESSAGES ) , ] ; const events = [ ] ; for ( let queue of queues ) { if ( ! queue ) { continue ; } for ( let event of queue ) { switch ( event . name ) { case ClientEvents . GUILD_BAN_ADD : { if ( auditLog . createdAtUnix <= ( event . happened - AUDIT_LEEWAY_TIME ) ) { continue ; } if ( event . payload . user . id === auditLog . targetId ) { events . push ( event ) ; } } ; break ; case ClientEvents . GUILD_MEMBER_REMOVE : { if ( auditLog . createdAtUnix <= ( event . happened - AUDIT_LEEWAY_TIME ) ) { continue ; } if ( event . payload . userId === auditLog . targetId ) { events . push ( event ) ; } } ; break ; case ClientEvents . MESSAGE_DELETE_BULK : { if ( auditLog . createdAtUnix <= ( ( event . happened - AUDIT_LEEWAY_TIME ) - 1000 ) ) { continue ; } const { messages } = event . payload ; if ( messages . every ( ( message ) => ( message ) ? message . author . id === auditLog . targetId : true ) ) { events . push ( event ) ; } } ; break ; } } } return events ; } export function findEventsForMemberBanRemove ( auditLog , storage ) { const queue = storage . queues . get ( GuildLoggerTypes . BANS ) ; if ( queue ) { return queue . filter ( ( event ) => { if ( auditLog . createdAtUnix <= ( event . happened - AUDIT_LEEWAY_TIME ) ) { return false ; } if ( event . name !== ClientEvents . GUILD_BAN_REMOVE ) { return false ; } return event . payload . user . id === auditLog . targetId ; } ) ; } return [ ] ; } export function findEventsForMemberKick ( auditLog , storage ) { const queue = storage . queues . get ( GuildLoggerTypes . MEMBERS ) ; if ( queue ) { return queue . filter ( ( event ) => { if ( auditLog . createdAtUnix <= ( event . happened - AUDIT_LEEWAY_TIME ) ) { return false ; } if ( event . name !== ClientEvents . GUILD_MEMBER_REMOVE ) { return false ; } return event . payload . userId === auditLog . targetId ; } ) ; } return [ ] ; } export function findEventsForMemberRoleUpdate ( auditLog , storage ) { const queue = storage . queues . get ( GuildLoggerTypes . MEMBERS ) ; if ( queue ) { return queue . filter ( ( event ) => { if ( event . name !== ClientEvents . GUILD_MEMBER_UPDATE ) { return false ; } if ( auditLog . createdAtUnix <= ( event . happened - AUDIT_LEEWAY_TIME ) ) { return false ; } const { member , old } = event . cached ; if ( auditLog . targetId !== member . id ) { return false ; } if ( event . audits ) { const alreadyHas = event . audits . some ( ( { changes } ) => { if ( changes . length !== auditLog . changes . length ) { return false ; } return changes . every ( ( change ) => { const currentChange = auditLog . changes . get ( change . key ) ; if ( currentChange ) { switch ( change . key ) { case AuditLogChangeKeys . ROLES_ADD : case AuditLogChangeKeys . ROLES_REMOVE : { if ( change . newValue . length !== currentChange . newValue . length ) { return true ; } return change . newValue . every ( ( value ) => { return currentChange . newValue . some ( ( currentValue ) => value . id === currentValue . id ) ; } ) ; } ; } } return false ; } ) ; } ) ; if ( alreadyHas ) { return false ; } } if ( member . hasDifference ( 'roles' , old . _roles ) ) { return auditLog . changes . every ( ( change ) => { switch ( change . key ) { case AuditLogChangeKeys . ROLES_ADD : { return change . newValue . every ( ( raw ) => { return ! old . roles . has ( raw . id ) && member . roles . has ( raw . id ) ; } ) ; } ; case AuditLogChangeKeys . ROLES_REMOVE : { return change . newValue . every ( ( raw ) => { return old . roles . has ( raw . id ) && ! member . roles . has ( raw . id ) ; } ) ; } ; } } ) ; } return false ; } ) ; } return [ ] ; } export function findEventsForMemberUpdate ( auditLog , storage ) { const queues = [ storage . queues . get ( GuildLoggerTypes . MEMBERS ) , storage . queues . get ( GuildLoggerTypes . VOICE ) , ] ; const events = [ ] ; for ( let queue of queues ) { if ( ! queue ) { continue ; } for ( let event of queue ) { if ( auditLog . createdAtUnix <= ( event . happened - AUDIT_LEEWAY_TIME ) ) { continue ; } switch ( event . name ) { case ClientEvents . GUILD_MEMBER_UPDATE : { const { member } = event . payload ; if ( auditLog . targetId !== member . id ) { continue ; } } ; break ; case ClientEvents . VOICE_STATE_UPDATE : { const { voiceState } = event . payload ; if ( auditLog . targetId !== voiceState . userId ) { continue ; } } ; break ; default : { continue ; } ; } if ( event . audits ) { const alreadyHas = event . audits . some ( ( { changes } ) => { if ( changes . length !== auditLog . changes . length ) { return false ; } return changes . every ( ( change ) => { const currentChange = auditLog . changes . get ( change . key ) ; if ( currentChange ) { return change . newValue === currentChange . newValue ; } return false ; } ) ; } ) ; if ( alreadyHas ) { continue ; } } switch ( event . name ) { case ClientEvents . GUILD_MEMBER_UPDATE : { const { member , old } = event . cached ; const matchesChanges = auditLog . changes . every ( ( change ) => { if ( change . oldValue !== undefined && old . hasDifference ( change . key , change . oldValue ) ) { return false ; } if ( change . newValue === undefined ) { return ! ! member . _getFromSnake ( change . key ) === ! ! change . newValue ; } else { return ! member . hasDifference ( change . key , change . newValue ) ; } } ) ; if ( matchesChanges ) { events . push ( event ) ; } } ; break ; case ClientEvents . VOICE_STATE_UPDATE : { const { old , voiceState } = event . cached ; const matchesChanges = auditLog . changes . every ( ( change ) => { if ( old ) { if ( change . oldValue !== undefined && old . hasDifference ( change . key , change . oldValue ) ) { return false ; } } return ! voiceState . hasDifference ( change . key , change . newValue ) ; } ) ; if ( matchesChanges ) { events . push ( event ) ; } } ; break ; } } } return events ; } export function findEventsForMessageDelete ( auditLog , storage ) { const queue = storage . queues . get ( GuildLoggerTypes . MESSAGES ) ; if ( queue ) { return queue . filter ( ( event ) => { if ( event . name !== ClientEvents . MESSAGE_DELETE ) { return false ; } if ( event . audits ) { return false ; } const { channelId , message } = event . payload ; if ( message && auditLog . options && auditLog . options . channelId === channelId ) { return message . author . id === auditLog . targetId ; } return false ; } ) ; } return [ ] ; } export function findEventsForMessageBulkDelete ( auditLog , storage ) { const queue = storage . queues . get ( GuildLoggerTypes . MESSAGES ) ; if ( queue ) { return queue . filter ( ( event ) => { if ( event . name !== ClientEvents . MESSAGE_DELETE_BULK ) { return false ; } if ( event . audits ) { return false ; } const { amount , channelId } = event . payload ; if ( channelId === auditLog . targetId && auditLog . options && auditLog . options . count ) { return amount <= auditLog . options . count ; } return false ; } ) ; } return [ ] ; } export function findEventsForRoleCreate ( auditLog , storage ) { const queue = storage . queues . get ( GuildLoggerTypes . ROLES ) ; if ( queue ) { return queue . filter ( ( event ) => { if ( event . name !== ClientEvents . GUILD_ROLE_CREATE ) { return false ; } if ( auditLog . createdAtUnix <= ( event . happened - AUDIT_LEEWAY_TIME ) ) { return false ; } return event . payload . role . id === auditLog . targetId ; } ) ; } return [ ] ; } export function findEventsForRoleDelete ( auditLog , storage ) { const queue = storage . queues . get ( GuildLoggerTypes . ROLES ) ; if ( queue ) { return queue . filter ( ( event ) => { if ( event . name !== ClientEvents . GUILD_ROLE_DELETE ) { return false ; } if ( auditLog . createdAtUnix <= ( event . happened - AUDIT_LEEWAY_TIME ) ) { return false ; } return event . payload . roleId === auditLog . targetId ; } ) ; } return [ ] ; } export function findEventsForRoleUpdate ( auditLog , storage ) { const queue = storage . queues . get ( GuildLoggerTypes . ROLES ) ; if ( queue ) { return queue . filter ( ( event ) => { if ( event . name !== ClientEvents . GUILD_ROLE_UPDATE ) { return false ; } if ( auditLog . createdAtUnix <= ( event . happened - AUDIT_LEEWAY_TIME ) ) { return false ; } const { differences , old , role } = event . payload ; if ( role . id !== auditLog . targetId ) { return false ; } let matches = false ; if ( differences && auditLog . changes . length ) { for ( let [ key , change ] of auditLog . changes ) { if ( change . key in differences ) { matches = change . oldValue == differences [ change . key ] && change . newValue == role . _getFromSnake ( change . key ) ; } } } return matches ; } ) ; } return [ ] ; }