import { Channel , GuildChannel , TextChannel , Role , GuildMember , Guild , User , Message , Attachment , Collection , MessageReaction , DMChannel } from "discord.js" ; import { Bot } from "." ; import { timeFormat , getDurationDiff , getDayDiff } from "./utils/time" ; import dateFormat = require ( 'dateformat' ) ; export param { GuildChannel } channel param { boolean } created returns memberof export async function logChannelToggle ( channel , created ) { let megalogDoc = await Bot . database . findMegalogDoc ( channel . guild . id ) ; if ( ! megalogDoc ) return ; if ( megalogDoc . ignoreChannels && megalogDoc . ignoreChannels . includes ( channel . id ) ) return ; if ( ! megalogDoc . channelCreate && created ) return ; if ( ! megalogDoc . channelDelete && ! created ) return ; let logChannel = channel . guild . channels . get ( created ? megalogDoc . toObject ( ) . channelCreate : megalogDoc . toObject ( ) . channelDelete ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; await logChannel . send ( { "embed" : { "description" : `**${ channel . type === 'category' ? "Category" : "Channel" } ${ created ? 'Created' : 'Deleted' }: ${ created ? channel . toString ( ) : '#' + channel . name }**` , "color" : ( created ? Bot . database . settingsDB . cache . embedColors . positive : Bot . database . settingsDB . cache . embedColors . negative ) , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : "ID: " + channel . id } , "author" : { "name" : channel . guild . name , "icon_url" : channel . guild . iconURL } } } ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( created ? 'channelCreate' : 'channelDelete' ) ; } export param { GuildChannel } oldChannel param { GuildChannel } newChannel returns memberof export async function logChannelUpdate ( oldChannel , newChannel ) { let megalogDoc = await Bot . database . findMegalogDoc ( newChannel . guild . id ) ; if ( ! megalogDoc ) return ; if ( megalogDoc . ignoreChannels && megalogDoc . ignoreChannels . includes ( newChannel . id ) ) return ; if ( ! megalogDoc . channelUpdate ) return ; let logChannel = newChannel . guild . channels . get ( megalogDoc . toObject ( ) . channelUpdate ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; if ( oldChannel . name != newChannel . name ) { await logChannel . send ( { "embed" : { "description" : `**${ newChannel . type === 'category' ? "Category" : "Channel" } name changed of ${ newChannel . toString ( ) }**` , "color" : Bot . database . settingsDB . cache . embedColors . default , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : "ID: " + newChannel . id } , "author" : { "name" : newChannel . guild . name , "icon_url" : newChannel . guild . iconURL } , "fields" : [ { "name" : "Old" , "value" : oldChannel . name , "inline" : true } , { "name" : "New" , "value" : newChannel . name , "inline" : true } ] } } ) ; Bot . mStats . logMessageSend ( ) ; } if ( oldChannel instanceof TextChannel && newChannel instanceof TextChannel && oldChannel . topic != newChannel . topic ) { await logChannel . send ( { "embed" : { "description" : `**${ newChannel . type === 'category' ? "Category" : "Channel" } topic changed of ${ newChannel . toString ( ) }**` , "color" : Bot . database . settingsDB . cache . embedColors . default , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : "ID: " + newChannel . id } , "author" : { "name" : newChannel . guild . name , "icon_url" : newChannel . guild . iconURL } , "fields" : [ { "name" : "Old" , "value" : oldChannel . topic ? oldChannel . topic : '*empty topic*' , "inline" : true } , { "name" : "New" , "value" : newChannel . topic ? newChannel . topic : '*empty topic*' , "inline" : true } ] } } ) ; Bot . mStats . logMessageSend ( ) ; } let permDiff = oldChannel . permissionOverwrites . filter ( x => { if ( newChannel . permissionOverwrites . find ( y => y . allowed . bitfield == x . allowed . bitfield ) && newChannel . permissionOverwrites . find ( y => y . denied . bitfield == x . denied . bitfield ) ) return false ; return true ; } ) . concat ( newChannel . permissionOverwrites . filter ( x => { if ( oldChannel . permissionOverwrites . find ( y => y . allowed . bitfield == x . allowed . bitfield ) && oldChannel . permissionOverwrites . find ( y => y . denied . bitfield == x . denied . bitfield ) ) return false ; return true ; } ) ) ; if ( permDiff . size ) { let embed = { "embed" : { "description" : `**${ newChannel . type === 'category' ? "Category" : "Channel" } permissions changed of ${ newChannel . toString ( ) }**\n*note:* check [docs](https://discordapp.com/developers/docs/topics/permissions) to see what the numbers mean` , "color" : Bot . database . settingsDB . cache . embedColors . default , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : "ID: " + newChannel . id } , "author" : { "name" : newChannel . guild . name , "icon_url" : newChannel . guild . iconURL } , "fields" : [ ] } } ; for ( const permID of permDiff . keys ( ) ) { let oldPerm = oldChannel . permissionOverwrites . get ( permID ) || { } ; let newPerm = newChannel . permissionOverwrites . get ( permID ) || { } ; let oldBitfields = { allowed : oldPerm . allowed ? oldPerm . allowed . bitfield : 0 , denied : oldPerm . denied ? oldPerm . denied . bitfield : 0 } ; let newBitfields = { allowed : newPerm . allowed ? newPerm . allowed . bitfield : 0 , denied : newPerm . denied ? newPerm . denied . bitfield : 0 } ; var role ; var member ; if ( oldPerm . type == 'role' || newPerm . type == 'role' ) role = newChannel . guild . roles . get ( newPerm . id || oldPerm . id ) ; if ( oldPerm . type == 'member' || newPerm . type == 'member' ) member = await newChannel . guild . fetchMember ( newPerm . id || oldPerm . id ) ; let value = '' ; if ( oldBitfields . allowed !== newBitfields . allowed ) { value += `Allowed Perms: \`${ oldBitfields . allowed }\` to \`${ newBitfields . allowed }\`\n` ; } if ( oldBitfields . denied !== newBitfields . denied ) { value += `Denied Perms: \`${ oldBitfields . denied }\` to \`${ newBitfields . denied }\`` ; } if ( ! value . length ) value = 'Overwrite got deleted' ; embed . embed . fields . push ( { "name" : role ? role . name + ` (ID: ${ role . id }):` : member . user . username + ` (ID: ${ member . id }):` , "value" : value } ) ; } await logChannel . send ( embed ) ; Bot . mStats . logMessageSend ( ) ; } Bot . mStats . logMegalogLog ( 'channelUpdate' ) ; } export param { Guild } guild param { User } user param { boolean } banned returns memberof export async function logBan ( guild , user , banned ) { let megalogDoc = await Bot . database . findMegalogDoc ( guild . id ) ; if ( ! megalogDoc ) return ; if ( ( ! megalogDoc . ban && banned ) || ( ! megalogDoc . unban && ! banned ) ) return ; let logChannel = guild . channels . get ( banned ? megalogDoc . toObject ( ) . ban : megalogDoc . toObject ( ) . unban ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; await logChannel . send ( { "embed" : { "description" : `${ user . toString ( ) }\n${ user . tag }` , "color" : Bot . database . settingsDB . cache . embedColors [ banned ? 'negative' : 'positive' ] , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : "ID: " + user . id } , "thumbnail" : { "url" : user . displayAvatarURL } , "author" : { "name" : "User " + ( banned ? 'Banned' : 'Unbanned' ) , "icon_url" : user . displayAvatarURL } } } ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( banned ? 'ban' : 'unban' ) ; } export param { GuildMember } member param { boolean } joined returns memberof export async function logMember ( member , joined ) { let megalogDoc = await Bot . database . findMegalogDoc ( member . guild . id ) ; if ( ! megalogDoc ) return ; if ( ( ! megalogDoc . memberJoin && joined ) || ( ! megalogDoc . memberLeave && ! joined ) ) return ; let logChannel = member . guild . channels . get ( joined ? megalogDoc . toObject ( ) . memberJoin : megalogDoc . toObject ( ) . memberLeave ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; let embed = { "embed" : { "description" : member . toString ( ) + "\n" + member . user . tag , "color" : Bot . database . settingsDB . cache . embedColors [ joined ? 'positive' : 'negative' ] , "timestamp" : joined ? member . joinedAt . toISOString ( ) : new Date ( ) . toISOString ( ) , "footer" : { "text" : "ID: " + member . id } , "thumbnail" : { "url" : member . user . displayAvatarURL } , "author" : { "name" : "User " + ( joined ? 'Joined' : 'Left' ) , "icon_url" : member . user . displayAvatarURL } } } ; if ( ! joined ) { embed . embed . fields = [ { "name" : "Joined At" , "value" : dateFormat ( member . joinedAt , timeFormat ) + ` (${ getDayDiff ( member . joinedTimestamp , Date . now ( ) ) } days ago)` } ] ; } await logChannel . send ( embed ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( joined ? 'memberJoin' : 'memberLeave' ) ; } export param { GuildMember } oldMember param { GuildMember } newMember returns memberof export async function logNickname ( oldMember , newMember ) { if ( oldMember . nickname == newMember . nickname ) return ; let megalogDoc = await Bot . database . findMegalogDoc ( newMember . guild . id ) ; if ( ! megalogDoc ) return ; if ( ! megalogDoc . nicknameChange ) return ; let logChannel = newMember . guild . channels . get ( megalogDoc . nicknameChange ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; await logChannel . send ( { "embed" : { "description" : `**${ newMember . toString ( ) } nickname changed**` , "color" : Bot . database . settingsDB . cache . embedColors . default , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : "ID: " + newMember . id } , "author" : { "name" : newMember . user . tag , "icon_url" : newMember . user . displayAvatarURL } , "fields" : [ { "name" : "Before" , "value" : oldMember . nickname ? oldMember . nickname : '*None*' , "inline" : true } , { "name" : "After" , "value" : newMember . nickname ? newMember . nickname : '*None*' , "inline" : true } ] } } ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( 'nicknameChange' ) ; } export param { GuildMember } oldMember param { GuildMember } newMember returns memberof export async function logMemberRoles ( oldMember , newMember ) { let rolesAdded = newMember . roles . filter ( x => ! oldMember . roles . get ( x . id ) ) ; let rolesRemoved = oldMember . roles . filter ( x => ! newMember . roles . get ( x . id ) ) ; if ( rolesAdded . size == 0 && rolesRemoved . size == 0 ) return ; let megalogDoc = await Bot . database . findMegalogDoc ( newMember . guild . id ) ; if ( ! megalogDoc ) return ; if ( ! megalogDoc . memberRolesChange ) return ; let logChannel = newMember . guild . channels . get ( megalogDoc . memberRolesChange ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; let roleAddedString = '' ; for ( const role of rolesAdded . array ( ) ) { roleAddedString += role . toString ( ) ; } let roleRemovedString = '' ; for ( const role of rolesRemoved . array ( ) ) { roleRemovedString += role . toString ( ) ; } await logChannel . send ( { "embed" : { "description" : `**${ newMember . toString ( ) } roles changed**` , "color" : Bot . database . settingsDB . cache . embedColors . default , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : "ID: " + newMember . id } , "author" : { "name" : newMember . user . tag , "icon_url" : newMember . user . displayAvatarURL } , "fields" : [ { "name" : `Added Roles [${ rolesAdded . size }]` , "value" : roleAddedString . length == 0 ? '*None*' : roleAddedString , "inline" : true } , { "name" : `Removed Roles [${ rolesRemoved . size }]` , "value" : roleRemovedString . length == 0 ? '*None*' : roleRemovedString , "inline" : true } ] } } ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( 'memberRolesChange' ) ; } export param { Guild } oldGuild param { Guild } newGuild returns memberof export async function logGuildName ( oldGuild , newGuild ) { if ( oldGuild . name == newGuild . name ) return ; let megalogDoc = await Bot . database . findMegalogDoc ( newGuild . id ) ; if ( ! megalogDoc ) return ; if ( ! megalogDoc . guildNameChange ) return ; let logChannel = newGuild . channels . get ( megalogDoc . guildNameChange ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; await logChannel . send ( { "embed" : { "description" : `**Server name changed**` , "color" : Bot . database . settingsDB . cache . embedColors . default , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : "ID: " + newGuild . id } , "author" : { "name" : newGuild . name , "icon_url" : newGuild . iconURL } , "fields" : [ { "name" : "Before" , "value" : oldGuild . name , "inline" : true } , { "name" : "After" , "value" : newGuild . name , "inline" : true } ] } } ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( 'guildNameChange' ) ; } export param { Message } message returns memberof export async function logMessageDelete ( message ) { if ( message . channel instanceof DMChannel ) return ; let megalogDoc = await Bot . database . findMegalogDoc ( message . guild . id ) ; if ( ! megalogDoc ) return ; if ( ! megalogDoc ) return ; if ( megalogDoc . ignoreChannels && megalogDoc . ignoreChannels . includes ( message . channel . id ) ) return ; if ( ! megalogDoc . messageDelete ) return ; if ( message . channel . id == megalogDoc . messageDelete ) return ; let logChannel = message . guild . channels . get ( megalogDoc . messageDelete ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; let shortened = false ; let content = message . content ; if ( content . length > 1024 ) { content = content . slice ( 0 , 1020 ) + '...' ; shortened = true ; } let embed = { "embed" : { "description" : `**Message from ${ message . author . toString ( ) } deleted in ${ message . channel . toString ( ) }**` , "color" : Bot . database . settingsDB . cache . embedColors . negative , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : `Author: ${ message . author . id } | Message: ${ message . id }` } , "author" : { "name" : message . author . tag , "icon_url" : message . author . displayAvatarURL } , "fields" : [ { "name" : "Content" + ( shortened ? ' (shortened)' : '' ) , "value" : message . content . length > 0 ? content : '*no content*' } ] } } ; if ( message . attachments . size > 0 ) { let attachments = '' ; for ( const attachment of message . attachments ) attachments += attachment [ 1 ] . url + '\n' ; embed . embed . fields . push ( { "name" : "Attachments" , "value" : attachments } ) ; let cachedAttachments = await getAttachmentCache ( message , megalogDoc . toObject ( ) . attachmentCache ) ; let cachedString ; var cachedArray = [ ] ; if ( cachedAttachments && cachedAttachments . size > 0 ) { cachedString = 'Cached attachments are also attachments of this message\n' ; for ( const attachment of cachedAttachments ) { cachedString += attachment [ 1 ] . url + '\n' ; cachedArray . push ( attachment [ 1 ] . url ) ; } } else { cachedString = 'Couldn\'t find and cached attachments. Either the `attachmentCache` channel isn\'t defined in the megalogger or the message wasn\'t cached.' ; } embed . embed . fields . push ( { "name" : "Cached Attachments" , "value" : cachedString } ) ; embed . files = cachedArray ; } await logChannel . send ( embed ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( 'messageDelete' ) ; } export param { Collection < string , Message > } messages returns memberof export async function logMessageBulkDelete ( messages ) { if ( messages . first ( ) . channel instanceof DMChannel ) return ; let megalogDoc = await Bot . database . findMegalogDoc ( messages . first ( ) . guild . id ) ; if ( ! megalogDoc ) return ; if ( megalogDoc . ignoreChannels && megalogDoc . ignoreChannels . includes ( messages . first ( ) . channel . id ) ) return ; if ( ! megalogDoc . messageDelete ) return ; if ( messages . first ( ) . channel . id == megalogDoc . messageDelete ) return ; let logChannel = messages . first ( ) . guild . channels . get ( megalogDoc . messageDelete ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; let humanLog = `**Deleted Messages from #${ messages . first ( ) . channel . name } (${ messages . first ( ) . channel . id }) in ${ messages . first ( ) . guild . name } (${ messages . first ( ) . guild . id })**` ; for ( const message of messages . array ( ) . reverse ( ) ) { humanLog += `\r\n\r\n[${ dateFormat ( message . createdAt , timeFormat ) }] ${ message . author . tag } (${ message . id })` ; humanLog += ' : ' + message . content ; if ( message . attachments . size ) { humanLog += '\n*Attachments:*' ; let cachedAttachments = await getAttachmentCache ( message , megalogDoc . toObject ( ) . attachmentCache ) ; if ( cachedAttachments || cachedAttachments . size ) { for ( const attachment of cachedAttachments . array ( ) ) { humanLog += '\n' + attachment . url ; } } else { humanLog += '\n*No cache found*' ; } } } let attachment = new Attachment ( Buffer . from ( humanLog , 'utf-8' ) , 'DeletedMessages.txt' ) ; let logMessage = await logChannel . send ( attachment ) ; logMessage . edit ( { "embed" : { "description" : `**Bulk deleted messages in ${ messages . first ( ) . channel . toString ( ) }**` , "color" : Bot . database . settingsDB . cache . embedColors . negative , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : "Channel: " + messages . first ( ) . channel . id } , "author" : { "name" : messages . first ( ) . channel . name , "icon_url" : messages . first ( ) . guild . iconURL } , "fields" : [ { "name" : "Message Count" , "value" : messages . size , "inline" : true } , { "name" : "Deleted Messages" , "value" : `[view](https://txt.discord.website/?txt=${ logChannel . id }/${ logMessage . attachments . first ( ) . id }/DeletedMessages)` , "inline" : true } ] } } ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( 'messageDelete' ) ; } param { Message } message param { string } cacheChannelID param { number } timerange returns memberof async function getAttachmentCache ( message , cacheChannelID , timerange = 3000 ) { let cacheChannel = message . guild . channels . get ( cacheChannelID ) ; if ( ! cacheChannel || ! ( cacheChannel instanceof TextChannel ) ) return ; var cache = await cacheChannel . fetchMessages ( { limit : 20 , around : message . id } ) ; var cacheMessage = cache . find ( x => x . content . includes ( `BulletBotCacheTagThing: ${ message . url }` ) ) ; if ( ! cacheMessage || ! cacheMessage . attachments . size ) return ; return cacheMessage . attachments ; } export param { Message } message returns memberof export async function cacheAttachment ( message ) { if ( message . channel instanceof DMChannel ) return ; if ( message . attachments . size == 0 ) return ; let megalogDoc = await Bot . database . findMegalogDoc ( message . guild . id ) ; if ( ! megalogDoc ) return ; if ( megalogDoc . ignoreChannels && megalogDoc . ignoreChannels . includes ( message . channel . id ) ) return ; if ( ! megalogDoc . attachmentCache ) return ; let logChannel = message . guild . channels . get ( megalogDoc . attachmentCache ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; let attachments = [ ] ; for ( const attachment of message . attachments . array ( ) ) { attachments . push ( attachment . url ) ; } try { await logChannel . send ( `from ${ message . author . tag } (${ message . author . id }) in ${ message . channel } (${ message . channel . id })\nBulletBotCacheTagThing: ${ message . url }` , { files : attachments } ) ; } catch ( e ) { await logChannel . send ( `from ${ message . author . tag } (${ message . author . id }) in ${ message . channel } (${ message . channel . id })\n${ message . url }\nAttachments too large to send` ) ; } Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( 'attachmentCache' ) ; } export param { Message } oldMessage param { Message } newMessage returns memberof export async function logMessageEdit ( oldMessage , newMessage ) { if ( newMessage . channel instanceof DMChannel ) return ; if ( oldMessage . content == newMessage . content ) return ; let megalogDoc = await Bot . database . findMegalogDoc ( newMessage . guild . id ) ; if ( ! megalogDoc ) return ; if ( megalogDoc . ignoreChannels && megalogDoc . ignoreChannels . includes ( newMessage . channel . id ) ) return ; if ( ! megalogDoc . messageEdit ) return ; if ( megalogDoc . messageDelete == newMessage . channel . id && newMessage . author . id == Bot . client . user . id ) return ; let logChannel = newMessage . guild . channels . get ( megalogDoc . messageEdit ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; let oldShortened = false ; let oldContent = oldMessage . content ; if ( oldContent . length > 1024 ) { oldContent = oldContent . slice ( 0 , 1020 ) + '...' ; oldShortened = true ; } let newShortened = false ; let newContent = newMessage . content ; if ( newContent . length > 1024 ) { newContent = newContent . slice ( 0 , 1020 ) + '...' ; newShortened = true ; } await logChannel . send ( { "embed" : { "description" : `**Message of ${ newMessage . author . toString ( ) } edited in ${ newMessage . channel . toString ( ) }** [Jump to Message](${ newMessage . url })` , "color" : Bot . database . settingsDB . cache . embedColors . default , "timestamp" : newMessage . editedAt . toISOString ( ) , "footer" : { "text" : `Author: ${ newMessage . author . id } | Message: ${ newMessage . id }` } , "author" : { "name" : newMessage . author . tag , "icon_url" : newMessage . author . displayAvatarURL } , "fields" : [ { "name" : "Before" + ( oldShortened ? ' (shortened)' : '' ) , "value" : oldMessage . content . length > 0 ? oldContent : '*empty message*' } , { "name" : "After" + ( newShortened ? ' (shortened)' : '' ) , "value" : newMessage . content . length > 0 ? newContent : '*empty message*' } ] } } ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( 'messageEdit' ) ; } export param { MessageReaction } reaction param { User } user param { boolean } reacted returns memberof export async function logReactionToggle ( reaction , user , reacted ) { if ( reaction . message . channel instanceof DMChannel ) return ; let megalogDoc = await Bot . database . findMegalogDoc ( reaction . message . guild . id ) ; if ( ! megalogDoc ) return ; if ( megalogDoc . ignoreChannels && megalogDoc . ignoreChannels . includes ( reaction . message . channel . id ) ) return ; if ( ( reacted && ! megalogDoc . reactionAdd ) || ( ! reacted && ! megalogDoc . reactionRemove ) ) return ; let logChannel = reaction . message . guild . channels . get ( reacted ? megalogDoc . reactionAdd : megalogDoc . reactionRemove ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; await logChannel . send ( { "embed" : { "description" : `**${ user . toString ( ) } ${ ! reacted ? 'un' : '' }reacted with ${ reaction . emoji . toString ( ) } to [this message](${ reaction . message . url })${ ! reacted ? ' (or reaction got removed)' : '' }** ` , "color" : Bot . database . settingsDB . cache . embedColors [ reacted ? 'positive' : 'negative' ] , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : `User: ${ user . id } | Message: ${ reaction . message . id } ` } , "author" : { "name" : user . tag , "icon_url" : user . displayAvatarURL } , } } ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( reacted ? 'reactionAdd' : 'reactionRemove' ) ; } export param { Message } message returns memberof export async function logReactionRemoveAll ( message ) { if ( message . channel instanceof DMChannel ) return ; let megalogDoc = await Bot . database . findMegalogDoc ( message . guild . id ) ; if ( ! megalogDoc ) return ; if ( megalogDoc . ignoreChannels && megalogDoc . ignoreChannels . includes ( message . channel . id ) ) return ; if ( ! megalogDoc . reactionRemove ) return ; let logChannel = message . guild . channels . get ( megalogDoc . reactionRemove ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; await logChannel . send ( { "embed" : { "description" : `**All reactions were removed from a message of ${ message . author . toString ( ) } in ${ message . channel . toString ( ) }** [Jump to Message](${ message . url })` , "color" : Bot . database . settingsDB . cache . embedColors . negative , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : `Author: ${ message . author . id } | Message: ${ message . id }` } , "author" : { "name" : message . author . tag , "icon_url" : message . author . displayAvatarURL } } } ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( 'reactionRemove' ) ; } export param { Role } role param { boolean } created returns memberof export async function logRoleToggle ( role , created ) { let megalogDoc = await Bot . database . findMegalogDoc ( role . guild . id ) ; if ( ! megalogDoc ) return ; if ( ( created && ! megalogDoc . roleCreate ) || ( ! created && ! megalogDoc . roleDelete ) ) return ; let logChannel = role . guild . channels . get ( created ? megalogDoc . roleCreate : megalogDoc . roleDelete ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; try { await logChannel . send ( { "embed" : { "description" : `**Role ${ role } (${ role . name }) was ${ created ? 'created' : 'deleted' }**` , "color" : Bot . database . settingsDB . cache . embedColors [ created ? 'positive' : 'negative' ] , "timestamp" : created ? role . createdAt . toISOString ( ) : new Date ( ) . toISOString ( ) , "footer" : { "text" : "ID: " + role . id } , "author" : { "name" : role . guild . name , "icon_url" : role . guild . iconURL } } } ) ; } catch ( e ) { return ; } Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( created ? 'roleCreate' : 'roleDelete' ) ; } export param { Role } oldRole param { Role } newRole returns memberof export async function logRoleUpdate ( oldRole , newRole ) { if ( ( oldRole . name == newRole . name ) && ( oldRole . color == newRole . color ) && ( oldRole . permissions == newRole . permissions ) ) return ; let megalogDoc = await Bot . database . findMegalogDoc ( newRole . guild . id ) ; if ( ! megalogDoc ) return ; if ( ! megalogDoc . roleUpdate ) return ; let logChannel = newRole . guild . channels . get ( megalogDoc . roleUpdate ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; let date = new Date ( ) ; if ( oldRole . name != newRole . name ) { await logChannel . send ( { "embed" : { "description" : `**Role name of ${ newRole } (${ newRole . name }) changed**` , "color" : Bot . database . settingsDB . cache . embedColors . default , "timestamp" : date . toISOString ( ) , "footer" : { "text" : "ID: " + newRole . id } , "author" : { "name" : newRole . guild . name , "icon_url" : newRole . guild . iconURL } , "fields" : [ { "name" : "Before" , "value" : oldRole . name } , { "name" : "After" , "value" : newRole . name } ] } } ) ; Bot . mStats . logMessageSend ( ) ; } if ( oldRole . color != newRole . color ) { await logChannel . send ( { "embed" : { "description" : `**Role color of ${ newRole } (${ newRole . name }) changed**` , "color" : Bot . database . settingsDB . cache . embedColors . default , "timestamp" : date . toISOString ( ) , "footer" : { "text" : "ID: " + newRole . id } , "author" : { "name" : newRole . guild . name , "icon_url" : newRole . guild . iconURL } , "fields" : [ { "name" : "Before" , "value" : `${ oldRole . color } ([${ oldRole . hexColor }](https://www.color-hex.com/color/${ oldRole . hexColor . slice ( 1 ) }))` } , { "name" : "After" , "value" : `${ newRole . color } ([${ newRole . hexColor }](https://www.color-hex.com/color/${ newRole . hexColor . slice ( 1 ) }))` } ] } } ) ; Bot . mStats . logMessageSend ( ) ; } if ( oldRole . permissions != newRole . permissions ) { await logChannel . send ( { "embed" : { "description" : `**Role permissions of ${ newRole } (${ newRole . name }) changed**\n[What those numbers mean](https://discordapp.com/developers/docs/topics/permissions)` , "color" : Bot . database . settingsDB . cache . embedColors . default , "timestamp" : date . toISOString ( ) , "footer" : { "text" : "ID: " + newRole . id } , "author" : { "name" : newRole . guild . name , "icon_url" : newRole . guild . iconURL } , "fields" : [ { "name" : "Before" , "value" : oldRole . permissions } , { "name" : "After" , "value" : newRole . permissions } ] } } ) ; Bot . mStats . logMessageSend ( ) ; } Bot . mStats . logMegalogLog ( 'roleUpdate' ) ; } export param { GuildMember } oldMember param { GuildMember } newMember returns memberof export async function logVoiceTransfer ( oldMember , newMember ) { if ( oldMember . voiceChannelID == newMember . voiceChannelID ) return ; let megalogDoc = await Bot . database . findMegalogDoc ( newMember . guild . id ) ; if ( ! megalogDoc ) return ; if ( ! megalogDoc . voiceTransfer ) return ; let logChannel = newMember . guild . channels . get ( megalogDoc . voiceTransfer ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; await logChannel . send ( { "embed" : { "description" : `**${ newMember . toString ( ) } moved from voice channels ${ oldMember . voiceChannel ? oldMember . voiceChannel : 'None' } to ${ newMember . voiceChannel ? newMember . voiceChannel : 'None' }**` , "color" : Bot . database . settingsDB . cache . embedColors [ ! oldMember . voiceChannelID ? 'positive' : ( ! newMember . voiceChannelID ? 'negative' : 'default' ) ] , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : "User: " + newMember . id } , "author" : { "name" : newMember . user . username , "icon_url" : newMember . user . displayAvatarURL } } } ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( 'voiceTransfer' ) ; } export param { GuildMember } oldMember param { GuildMember } newMember returns memberof export async function logVoiceMute ( oldMember , newMember ) { if ( oldMember . mute == newMember . mute ) return ; let megalogDoc = await Bot . database . findMegalogDoc ( newMember . guild . id ) ; if ( ! megalogDoc ) return ; if ( ! megalogDoc . voiceMute ) return ; let logChannel = newMember . guild . channels . get ( megalogDoc . voiceMute ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; await logChannel . send ( { "embed" : { "description" : `**${ newMember } was voice ${ newMember . mute ? '' : 'un' }muted in ${ newMember . voiceChannel }**` , "color" : Bot . database . settingsDB . cache . embedColors [ newMember . mute ? 'negative' : 'positive' ] , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : "User: " + newMember . id } , "author" : { "name" : newMember . user . username , "icon_url" : newMember . user . displayAvatarURL } } } ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( 'voiceMute' ) ; } export param { GuildMember } oldMember param { GuildMember } newMember returns memberof export async function logVoiceDeaf ( oldMember , newMember ) { if ( oldMember . deaf == newMember . deaf ) return ; let megalogDoc = await Bot . database . findMegalogDoc ( newMember . guild . id ) ; if ( ! megalogDoc ) return ; if ( ! megalogDoc . voiceDeaf ) return ; let logChannel = newMember . guild . channels . get ( megalogDoc . voiceDeaf ) ; if ( ! logChannel || ! ( logChannel instanceof TextChannel ) ) return ; await logChannel . send ( { "embed" : { "description" : `**${ newMember } was voice ${ newMember . deaf ? '' : 'un' }deafed in ${ newMember . voiceChannel }**` , "color" : Bot . database . settingsDB . cache . embedColors [ newMember . deaf ? 'negative' : 'positive' ] , "timestamp" : new Date ( ) . toISOString ( ) , "footer" : { "text" : "User: " + newMember . id } , "author" : { "name" : newMember . user . username , "icon_url" : newMember . user . displayAvatarURL } } } ) ; Bot . mStats . logMessageSend ( ) ; Bot . mStats . logMegalogLog ( 'voiceDeaf' ) ; }