diff --git a/src/plugins/messageTags.ts b/src/plugins/messageTags.ts new file mode 100644 index 00000000..c5de93a8 --- /dev/null +++ b/src/plugins/messageTags.ts @@ -0,0 +1,247 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2022 Vendicated and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +import { DataStore } from "../api"; +import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, registerCommand, sendBotMessage, unregisterCommand } from "../api/Commands"; +import { Settings } from "../api/settings"; +import { Devs } from "../utils/constants"; +import definePlugin, { OptionType } from "../utils/types"; + +const settings = Settings.plugins.MessageTags; +const EMOTE = "<:luna:1035316192220553236>"; +const DATA_KEY = "MessageTags_TAGS"; +const MessageTagsMarker = Symbol("MessageTags"); +const author = { + id: "821472922140803112", + bot: false +}; + +interface Tag { + name: string; + message: string; + enabled: boolean; +} + +const getTags = () => DataStore.get(DATA_KEY).then(t => t ?? []); +const getTag = (name: string) => DataStore.get(DATA_KEY).then((t: Tag[]) => (t ?? []).find((tt: Tag) => tt.name === name) ?? null); +const addTag = async (tag: Tag) => { + const tags = await getTags(); + tags.push(tag); + DataStore.set(DATA_KEY, tags); + return tags; +}; +const removeTag = async (name: string) => { + let tags = await getTags(); + tags = await tags.filter((t: Tag) => t.name !== name); + DataStore.set(DATA_KEY, tags); + return tags; +}; + +function createTagCommand(tag: Tag) { + registerCommand({ + name: tag.name, + description: tag.name, + inputType: ApplicationCommandInputType.BUILT_IN_TEXT, + execute: async (_, ctx) => { + if (!await getTag(tag.name)) { + sendBotMessage(ctx.channel.id, { + author, + content: `${EMOTE} The tag **${tag.name}** does not exist anymore! Please reload ur Discord to fix :)` + }); + return { content: `/${tag.name}` }; + } + + if (settings.clyde) sendBotMessage(ctx.channel.id, { + author, + content: `${EMOTE} The tag **${tag.name}** has been sent!` + }); + return { content: tag.message.replaceAll("\\n", "\n") }; + }, + [MessageTagsMarker]: true, + }, "CustomTags"); +} + + +export default definePlugin({ + name: "MessageTags", + description: "Allows you to save messages and to use them with a simple command.", + authors: [Devs.Luna], + options: { + clyde: { + name: "Clyde message on send", + description: "If enabled, clyde will send you an ephemeral message when a tag was used.", + type: OptionType.BOOLEAN, + default: true + } + }, + dependencies: ["CommandsAPI"], + + async start() { + for (const tag of await getTags()) createTagCommand(tag); + }, + + commands: [ + { + name: "tags", + description: "Manage all the tags for yourself", + inputType: ApplicationCommandInputType.BUILT_IN, + options: [ + { + name: "create", + description: "Create a new tag", + type: ApplicationCommandOptionType.SUB_COMMAND, + options: [ + { + name: "tag-name", + description: "The name of the tag to trigger the response", + type: ApplicationCommandOptionType.STRING, + required: true + }, + { + name: "message", + description: "The message that you will send when using this tag", + type: ApplicationCommandOptionType.STRING, + required: true + } + ] + }, + { + name: "list", + description: "List all tags from yourself", + type: ApplicationCommandOptionType.SUB_COMMAND, + options: [] + }, + { + name: "delete", + description: "Remove a tag from your yourself", + type: ApplicationCommandOptionType.SUB_COMMAND, + options: [ + { + name: "tag-name", + description: "The name of the tag to trigger the response", + type: ApplicationCommandOptionType.STRING, + required: true + } + ] + }, + { + name: "preview", + description: "Preview a tag without sending it publicly", + type: ApplicationCommandOptionType.SUB_COMMAND, + options: [ + { + name: "tag-name", + description: "The name of the tag to trigger the response", + type: ApplicationCommandOptionType.STRING, + required: true + } + ] + } + ], + + async execute(args, ctx) { + + switch (args[0].name) { + case "create": { + const name: string = findOption(args[0].options, "tag-name", ""); + const message: string = findOption(args[0].options, "message", ""); + + if (await getTag(name)) + return sendBotMessage(ctx.channel.id, { + author, + content: `${EMOTE} A Tag with the name **${name}** already exists!` + }); + + const tag = { + name: name, + enabled: true, + message: message + }; + + createTagCommand(tag); + await addTag(tag); + + sendBotMessage(ctx.channel.id, { + author, + content: `${EMOTE} Successfully created the tag **${name}**!` + }); + break; // end 'create' + } + case "delete": { + const name: string = findOption(args[0].options, "tag-name", ""); + + if (!await getTag(name)) + return sendBotMessage(ctx.channel.id, { + author, + content: `${EMOTE} A Tag with the name **${name}** does not exist!` + }); + + unregisterCommand(name); + await removeTag(name); + + sendBotMessage(ctx.channel.id, { + author, + content: `${EMOTE} Successfully deleted the tag **${name}**!` + }); + break; // end 'delete' + } + case "list": { + sendBotMessage(ctx.channel.id, { + author, + embeds: [ + { + // @ts-ignore + title: "All Tags:", + // @ts-ignore + description: (await getTags()) + .map(tag => `\`${tag.name}\`: ${tag.message.slice(0, 72).replaceAll("\\n", " ")}${tag.message.length > 72 ? "..." : ""}`) + .join("\n") || `${EMOTE} Woops! There are no tags yet, use \`/tags create\` to create one!`, + // @ts-ignore + color: 0xd77f7f, + type: "rich", + } + ] + }); + break; // end 'list' + } + case "preview": { + const name: string = findOption(args[0].options, "tag-name", ""); + const tag = await getTag(name); + + if (!tag) + return sendBotMessage(ctx.channel.id, { + author, + content: `${EMOTE} A Tag with the name **${name}** does not exist!` + }); + + sendBotMessage(ctx.channel.id, { + author, + content: tag.message.replaceAll("\\n", "\n") + }); + break; // end 'preview' + } + } + + return sendBotMessage(ctx.channel.id, { + author, + content: "Invalid sub-command" + }); + } + } + ] +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index dc0a80d4..3766c74f 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -152,5 +152,9 @@ export const Devs = Object.freeze({ jewdev: { name: "jewdev", id: 222369866529636353n + }, + Luna: { + name: "Luny", + id: 821472922140803112n } });