diff --git a/bot/package.json b/bot/package.json index ecc45d7..ea4e398 100644 --- a/bot/package.json +++ b/bot/package.json @@ -23,6 +23,7 @@ "log75": "^2.2.0", "monk": "^7.3.4", "prom-client": "^14.0.1", + "revolt-api": "^0.5.3-rc.15", "ulid": "^2.3.0", "xlsx": "^0.17.3" }, diff --git a/bot/src/bot/commands/bridge.ts b/bot/src/bot/commands/bridge.ts index 7943d15..8195472 100644 --- a/bot/src/bot/commands/bridge.ts +++ b/bot/src/bot/commands/bridge.ts @@ -1,10 +1,12 @@ +import { Message } from "@janderedev/revolt.js"; import { ulid } from "ulid"; +import { SendableEmbed } from "revolt-api"; 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 { isBotManager, NO_MANAGER_MSG } from "../util"; +import { 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 @@ -14,10 +16,10 @@ export default { description: 'Bridge a channel with Discord', category: CommandCategory.Misc, run: async (message: MessageCommandContext, args: string[]) => { - if (!await isBotManager(message)) return message.reply(NO_MANAGER_MSG); - 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.channel_id }); if (count) return message.reply(`This channel is already bridged.`); @@ -50,12 +52,16 @@ export default { break; } case 'unlink': { + if (!await isBotManager(message)) return message.reply(NO_MANAGER_MSG); + const res = await dbs.BRIDGE_CONFIG.remove({ revolt: message.channel_id }); 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: message.channel?.server?.channel_ids || [] } }; if (args[1] == 'CONFIRM') { const res = await dbs.BRIDGE_CONFIG.remove(query); @@ -75,6 +81,8 @@ export default { break; } case 'list': { + if (!await isBotManager(message)) return message.reply(NO_MANAGER_MSG); + const links = await dbs.BRIDGE_CONFIG.find({ revolt: { $in: message.channel?.server?.channel_ids || [] } }); await message.reply({ @@ -89,6 +97,67 @@ export default { }); break; } + case 'info': { + try { + if (!message.reply_ids) { + return await message.reply('Please run this command again while replying to a message.'); + } + + if (message.reply_ids.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.reply_ids?.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 'help': { await message.reply({ content: '#', @@ -103,7 +172,9 @@ export default { + `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\`.` + + `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.` } ] }); diff --git a/bot/src/index.ts b/bot/src/index.ts index 0771f66..f165816 100644 --- a/bot/src/index.ts +++ b/bot/src/index.ts @@ -13,6 +13,7 @@ import { VoteEntry } from './bot/commands/votekick'; import ScannedUser from './struct/ScannedUser'; import BridgeRequest from './struct/BridgeRequest'; import BridgeConfig from './struct/BridgeConfig'; +import BridgedMessage from './struct/BridgedMessage'; logger.info('Initializing client'); @@ -36,6 +37,7 @@ const dbs = { VOTEKICKS: db.get('votekicks'), SCANNED_USERS: db.get('scanned_users'), BRIDGE_CONFIG: db.get('bridge_config'), + BRIDGED_MESSAGES: db.get('bridged_messages'), BRIDGE_REQUESTS: db.get('bridge_requests'), } diff --git a/bot/src/struct/BridgedMessage.ts b/bot/src/struct/BridgedMessage.ts new file mode 100644 index 0000000..698d542 --- /dev/null +++ b/bot/src/struct/BridgedMessage.ts @@ -0,0 +1,18 @@ +export default class { + origin: 'discord'|'revolt'; + + discord: { + messageId?: string; + } + + revolt: { + messageId?: string; + nonce?: string; + } + + // Required to sync message deletions + channels?: { + discord: string; + revolt: string; + } +} diff --git a/bot/yarn.lock b/bot/yarn.lock index 8988d54..b3d587c 100644 --- a/bot/yarn.lock +++ b/bot/yarn.lock @@ -600,7 +600,7 @@ require-at@^1.0.6: resolved "https://registry.yarnpkg.com/require-at/-/require-at-1.0.6.tgz#9eb7e3c5e00727f5a4744070a7f560d4de4f6e6a" integrity sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g== -revolt-api@0.5.3-rc.15: +revolt-api@0.5.3-rc.15, revolt-api@^0.5.3-rc.15: version "0.5.3-rc.15" resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.3-rc.15.tgz#abd08dd8109d0ca31be118461eabbeb6c3b7792e" integrity sha512-MYin3U+KoObNkILPf2cz+FPperynExkUu7CjzurMJCRvBncpnhb2czvWDvnhLDKBHlpo8W597xNqzQnaklV4ug==