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

202 lines
6.8 KiB
JavaScript

const path = require('path');
const chalk = require('chalk');
const { Observer } = require("../../../interfaces");
const { Util, Collection } = require('../../../../util/');
const CONSTANTS = {
IMAGES: {
PREMIUM_LIMIT: 2,
UPLOAD_LIMIT: {
'0': 8,
'1': 8,
'2': 50,
'3': 100
},
MB_DIVIDER: 1024*1024,
PREMIUM_DELETE: {
'0': 0,
'1': 0,
'2': 2,
'3': 4
}
},
DAY: 24 * 60 * 60
};
class MessageCache extends Observer {
constructor(client) {
super(client, {
name: 'messageCache',
priority: 0,
disabled: false
});
this.client = client;
this.hooks = [
['message', this.cacheAttachments.bind(this)],
['messageDelete', this.cacheMessage.bind(this)]
];
this.attachments = {};
this.messages = new Collection();
setInterval(() => {
this._sweepCache();
this._sweepDatabase();
}, 3600*1000); // 1 hour
}
async cacheMessage(message) {
if(!this.client._built
|| message.webhookID
|| message.author.bot
|| !message.guild
|| !message.guild.available) return undefined;
const metadata = {
guild: message.guild.id,
message: message.id,
author: message.author.id,
channel: message.channel.id,
content: message.content,
id: message.id,
timestamp: message.createdTimestamp,
attachments: this.attachments[message.id] || [],
removeAt: Math.floor(Date.now()/1000 + CONSTANTS.IMAGES.PREMIUM_DELETE[message.guild.premium]*24*60*60)
};
this.messages.set(message.id, metadata);
await this.client.storageManager.mongodb.messages.insertOne(metadata);
}
async cacheAttachments(message) {
if(!this.client._built
|| message.webhookID
|| message.author.bot
|| !message.guild
|| !message.guild.available) return undefined;
const settings = await message.guild.settings();
const { messageLog } = settings;
let ignoredRole = false;
if(messageLog.enabled && messageLog.ignoredRoles.length > 0) {
for(const role of message.member.roles.cache.keys()) {
if(messageLog.ignoredRoles.includes(role)) ignoredRole = true;
}
}
if(message.attachments.size > 0
&& message.guild.premium >= 2
&& messageLog.channel
&& !messageLog.ignore.includes(message.channel.id)
&& !ignoredRole) {
let size = 0;
const attachments = [];
for(const attachment of message.attachments.values()) {
const data = {
size: attachment.size,
dimensions: { x: attachment.width, y: attachment.height },
extension: path.extname(attachment.name),
url: attachment.proxyURL || attachment.url,
name: attachment.name,
id: attachment.id
};
const fsize = data.size/CONSTANTS.IMAGES.MB_DIVIDER; // File size in MB
if(fsize > CONSTANTS.IMAGES.UPLOAD_LIMIT[message.guild.premiumTier]) {
attachments.data.push(data);
continue; //Cannot upload images larger than the guild's upload limit (Users w/ nitro can upload more than that, but the bot will be unable to post them.)
}
const buffer = await Util.downloadAsBuffer(attachment.proxyURL || attachment.url).catch((err) => {
this.client.logger.error(`Failed to download buffer for "${chalk.bold(data.name)}".\n${err.stack || err}\nhttps://discord.com/channels/${message.guild.id}/${message.channel.id}/${message.id}`);
return null;
});
if(buffer) {
if(fsize < 15) {
const result = await this.client.storageManager.mongodb.attachments.insertOne({
attachmentId: attachment.id,
buffer
});
data.index = result?.insertedId; //NOTE: It is NOT GARAUNTEED FOR EVERY ATTACHMENT TO HAVE AN INDEX. If theres no index, it either failed to be pushed to database or could not be saved.
} else {
//Upload using GridFS, not a priority right now.
//this.client.logger.error(`Temporary logging; attachment "${chalk.bold(data.name)}" exceeds 15mb.`);
}
}
attachments.push(data);
size += fsize;
}
if(attachments.length > 0) this.attachments[message.id] = attachments;
this.client.logger.debug(`${chalk.bold('[IMAGE]')} User ${message.author.tag} in guild ${message.guild.name} (#${message.channel.name}) uploaded ${message.attachments.size} attachment${message.attachments.size === 1 ? '' : 's'} (${size.toFixed(2)}mb).`);
}
}
async _sweepDatabase() {
const messages = await this.client.storageManager.mongodb.messages.find({
removeAt: {
$lt: Date.now()/1000
}
});
if(messages.length > 0) {
const attachmentIds = messages.map((m) => m.attachments).reduce((a, b) => b.concat(a)).filter((a) => a).map((a) => a.index);
const deleteAttachments = await this.client.storageManager.mongodb.attachments.deleteMany({
_id: { $in: attachmentIds }
});
const msgIds = messages.map((m) => m._id);
const deleteMessages = await this.client.storageManager.mongodb.messages.deleteMany({
_id: { $in: msgIds }
});
const messageCount = deleteMessages.deletedCount;
const attachmentCount = deleteAttachments.deletedCount;
this.client.logger.info(`${chalk.bold('[IMAGE]')} Trashed ${messageCount} message${messageCount === 1 ? '' : 's'} and ${attachmentCount} attachment${attachmentCount === 1 ? '' : 's'}.`);
}
}
_sweepCache() {
const ms = 3600000; // 1 hour in ms ( i think )
const filtered = this.messages.filter((m) => {
const time = Date.now() - m.timestamp;
return time < ms;
});
const difference = this.messages.size-filtered.size;
if(difference > 0) {
this.client.logger.debug(`Trashed ${difference} items from the message cache.`);
this.messages = filtered;
}
return filtered;
}
}
module.exports = MessageCache;