From e8ff2973680dde8bde0c5cc86804110447c299cb Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Thu, 6 Aug 2020 00:46:47 +0300 Subject: [PATCH] Add !message/!messageinfo command --- assets/icons/LICENSE | 4 + assets/icons/memo.svg | 1 + assets/icons/message.png | Bin 0 -> 4808 bytes assets/icons/message.svg | 33 +++++ backend/src/plugins/Utility/UtilityPlugin.ts | 4 + .../Utility/commands/MessageInfoCmd.ts | 31 +++++ .../Utility/functions/getMessageInfoEmbed.ts | 129 ++++++++++++++++++ backend/src/plugins/Utility/types.ts | 1 + backend/src/utils.ts | 7 + 9 files changed, 210 insertions(+) create mode 100644 assets/icons/LICENSE create mode 100644 assets/icons/memo.svg create mode 100644 assets/icons/message.png create mode 100644 assets/icons/message.svg create mode 100644 backend/src/plugins/Utility/commands/MessageInfoCmd.ts create mode 100644 backend/src/plugins/Utility/functions/getMessageInfoEmbed.ts diff --git a/assets/icons/LICENSE b/assets/icons/LICENSE new file mode 100644 index 00000000..8518a347 --- /dev/null +++ b/assets/icons/LICENSE @@ -0,0 +1,4 @@ +# TWEMOJI +Copyright 2020 Twitter, Inc and other contributors +Code licensed under the MIT License: http://opensource.org/licenses/MIT +Graphics licensed under CC-BY 4.0: https://creativecommons.org/licenses/by/4.0/ diff --git a/assets/icons/memo.svg b/assets/icons/memo.svg new file mode 100644 index 00000000..1697ffbe --- /dev/null +++ b/assets/icons/memo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/message.png b/assets/icons/message.png new file mode 100644 index 0000000000000000000000000000000000000000..e10d5af872c6e222141f9511521bf7e6892307f3 GIT binary patch literal 4808 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/src/plugins/Utility/UtilityPlugin.ts b/backend/src/plugins/Utility/UtilityPlugin.ts index 3b909040..ad0fbd37 100644 --- a/backend/src/plugins/Utility/UtilityPlugin.ts +++ b/backend/src/plugins/Utility/UtilityPlugin.ts @@ -29,6 +29,7 @@ import { CleanCmd } from "./commands/CleanCmd"; import { Message } from "eris"; import { InviteInfoCmd } from "./commands/InviteInfoCmd"; import { ChannelInfoCmd } from "./commands/ChannelInfoCmd"; +import { MessageInfoCmd } from "./commands/MessageInfoCmd"; const defaultOptions: PluginOptions = { config: { @@ -40,6 +41,7 @@ const defaultOptions: PluginOptions = { can_server: false, can_invite: false, can_channel: false, + can_message: false, can_reload_guild: false, can_nickname: false, can_ping: false, @@ -65,6 +67,7 @@ const defaultOptions: PluginOptions = { can_server: true, can_invite: true, can_channel: true, + can_message: true, can_nickname: true, can_vcmove: true, can_help: true, @@ -116,6 +119,7 @@ export const UtilityPlugin = zeppelinPlugin()("utility", { CleanCmd, InviteInfoCmd, ChannelInfoCmd, + MessageInfoCmd, ], onLoad(pluginData) { diff --git a/backend/src/plugins/Utility/commands/MessageInfoCmd.ts b/backend/src/plugins/Utility/commands/MessageInfoCmd.ts new file mode 100644 index 00000000..7072c72c --- /dev/null +++ b/backend/src/plugins/Utility/commands/MessageInfoCmd.ts @@ -0,0 +1,31 @@ +import { utilityCmd } from "../types"; +import { commandTypeHelpers as ct } from "../../../commandTypes"; +import { sendErrorMessage } from "../../../pluginUtils"; +import { getMessageInfoEmbed } from "../functions/getMessageInfoEmbed"; +import { canReadChannel } from "../../../utils/canReadChannel"; + +export const MessageInfoCmd = utilityCmd({ + trigger: ["message", "messageinfo"], + description: "Show information about a message", + usage: "!message 534722016549404673-534722219696455701", + permission: "can_message", + + signature: { + message: ct.messageTarget(), + }, + + async run({ message, args, pluginData }) { + if (!canReadChannel(args.message.channel, message.author.id)) { + sendErrorMessage(pluginData, message.channel, "Unknown message"); + return; + } + + const embed = await getMessageInfoEmbed(pluginData, args.message.channel.id, args.message.messageId); + if (!embed) { + sendErrorMessage(pluginData, message.channel, "Unknown message"); + return; + } + + message.channel.createMessage({ embed }); + }, +}); diff --git a/backend/src/plugins/Utility/functions/getMessageInfoEmbed.ts b/backend/src/plugins/Utility/functions/getMessageInfoEmbed.ts new file mode 100644 index 00000000..083f6cfd --- /dev/null +++ b/backend/src/plugins/Utility/functions/getMessageInfoEmbed.ts @@ -0,0 +1,129 @@ +import { PluginData } from "knub"; +import { UtilityPluginType } from "../types"; +import { Constants, EmbedOptions } from "eris"; +import moment from "moment-timezone"; +import humanizeDuration from "humanize-duration"; +import { chunkMessageLines, preEmbedPadding, trimEmptyLines, trimLines } from "../../../utils"; +import { getDefaultPrefix } from "knub/dist/commands/commandUtils"; + +const MESSAGE_ICON = "https://cdn.discordapp.com/attachments/740650744830623756/740685652152025088/message.png"; + +export async function getMessageInfoEmbed( + pluginData: PluginData, + channelId: string, + messageId: string, +): Promise { + const message = await pluginData.client.getMessage(channelId, messageId).catch(() => null); + if (!message) { + return null; + } + + const embed: EmbedOptions = { + fields: [], + }; + + embed.author = { + name: `Message: ${message.id}`, + icon_url: MESSAGE_ICON, + }; + + const createdAt = moment(message.createdAt, "x"); + const messageAge = humanizeDuration(Date.now() - message.createdAt, { + largest: 2, + round: true, + }); + + const editedAt = message.editedTimestamp && moment(message.editedTimestamp, "x"); + const editAge = + message.editedTimestamp && + humanizeDuration(Date.now() - message.editedTimestamp, { + largest: 2, + round: true, + }); + + const type = + { + [Constants.MessageTypes.DEFAULT]: "Regular message", + [Constants.MessageTypes.CHANNEL_PINNED_MESSAGE]: "System message", + [Constants.MessageTypes.GUILD_MEMBER_JOIN]: "System message", + [Constants.MessageTypes.USER_PREMIUM_GUILD_SUBSCRIPTION]: "System message", + [Constants.MessageTypes.USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1]: "System message", + [Constants.MessageTypes.USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2]: "System message", + [Constants.MessageTypes.USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3]: "System message", + [Constants.MessageTypes.CHANNEL_FOLLOW_ADD]: "System message", + [Constants.MessageTypes.GUILD_DISCOVERY_DISQUALIFIED]: "System message", + [Constants.MessageTypes.GUILD_DISCOVERY_REQUALIFIED]: "System message", + }[message.type] || "Unknown"; + + embed.fields.push({ + name: preEmbedPadding + "Message information", + value: trimEmptyLines( + trimLines(` + ID: \`${message.id}\` + Channel: <#${message.channel.id}> + Channel ID: \`${message.channel.id}\` + Created: **${messageAge} ago** (\`${createdAt.format("MMM D, YYYY [at] H:mm [UTC]")}\`) + ${editedAt ? `Edited at: **${editAge} ago** (\`${editedAt.format("MMM D, YYYY [at] H:mm [UTC]")}\`)` : ""} + Type: **${type}** + Link: [**Go to message ➔**](https://discord.com/channels/${pluginData.guild.id}/${message.channel.id}/${ + message.id + }) + `), + ), + }); + + const authorCreatedAt = moment(message.author.createdAt); + const authorAccountAge = humanizeDuration(Date.now() - message.author.createdAt, { + largest: 2, + round: true, + }); + + const authorJoinedAt = message.member && moment(message.member.joinedAt); + const authorServerAge = + message.member && + humanizeDuration(Date.now() - message.member.joinedAt, { + largest: 2, + round: true, + }); + + embed.fields.push({ + name: preEmbedPadding + "Author information", + value: trimLines(` + Name: **${message.author.username}#${message.author.discriminator}** + ID: \`${message.author.id}\` + Created: **${authorAccountAge} ago** (\`${authorCreatedAt.format("MMM D, YYYY [at] H:mm [UTC]")}\`) + ${ + authorJoinedAt + ? `Joined: **${authorServerAge} ago** (\`${authorJoinedAt.format("MMM D, YYYY [at] H:mm [UTC]")}\`)` + : "" + } + Mention: <@!${message.author.id}> + `), + }); + + const textContent = message.content || ""; + const chunked = chunkMessageLines(textContent, 1014); + for (const [i, chunk] of chunked.entries()) { + embed.fields.push({ + name: i === 0 ? preEmbedPadding + "Text content" : "[...]", + value: chunk, + }); + } + + if (message.attachments.length) { + embed.fields.push({ + name: preEmbedPadding + "Attachments", + value: message.attachments[0].url, + }); + } + + if (message.embeds.length) { + const prefix = pluginData.guildConfig.prefix || getDefaultPrefix(pluginData.client); + embed.fields.push({ + name: preEmbedPadding + "Embeds", + value: `Message contains an embed, use \`${prefix}source\` to see the embed source`, + }); + } + + return embed; +} diff --git a/backend/src/plugins/Utility/types.ts b/backend/src/plugins/Utility/types.ts index 5efbadc3..e085e2da 100644 --- a/backend/src/plugins/Utility/types.ts +++ b/backend/src/plugins/Utility/types.ts @@ -15,6 +15,7 @@ export const ConfigSchema = t.type({ can_server: t.boolean, can_invite: t.boolean, can_channel: t.boolean, + can_message: t.boolean, can_reload_guild: t.boolean, can_nickname: t.boolean, can_ping: t.boolean, diff --git a/backend/src/utils.ts b/backend/src/utils.ts index c6c91546..8bda4bda 100644 --- a/backend/src/utils.ts +++ b/backend/src/utils.ts @@ -492,6 +492,13 @@ export function trimLines(str: string) { .trim(); } +export function trimEmptyLines(str: string) { + return str + .split("\n") + .filter(l => l.trim() !== "") + .join("\n"); +} + export function asSingleLine(str: string) { return trimLines(str).replace(/\n/g, " "); }