diff --git a/backend/src/plugins/Automod/Automod.ts b/backend/src/plugins/Automod/Automod.ts index 04456d76..21635734 100644 --- a/backend/src/plugins/Automod/Automod.ts +++ b/backend/src/plugins/Automod/Automod.ts @@ -13,6 +13,7 @@ import { messageSummary, MINUTES, noop, + renderRecursively, SECONDS, stripObjectToScalars, tDeepPartial, @@ -1297,12 +1298,24 @@ export class AutomodPlugin extends ZeppelinPlugin + renderTemplate(str, { + user: stripObjectToScalars(user), + }); + const formatted = + typeof rule.actions.reply === "string" + ? await renderReplyText(rule.actions.reply) + : await renderRecursively(rule.actions.reply.text, renderReplyText); + if (formatted) { - await channel.createMessage(formatted); + const replyMsg = await channel.createMessage(formatted); actionsTaken.push("reply"); + + if (typeof rule.actions.reply === "object" && rule.actions.reply.auto_delete != null) { + const delay = convertDelayStringToMS(String(rule.actions.reply.auto_delete)); + setTimeout(() => replyMsg.delete().catch(noop), delay); + console.log("deleting in", delay); + } } } } diff --git a/backend/src/plugins/Automod/types.ts b/backend/src/plugins/Automod/types.ts index 7a64a90c..e315122e 100644 --- a/backend/src/plugins/Automod/types.ts +++ b/backend/src/plugins/Automod/types.ts @@ -1,6 +1,6 @@ import * as t from "io-ts"; import { TSafeRegex } from "../../validatorUtils"; -import { tDelayString, tNullable } from "../../utils"; +import { tDelayString, tMessageContent, tNullable } from "../../utils"; export enum RecentActionType { Message = 1, @@ -286,7 +286,13 @@ export const RemoveRolesAction = t.array(t.string); export const SetAntiraidLevelAction = t.string; -export const ReplyAction = t.string; +export const ReplyAction = t.union([ + t.string, + t.type({ + text: tMessageContent, + auto_delete: tNullable(t.union([t.string, t.number])), + }), +]); /** * RULES diff --git a/backend/src/plugins/Tags.ts b/backend/src/plugins/Tags.ts index f454103d..58250ab9 100644 --- a/backend/src/plugins/Tags.ts +++ b/backend/src/plugins/Tags.ts @@ -3,6 +3,7 @@ import { Member, Message, TextChannel } from "eris"; import { convertDelayStringToMS, errorMessage, + renderRecursively, StrictMessageContent, stripObjectToScalars, tEmbed, @@ -353,29 +354,6 @@ export class TagsPlugin extends ZeppelinPlugin { const variableStr = str.slice(prefix.length + tagName.length).trim(); const tagArgs = parseArguments(variableStr).map(v => v.value); - // Renders strings in objects and arrays recursively, effectively supporting embeds for tags - const renderTagValue = async value => { - if (Array.isArray(value)) { - const result = []; - for (const item of value) { - result.push(await renderTagValue(item)); - } - return result; - } else if (value == null) { - return null; - } else if (typeof value === "object") { - const result = {}; - for (const [prop, _value] of Object.entries(value)) { - result[prop] = await renderTagValue(_value); - } - return result; - } else if (typeof value === "string") { - return renderTagString(value); - } - - return value; - }; - const renderTagString = async _str => { let rendered = await this.renderTag(_str, tagArgs, { member: stripObjectToScalars(member, ["user"]), @@ -388,7 +366,9 @@ export class TagsPlugin extends ZeppelinPlugin { // Format the string try { - return typeof tagBody === "string" ? { content: await renderTagString(tagBody) } : await renderTagValue(tagBody); + 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}`); diff --git a/backend/src/utils.ts b/backend/src/utils.ts index b4843fb7..999d1897 100644 --- a/backend/src/utils.ts +++ b/backend/src/utils.ts @@ -204,6 +204,8 @@ export const tStrictMessageContent = t.type({ embed: tNullable(tEmbed), }); +export const tMessageContent = t.union([t.string, tStrictMessageContent]); + export function dropPropertiesByName(obj, propName) { if (obj.hasOwnProperty(propName)) delete obj[propName]; for (const value of Object.values(obj)) { @@ -1126,3 +1128,27 @@ export function memoize(fn: (...args: any[]) => T, key?, time?): T { return value; } + +type RecursiveRenderFn = (str: string) => string | Promise; + +export async function renderRecursively(value, fn: RecursiveRenderFn) { + if (Array.isArray(value)) { + const result = []; + for (const item of value) { + result.push(await renderRecursively(item, fn)); + } + return result; + } else if (value === null) { + return null; + } else if (typeof value === "object") { + const result = {}; + for (const [prop, _value] of Object.entries(value)) { + result[prop] = await renderRecursively(_value, fn); + } + return result; + } else if (typeof value === "string") { + return fn(value); + } + + return value; +}