Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup + improvements #1

Open
wants to merge 13 commits into
base: development
Choose a base branch
from
Next Next commit
cleanup
  • Loading branch information
Elara-Discord-Bots committed Apr 15, 2024
commit 872d6d49b338712a910bd4243d939c53d2fbf3dc
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ DISCORD_SUPPORT_SERVER=discord.gg/WYTxVjzHnc # Discord invite for your support
DISCORD_WEBHOOK_URL=yourWebhookUrl # Webhook URL that's used for sending certain dev-logs to Discord.
ENABLE_TEXT_COMMANDS=true # Whether to allow users to utilise prefix/text commands. 'true' or 'false'. Recommend true.
GLOBAL_BOT_PREFIX=% # Prefix to use for prefix/text commands.
MESSAGE_ARCHIVE_SIZE=1000 # How many messages to fetch for the archive commands
MESSAGE_BATCH_SIZE=100 # How many messages to store in 'cache' before dumping into database. Defaults to 1000.
MESSAGE_HISTORY_DAYS=2 # Maximum age of messages stored in the database. Note that you have to manually setup clearing, e.g. via pg_cron https://github.com/citusdata/pg_cron.
PASTE_SITE_ROOT_URL=https://my-paste-site.com # URL of your selfhosted [haste-server](https://github.com/toptal/haste-server). Used in archive commands and messageDeleteBulk event.
Expand Down
17 changes: 17 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"env": {
"node": true,
"commonjs": true,
"es2021": true
},
"extends": "eslint:recommended",
"overrides": [
],
"parserOptions": {
"ecmaVersion": "latest"
},
"rules": {
"no-useless-escape": "off",
"no-prototype-builtins": "off"
}
}
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ global.webhook = require('./src/miscellaneous/webhooklogger')
global.cluster = require('cluster')
require('./src/miscellaneous/logger')
require('dotenv').config()
if (cluster.isMaster) {
if (global.cluster.isMaster) {
global.logger.startup('Master node init')
require('./primary')
} else {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "index.js",
"scripts": {
"test": "standard",
"lint": "node_modules/.bin/eslint . --ext .js"
"lint": "eslint . --ext .js"
},
"keywords": [
"logging",
Expand Down
35 changes: 14 additions & 21 deletions src/bot/commands/archive.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
const sa = require('superagent')
const { createHaste } = require('../utils/createHaste')

module.exports = {
func: async (message, suffix) => {
if (!process.env.PASTE_SITE_ROOT_URL) return message.channel.createMessage('The bot owner hasn\'t yet configured the paste site, so this command is unavailable.')
if (!suffix || isNaN(suffix)) return message.channel.createMessage('That isn\'t a valid suffix! Please provide any number between 5 and 1000 (10,000 if Patreon).')
const limit = parseInt(process.env.MESSAGE_ARCHIVE_SIZE || 1000);
if (!suffix || isNaN(suffix)) return message.channel.createMessage(`That isn't a valid suffix! Please provide any number between 5 and ${limit}.`)
const num = parseInt(suffix)
if (num < 5 || num > 1000) return message.channel.createMessage('That number is invalid! Please provide any number between 5 and 1000 (10,000 if Patreon)')
message.channel.getMessages({ limit: num }).then(messages => {
const pasteString = messages.reverse().filter(m => !m.applicationID).map(m => `${m.author.username}${m.author.discriminator === '0' ? '' : `#${m.author.discriminator}`} (${m.author.id}) | ${new Date(m.timestamp).toUTCString()}: ${m.content ? m.content : ''} ${m.embeds.length === 0 ? '' : `| {"embeds": [${m.embeds.map(e => JSON.stringify(e))}]}`} | ${m.attachments.length === 0 ? '' : ` =====> Attachment: ${m.attachments[0].filename}:${m.attachments[0].url}`}`).join('\r\n')
sa
.post(`${process.env.PASTE_SITE_ROOT_URL.endsWith("/") ? process.env.PASTE_SITE_ROOT_URL.slice(0, -1) : process.env.PASTE_SITE_ROOT_URL}/documents`)
.set('Authorization', process.env.PASTE_SITE_TOKEN ?? '')
.set('Content-Type', 'text/plain')
.send(pasteString || 'No messages were able to be archived')
.end((err, res) => {
if (!err && res.statusCode === 200 && res.body.key) {
message.channel.createMessage(`<@${message.author.id}>, **${messages.length}** message(s) could be archived. Link: ${process.env.PASTE_SITE_ROOT_URL.endsWith("/") ? process.env.PASTE_SITE_ROOT_URL.slice(0, -1) : process.env.PASTE_SITE_ROOT_URL}/${res.body.key}.txt`)
} else {
global.logger.error(err, res.body)
global.webhook.error('An error has occurred while posting to the paste website. Check logs for more.')
}
})
})
if (num < 5 || num > limit) return message.channel.createMessage(`That number is invalid! Please provide any number between 5 and ${limit}`)

const messages = await message.channel.getMessages({ limit: num })
const pasteString = messages.reverse().filter(m => !m.applicationID).map(m => `${m.author.username}${m.author.discriminator === '0' ? '' : `#${m.author.discriminator}`} (${m.author.id}) | ${new Date(m.timestamp).toUTCString()}: ${m.content ? m.content : ''} ${m.embeds.length === 0 ? '' : `| {"embeds": [${m.embeds.map(e => JSON.stringify(e))}]}`} | ${m.attachments.length === 0 ? '' : ` =====> Attachment: ${m.attachments[0].filename}:${m.attachments[0].url}`}`).join('\r\n')
const link = await createHaste(pasteString)
if (!link) {
return message.channel.createMessage('Unable to get the archive haste link')
}
message.channel.createMessage(`<@${message.author.id}>, **${messages.length}** message(s) could be archived. Link: ${link}`)
},
name: 'archive',
category: 'Utility',
perm: 'manageMessages',
quickHelp: 'Makes a log online of up to the last 1000 messages in a channel. Does NOT delete any messages. Patreon bot only: fetch 10,000 messages & [upgraded log site](https://logs.discord.website/logs/W9NbmmULEpxMFMoiBuKrYG)',
quickHelp: `Makes a log online of up to the last ${process.env.MESSAGE_ARCHIVE_SIZE || 100} messages in a channel. Does NOT delete any messages.`,
examples: `\`${process.env.GLOBAL_BOT_PREFIX}archive 5\` <- lowest amount possible
\`${process.env.GLOBAL_BOT_PREFIX}archive 1000\` <- maximum count of messages to archive
\`${process.env.GLOBAL_BOT_PREFIX}archive ${process.env.MESSAGE_ARCHIVE_SIZE || 1000}\` <- maximum count of messages to archive
\`${process.env.GLOBAL_BOT_PREFIX}archive 25\` <- create a log of the last 25 messages in the channel`
}
6 changes: 3 additions & 3 deletions src/bot/commands/getcpu.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
module.exports = {
func: async (message, suffix) => {
func: async (message) => {
const os = require('os-utils')

os.cpuUsage(async v => {
await message.channel.createMessage(`[${cluster.worker.rangeForShard}] CPU usage: ${v * 100}%`)
await message.channel.createMessage(`[${global.cluster.worker.rangeForShard}] CPU usage: ${v * 100}%`)
})
os.cpuFree(async v => {
await message.channel.createMessage(`[${cluster.worker.rangeForShard}] CPU free: ${v * 100}%`)
await message.channel.createMessage(`[${global.cluster.worker.rangeForShard}] CPU free: ${v * 100}%`)
})
},
name: 'getcpu',
Expand Down
2 changes: 1 addition & 1 deletion src/bot/commands/reindexcommands.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const commandIndexer = require('../../miscellaneous/commandIndexer')

module.exports = {
func: async function (message, suffix) {
func: async function (message) {
try {
global.bot.commands = {}
commandIndexer()
Expand Down
2 changes: 1 addition & 1 deletion src/bot/commands/reset.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module.exports = {
let complete = false
global.bot.on('messageCreate', async function temp (m) {
if (i === 0) {
const timeout = setTimeout(() => {
setTimeout(() => {
global.bot.removeListener('messageCreate', temp)
if (!complete) {
message.channel.createMessage({
Expand Down
4 changes: 2 additions & 2 deletions src/bot/commands/setchannel.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ module.exports = {
func: async (message, suffix) => {
const botPerms = message.channel.permissionsOf(global.bot.user.id).json
if (!botPerms.manageWebhooks || !botPerms.viewAuditLogs) {
message.channel.createMessage('I need manage webhooks and view audit logs permissions to run setchannel! This is necessary for me to send messages to your configured logging channel.').catch(_ => {})
message.addReaction('❌').catch(_ => {})
message.channel.createMessage('I need manage webhooks and view audit logs permissions to run setchannel! This is necessary for me to send messages to your configured logging channel.').catch(() => {})
message.addReaction('❌').catch(() => {})
return
}
let events = suffix.split(', ')
Expand Down
2 changes: 1 addition & 1 deletion src/bot/events/channelCreate.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module.exports = {
name: 'Unknown User',
icon_url: 'https://logger.bot/staticfiles/red-x.png'
},
description: `${CHANNEL_TYPE_MAP[newChannel.type] ? CHANNEL_TYPE_MAP[newChannel.type] : 'Unsupported channel type'} created <#${newChannel.id}>`,
description: `${CHANNEL_TYPE_MAP[newChannel.type] || 'Unsupported channel type'} created <#${newChannel.id}>`,
fields: [{
name: 'Name',
value: newChannel.name
Expand Down
2 changes: 1 addition & 1 deletion src/bot/events/channelDelete.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module.exports = {
name: 'Unknown User',
icon_url: 'https://logger.bot/staticfiles/red-x.png'
},
description: `${CHANNEL_TYPE_MAP[channel.type] ? CHANNEL_TYPE_MAP[channel.type] : 'Unsupported channel type'} deleted (${channel.name})`,
description: `${CHANNEL_TYPE_MAP[channel.type] || 'Unsupported channel type'} deleted (${channel.name})`,
fields: [{
name: 'Name',
value: channel.name
Expand Down
1 change: 0 additions & 1 deletion src/bot/events/channelUpdate.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const { Permission } = require('eris')
const send = require('../modules/webhooksender')
const escape = require('markdown-escape')
const CHANNEL_TYPE_MAP = {
Expand Down
2 changes: 1 addition & 1 deletion src/bot/events/disconnect.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
handle: () => {
statAggregator.incrementMisc('disconnect')
reconnects++
global.logger.error(`Worker instance hosting ${cluster.worker.rangeForShard} on id ${cluster.worker.id} disconnected from the gateway. ${reconnects} out of 20.`)
global.logger.error(`Worker instance hosting ${global.cluster.worker.rangeForShard} on id ${global.cluster.worker.id} disconnected from the gateway. ${reconnects} out of 20.`)
if (reconnects >= 20) {
global.bot.disconnect({ reconnect: true }) // Disconnect the bot but don't destroy member caches
}
Expand Down
1 change: 0 additions & 1 deletion src/bot/events/guildMemberRemove.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const send = require('../modules/webhooksender')
const prunecache = require('../modules/prunecache')
const { User } = require('eris')

module.exports = {
Expand Down
4 changes: 3 additions & 1 deletion src/bot/events/guildUpdate.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const explicitContentLevels = {
module.exports = {
name: 'guildUpdate',
type: 'on',
handle: async (newGuild, oldGuild) => {
handle: async (newGuild) => {
const fields = []
newGuild.getAuditLogs({ actionType: 1, limit: 1 }).then((log) => {
if (!log || !log.entries || log.entries.length === 0 || new Date().getTime() - new Date((log.entries[0].id / 4194304) + 1420070400000).getTime() > 3000) return // this could be null coalesced but why not make it backwards compatible
Expand Down Expand Up @@ -99,7 +99,9 @@ module.exports = {
value: `► Now: **${after}**\n► Was: **${before}**`
}
case 'afk_channel_id':
// eslint-disable-next-line no-case-declarations
const beforeChannel = logEntry.before && newGuild.channels.get(logEntry.before.afk_channel_id)
// eslint-disable-next-line no-case-declarations
const afterChannel = logEntry.after && newGuild.channels.get(logEntry.after.afk_channel_id)
if (!beforeChannel) {
before = 'None'
Expand Down
2 changes: 1 addition & 1 deletion src/bot/events/interactionCreate.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module.exports = {
name: 'interactionCreate',
type: 'on',
handle (interaction) {
return new Promise((resolve, reject) => { // why use a promise? awaitCustomID and handle are still sync and can share the timeout/callback maps nicely?
return new Promise((resolve) => { // why use a promise? awaitCustomID and handle are still sync and can share the timeout/callback maps nicely?
if (interaction.applicationID !== global.bot.user.id) {
resolve()
}
Expand Down
28 changes: 8 additions & 20 deletions src/bot/events/messageDelete.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
const send = require('../modules/webhooksender')
const getMessageFromDB = require('../../db/interfaces/postgres/read').getMessageById
const getMessageFromBatch = require('../../db/messageBatcher').getMessage
const deleteMessage = require('../../db/interfaces/postgres/delete').deleteMessage
const { getMessageById } = require('../../db/interfaces/postgres/read')
const { getMessage } = require('../../db/messageBatcher')
const { deleteMessage } = require('../../db/interfaces/postgres/delete')
const cacheGuild = require('../utils/cacheGuild')
const { chunkify } = require('../utils/constants')

module.exports = {
name: 'messageDelete',
Expand All @@ -12,10 +13,7 @@ module.exports = {
const guildSettings = global.bot.guildSettingsCache[message.channel.guild.id]
if (!guildSettings) await cacheGuild(message.channel.guild.id)
if (global.bot.guildSettingsCache[message.channel.guild.id].isChannelIgnored(message.channel.id)) return
let cachedMessage = getMessageFromBatch(message.id)
if (!cachedMessage) {
cachedMessage = await getMessageFromDB(message.id)
}
const cachedMessage = getMessage(message.id) || await getMessageById(message.id);
if (!cachedMessage) return
await deleteMessage(message.id)
let cachedUser = global.bot.users.get(cachedMessage.author_id)
Expand Down Expand Up @@ -44,7 +42,7 @@ module.exports = {
let messageChunks = []
if (cachedMessage.content) {
if (cachedMessage.content.length > 1000) {
messageChunks = chunkify(cachedMessage.content.replace(/\"/g, '"').replace(/`/g, ''))
messageChunks = chunkify(cachedMessage.content.replace(/"/g, '"').replace(/`/g, ''))
} else {
messageChunks.push(cachedMessage.content)
}
Expand All @@ -66,7 +64,7 @@ module.exports = {
})

if (cachedMessage.attachment_b64) {
attachment_b64urls = cachedMessage.attachment_b64.split("|")
let attachment_b64urls = cachedMessage.attachment_b64.split("|")
attachment_b64urls.forEach(
(base64url, indx) => messageDeleteEvent.embeds[indx] = {
...messageDeleteEvent.embeds[indx],
Expand All @@ -77,14 +75,4 @@ module.exports = {
}
await send(messageDeleteEvent)
}
}

function chunkify (toChunk) {
const lenChunks = Math.ceil(toChunk.length / 1000)
const chunksToReturn = []
for (let i = 0; i < lenChunks; i++) {
const chunkedStr = toChunk.substring((1000 * i), i === 0 ? 1000 : 1000 * (i + 1))
chunksToReturn.push(chunkedStr)
}
return chunksToReturn
}
}
78 changes: 30 additions & 48 deletions src/bot/events/messageDeleteBulk.js
Original file line number Diff line number Diff line change
@@ -1,71 +1,53 @@
const sa = require('superagent')
const getMessagesByIds = require('../../db/interfaces/postgres/read').getMessagesByIds
const { getMessagesByIds } = require('../../db/interfaces/postgres/read')
const send = require('../modules/webhooksender')
const { EMBED_COLORS } = require('../utils/constants')
const { createHaste } = require('../utils/createHaste')

module.exports = {
name: 'messageDeleteBulk',
type: 'on',
handle: async messages => {
if (messages.length === 0) return // TODO: TEST!

if (!process.env.PASTE_SITE_ROOT_URL) {
if (!messages[0].guildId) return;

return send({
guildID: messages[0].guildId,
eventName: 'messageDeleteBulk',
embeds: [{
description: `${messages.length} messages were bulk deleted. :warning: The bot owner hasn't configured a paste site so contents of deleted messages not shown. :warning:`,
color: EMBED_COLORS.YELLOW_ORANGE,
}]
});
}

if (!messages.length) return // TODO: TEST!
if (!messages[0].guildId) return;
const dbMessages = await getMessagesByIds(messages.map(m => m.id))
await paste(dbMessages, messages[0].channel.guild.id)
await paste(dbMessages, messages[0].guildId)
}
}

async function paste (messages, guildID) {
async function paste(messages, guildID) {
if (!messages) return
const messageDeleteBulkEvent = {
guildID: guildID,
eventName: 'messageDeleteBulk',
embeds: [{
description: `**${messages.length}** message(s) were deleted and known in cache.`,
fields: [],
color: 15550861
}]
}
const pasteString = messages.reverse().map(m => {
let globalUser = global.bot.users.get(m.author_id)
if (!globalUser) {
globalUser = {
username: 'Unknown',
discriminator: '0000',
discriminator: '0',
avatarURL: '<no avatar>'
}
}
return `${globalUser.username}${globalUser.discriminator === '0' ? '' : `#${globalUser.discriminator}`} (${m.author_id}) | (${globalUser.avatarURL}) | ${new Date(m.ts).toUTCString()}: ${m.content}`
}).join('\r\n')
if (pasteString) {
sa
.post(`${process.env.PASTE_SITE_ROOT_URL.endsWith("/") ? process.env.PASTE_SITE_ROOT_URL.slice(0, -1) : process.env.PASTE_SITE_ROOT_URL}/documents`)
.set('Authorization', process.env.PASTE_SITE_TOKEN ?? '')
.set('Content-Type', 'text/plain')
.send(pasteString || 'An error has occurred while fetching pastes. Please contact the bot author.')
.end((err, res) => {
if (!err && res.body && res.statusCode === 200 && res.body.key) {
messageDeleteBulkEvent.embeds[0].fields.push({
name: 'Link',
value: `${process.env.PASTE_SITE_ROOT_URL.endsWith("/") ? process.env.PASTE_SITE_ROOT_URL.slice(0, -1) : process.env.PASTE_SITE_ROOT_URL}/${res.body.key}.txt`
})
send(messageDeleteBulkEvent)
} else {
global.logger.error(err)
global.webhook.error('An error has occurred while posting to the paste website. Check logs for more.')
}
})
if (!pasteString) {
return
}
const messageDeleteBulkEvent = {
guildID: guildID,
eventName: 'messageDeleteBulk',
embeds: [{
description: `**${messages.length}** message(s) were deleted and known in cache.`,
fields: [],
color: 15550861
}],
file: [
// Send a messages.txt file to the channel.
{ name: `messages.txt`, file: Buffer.from(pasteString) }
]
}
const link = await createHaste(pasteString)
if (link) {
messageDeleteBulkEvent.embeds[0].fields.push({
name: 'Link',
value: link
})
}
send(messageDeleteBulkEvent)
}
Loading