forked from Galactic/galactic-bot
445 lines
17 KiB
JavaScript
445 lines
17 KiB
JavaScript
const { MessageAttachment, WebhookClient } = require('discord.js');
|
|
|
|
const { Observer } = require('../../../interfaces/');
|
|
const { Util } = require('../../../../util');
|
|
|
|
const CONSTANTS = {
|
|
COLORS: {
|
|
RED: 16711680, // message delete
|
|
YELLOW: 15120384, // message edit
|
|
LIGHT_BLUE: 11337726, // message pin
|
|
BLUE: 479397
|
|
},
|
|
IMAGES: {
|
|
PREMIUM_LIMIT: 2,
|
|
UPLOAD_LIMIT: {
|
|
'0': 8,
|
|
'1': 8,
|
|
'2': 50,
|
|
'3': 100
|
|
},
|
|
MB_DIVIDER: 1024*1024
|
|
},
|
|
WEEK: 7 * 24 * 60 * 60
|
|
};
|
|
|
|
class GuildLogger extends Observer {
|
|
|
|
constructor(client) {
|
|
|
|
super(client, {
|
|
name: 'guildLogger',
|
|
priority: 3
|
|
// disabled: true
|
|
});
|
|
|
|
this.hooks = [
|
|
// ['message', this.storeAttachment.bind(this)], //Attachment logging
|
|
['messageDelete', this.messageDelete.bind(this)]
|
|
// ['messageDeleteBulk', this.messageDeleteBulk.bind(this)],
|
|
// ['messageUpdate', this.messageEdit.bind(this)],
|
|
// ['voiceStateUpdate', this.voiceState.bind(this)],
|
|
// ['guildBanAdd', this.ban.bind(this)],
|
|
// ['guildBanRemove', this.unban.bind(this)],
|
|
// ['guildMemberAdd', this.memberJoin.bind(this)],
|
|
// ['guildMemberRemove', this.memberLeave.bind(this)],
|
|
// ['guildMemberUpdate', this.memberUpdate.bind(this)]
|
|
];
|
|
|
|
this.attachmentWebhook = new WebhookClient(
|
|
this.client._options.moderation.attachments.webhook.id,
|
|
this.client._options.moderation.attachments.webhook.token
|
|
);
|
|
|
|
}
|
|
|
|
//TODO: Figure this thing out, this should be called from messageDelete and rawMessageDelete if any attachments are present
|
|
//data should be an object containing the necessary information to query for the attachment from the db, and the relevant information to log it
|
|
//Will figure this out once I get to that point
|
|
async logAttachment(data) {
|
|
|
|
}
|
|
|
|
async messageDelete(message) {
|
|
|
|
if(!this.client._built
|
|
|| message.webhookID
|
|
|| message.author.bot
|
|
|| !message.guild
|
|
|| !message.guild.available) return;
|
|
|
|
await message.guild.settings();
|
|
|
|
if (!message.member) message.member = await message.guild.members.fetch(message.author.id);
|
|
|
|
const { messageLog } = message.guild._settings;
|
|
if(!messageLog.channel) return undefined;
|
|
|
|
const { ignoredRoles, ignoredChannels } = messageLog;
|
|
const logChannel = await message.guild.resolveChannel(messageLog.channel);
|
|
if(!logChannel) return undefined;
|
|
|
|
const perms = logChannel.permissionsFor(message.guild.me);
|
|
if(!perms.has('SEND_MESSAGES') || !perms.has('VIEW_CHANNEL')) return undefined;
|
|
|
|
if(ignoredRoles.length && message.member.roles.cache.size) {
|
|
const roles = message.member.roles.cache.map((r) => r.id);
|
|
for (const role of ignoredRoles) {
|
|
if (roles.includes(role)) return undefined;
|
|
}
|
|
}
|
|
|
|
if(ignoredChannels && ignoredChannels.includes(message.channel.id)) return undefined;
|
|
|
|
const embed = {
|
|
author: {
|
|
name: `${message.author.tag} (${message.author.id})`,
|
|
icon_url: message.author.displayAvatarURL({ size: 32 }) //eslint-disable-line camelcase
|
|
},
|
|
description: Util.escapeMarkdown(message.content).replace(/\\n/gu, ' '),
|
|
color: CONSTANTS.COLORS.RED,
|
|
footer: {
|
|
text: `Message deleted in #${message.channel.name} | Message ID: ${message.id}`
|
|
},
|
|
timestamp: message.createdAt
|
|
};
|
|
|
|
const uploadedFiles = [];
|
|
if(message.attachments.size > 0 && messageLog.attachments) {
|
|
const imageExtensions = ['.png', '.webp', '.jpg', '.jpeg', '.gif'];
|
|
const data = await this.client.transactionHandler.send({
|
|
provider: 'mongodb',
|
|
request: {
|
|
collection: 'messages',
|
|
type: 'findOne',
|
|
query: {
|
|
id: message.id
|
|
}
|
|
}
|
|
});
|
|
|
|
const attachments = await this.client.transactionHandler.send({
|
|
provider: 'mongodb',
|
|
request: {
|
|
collection: 'attachments',
|
|
type: 'find',
|
|
query: {
|
|
_id: { $in: data.attachments.filter((a) => a.index).map((a) => a.index) }
|
|
}
|
|
}
|
|
});
|
|
|
|
const sortedAttachments = data.attachments.sort((a, b) => b.size-a.size);
|
|
|
|
const files = [];
|
|
for(const attachment of sortedAttachments) {
|
|
const attachmentData = attachments.find((a) => a.attachmentId === attachment.id);
|
|
if(attachmentData) {
|
|
attachmentData.buffer = Buffer.from(attachmentData.buffer, 'base64');
|
|
const messageAttachment = new MessageAttachment(attachmentData.buffer, attachment.name, { size: attachment.size });
|
|
if(messageAttachment.size < CONSTANTS.IMAGES.UPLOAD_LIMIT[message.guild.premiumTier]*CONSTANTS.IMAGES.MB_DIVIDER) {
|
|
if(imageExtensions.includes(attachment.extension) && uploadedFiles.length === 0) {
|
|
uploadedFiles.push(messageAttachment);
|
|
embed.image = {
|
|
url: `attachment://${attachment.name}`
|
|
};
|
|
} else {
|
|
if(messageAttachment.size > 8*CONSTANTS.IMAGES.MB_DIVIDER) {
|
|
const combined = uploadedFiles.length > 0 ? uploadedFiles.map((f) => f.size).reduce((p, v) => p+v) : 0;
|
|
if((combined + messageAttachment.size)/CONSTANTS.IMAGES.MB_DIVIDER < CONSTANTS.IMAGES.UPLOAD_LIMIT[message.guild.premiumTier]) {
|
|
uploadedFiles.push(messageAttachment);
|
|
}
|
|
} else {
|
|
files.push(messageAttachment);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let currentFiles = [];
|
|
const uploaded = [];
|
|
|
|
const upload = async (files) => {
|
|
const attachmentMessage = await this.attachmentWebhook.send(null, files).catch((error) => console.error(error));
|
|
attachmentMessage.attachments.map((a) => uploaded.push(`[${a.filename} (${(a.size/CONSTANTS.IMAGES.MB_DIVIDER).toFixed(2)}mb)](${a.url})`));
|
|
console.log(attachmentMessage.attachments);
|
|
};
|
|
|
|
for(const file of files) {
|
|
const currentMb = currentFiles.length > 0 ? currentFiles.map((f) => f.size).reduce((p, v) => p+v) : 0;
|
|
if(currentMb + file.size > 8*CONSTANTS.IMAGES.MB_DIVIDER) {
|
|
await upload(currentFiles);
|
|
currentFiles = [];
|
|
} else {
|
|
currentFiles.push(file);
|
|
}
|
|
}
|
|
|
|
if(currentFiles.length > 0) {
|
|
await upload(currentFiles);
|
|
}
|
|
|
|
console.log(uploaded.length);
|
|
|
|
if(uploaded.length > 0) {
|
|
embed.description += `\n\n**${uploadedFiles.length > 0 ? 'Additional ' : ''}Attachment${uploaded.length > 1 ? 's' : ''}:** ${uploaded.join(', ')}`;
|
|
}
|
|
|
|
}
|
|
|
|
logChannel.send({ embed, files: uploadedFiles });
|
|
|
|
/*
|
|
if(message.attachments.size > 0) {
|
|
return this.logAttachment({ msgID: message.id, guildID: guild.id, channelID: channel.id, logChannel });
|
|
}
|
|
|
|
const embed = {
|
|
title: message.format('MSGLOG_DELETE_TITLE', { author: Util.escapeMarkdown(author.tag), channel: channel.name }),
|
|
description: message.content,
|
|
footer: {
|
|
text: message.format('MSGLOG_DELETE_FOOTER', { msgID: message.id, userID: author.id })
|
|
},
|
|
color: CONSTANTS.COLORS.RED,
|
|
timestamp: message.createdAt
|
|
};
|
|
|
|
await logChannel.send({ embed });
|
|
*/
|
|
|
|
}
|
|
|
|
async messageDeleteBulk(messages) {
|
|
|
|
}
|
|
|
|
async messageEdit(oldMessage, newMessage) {
|
|
|
|
// embeds loading in (ex. when a link is posted) would cause a message edit event
|
|
if(oldMessage.embeds.length !== newMessage.embeds.length) return;
|
|
if(oldMessage.author.bot) return;
|
|
|
|
const { guild } = oldMessage;
|
|
if (!guild) return;
|
|
|
|
if (!oldMessage.member) oldMessage.member = await guild.members.fetch(oldMessage.author);
|
|
const { member, channel, author } = oldMessage;
|
|
|
|
const settings = await guild.settings();
|
|
const chatlogs = settings.messageLog;
|
|
if (!chatlogs.channel) return;
|
|
|
|
const { ignoredRoles, ignoredChannels } = chatlogs;
|
|
const logChannel = await guild.resolveChannel(chatlogs.channel);
|
|
if(!logChannel) return;
|
|
|
|
const perms = logChannel.permissionsFor(guild.me);
|
|
if(!perms.has('SEND_MESSAGES') || !perms.has('VIEW_CHANNEL')) return;
|
|
|
|
if (ignoredRoles && member.roles.cache.size) {
|
|
const roles = member.roles.cache.map((r) => r.id);
|
|
for (const role of ignoredRoles) {
|
|
if (roles.includes(role)) return;
|
|
}
|
|
}
|
|
|
|
if (ignoredChannels && ignoredChannels.includes(channel.id)) return;
|
|
|
|
if(oldMessage.content === newMessage.content && oldMessage.pinned !== newMessage.pinned) {
|
|
|
|
const embed = {
|
|
title: oldMessage.format('MSGLOG_PINNED_TITLE', { author: Util.escapeMarkdown(author.tag), channel: channel.name, pinned: oldMessage.format('PIN_TOGGLE', { toggle: newMessage.pinned }, true) }),
|
|
description: oldMessage.format('MSGLOG_EDIT_JUMP', { guild: guild.id, channel: channel.id, message: oldMessage.id }),
|
|
color: CONSTANTS.COLORS.LIGHT_BLUE
|
|
};
|
|
|
|
if(oldMessage.content.length) embed.description += '\n' + oldMessage.content.substring(0, 1900);
|
|
if(oldMessage.attachments.size) {
|
|
const img = oldMessage.attachments.first();
|
|
if(img.height && img.width) embed.image = { url: img.url };
|
|
}
|
|
|
|
await logChannel.send({ embed }).catch(this.client.logger.error);
|
|
|
|
} else {
|
|
|
|
const embed = {
|
|
title: oldMessage.format('MSGLOG_EDIT_TITLE', { author: Util.escapeMarkdown(author.tag), channel: channel.name }),
|
|
footer: {
|
|
text: oldMessage.format('MSGLOG_EDIT_FOOTER', { msgID: oldMessage.id, userID: author.id })
|
|
},
|
|
description: oldMessage.format('MSGLOG_EDIT_JUMP', { guild: guild.id, channel: channel.id, message: oldMessage.id }),
|
|
color: CONSTANTS.COLORS.YELLOW,
|
|
timestamp: oldMessage.createdAt,
|
|
fields: [
|
|
// {
|
|
// name: oldMessage.format('MSGLOG_EDIT_OLD'),
|
|
// value: oldMessage.content.length > 1024 ? oldMessage.content.substring(0, 1021) + '...' : oldMessage.content
|
|
// },
|
|
// {
|
|
// name: oldMessage.format('MSGLOG_EDIT_NEW'),
|
|
// value: newMessage.content.length > 1024 ? newMessage.content.substring(0, 1021) + '...' : newMessage.content
|
|
// }
|
|
]
|
|
};
|
|
|
|
const oldCon = oldMessage.content,
|
|
newCon = newMessage.content;
|
|
//Original content
|
|
embed.fields.push({
|
|
name: oldMessage.format('MSGLOG_EDIT_OLD'),
|
|
value: oldCon.length > 1024 ? oldCon.substring(0, 1021) + '...' : oldCon
|
|
});
|
|
if(oldCon.length > 1024) embed.fields.push({
|
|
name: '\u200b',
|
|
value: '...' + oldCon.substring(1021)
|
|
});
|
|
//Edited content
|
|
embed.fields.push({
|
|
name: oldMessage.format('MSGLOG_EDIT_NEW'),
|
|
value: newCon.length > 1024 ? newCon.substring(0, 1021) + '...' : newCon
|
|
});
|
|
if(newCon.length > 1024) embed.fields.push({
|
|
name: '\u200b',
|
|
value: '...' + newCon.substring(1021)
|
|
});
|
|
|
|
//if(oldMessage.content.length > 1024) embed.description += '\n' + oldMessage.format('MSGLOG_EDIT_OLD_CUTOFF');
|
|
//if(newMessage.content.length > 1024) embed.description += '\n' + oldMessage.format('MSGLOG_EDIT_NEW_CUTOFF');
|
|
|
|
await logChannel.send({ embed }).catch((err) => {
|
|
this.client.logger.error('Error in message edit:\n' + err.stack);
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
async voiceState(oldState, newState) {
|
|
|
|
if(oldState.channel && newState.channel && oldState.channel === newState.channel) return;
|
|
const { guild, member } = oldState;
|
|
|
|
//TODO: add checks for disconnecting bot from vc when left alone in one (music player)
|
|
|
|
const settings = await guild.settings();
|
|
const setting = settings.voiceLog;
|
|
if(!setting || !setting.channel) return;
|
|
|
|
const logChannel = await guild.resolveChannel(setting.channel);
|
|
if(!logChannel) return;
|
|
|
|
const perms = logChannel.permissionsFor(guild.me);
|
|
if(!perms.has('SEND_MESSAGES') || !perms.has('VIEW_CHANNEL')) return;
|
|
|
|
let index = null;
|
|
const langParams = {
|
|
nickname: member.nickname ? `\`(${member.nickname})\`` : '',
|
|
tag: Util.escapeMarkdown(member.user.tag),
|
|
id: member.id,
|
|
oldChannel: oldState.channel?.name,
|
|
newChannel: newState.channel?.name
|
|
};
|
|
|
|
if(!oldState.channel && newState.channel) index = 'VCLOG_JOIN';
|
|
else if(oldState.channel && newState.channel) index = 'VCLOG_SWITCH';
|
|
else index = 'VCLOG_LEAVE';
|
|
|
|
this.client.rateLimiter.queueSend(logChannel, guild.format(index, langParams).trim());
|
|
|
|
}
|
|
|
|
async ban(guild, user) {
|
|
|
|
}
|
|
|
|
async unban(guild, user) {
|
|
|
|
}
|
|
|
|
_replaceTags(text, member) {
|
|
const { user, guild } = member;
|
|
return text
|
|
.replace(/\{mention\}/gu, `<@${member.id}>`)
|
|
.replace(/\{tag\}/gu, Util.escapeMarkdown(user.tag))
|
|
.replace(/\{user\}/gu, Util.escapeMarkdown(user.username))
|
|
.replace(/\{guildsize\}/gu, guild.memberCount)
|
|
.replace(/\{guildname\}/gu, guild.name)
|
|
.replace(/\{accage\}/gu, this.client.resolver.timeAgo(Date.now()/1000 - user.createdTimestamp/1000)) //.replace(/a/, '1')
|
|
.replace(/\{id\}/gu, user.id)
|
|
.trim();
|
|
}
|
|
|
|
async memberJoin(member) {
|
|
|
|
const { guild } = member;
|
|
const settings = await guild.settings();
|
|
const setting = settings.memberLog;
|
|
if(!setting.channel) return;
|
|
|
|
const logChannel = await guild.resolveChannel(setting.channel);
|
|
if(!logChannel) return;
|
|
|
|
const perms = logChannel.permissionsFor(guild.me);
|
|
if(!perms.has('SEND_MESSAGES') || !perms.has('VIEW_CHANNEL')) return;
|
|
|
|
let { joinMessage } = setting;
|
|
joinMessage = this._replaceTags(joinMessage, member);
|
|
this.client.rateLimiter.queueSend(logChannel, joinMessage);
|
|
|
|
}
|
|
|
|
async memberLeave(member) {
|
|
|
|
const { guild } = member;
|
|
const settings = await guild.settings();
|
|
const setting = settings.memberLog;
|
|
if(!setting.channel) return;
|
|
|
|
const logChannel = await guild.resolveChannel(setting.channel);
|
|
if(!logChannel) return;
|
|
|
|
const perms = logChannel.permissionsFor(guild.me);
|
|
if(!perms.has('SEND_MESSAGES') || !perms.has('VIEW_CHANNEL')) return;
|
|
|
|
let { leaveMessage } = setting;
|
|
leaveMessage = this._replaceTags(leaveMessage, member);
|
|
this.client.rateLimiter.queueSend(logChannel, leaveMessage);
|
|
|
|
}
|
|
|
|
async memberUpdate(oldMember, newMember) {
|
|
|
|
if(oldMember.nickname === newMember.nickname) return;
|
|
|
|
const { guild, user } = oldMember;
|
|
const settings = await guild.settings();
|
|
const setting = settings.nicknameLog;
|
|
if(!setting.channel) return;
|
|
|
|
const logChannel = await guild.resolveChannel(setting.channel);
|
|
if(!logChannel) return;
|
|
|
|
const perms = logChannel.permissionsFor(guild.me);
|
|
if(!perms.has('SEND_MESSAGES') || !perms.has('VIEW_CHANNEL')) return;
|
|
|
|
const oldNick = oldMember.nickname || oldMember.user.username;
|
|
const newNick = newMember.nickname || newMember.user.username;
|
|
|
|
const embed = {
|
|
title: guild.format('NICKLOG_TITLE', { user: Util.escapeMarkdown(user.tag) }),
|
|
description: guild.format('NICKLOG_DESCRIPTION', { oldNick, newNick }),
|
|
footer: {
|
|
text: guild.format('NICKLOG_FOOTER', { id: user.id })
|
|
},
|
|
color: CONSTANTS.COLORS.BLUE
|
|
};
|
|
|
|
logChannel.send({ embed });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = GuildLogger; |