Migrate Tags to new Plugin structure
This commit is contained in:
parent
cfe3c65abc
commit
7ca14d090b
12 changed files with 521 additions and 0 deletions
76
backend/src/plugins/Tags/TagsPlugin.ts
Normal file
76
backend/src/plugins/Tags/TagsPlugin.ts
Normal file
|
@ -0,0 +1,76 @@
|
|||
import { zeppelinPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { ConfigSchema, TagsPluginType } from "./types";
|
||||
import { PluginOptions } from "knub";
|
||||
import { GuildArchives } from "src/data/GuildArchives";
|
||||
import { GuildTags } from "src/data/GuildTags";
|
||||
import { GuildSavedMessages } from "src/data/GuildSavedMessages";
|
||||
import { GuildLogs } from "src/data/GuildLogs";
|
||||
import { onMessageCreate } from "./util/onMessageCreate";
|
||||
import { onMessageDelete } from "./util/onMessageDelete";
|
||||
import { TagCreateCmd } from "./commands/TagCreateCmd";
|
||||
import { TagDeleteCmd } from "./commands/TagDeleteCmd";
|
||||
import { TagEvalCmd } from "./commands/TagEvalCmd";
|
||||
import { TagListCmd } from "./commands/TagListCmd";
|
||||
import { TagSourceCmd } from "./commands/TagSourceCmd";
|
||||
|
||||
const defaultOptions: PluginOptions<TagsPluginType> = {
|
||||
config: {
|
||||
prefix: "!!",
|
||||
delete_with_command: true,
|
||||
|
||||
user_tag_cooldown: null,
|
||||
global_tag_cooldown: null,
|
||||
user_cooldown: null,
|
||||
global_cooldown: null,
|
||||
|
||||
categories: {},
|
||||
|
||||
can_create: false,
|
||||
can_use: false,
|
||||
can_list: false,
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
level: ">=50",
|
||||
config: {
|
||||
can_use: true,
|
||||
can_create: true,
|
||||
can_list: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const TagsPlugin = zeppelinPlugin<TagsPluginType>()("tags", {
|
||||
configSchema: ConfigSchema,
|
||||
defaultOptions,
|
||||
|
||||
// prettier-ignore
|
||||
commands: [
|
||||
TagEvalCmd,
|
||||
TagCreateCmd,
|
||||
TagDeleteCmd,
|
||||
TagListCmd,
|
||||
TagSourceCmd,
|
||||
],
|
||||
|
||||
onLoad(pluginData) {
|
||||
const { state, guild } = pluginData;
|
||||
|
||||
state.archives = GuildArchives.getGuildInstance(guild.id);
|
||||
state.tags = GuildTags.getGuildInstance(guild.id);
|
||||
state.savedMessages = GuildSavedMessages.getGuildInstance(guild.id);
|
||||
state.logs = new GuildLogs(guild.id);
|
||||
|
||||
state.onMessageCreateFn = msg => onMessageCreate(pluginData, msg);
|
||||
state.savedMessages.events.on("create", state.onMessageCreateFn);
|
||||
|
||||
state.onMessageDeleteFn = msg => onMessageDelete(pluginData, msg);
|
||||
state.savedMessages.events.on("delete", state.onMessageDeleteFn);
|
||||
},
|
||||
|
||||
onUnload(pluginData) {
|
||||
pluginData.state.savedMessages.events.off("create", pluginData.state.onMessageCreateFn);
|
||||
pluginData.state.savedMessages.events.off("delete", pluginData.state.onMessageDeleteFn);
|
||||
},
|
||||
});
|
32
backend/src/plugins/Tags/commands/TagCreateCmd.ts
Normal file
32
backend/src/plugins/Tags/commands/TagCreateCmd.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { tagsCmd } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { parseTemplate, TemplateParseError } from "src/templateFormatter";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "src/pluginUtils";
|
||||
|
||||
export const TagCreateCmd = tagsCmd({
|
||||
trigger: "tag",
|
||||
permission: "can_create",
|
||||
|
||||
signature: {
|
||||
tag: ct.string(),
|
||||
body: ct.string({ catchAll: true }),
|
||||
},
|
||||
|
||||
async run({ message: msg, args, pluginData }) {
|
||||
try {
|
||||
parseTemplate(args.body);
|
||||
} catch (e) {
|
||||
if (e instanceof TemplateParseError) {
|
||||
sendErrorMessage(pluginData, msg.channel, `Invalid tag syntax: ${e.message}`);
|
||||
return;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
await pluginData.state.tags.createOrUpdate(args.tag, args.body, msg.author.id);
|
||||
|
||||
const prefix = pluginData.config.get().prefix;
|
||||
sendSuccessMessage(pluginData, msg.channel, `Tag set! Use it with: \`${prefix}${args.tag}\``);
|
||||
},
|
||||
});
|
23
backend/src/plugins/Tags/commands/TagDeleteCmd.ts
Normal file
23
backend/src/plugins/Tags/commands/TagDeleteCmd.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { tagsCmd } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage, sendSuccessMessage } from "src/pluginUtils";
|
||||
|
||||
export const TagDeleteCmd = tagsCmd({
|
||||
trigger: "tag delete",
|
||||
permission: "can_create",
|
||||
|
||||
signature: {
|
||||
tag: ct.string(),
|
||||
},
|
||||
|
||||
async run({ message: msg, args, pluginData }) {
|
||||
const tag = await pluginData.state.tags.find(args.tag);
|
||||
if (!tag) {
|
||||
sendErrorMessage(pluginData, msg.channel, "No tag with that name");
|
||||
return;
|
||||
}
|
||||
|
||||
await pluginData.state.tags.delete(args.tag);
|
||||
sendSuccessMessage(pluginData, msg.channel, "Tag deleted!");
|
||||
},
|
||||
});
|
17
backend/src/plugins/Tags/commands/TagEvalCmd.ts
Normal file
17
backend/src/plugins/Tags/commands/TagEvalCmd.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { tagsCmd } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { renderTag } from "../util/renderTag";
|
||||
|
||||
export const TagEvalCmd = tagsCmd({
|
||||
trigger: "tag eval",
|
||||
permission: "can_create",
|
||||
|
||||
signature: {
|
||||
body: ct.string({ catchAll: true }),
|
||||
},
|
||||
|
||||
async run({ message: msg, args, pluginData }) {
|
||||
const rendered = await renderTag(pluginData, args.body);
|
||||
msg.channel.createMessage(rendered);
|
||||
},
|
||||
});
|
18
backend/src/plugins/Tags/commands/TagListCmd.ts
Normal file
18
backend/src/plugins/Tags/commands/TagListCmd.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { tagsCmd } from "../types";
|
||||
|
||||
export const TagListCmd = tagsCmd({
|
||||
trigger: ["tag list", "tags", "taglist"],
|
||||
permission: "can_list",
|
||||
|
||||
async run({ message: msg, pluginData }) {
|
||||
const tags = await pluginData.state.tags.all();
|
||||
if (tags.length === 0) {
|
||||
msg.channel.createMessage(`No tags created yet! Use \`tag create\` command to create one.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const prefix = pluginData.config.getForMessage(msg).prefix;
|
||||
const tagNames = tags.map(tag => tag.tag).sort();
|
||||
msg.channel.createMessage(`Available tags (use with ${prefix}tag): \`\`\`${tagNames.join(", ")}\`\`\``);
|
||||
},
|
||||
});
|
40
backend/src/plugins/Tags/commands/TagSourceCmd.ts
Normal file
40
backend/src/plugins/Tags/commands/TagSourceCmd.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { tagsCmd } from "../types";
|
||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { sendErrorMessage, getBaseUrl, sendSuccessMessage } from "src/pluginUtils";
|
||||
import moment from "moment-timezone";
|
||||
|
||||
export const TagSourceCmd = tagsCmd({
|
||||
trigger: "tag",
|
||||
permission: "can_create",
|
||||
|
||||
signature: {
|
||||
tag: ct.string(),
|
||||
|
||||
delete: ct.bool({ option: true, shortcut: "d", isSwitch: true }),
|
||||
},
|
||||
|
||||
async run({ message: msg, args, pluginData }) {
|
||||
if (args.delete) {
|
||||
const actualTag = await pluginData.state.tags.find(args.tag);
|
||||
if (!actualTag) {
|
||||
sendErrorMessage(pluginData, msg.channel, "No tag with that name");
|
||||
return;
|
||||
}
|
||||
|
||||
await pluginData.state.tags.delete(args.tag);
|
||||
sendSuccessMessage(pluginData, msg.channel, "Tag deleted!");
|
||||
return;
|
||||
}
|
||||
|
||||
const tag = await pluginData.state.tags.find(args.tag);
|
||||
if (!tag) {
|
||||
sendErrorMessage(pluginData, msg.channel, "No tag with that name");
|
||||
return;
|
||||
}
|
||||
|
||||
const archiveId = await pluginData.state.archives.create(tag.body, moment().add(10, "minutes"));
|
||||
const url = pluginData.state.archives.getUrl(getBaseUrl(pluginData), archiveId);
|
||||
|
||||
msg.channel.createMessage(`Tag source:\n${url}`);
|
||||
},
|
||||
});
|
58
backend/src/plugins/Tags/types.ts
Normal file
58
backend/src/plugins/Tags/types.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import * as t from "io-ts";
|
||||
import { BasePluginType, command, eventListener } from "knub";
|
||||
import { tNullable, tEmbed } from "src/utils";
|
||||
import { GuildArchives } from "src/data/GuildArchives";
|
||||
import { GuildTags } from "src/data/GuildTags";
|
||||
import { GuildSavedMessages } from "src/data/GuildSavedMessages";
|
||||
import { GuildLogs } from "src/data/GuildLogs";
|
||||
|
||||
export const Tag = t.union([t.string, tEmbed]);
|
||||
|
||||
const TagCategory = t.type({
|
||||
prefix: tNullable(t.string),
|
||||
delete_with_command: tNullable(t.boolean),
|
||||
|
||||
user_tag_cooldown: tNullable(t.union([t.string, t.number])), // Per user, per tag
|
||||
user_category_cooldown: tNullable(t.union([t.string, t.number])), // Per user, per tag category
|
||||
global_tag_cooldown: tNullable(t.union([t.string, t.number])), // Any user, per tag
|
||||
global_category_cooldown: tNullable(t.union([t.string, t.number])), // Any user, per category
|
||||
|
||||
tags: t.record(t.string, Tag),
|
||||
|
||||
can_use: tNullable(t.boolean),
|
||||
});
|
||||
|
||||
export const ConfigSchema = t.type({
|
||||
prefix: t.string,
|
||||
delete_with_command: t.boolean,
|
||||
|
||||
user_tag_cooldown: tNullable(t.union([t.string, t.number])), // Per user, per tag
|
||||
global_tag_cooldown: tNullable(t.union([t.string, t.number])), // Any user, per tag
|
||||
user_cooldown: tNullable(t.union([t.string, t.number])), // Per user
|
||||
global_cooldown: tNullable(t.union([t.string, t.number])), // Any tag use
|
||||
|
||||
categories: t.record(t.string, TagCategory),
|
||||
|
||||
can_create: t.boolean,
|
||||
can_use: t.boolean,
|
||||
can_list: t.boolean,
|
||||
});
|
||||
export type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
|
||||
|
||||
export interface TagsPluginType extends BasePluginType {
|
||||
config: TConfigSchema;
|
||||
state: {
|
||||
archives: GuildArchives;
|
||||
tags: GuildTags;
|
||||
savedMessages: GuildSavedMessages;
|
||||
logs: GuildLogs;
|
||||
|
||||
onMessageCreateFn;
|
||||
onMessageDeleteFn;
|
||||
|
||||
tagFunctions: any;
|
||||
};
|
||||
}
|
||||
|
||||
export const tagsCmd = command<TagsPluginType>();
|
||||
export const tagsEvent = eventListener<TagsPluginType>();
|
145
backend/src/plugins/Tags/util/onMessageCreate.ts
Normal file
145
backend/src/plugins/Tags/util/onMessageCreate.ts
Normal file
|
@ -0,0 +1,145 @@
|
|||
import { TagsPluginType } from "../types";
|
||||
import { SavedMessage } from "src/data/entities/SavedMessage";
|
||||
import { PluginData } from "knub";
|
||||
import { resolveMember, convertDelayStringToMS, tStrictMessageContent } from "src/utils";
|
||||
import escapeStringRegexp from "escape-string-regexp";
|
||||
import { validate } from "src/validatorUtils";
|
||||
import { LogType } from "src/data/LogType";
|
||||
import { TextChannel } from "eris";
|
||||
import { renderSafeTagFromMessage } from "./renderSafeTagFromMessage";
|
||||
|
||||
export async function onMessageCreate(pluginData: PluginData<TagsPluginType>, msg: SavedMessage) {
|
||||
if (msg.is_bot) return;
|
||||
if (!msg.data.content) return;
|
||||
|
||||
const member = await resolveMember(pluginData.client, pluginData.guild, msg.user_id);
|
||||
if (!member) return;
|
||||
|
||||
const config = pluginData.config.getMatchingConfig({ member, channelId: msg.channel_id });
|
||||
let deleteWithCommand = false;
|
||||
|
||||
// Find potential matching tag, looping through categories first and checking dynamic tags last
|
||||
let renderedTag = null;
|
||||
let matchedTagName;
|
||||
const cooldowns = [];
|
||||
|
||||
for (const [name, category] of Object.entries(config.categories)) {
|
||||
const canUse = category.can_use != null ? category.can_use : config.can_use;
|
||||
if (canUse !== true) continue;
|
||||
|
||||
const prefix = category.prefix != null ? category.prefix : config.prefix;
|
||||
if (prefix !== "" && !msg.data.content.startsWith(prefix)) continue;
|
||||
|
||||
const withoutPrefix = msg.data.content.slice(prefix.length);
|
||||
|
||||
for (const [tagName, tagBody] of Object.entries(category.tags)) {
|
||||
const regex = new RegExp(`^${escapeStringRegexp(tagName)}(?:\\s|$)`);
|
||||
if (regex.test(withoutPrefix)) {
|
||||
renderedTag = await renderSafeTagFromMessage(
|
||||
pluginData,
|
||||
msg.data.content,
|
||||
prefix,
|
||||
tagName,
|
||||
category.tags[tagName],
|
||||
member,
|
||||
);
|
||||
if (renderedTag) {
|
||||
matchedTagName = tagName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (renderedTag) {
|
||||
if (category.user_tag_cooldown) {
|
||||
const delay = convertDelayStringToMS(String(category.user_tag_cooldown), "s");
|
||||
cooldowns.push([`tags-category-${name}-user-${msg.user_id}-tag-${matchedTagName}`, delay]);
|
||||
}
|
||||
if (category.global_tag_cooldown) {
|
||||
const delay = convertDelayStringToMS(String(category.global_tag_cooldown), "s");
|
||||
cooldowns.push([`tags-category-${name}-tag-${matchedTagName}`, delay]);
|
||||
}
|
||||
if (category.user_category_cooldown) {
|
||||
const delay = convertDelayStringToMS(String(category.user_category_cooldown), "s");
|
||||
cooldowns.push([`tags-category-${name}-user--${msg.user_id}`, delay]);
|
||||
}
|
||||
if (category.global_category_cooldown) {
|
||||
const delay = convertDelayStringToMS(String(category.global_category_cooldown), "s");
|
||||
cooldowns.push([`tags-category-${name}`, delay]);
|
||||
}
|
||||
|
||||
deleteWithCommand =
|
||||
category.delete_with_command != null ? category.delete_with_command : config.delete_with_command;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Matching tag was not found from the config, try a dynamic tag
|
||||
if (!renderedTag) {
|
||||
if (config.can_use !== true) return;
|
||||
|
||||
const prefix = config.prefix;
|
||||
if (!msg.data.content.startsWith(prefix)) return;
|
||||
|
||||
const tagNameMatch = msg.data.content.slice(prefix.length).match(/^\S+/);
|
||||
if (tagNameMatch === null) return;
|
||||
|
||||
const tagName = tagNameMatch[0];
|
||||
const tag = await pluginData.state.tags.find(tagName);
|
||||
if (!tag) return;
|
||||
|
||||
matchedTagName = tagName;
|
||||
|
||||
renderedTag = await renderSafeTagFromMessage(pluginData, msg.data.content, prefix, tagName, tag.body, member);
|
||||
}
|
||||
|
||||
if (!renderedTag) return;
|
||||
|
||||
if (config.user_tag_cooldown) {
|
||||
const delay = convertDelayStringToMS(String(config.user_tag_cooldown), "s");
|
||||
cooldowns.push([`tags-user-${msg.user_id}-tag-${matchedTagName}`, delay]);
|
||||
}
|
||||
|
||||
if (config.global_tag_cooldown) {
|
||||
const delay = convertDelayStringToMS(String(config.global_tag_cooldown), "s");
|
||||
cooldowns.push([`tags-tag-${matchedTagName}`, delay]);
|
||||
}
|
||||
|
||||
if (config.user_cooldown) {
|
||||
const delay = convertDelayStringToMS(String(config.user_cooldown), "s");
|
||||
cooldowns.push([`tags-user-${matchedTagName}`, delay]);
|
||||
}
|
||||
|
||||
if (config.global_cooldown) {
|
||||
const delay = convertDelayStringToMS(String(config.global_cooldown), "s");
|
||||
cooldowns.push([`tags`, delay]);
|
||||
}
|
||||
|
||||
const isOnCooldown = cooldowns.some(cd => pluginData.cooldowns.isOnCooldown(cd[0]));
|
||||
if (isOnCooldown) return;
|
||||
|
||||
for (const cd of cooldowns) {
|
||||
pluginData.cooldowns.setCooldown(cd[0], cd[1]);
|
||||
}
|
||||
|
||||
deleteWithCommand = config.delete_with_command;
|
||||
|
||||
const validationError = await validate(tStrictMessageContent, renderedTag);
|
||||
if (validationError) {
|
||||
pluginData.state.logs.log(LogType.BOT_ALERT, {
|
||||
body: `Rendering tag ${matchedTagName} resulted in an invalid message: ${validationError.message}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const channel = pluginData.guild.channels.get(msg.channel_id) as TextChannel;
|
||||
const responseMsg = await channel.createMessage(renderedTag);
|
||||
|
||||
// Save the command-response message pair once the message is in our database
|
||||
if (deleteWithCommand) {
|
||||
pluginData.state.savedMessages.onceMessageAvailable(responseMsg.id, async () => {
|
||||
await pluginData.state.tags.addResponse(msg.id, responseMsg.id);
|
||||
});
|
||||
}
|
||||
}
|
32
backend/src/plugins/Tags/util/onMessageDelete.ts
Normal file
32
backend/src/plugins/Tags/util/onMessageDelete.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { PluginData } from "knub";
|
||||
import { TagsPluginType } from "../types";
|
||||
import { SavedMessage } from "src/data/entities/SavedMessage";
|
||||
import { TextChannel } from "eris";
|
||||
|
||||
export async function onMessageDelete(pluginData: PluginData<TagsPluginType>, msg: SavedMessage) {
|
||||
// Command message was deleted -> delete the response as well
|
||||
const commandMsgResponse = await pluginData.state.tags.findResponseByCommandMessageId(msg.id);
|
||||
if (commandMsgResponse) {
|
||||
const channel = pluginData.guild.channels.get(msg.channel_id) as TextChannel;
|
||||
if (!channel) return;
|
||||
|
||||
const responseMsg = await pluginData.state.savedMessages.find(commandMsgResponse.response_message_id);
|
||||
if (!responseMsg || responseMsg.deleted_at != null) return;
|
||||
|
||||
await channel.deleteMessage(commandMsgResponse.response_message_id);
|
||||
return;
|
||||
}
|
||||
|
||||
// Response was deleted -> delete the command message as well
|
||||
const responseMsgResponse = await pluginData.state.tags.findResponseByResponseMessageId(msg.id);
|
||||
if (responseMsgResponse) {
|
||||
const channel = pluginData.guild.channels.get(msg.channel_id) as TextChannel;
|
||||
if (!channel) return;
|
||||
|
||||
const commandMsg = await pluginData.state.savedMessages.find(responseMsgResponse.command_message_id);
|
||||
if (!commandMsg || commandMsg.deleted_at != null) return;
|
||||
|
||||
await channel.deleteMessage(responseMsgResponse.command_message_id);
|
||||
return;
|
||||
}
|
||||
}
|
45
backend/src/plugins/Tags/util/renderSafeTagFromMessage.ts
Normal file
45
backend/src/plugins/Tags/util/renderSafeTagFromMessage.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import { Tag, TagsPluginType } from "../types";
|
||||
import { Member } from "eris";
|
||||
import * as t from "io-ts";
|
||||
import { StrictMessageContent, stripObjectToScalars, renderRecursively } from "src/utils";
|
||||
import { parseArguments } from "knub-command-manager";
|
||||
import { TemplateParseError } from "src/templateFormatter";
|
||||
import { PluginData } from "knub";
|
||||
import { renderTag } from "./renderTag";
|
||||
import { logger } from "src/logger";
|
||||
|
||||
export async function renderSafeTagFromMessage(
|
||||
pluginData: PluginData<TagsPluginType>,
|
||||
str: string,
|
||||
prefix: string,
|
||||
tagName: string,
|
||||
tagBody: t.TypeOf<typeof Tag>,
|
||||
member: Member,
|
||||
): Promise<StrictMessageContent | null> {
|
||||
const variableStr = str.slice(prefix.length + tagName.length).trim();
|
||||
const tagArgs = parseArguments(variableStr).map(v => v.value);
|
||||
|
||||
const renderTagString = async _str => {
|
||||
let rendered = await renderTag(pluginData, _str, tagArgs, {
|
||||
member: stripObjectToScalars(member, ["user"]),
|
||||
user: stripObjectToScalars(member.user),
|
||||
});
|
||||
rendered = rendered.trim();
|
||||
|
||||
return rendered;
|
||||
};
|
||||
|
||||
// Format the string
|
||||
try {
|
||||
return typeof tagBody === "string"
|
||||
? { content: await renderTagString(tagBody) }
|
||||
: await renderRecursively(tagBody, renderTagString);
|
||||
} catch (e) {
|
||||
if (e instanceof TemplateParseError) {
|
||||
logger.warn(`Invalid tag format!\nError: ${e.message}\nFormat: ${tagBody}`);
|
||||
return null;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
33
backend/src/plugins/Tags/util/renderTag.ts
Normal file
33
backend/src/plugins/Tags/util/renderTag.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { renderTemplate } from "src/templateFormatter";
|
||||
import { PluginData, plugin } from "knub";
|
||||
import { TagsPluginType } from "../types";
|
||||
|
||||
export async function renderTag(pluginData: PluginData<TagsPluginType>, body, args = [], extraData = {}) {
|
||||
const dynamicVars = {};
|
||||
const maxTagFnCalls = 25;
|
||||
let tagFnCalls = 0;
|
||||
|
||||
const data = {
|
||||
args,
|
||||
...extraData,
|
||||
...pluginData.state.tagFunctions,
|
||||
set(name, val) {
|
||||
if (typeof name !== "string") return;
|
||||
dynamicVars[name] = val;
|
||||
},
|
||||
get(name) {
|
||||
return dynamicVars[name] == null ? "" : dynamicVars[name];
|
||||
},
|
||||
tag: async (name, ...subTagArgs) => {
|
||||
if (tagFnCalls++ > maxTagFnCalls) return "\\_recursion\\_";
|
||||
if (typeof name !== "string") return "";
|
||||
if (name === "") return "";
|
||||
// TODO: Incorporate tag categories here
|
||||
const subTag = await pluginData.state.tags.find(name);
|
||||
if (!subTag) return "";
|
||||
return renderTemplate(subTag.body, { ...data, args: subTagArgs });
|
||||
},
|
||||
};
|
||||
|
||||
return renderTemplate(body, data);
|
||||
}
|
|
@ -12,6 +12,7 @@ import { PingableRolesPlugin } from "./PingableRoles/PingableRolesPlugin";
|
|||
import { GuildConfigReloaderPlugin } from "./GuildConfigReloader/GuildConfigReloaderPlugin";
|
||||
import { CasesPlugin } from "./Cases/CasesPlugin";
|
||||
import { MutesPlugin } from "./Mutes/MutesPlugin";
|
||||
import { TagsPlugin } from "./Tags/TagsPlugin";
|
||||
|
||||
// prettier-ignore
|
||||
export const guildPlugins: Array<ZeppelinPluginBlueprint<any>> = [
|
||||
|
@ -22,6 +23,7 @@ export const guildPlugins: Array<ZeppelinPluginBlueprint<any>> = [
|
|||
MessageSaverPlugin,
|
||||
NameHistoryPlugin,
|
||||
RemindersPlugin,
|
||||
TagsPlugin,
|
||||
UsernameSaverPlugin,
|
||||
UtilityPlugin,
|
||||
WelcomeMessagePlugin,
|
||||
|
|
Loading…
Add table
Reference in a new issue