2021-05-06 13:30:51 +00:00
|
|
|
import {NamedCommand, RestCommand, CHANNEL_TYPE} from "onion-lasers";
|
|
|
|
import {TextChannel, NewsChannel, Permissions} from "discord.js";
|
|
|
|
import {searchNearestEmote} from "../utility/modules/emote-utils";
|
|
|
|
import {resolveWebhook} from "../../modules/webhookStorageManager";
|
|
|
|
import {parseVarsCallback} from "../../lib";
|
2020-12-15 01:44:28 +00:00
|
|
|
|
2021-05-06 13:30:51 +00:00
|
|
|
// Description //
|
|
|
|
// This is the message-based counterpart to the react command, which replicates Nitro's ability to send emotes in messages.
|
|
|
|
// This takes advantage of webhooks' ability to change the username and avatar per request.
|
|
|
|
// Uses "@user says:" as a fallback in case no webhook is set for the channel.
|
|
|
|
|
|
|
|
// Limitations / Points of Interest //
|
|
|
|
// - Webhooks can fetch any emote in existence and use it as long as it hasn't been deleted.
|
|
|
|
// - The emote name from <:name:id> DOES matter if the user isn't part of that guild. That's the fallback essentially, otherwise, it doesn't matter.
|
|
|
|
// - The animated flag must be correct. <:name:id> on an animated emote will make it not animated, <a:name:id> will display an invalid image.
|
|
|
|
// - Rate limits for webhooks shouldn't be that big of an issue (5 requests every 2 seconds).
|
2021-04-05 04:35:12 +00:00
|
|
|
export default new NamedCommand({
|
2021-05-06 13:30:51 +00:00
|
|
|
aliases: ["s"],
|
|
|
|
channelType: CHANNEL_TYPE.GUILD,
|
|
|
|
description: "Repeats your message with emotes in /slashes/.",
|
2020-12-15 01:44:28 +00:00
|
|
|
usage: "<message>",
|
|
|
|
run: "Please provide a message for me to say!",
|
2021-04-10 17:07:55 +00:00
|
|
|
any: new RestCommand({
|
2020-12-15 01:44:28 +00:00
|
|
|
description: "Message to repeat.",
|
2021-05-06 13:30:51 +00:00
|
|
|
async run({send, channel, author, member, message, combined, guild}) {
|
|
|
|
const webhook = await resolveWebhook(channel as TextChannel | NewsChannel);
|
|
|
|
|
|
|
|
if (webhook) {
|
|
|
|
const resolvedMessage = resolveMessageWithEmotes(combined);
|
|
|
|
|
|
|
|
if (resolvedMessage)
|
2021-10-29 12:52:46 +00:00
|
|
|
webhook.send({
|
|
|
|
content: resolvedMessage,
|
2021-05-06 13:30:51 +00:00
|
|
|
username: member!.nickname ?? author.username,
|
|
|
|
// Webhooks cannot have animated avatars, so requesting the animated version is a moot point.
|
|
|
|
avatarURL:
|
|
|
|
author.avatarURL({
|
|
|
|
format: "png"
|
|
|
|
}) || author.defaultAvatarURL,
|
|
|
|
allowedMentions: {parse: []}, // avoids double pings
|
|
|
|
// "embeds" will not be included because it messes with the default ones that generate
|
2021-10-29 12:52:46 +00:00
|
|
|
files: Array.from(message.attachments.values())
|
2021-05-06 13:30:51 +00:00
|
|
|
});
|
|
|
|
else send("Cannot send an empty message.");
|
|
|
|
} else {
|
|
|
|
const resolvedMessage = resolveMessageWithEmotes(combined);
|
2021-10-29 12:52:46 +00:00
|
|
|
if (resolvedMessage)
|
|
|
|
send({content: `*${author} says:*\n${resolvedMessage}`, allowedMentions: {parse: []}});
|
2021-05-06 13:30:51 +00:00
|
|
|
else send("Cannot send an empty message.");
|
|
|
|
}
|
|
|
|
|
2021-05-17 22:12:14 +00:00
|
|
|
if (guild!.me?.permissions.has(Permissions.FLAGS.MANAGE_MESSAGES)) message.delete();
|
2020-12-15 01:44:28 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
});
|
2021-05-06 13:30:51 +00:00
|
|
|
|
|
|
|
const FETCH_EMOTE_PATTERN = /^(\d{17,})(?: ([^ ]+?))?(?: (a))?$/;
|
|
|
|
|
|
|
|
// Send extra emotes only for webhook messages (because the bot user can't fetch any emote in existence while webhooks can).
|
|
|
|
function resolveMessageWithEmotes(text: string, extraEmotes?: null): string {
|
|
|
|
return parseVarsCallback(
|
|
|
|
text,
|
|
|
|
(variable) => {
|
|
|
|
if (FETCH_EMOTE_PATTERN.test(variable)) {
|
|
|
|
// Although I *could* make this ping the CDN to see if gif exists to see whether it's animated or not, it'd take too much time to wait on it.
|
|
|
|
// Plus, with the way this function is setup, I wouldn't be able to incorporate a search without changing the function to async.
|
|
|
|
const [_, id, name, animated] = FETCH_EMOTE_PATTERN.exec(variable)!;
|
|
|
|
return `<${animated ?? ""}:${name ?? "_"}:${id}>`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return searchNearestEmote(variable);
|
|
|
|
},
|
|
|
|
"/"
|
|
|
|
);
|
|
|
|
}
|