mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-02-23 15:05:11 +00:00
Added custom params support
Added listing existing tags in /tag delete and /tag preview Refactored file extension to tsx in order to use the modal
This commit is contained in:
parent
2cb9dc2314
commit
e2d423941e
2 changed files with 369 additions and 247 deletions
|
@ -1,247 +0,0 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, registerCommand, sendBotMessage, unregisterCommand } from "@api/Commands";
|
||||
import * as DataStore from "@api/DataStore";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
const EMOTE = "<:luna:1035316192220553236>";
|
||||
const DATA_KEY = "MessageTags_TAGS";
|
||||
const MessageTagsMarker = Symbol("MessageTags");
|
||||
|
||||
interface Tag {
|
||||
name: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
function getTags() {
|
||||
return settings.store.tagsList;
|
||||
}
|
||||
|
||||
function getTag(name: string) {
|
||||
return settings.store.tagsList[name] ?? null;
|
||||
}
|
||||
|
||||
function addTag(tag: Tag) {
|
||||
settings.store.tagsList[tag.name] = tag;
|
||||
}
|
||||
|
||||
function removeTag(name: string) {
|
||||
delete settings.store.tagsList[name];
|
||||
}
|
||||
|
||||
function createTagCommand(tag: Tag) {
|
||||
registerCommand({
|
||||
name: tag.name,
|
||||
description: tag.name,
|
||||
inputType: ApplicationCommandInputType.BUILT_IN_TEXT,
|
||||
execute: async (_, ctx) => {
|
||||
if (!getTag(tag.name)) {
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} The tag **${tag.name}** does not exist anymore! Please reload ur Discord to fix :)`
|
||||
});
|
||||
return { content: `/${tag.name}` };
|
||||
}
|
||||
|
||||
if (settings.store.clyde) sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} The tag **${tag.name}** has been sent!`
|
||||
});
|
||||
return { content: tag.message.replaceAll("\\n", "\n") };
|
||||
},
|
||||
[MessageTagsMarker]: true,
|
||||
}, "CustomTags");
|
||||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
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
|
||||
},
|
||||
tagsList: {
|
||||
type: OptionType.CUSTOM,
|
||||
default: {} as Record<string, Tag>,
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "MessageTags",
|
||||
description: "Allows you to save messages and to use them with a simple command.",
|
||||
authors: [Devs.Luna],
|
||||
settings,
|
||||
|
||||
async start() {
|
||||
// TODO(OptionType.CUSTOM Related): Remove DataStore tags migration once enough time has passed
|
||||
const oldTags = await DataStore.get<Tag[]>(DATA_KEY);
|
||||
if (oldTags != null) {
|
||||
// @ts-ignore
|
||||
settings.store.tagsList = Object.fromEntries(oldTags.map(oldTag => (delete oldTag.enabled, [oldTag.name, oldTag])));
|
||||
await DataStore.del(DATA_KEY);
|
||||
}
|
||||
|
||||
const tags = getTags();
|
||||
for (const tagName in tags) {
|
||||
createTagCommand(tags[tagName]);
|
||||
}
|
||||
},
|
||||
|
||||
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 (getTag(name))
|
||||
return sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} A Tag with the name **${name}** already exists!`
|
||||
});
|
||||
|
||||
const tag = {
|
||||
name: name,
|
||||
message: message
|
||||
};
|
||||
|
||||
createTagCommand(tag);
|
||||
addTag(tag);
|
||||
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} Successfully created the tag **${name}**!`
|
||||
});
|
||||
break; // end 'create'
|
||||
}
|
||||
case "delete": {
|
||||
const name: string = findOption(args[0].options, "tag-name", "");
|
||||
|
||||
if (!getTag(name))
|
||||
return sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} A Tag with the name **${name}** does not exist!`
|
||||
});
|
||||
|
||||
unregisterCommand(name);
|
||||
removeTag(name);
|
||||
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} Successfully deleted the tag **${name}**!`
|
||||
});
|
||||
break; // end 'delete'
|
||||
}
|
||||
case "list": {
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
embeds: [
|
||||
{
|
||||
title: "All Tags:",
|
||||
description: Object.values(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 = getTag(name);
|
||||
|
||||
if (!tag)
|
||||
return sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} A Tag with the name **${name}** does not exist!`
|
||||
});
|
||||
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: tag.message.replaceAll("\\n", "\n")
|
||||
});
|
||||
break; // end 'preview'
|
||||
}
|
||||
|
||||
default: {
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: "Invalid sub-command"
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
369
src/plugins/messageTags/index.tsx
Normal file
369
src/plugins/messageTags/index.tsx
Normal file
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ApplicationCommandInputType, ApplicationCommandOptionType, Argument, CommandContext, findOption, Option, registerCommand, sendBotMessage, unregisterCommand } from "@api/Commands";
|
||||
import * as DataStore from "@api/DataStore";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { openModal } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
import { ParamsConfigModal, ParamsPreviewModal } from "./components/ParamsConfigModal";
|
||||
|
||||
const EMOTE = "<:luna:1035316192220553236>";
|
||||
const DATA_KEY = "MessageTags_TAGS";
|
||||
const MessageTagsMarker = Symbol("MessageTags");
|
||||
|
||||
export interface Param {
|
||||
name: string;
|
||||
default?: string;
|
||||
}
|
||||
|
||||
interface Tag {
|
||||
name: string;
|
||||
message: string;
|
||||
params?: Param[];
|
||||
}
|
||||
|
||||
function getTags() {
|
||||
return settings.store.tagsList;
|
||||
}
|
||||
|
||||
function getTag(name: string) {
|
||||
return settings.store.tagsList[name] ?? null;
|
||||
}
|
||||
|
||||
function addTag(tag: Tag) {
|
||||
settings.store.tagsList[tag.name] = tag;
|
||||
}
|
||||
|
||||
function removeTag(name: string) {
|
||||
delete settings.store.tagsList[name];
|
||||
}
|
||||
|
||||
function generateOptionsFromParamsTag(params: Param[] | null) {
|
||||
const options: Option[] = [];
|
||||
params?.forEach(param => {
|
||||
const notDefault = param.default === undefined;
|
||||
options.push({
|
||||
name: param.name,
|
||||
description: param.name + (!notDefault ? ` (Default: "${param.default}")` : ""),
|
||||
type: ApplicationCommandOptionType.STRING,
|
||||
required: notDefault,
|
||||
});
|
||||
});
|
||||
return options;
|
||||
}
|
||||
|
||||
function createTagCommand(tag: Tag) {
|
||||
const options = generateOptionsFromParamsTag(tag.params || null);
|
||||
registerCommand({
|
||||
name: tag.name,
|
||||
description: tag.name,
|
||||
inputType: ApplicationCommandInputType.BUILT_IN_TEXT,
|
||||
options,
|
||||
execute: async (args, ctx) => {
|
||||
if (!getTag(tag.name)) {
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} The tag **${tag.name}** does not exist anymore! Please reload ur Discord to fix :)`
|
||||
});
|
||||
return { content: `/${tag.name}` };
|
||||
}
|
||||
|
||||
if (settings.store.clyde) sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} The tag **${tag.name}** has been sent!`
|
||||
});
|
||||
let finalMessage = tag.message.replaceAll("\\n", "\n");
|
||||
args.forEach(arg => {
|
||||
finalMessage = finalMessage.replaceAll(`$${arg.name}$`, arg.value);
|
||||
});
|
||||
tag.params?.forEach(param => {
|
||||
if (!param.default) return;
|
||||
finalMessage = finalMessage.replaceAll(`$${param.name}$`, param.default);
|
||||
});
|
||||
return { content: finalMessage };
|
||||
},
|
||||
[MessageTagsMarker]: true,
|
||||
}, "CustomTags");
|
||||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
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
|
||||
},
|
||||
tagsList: {
|
||||
type: OptionType.CUSTOM,
|
||||
default: {} as Record<string, Tag>,
|
||||
}
|
||||
});
|
||||
|
||||
const execute = async (args: Argument[], ctx: CommandContext) => {
|
||||
switch (args[0].name) {
|
||||
case "create": {
|
||||
const name: string = findOption(args[0].options, "tag-name", "");
|
||||
const message: string = findOption(args[0].options, "message", "");
|
||||
|
||||
if (getTag(name))
|
||||
return sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} A Tag with the name **${name}** already exists!`
|
||||
});
|
||||
|
||||
const matches = new Set<string>();
|
||||
message.matchAll(/\$(\S+?)\$/g).forEach(match => { matches.add(match[1]); });
|
||||
const paramNames = Array.from(matches);
|
||||
|
||||
const tag: Tag = {
|
||||
name: name,
|
||||
message: message,
|
||||
params: paramNames.length ? [] : undefined
|
||||
};
|
||||
|
||||
paramNames.forEach(p => { tag.params?.push({ name: p }); });
|
||||
|
||||
const createTag = () => {
|
||||
createTagCommand(tag);
|
||||
addTag(tag);
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} Successfully created the tag **${name}**!`
|
||||
});
|
||||
updateCommandsList();
|
||||
};
|
||||
if (!tag.params?.lastIndexOf) {
|
||||
createTag();
|
||||
break;
|
||||
}
|
||||
openModal(modalProps => (<ParamsConfigModal
|
||||
modalProps={modalProps}
|
||||
params={paramNames}
|
||||
onSave={(values: { [key: string]: string; }) => {
|
||||
tag.params = tag.params?.map(param => ({
|
||||
...param,
|
||||
default: values[param.name] || undefined,
|
||||
}));
|
||||
createTag();
|
||||
}}
|
||||
/>));
|
||||
break; // end 'create'
|
||||
}
|
||||
case "delete": {
|
||||
const name: string = findOption(args[0].options, "tag-name", "");
|
||||
|
||||
if (!getTag(name))
|
||||
return sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} A Tag with the name **${name}** does not exist!`
|
||||
});
|
||||
|
||||
unregisterCommand(name);
|
||||
removeTag(name);
|
||||
updateCommandsList();
|
||||
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} Successfully deleted the tag **${name}**!`
|
||||
});
|
||||
break; // end 'delete'
|
||||
}
|
||||
case "list": {
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
embeds: [
|
||||
{
|
||||
title: "All Tags:",
|
||||
description: Object.values(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 = getTag(name);
|
||||
|
||||
if (!tag)
|
||||
return sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} A Tag with the name **${name}** does not exist!`
|
||||
});
|
||||
let finalMessage = tag.message.replaceAll("\\n", "\n");
|
||||
const preview = () => {
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: finalMessage
|
||||
});
|
||||
};
|
||||
if (!tag.params?.length) {
|
||||
preview();
|
||||
break;
|
||||
}
|
||||
openModal(modalProps => (<ParamsPreviewModal
|
||||
modalProps={modalProps}
|
||||
params={tag.params || []}
|
||||
onSave={(values: { [key: string]: string; }) => {
|
||||
tag.params?.forEach(p => {
|
||||
finalMessage = finalMessage.replaceAll(`$${p.name}$`, values[p.name]);
|
||||
});
|
||||
preview();
|
||||
}}
|
||||
/>));
|
||||
break; // end 'preview'
|
||||
}
|
||||
|
||||
default: {
|
||||
sendBotMessage(ctx.channel.id, {
|
||||
content: "Invalid sub-command"
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const updateCommandsList = () => {
|
||||
unregisterCommand("tags delete");
|
||||
unregisterCommand("tags preview");
|
||||
const newChoicesList = Object.keys(getTags()).map(e => ({
|
||||
label: e,
|
||||
value: e,
|
||||
name: e
|
||||
}));
|
||||
registerCommand({
|
||||
name: "tags",
|
||||
description: "Manage all the tags for yourself",
|
||||
inputType: ApplicationCommandInputType.BUILT_IN,
|
||||
options: [
|
||||
{
|
||||
name: "delete",
|
||||
description: "Remove a tag from yourself",
|
||||
type: ApplicationCommandOptionType.SUB_COMMAND,
|
||||
options: [
|
||||
{
|
||||
name: "tag-name",
|
||||
description: "The name of the tag to delete",
|
||||
type: ApplicationCommandOptionType.STRING,
|
||||
required: true,
|
||||
choices: newChoicesList
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
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 preview the response",
|
||||
type: ApplicationCommandOptionType.STRING,
|
||||
required: true,
|
||||
choices: newChoicesList
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
execute
|
||||
}, "MessageTags");
|
||||
};
|
||||
|
||||
export default definePlugin({
|
||||
name: "MessageTags",
|
||||
description: "Allows you to save messages and to use them with a simple command.",
|
||||
authors: [Devs.Luna, Devs.Benjas333],
|
||||
settings,
|
||||
|
||||
async start() {
|
||||
// TODO(OptionType.CUSTOM Related): Remove DataStore tags migration once enough time has passed
|
||||
const oldTags = await DataStore.get<Tag[]>(DATA_KEY);
|
||||
if (oldTags != null) {
|
||||
// @ts-ignore
|
||||
settings.store.tagsList = Object.fromEntries(oldTags.map(oldTag => (delete oldTag.enabled, [oldTag.name, oldTag])));
|
||||
await DataStore.del(DATA_KEY);
|
||||
}
|
||||
|
||||
const tags = getTags();
|
||||
for (const tagName in tags) {
|
||||
createTagCommand(tags[tagName]);
|
||||
}
|
||||
updateCommandsList();
|
||||
},
|
||||
|
||||
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. For custom parameters use double $ (example: $name$)",
|
||||
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 delete",
|
||||
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 preview the response",
|
||||
type: ApplicationCommandOptionType.STRING,
|
||||
required: true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
execute,
|
||||
}
|
||||
]
|
||||
});
|
Loading…
Add table
Reference in a new issue