galactic-bot/structure/client/components/observers/GuildLogging.js

417 lines
15 KiB
JavaScript

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)]
];
}
async storeAttachment(message) {
const { guild, member, author, channel } = message;
if(!guild || author.bot) return;
if(!message.attachments.size) return;
console.log('Store call');
const settings = await guild.settings(),
setting = settings.messageLog,
roles = member.roles.cache.map((r) => r.id),
{ ignoredRoles } = setting;
//if(guild._settings.premium < CONSTANTS.IMAGES.PREMIUM_LIMIT) return;
if(!setting.attachments || setting.ignoredChannels.includes(channel.id)) return;
if(setting.ignoredRoles.length && roles.length) for(const role of ignoredRoles) if(roles.includes(role)) return;
console.log('Can store');
const attachments = message.attachments.values();
for(const attachment of attachments) {
const TH = this.client.transactionHandler;
const { size, name, id } = attachment;
const fsize = size/CONSTANTS.IMAGES.MB_DIVIDER; // File size in MB
if(fsize > CONSTANTS.IMAGES.UPLOAD_LIMIT[guild.premiumTier]) continue;
const buffer = await Util.downloadAsBuffer(attachment.url).catch((err) => {
this.client.logger.error(err); return null;
});
if(!buffer) return;
const data = {
buffer,
id
};
try {
//TODO: test this
//const start = Date.now();
const result = await TH.send({
provider: 'mongodb',
request: {
type: 'insertOne',
collection: 'attachment_logs',
data
}
});
//console.log(`Took ${Date.now()-start}ms to insert image.`);
//console.log(result);
const metadata = {
database_ID: result.insertedId,
id,
guild: guild.id,
message: message.id,
author: author.id,
name,
size,
timestamp: Math.floor(Date.now()/1000),
removeAt: Math.floor(Date.now()/1000 + guild._settings.premium * CONSTANTS.WEEK * 0.25)
};
//console.log(metadata);
await TH.send({
provider: 'mongodb',
request: {
type: 'insertOne',
collection: 'attachment_logs_index',
data: metadata
}
});
//console.log(`Took ${Date.now()-start}ms to insert entire entry.`);
} catch (err) {
this.client.logger.error('Something went wrong with storing image to db:\n' + err.stack);
}
}
}
//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(message.author.bot) return;
const { guild } = message;
if (!guild) return;
if (!message.member) message.member = await guild.members.fetch(message.author);
const { member, channel, author } = message;
const settings = await guild.settings();
const chatlogs = settings.messageLog;
if (!chatlogs || !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.length && 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 (message.attachments.size) 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;