diff --git a/bot/package.json b/bot/package.json index 2fe5bf5..b8157c7 100644 --- a/bot/package.json +++ b/bot/package.json @@ -17,7 +17,6 @@ "automod": "^0.1.0", "axios": "^0.22.0", "dayjs": "^1.10.7", - "discord.js": "^13.3.1", "dotenv": "^10.0.0", "form-data": "^4.0.0", "log75": "^2.2.0", diff --git a/bot/src/bot/commands/admin/botadm.ts b/bot/src/bot/commands/admin/botadm.ts index 23c8ee1..4916906 100644 --- a/bot/src/bot/commands/admin/botadm.ts +++ b/bot/src/bot/commands/admin/botadm.ts @@ -66,7 +66,6 @@ export default { + `Commit hash: \`${await getCommitHash() || 'Unknown'}\`\n` + `### Packages\n` + `revolt.js: \`${pjson.dependencies['revolt.js']}\`\n` - + `discord.js: \`${pjson.dependencies['discord.js']}\`\n` + `axios: \`${pjson.dependencies['axios']}\`\n` + `log75: \`${pjson.dependencies['log75']}\`\n` + `typescript: \`${pjson.devDependencies['typescript']}\`\n` diff --git a/bot/src/bot/commands/configuration/bridge.ts b/bot/src/bot/commands/configuration/bridge.ts deleted file mode 100644 index 449af15..0000000 --- a/bot/src/bot/commands/configuration/bridge.ts +++ /dev/null @@ -1,361 +0,0 @@ -import { Message } from "revolt.js"; -import { ulid } from "ulid"; -import { SendableEmbed } from "revolt-api"; -import { CONFIG_KEYS } from "automod/dist/misc/bridge_config_keys"; -import { dbs } from "../../.."; -import CommandCategory from "../../../struct/commands/CommandCategory"; -import SimpleCommand from "../../../struct/commands/SimpleCommand"; -import MessageCommandContext from "../../../struct/MessageCommandContext"; -import { DEFAULT_PREFIX } from "../../modules/command_handler"; -import { - embed, - EmbedColor, - isBotManager, - isModerator, - NO_MANAGER_MSG, -} from "../../util"; - -const DISCORD_INVITE_URL = - "https://discord.com/api/oauth2/authorize?client_id=965692929643524136&permissions=536996864&scope=bot%20applications.commands"; // todo: read this from env or smth - -export default { - name: "bridge", - aliases: null, - description: "Bridge a channel with Discord", - category: CommandCategory.Misc, - run: async (message: MessageCommandContext, args: string[]) => { - switch (args[0]?.toLowerCase()) { - case "link": { - if (!(await isBotManager(message))) - return message.reply(NO_MANAGER_MSG); - - const count = await dbs.BRIDGE_CONFIG.count({ - revolt: message.channelId, - }); - if (count) - return message.reply(`This channel is already bridged.`); - - // Invalidate previous bridge request - await dbs.BRIDGE_REQUESTS.remove({ - revolt: message.channelId, - }); - - const reqId = ulid(); - await dbs.BRIDGE_REQUESTS.insert({ - id: reqId, - revolt: message.channelId, - expires: Date.now() + 1000 * 60 * 15, - }); - - let text = - `### Link request created.\n` + - `Request ID: \`${reqId}\`\n\n` + - `[Invite the bridge bot to your Discord server](<${DISCORD_INVITE_URL}>) ` + - `and run \`/bridge confirm ${reqId}\` in the channel you wish to link.\n` + - `This request expires in 15 minutes.`; - - if ( - !message.channel!.havePermission("Masquerade") || - !message.channel!.havePermission("SendEmbeds") || - !message.channel!.havePermission("UploadFiles") - ) { - text += - "\n\n> :warning: I currently don't have all required permissions in this " + - 'channel for the bridge to work. Please make sure to grant the "Masquerade", ' + - '"Upload Files" and "Send Embeds" permission.'; - } - - await message.reply(text, false); - - break; - } - case "unlink": { - if (!(await isBotManager(message))) - return message.reply(NO_MANAGER_MSG); - - const res = await dbs.BRIDGE_CONFIG.remove({ - revolt: message.channelId, - }); - if (res.deletedCount) await message.reply(`Channel unlinked!`); - else - await message.reply(`Unable to unlink; no channel linked.`); - break; - } - case "unlink_all": { - if (!(await isBotManager(message))) - return message.reply(NO_MANAGER_MSG); - - const query = { - revolt: { $in: Array.from(message.channel?.server?.channelIds.values() ?? []) }, - }; - if (args[1] == "CONFIRM") { - const res = await dbs.BRIDGE_CONFIG.remove(query); - if (res.deletedCount) { - await message.reply( - `All channels have been unlinked. (Count: **${res.deletedCount}**)` - ); - } else { - await message.reply( - `No bridged channels found; nothing to delete.` - ); - } - } else { - const res = await dbs.BRIDGE_CONFIG.count(query); - if (!res) - await message.reply( - `No bridged channels found; nothing to delete.` - ); - else { - await message.reply( - `${res} bridged channels found. ` + - `Run \`${DEFAULT_PREFIX}bridge unlink_all CONFIRM\` to confirm deletion.` - ); - } - } - break; - } - case "list": { - if (!(await isBotManager(message))) - return message.reply(NO_MANAGER_MSG); - - const links = await dbs.BRIDGE_CONFIG.find({ - revolt: { $in: Array.from(message.channel?.server?.channelIds.values() ?? []) }, - }); - - await message.reply({ - content: "#", - embeds: [ - { - title: `Bridges in ${message.channel?.server?.name}`, - description: - `**${links.length}** bridged channels found.\n\n` + - links - .map( - (l) => - `<#${l.revolt}> **->** ${l.discord}` - ) - .join("\n"), - }, - ], - }); - break; - } - case "info": { - try { - if (!message.replyIds) { - return await message.reply( - "Please run this command again while replying to a message." - ); - } - - if ( - message.replyIds.length > 1 && - !(await isModerator(message, false)) - ) { - return await message.reply( - "To avoid spam, only moderators are allowed to query bridge info for more than one message at a time." - ); - } - - const messages = ( - await Promise.allSettled( - message.replyIds.map((m) => - message.channel!.fetchMessage(m) - ) || [] - ) - ) - .filter((m) => m.status == "fulfilled") - .map( - (m) => (m as PromiseFulfilledResult).value - ); - - if (!messages.length) { - return await message.reply( - "Something went wrong; could not fetch the target message(s)." - ); - } - - const embeds: SendableEmbed[] = await Promise.all( - messages.map(async (msg) => { - const bridgeData = - await dbs.BRIDGED_MESSAGES.findOne({ - "revolt.messageId": msg.id, - }); - - const embed: SendableEmbed = bridgeData - ? { - url: msg.url, - title: `Message ${ - bridgeData?.origin == "revolt" - ? `by ${msg.author?.username}` - : "from Discord" - }`, - colour: "#7e96ff", - description: - `**Origin:** ${ - bridgeData.origin == "revolt" - ? "Revolt" - : "Discord" - }\n` + - `**Bridge Status:** ${ - bridgeData.origin == "revolt" - ? bridgeData.discord.messageId - ? "Bridged" - : "Unbridged" - : bridgeData.revolt.messageId - ? "Bridged" - : bridgeData.revolt.nonce - ? "ID unknown" - : "Unbridged" - }\n` + - `### Bridge Data\n` + - `Origin: \`${bridgeData.origin}\`\n` + - `Discord ID: \`${bridgeData.discord.messageId}\`\n` + - `Revolt ID: \`${bridgeData.revolt.messageId}\`\n` + - `Revolt Nonce: \`${bridgeData.revolt.nonce}\`\n` + - `Discord Channel: \`${bridgeData.channels?.discord}\`\n` + - `Revolt Channel: \`${bridgeData.channels?.revolt}\``, - } - : { - url: msg.url, - title: `Message by ${msg.author?.username}`, - description: - "This message has not been bridged.", - colour: "#7e96ff", - }; - - return embed; - }) - ); - - await message.reply({ embeds }, false); - } catch (e) { - console.error(e); - message.reply("" + e)?.catch(() => {}); - } - break; - } - case "status": { - const link = await dbs.BRIDGE_CONFIG.findOne({ - revolt: message.channelId, - }); - - if (!link) - return await message.reply({ - embeds: [ - embed( - "This channel is **not** bridged, and no message data is being sent to Discord.", - "Bridge status", - EmbedColor.Success - ), - ], - }); - else - return await message.reply({ - embeds: [ - embed( - "This channel is bridged to Discord. Please refer to the [Privacy Policy]() for more info.", - "Bridge Status", - EmbedColor.Success - ), - ], - }); - } - case "config": { - if (!(await isBotManager(message))) - return message.reply(NO_MANAGER_MSG); - - const [_, configKey, newVal]: (string | undefined)[] = args; - - if (!configKey) { - return await message.reply({ - embeds: [ - { - title: "Bridge Configuration", - description: - `To modify a configuration option, run ${DEFAULT_PREFIX}bridge config [true|false].\n\n` + - `**Available configuration keys:**` + - Object.keys(CONFIG_KEYS).map( - (key) => `\n- ${key}` - ), - }, - ], - }); - } - - if (!Object.keys(CONFIG_KEYS).includes(configKey)) { - return await message.reply("Unknown configuration key."); - } - - const key = CONFIG_KEYS[configKey as keyof typeof CONFIG_KEYS]; - - if (!newVal) { - const bridgeConfig = await dbs.BRIDGE_CONFIG.findOne({ - revolt: message.channelId, - }); - return await message.reply({ - embeds: [ - { - title: "Bridge Configuration: " + configKey, - description: `**${key.friendlyName}**\n${ - key.description - }\n\nCurrent value: **${ - !!bridgeConfig?.config?.[ - configKey as keyof typeof CONFIG_KEYS - ] - }**`, - }, - ], - }); - } - - if (newVal != "true" && newVal != "false") { - return await message.reply( - "Value needs to be either `true` or `false`." - ); - } - - await dbs.BRIDGE_CONFIG.update( - { revolt: message.channelId }, - { - $set: { [`config.${configKey}`]: newVal == "true" }, - $setOnInsert: { revolt: message.channelId }, - }, - { upsert: true } - ); - return await message.reply( - `Configuration key **${configKey}** has been updated to **${newVal}**.` - ); - } - case "help": { - await message.reply({ - content: "#", - embeds: [ - { - title: "Discord Bridge", - description: - `Bridges allow you to link your Revolt server to a Discord server ` + - `by relaying all messages.\n\n` + - `To link a channel, first run \`${DEFAULT_PREFIX}bridge link\` on Revolt. ` + - `This will provide you with a link ID.\n` + - `On Discord, first [add the Bridge bot to your server](<${DISCORD_INVITE_URL}>), ` + - `then run the command: \`/bridge confirm [ID]\`.\n\n` + - `You can list all bridges in a Revolt server by running \`${DEFAULT_PREFIX}bridge list\`\n\n` + - `To unlink a channel, run \`/bridge unlink\` from either Discord or Revolt. If you wish to ` + - `unbridge all channels in a Revolt server, run \`${DEFAULT_PREFIX}bridge unlink_all\`.\n` + - `To view bridge info about a particular message, run \`${DEFAULT_PREFIX}bridge info\` ` + - `while replying to the message.\n` + - `You can customize how the bridge behaves using \`${DEFAULT_PREFIX}bridge config\`.`, - }, - ], - }); - break; - } - default: { - await message.reply( - `Run \`${DEFAULT_PREFIX}bridge help\` for help.` - ); - } - } - }, -} as SimpleCommand; diff --git a/bot/src/bot/commands/misc/debug.ts b/bot/src/bot/commands/misc/debug.ts deleted file mode 100644 index d7953f9..0000000 --- a/bot/src/bot/commands/misc/debug.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { decodeTime } from "ulid"; -import CommandCategory from "../../../struct/commands/CommandCategory"; -import SimpleCommand from "../../../struct/commands/SimpleCommand"; -import MessageCommandContext from "../../../struct/MessageCommandContext"; -import { ULID_REGEX } from "../../util"; - -export default { - name: 'debug', - aliases: null, - description: 'Gives info helpful for development and debugging', - syntax: '/debug [ULID|Discord ID|(empty)]', - category: CommandCategory.Misc, - run: async (message: MessageCommandContext, args: string[]) => { - if (ULID_REGEX.test(args[0])) { - const ts = decodeTime(args[0]); - const tsSmall = Math.round(ts / 1000); - await message.reply( - `ULID: \`${args[0]}\`\n` + - `TS: \`${ts}\` ( / )`, - false - ); - } else if (validateSnowflake(args[0])) { - const date = convertSnowflakeToDate(args[0]); - const ts = date.getTime(), - tsSmall = Math.round(ts / 1000); - - await message.reply( - `Discord Snowflake: \`${args[0]}\`\n` + - `TS: \`${ts}\` ( / )`, - false - ); - } else { - await message.reply( - `Server ID: ${message.channel?.serverId || 'None'}\n` - + `Server context: ${message.serverContext.id} ` - + `(${message.serverContext.id == message.channel?.serverId ? 'This server' : message.serverContext.name})\n` - + `Channel ID: ${message.channelId}\n` - + `User ID: ${message.authorId}`, - false - ); - } - } -} as SimpleCommand; - - -/* The below is yoinked from https://github.com/vegeta897/snow-stamp/blob/main/src/convert.js */ - -const DISCORD_EPOCH = 1420070400000; - -// Converts a snowflake ID string into a JS Date object using the provided epoch (in ms), or Discord's epoch if not provided -function convertSnowflakeToDate(snowflake: string|bigint, epoch = DISCORD_EPOCH) { - // Convert snowflake to BigInt to extract timestamp bits - // https://discord.com/developers/docs/reference#snowflakes - const milliseconds = BigInt(snowflake) >> 22n; - return new Date(Number(milliseconds) + epoch); -} - -// Validates a snowflake ID string and returns a JS Date object if valid -function validateSnowflake(snowflake: string, epoch?: number) { - if (isNaN(parseInt(snowflake))) return false; - - if (parseInt(snowflake) < 4194304) { - //throw new Error( - // "That doesn't look like a snowflake. Snowflakes are much larger numbers." - //) - return false; - } - - const timestamp = convertSnowflakeToDate(snowflake, epoch); - - if (Number.isNaN(timestamp.getTime())) { - //throw new Error( - // "That doesn't look like a snowflake. Snowflakes have fewer digits." - //) - return false; - } - - return timestamp; -} diff --git a/bot/src/bot/modules/mod_logs.ts b/bot/src/bot/modules/mod_logs.ts index 5654c64..8529500 100644 --- a/bot/src/bot/modules/mod_logs.ts +++ b/bot/src/bot/modules/mod_logs.ts @@ -35,13 +35,6 @@ client.on('messageUpdate', async (message, oldMessage) => { `[Jump to message](/server/${server.id}/channel/${channel.id}/${message.id})`, fields: [], color: "#829dff", - overrides: { - discord: { - description: `Author: @${ - message.author?.username || message.authorId || "Unknown" - } | Channel: ${channel.name || channel.id}`, - }, - }, }; if (attachFullMessage) { @@ -78,11 +71,6 @@ client.on('messageDelete', async (message) => { + `[\\[Jump to context\\]](/server/${channel.serverId}/channel/${channel.id}/${message.id})`, fields: [], color: '#ff6b6b', - overrides: { - discord: { - description: `Author: @${author?.username || message.authorId} | Channel: ${channel?.name || message.channelId}` - }, - } } if (msg.length > 1000) { @@ -136,11 +124,6 @@ client.on('messageDeleteBulk', async (messages) => { fields: [], attachments: [{ name: 'messages.csv', content: Buffer.from(csv) }], color: '#ff392b', - overrides: { - discord: { - description: `${messages.length} messages deleted in #${channel.name}`, - } - } } await sendLogMessage(config.logs.messageUpdate, embed); @@ -168,7 +151,6 @@ async function logModAction(type: 'warn'|'kick'|'ban'|'votekick', server: Server + `**Warn ID**: \`${infractionID}\`\n` + (extraText ?? ''), color: embedColor, - overrides: {}, }); } } catch(e) {