From 106a959b4dd7c56e868c995fcac44169b5fdbd95 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Thu, 6 Aug 2020 01:10:40 +0300 Subject: [PATCH] Add new multi-use !info command --- backend/src/commandTypes.ts | 52 +--------- backend/src/plugins/Utility/UtilityPlugin.ts | 14 +-- .../src/plugins/Utility/commands/InfoCmd.ts | 99 +++++++++++++++++++ .../Utility/functions/getGuildPreview.ts | 9 ++ .../Utility/functions/getServerInfoEmbed.ts | 7 +- backend/src/plugins/Utility/types.ts | 6 +- backend/src/utils/resolveMessageTarget.ts | 58 +++++++++++ 7 files changed, 184 insertions(+), 61 deletions(-) create mode 100644 backend/src/plugins/Utility/commands/InfoCmd.ts create mode 100644 backend/src/plugins/Utility/functions/getGuildPreview.ts create mode 100644 backend/src/utils/resolveMessageTarget.ts diff --git a/backend/src/commandTypes.ts b/backend/src/commandTypes.ts index 518dd157..9298a292 100644 --- a/backend/src/commandTypes.ts +++ b/backend/src/commandTypes.ts @@ -11,14 +11,7 @@ import { GuildChannel, Member, TextChannel, User } from "eris"; import { baseTypeConverters, baseTypeHelpers, CommandContext, TypeConversionError } from "knub"; import { createTypeHelper } from "knub-command-manager"; import { getChannelIdFromMessageId } from "./data/getChannelIdFromMessageId"; - -export interface MessageTarget { - channel: TextChannel; - messageId: string; -} - -const channelAndMessageIdRegex = /^(\d+)[\-\/](\d+)$/; -const messageLinkRegex = /^https:\/\/(?:\w+\.)?discord(?:app)?\.com\/channels\/\d+\/(\d+)\/(\d+)$/i; +import { MessageTarget, resolveMessageTarget } from "./utils/resolveMessageTarget"; export const commandTypes = { ...baseTypeConverters, @@ -63,47 +56,12 @@ export const commandTypes = { async messageTarget(value: string, context: CommandContext) { value = String(value).trim(); - const result = await (async () => { - if (isSnowflake(value)) { - const channelId = await getChannelIdFromMessageId(value); - if (!channelId) { - throw new TypeConversionError(`Could not find channel for message ID \`${disableInlineCode(value)}\``); - } - - return { - channelId, - messageId: value, - }; - } - - const channelAndMessageIdMatch = value.match(channelAndMessageIdRegex); - if (channelAndMessageIdMatch) { - return { - channelId: channelAndMessageIdMatch[1], - messageId: channelAndMessageIdMatch[2], - }; - } - - const messageLinkMatch = value.match(messageLinkRegex); - if (messageLinkMatch) { - return { - channelId: messageLinkMatch[1], - messageId: messageLinkMatch[2], - }; - } - - throw new TypeConversionError(`Invalid message ID \`${disableInlineCode(value)}\``); - })(); - - const channel = context.pluginData.guild.channels.get(result.channelId); - if (!channel || !(channel instanceof TextChannel)) { - throw new TypeConversionError(`Invalid channel ID \`${disableInlineCode(result.channelId)}\``); + const result = await resolveMessageTarget(context.pluginData, value); + if (!result) { + throw new TypeConversionError(`Unknown message \`${disableInlineCode(value)}\``); } - return { - channel, - messageId: result.messageId, - }; + return result; }, }; diff --git a/backend/src/plugins/Utility/UtilityPlugin.ts b/backend/src/plugins/Utility/UtilityPlugin.ts index 953ca1a0..5e31bc34 100644 --- a/backend/src/plugins/Utility/UtilityPlugin.ts +++ b/backend/src/plugins/Utility/UtilityPlugin.ts @@ -30,6 +30,7 @@ import { Message } from "eris"; import { InviteInfoCmd } from "./commands/InviteInfoCmd"; import { ChannelInfoCmd } from "./commands/ChannelInfoCmd"; import { MessageInfoCmd } from "./commands/MessageInfoCmd"; +import { InfoCmd } from "./commands/InfoCmd"; const defaultOptions: PluginOptions = { config: { @@ -39,9 +40,9 @@ const defaultOptions: PluginOptions = { can_clean: false, can_info: false, can_server: false, - can_invite: false, - can_channel: false, - can_message: false, + can_inviteinfo: false, + can_channelinfo: false, + can_messageinfo: false, can_userinfo: false, can_reload_guild: false, can_nickname: false, @@ -66,9 +67,9 @@ const defaultOptions: PluginOptions = { can_clean: true, can_info: true, can_server: true, - can_invite: true, - can_channel: true, - can_message: true, + can_inviteinfo: true, + can_channelinfo: true, + can_messageinfo: true, can_userinfo: true, can_nickname: true, can_vcmove: true, @@ -122,6 +123,7 @@ export const UtilityPlugin = zeppelinPlugin()("utility", { InviteInfoCmd, ChannelInfoCmd, MessageInfoCmd, + InfoCmd, ], onLoad(pluginData) { diff --git a/backend/src/plugins/Utility/commands/InfoCmd.ts b/backend/src/plugins/Utility/commands/InfoCmd.ts new file mode 100644 index 00000000..313301ff --- /dev/null +++ b/backend/src/plugins/Utility/commands/InfoCmd.ts @@ -0,0 +1,99 @@ +import { utilityCmd } from "../types"; +import { commandTypeHelpers as ct } from "../../../commandTypes"; +import { sendErrorMessage } from "../../../pluginUtils"; +import { getInviteInfoEmbed } from "../functions/getInviteInfoEmbed"; +import { parseInviteCodeInput, resolveInvite, resolveUser } from "../../../utils"; +import { getUserInfoEmbed } from "../functions/getUserInfoEmbed"; +import { resolveMessageTarget } from "../../../utils/resolveMessageTarget"; +import { canReadChannel } from "../../../utils/canReadChannel"; +import { getMessageInfoEmbed } from "../functions/getMessageInfoEmbed"; +import { getChannelInfoEmbed } from "../functions/getChannelInfoEmbed"; +import { getServerInfoEmbed } from "../functions/getServerInfoEmbed"; +import { getChannelId } from "knub/dist/utils"; +import { getGuildPreview } from "../functions/getGuildPreview"; + +export const InfoCmd = utilityCmd({ + trigger: "info", + description: "Show information about the specified thing", + usage: "!info", + permission: "can_info", + + signature: { + value: ct.string({ required: false }), + + compact: ct.switchOption({ shortcut: "c" }), + }, + + async run({ message, args, pluginData }) { + const value = args.value || message.author.id; + + // 1. Channel + const channelId = getChannelId(value); + const channel = channelId && pluginData.guild.channels.get(channelId); + if (channel) { + const embed = await getChannelInfoEmbed(pluginData, channelId); + if (embed) { + message.channel.createMessage({ embed }); + return; + } + } + + // 2. Server + const guild = pluginData.client.guilds.get(value); + if (guild) { + const embed = await getServerInfoEmbed(pluginData, value); + if (embed) { + message.channel.createMessage({ embed }); + return; + } + } + + // 3. User + const user = await resolveUser(pluginData.client, value); + if (user) { + const embed = await getUserInfoEmbed(pluginData, user.id, Boolean(args.compact)); + if (embed) { + message.channel.createMessage({ embed }); + return; + } + } + + // 4. Message + const messageTarget = await resolveMessageTarget(pluginData, value); + if (messageTarget) { + if (canReadChannel(messageTarget.channel, message.author.id)) { + const embed = await getMessageInfoEmbed(pluginData, messageTarget.channel.id, messageTarget.messageId); + if (embed) { + message.channel.createMessage({ embed }); + return; + } + } + } + + // 5. Invite + const inviteCode = await parseInviteCodeInput(value); + if (inviteCode) { + const invite = await resolveInvite(pluginData.client, inviteCode, true); + if (invite) { + const embed = await getInviteInfoEmbed(pluginData, inviteCode); + if (embed) { + message.channel.createMessage({ embed }); + return; + } + } + } + + // 6. Server again (fallback for discovery servers) + const serverPreview = getGuildPreview(pluginData.client, value).catch(() => null); + if (serverPreview) { + const embed = await getServerInfoEmbed(pluginData, value); + if (embed) { + message.channel.createMessage({ embed }); + return; + } + } + + // 7. No can do + sendErrorMessage(pluginData, message.channel, "Could not find anything with that value"); + }, +}); diff --git a/backend/src/plugins/Utility/functions/getGuildPreview.ts b/backend/src/plugins/Utility/functions/getGuildPreview.ts new file mode 100644 index 00000000..a72be048 --- /dev/null +++ b/backend/src/plugins/Utility/functions/getGuildPreview.ts @@ -0,0 +1,9 @@ +import { Client, GuildPreview } from "eris"; +import { memoize, MINUTES } from "../../../utils"; + +/** + * Memoized getGuildPreview + */ +export function getGuildPreview(client: Client, guildId: string): Promise { + return memoize(() => client.getGuildPreview(guildId).catch(() => null), `getGuildPreview_${guildId}`, 10 * MINUTES); +} diff --git a/backend/src/plugins/Utility/functions/getServerInfoEmbed.ts b/backend/src/plugins/Utility/functions/getServerInfoEmbed.ts index 4bdca88a..bc7f17f8 100644 --- a/backend/src/plugins/Utility/functions/getServerInfoEmbed.ts +++ b/backend/src/plugins/Utility/functions/getServerInfoEmbed.ts @@ -4,6 +4,7 @@ import { embedPadding, formatNumber, memoize, MINUTES, preEmbedPadding, resolveU import { CategoryChannel, EmbedOptions, Guild, RESTChannelInvite, TextChannel, VoiceChannel } from "eris"; import moment from "moment-timezone"; import humanizeDuration from "humanize-duration"; +import { getGuildPreview } from "./getGuildPreview"; export async function getServerInfoEmbed( pluginData: PluginData, @@ -14,11 +15,7 @@ export async function getServerInfoEmbed( thisServer ? memoize(() => pluginData.client.getRESTGuild(serverId), `getRESTGuild_${serverId}`, 10 * MINUTES) : null, - memoize( - () => pluginData.client.getGuildPreview(serverId).catch(() => null), - `getGuildPreview_${serverId}`, - 10 * MINUTES, - ), + getGuildPreview(pluginData.client, serverId), ]); if (!restGuild && !guildPreview) { diff --git a/backend/src/plugins/Utility/types.ts b/backend/src/plugins/Utility/types.ts index 4e209d62..b1ccbd7e 100644 --- a/backend/src/plugins/Utility/types.ts +++ b/backend/src/plugins/Utility/types.ts @@ -13,9 +13,9 @@ export const ConfigSchema = t.type({ can_clean: t.boolean, can_info: t.boolean, can_server: t.boolean, - can_invite: t.boolean, - can_channel: t.boolean, - can_message: t.boolean, + can_inviteinfo: t.boolean, + can_channelinfo: t.boolean, + can_messageinfo: t.boolean, can_userinfo: t.boolean, can_reload_guild: t.boolean, can_nickname: t.boolean, diff --git a/backend/src/utils/resolveMessageTarget.ts b/backend/src/utils/resolveMessageTarget.ts new file mode 100644 index 00000000..9ce2a3ba --- /dev/null +++ b/backend/src/utils/resolveMessageTarget.ts @@ -0,0 +1,58 @@ +import { disableInlineCode, isSnowflake } from "../utils"; +import { getChannelIdFromMessageId } from "../data/getChannelIdFromMessageId"; +import { PluginData, TypeConversionError } from "knub"; +import { TextChannel } from "eris"; + +const channelAndMessageIdRegex = /^(\d+)[\-\/](\d+)$/; +const messageLinkRegex = /^https:\/\/(?:\w+\.)?discord(?:app)?\.com\/channels\/\d+\/(\d+)\/(\d+)$/i; + +export interface MessageTarget { + channel: TextChannel; + messageId: string; +} + +export async function resolveMessageTarget(pluginData: PluginData, value: string) { + const result = await (async () => { + if (isSnowflake(value)) { + const channelId = await getChannelIdFromMessageId(value); + if (!channelId) { + return null; + } + + return { + channelId, + messageId: value, + }; + } + + const channelAndMessageIdMatch = value.match(channelAndMessageIdRegex); + if (channelAndMessageIdMatch) { + return { + channelId: channelAndMessageIdMatch[1], + messageId: channelAndMessageIdMatch[2], + }; + } + + const messageLinkMatch = value.match(messageLinkRegex); + if (messageLinkMatch) { + return { + channelId: messageLinkMatch[1], + messageId: messageLinkMatch[2], + }; + } + })(); + + if (!result) { + return null; + } + + const channel = pluginData.guild.channels.get(result.channelId); + if (!channel || !(channel instanceof TextChannel)) { + return null; + } + + return { + channel, + messageId: result.messageId, + }; +}