diff --git a/README.md b/README.md index 1d29e514..02eae9ee 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,8 @@ These instructions are intended for bot development only. Configuration is stored in the database in the `configs` table ```yml +prefix: '!' + # role id: level levels: "12345678": 100 # Example admin diff --git a/backend/src/api/docs.ts b/backend/src/api/docs.ts index 3009a4e1..8dbeb116 100644 --- a/backend/src/api/docs.ts +++ b/backend/src/api/docs.ts @@ -37,7 +37,7 @@ export function initDocs(app: express.Express) { app.get("/docs/plugins", (req: express.Request, res: express.Response) => { res.json( docsPlugins.map(plugin => { - const thinInfo = plugin.info ? { prettyName: plugin.info.prettyName } : {}; + const thinInfo = plugin.info ? { prettyName: plugin.info.prettyName, legacy: plugin.info.legacy ?? false } : {}; return { name: plugin.name, info: thinInfo, diff --git a/backend/src/plugins/Automod/AutomodPlugin.ts b/backend/src/plugins/Automod/AutomodPlugin.ts index eb2484dd..8170f632 100644 --- a/backend/src/plugins/Automod/AutomodPlugin.ts +++ b/backend/src/plugins/Automod/AutomodPlugin.ts @@ -73,6 +73,10 @@ const configPreprocessor: ConfigPreprocessorFn = options => { rule["enabled"] = true; } + if (rule["allow_further_rules"] == null) { + rule["allow_further_rules"] = false; + } + if (rule["affects_bots"] == null) { rule["affects_bots"] = false; } diff --git a/backend/src/plugins/Automod/functions/runAutomod.ts b/backend/src/plugins/Automod/functions/runAutomod.ts index bf22cc5e..67675565 100644 --- a/backend/src/plugins/Automod/functions/runAutomod.ts +++ b/backend/src/plugins/Automod/functions/runAutomod.ts @@ -72,7 +72,7 @@ export async function runAutomod(pluginData: GuildPluginData, matchResult.fullSummary = `Triggered automod rule **${ruleName}**\n${matchResult.summary}`.trim(); - break triggerLoop; + if (!rule.allow_further_rules) break triggerLoop; } } } @@ -94,7 +94,7 @@ export async function runAutomod(pluginData: GuildPluginData, }); } - break; + if (!rule.allow_further_rules) break; } } } diff --git a/backend/src/plugins/Automod/types.ts b/backend/src/plugins/Automod/types.ts index 3981860d..2f8107fc 100644 --- a/backend/src/plugins/Automod/types.ts +++ b/backend/src/plugins/Automod/types.ts @@ -26,6 +26,7 @@ export const Rule = t.type({ triggers: t.array(t.partial(AvailableTriggers.props)), actions: t.partial(AvailableActions.props), cooldown: tNullable(t.string), + allow_further_rules: t.boolean, }); export type TRule = t.TypeOf; diff --git a/backend/src/plugins/Censor/CensorPlugin.ts b/backend/src/plugins/Censor/CensorPlugin.ts index f8401f54..27f8ed9f 100644 --- a/backend/src/plugins/Censor/CensorPlugin.ts +++ b/backend/src/plugins/Censor/CensorPlugin.ts @@ -52,6 +52,7 @@ export const CensorPlugin = zeppelinGuildPlugin()({ Censor words, tokens, links, regex, etc. For more advanced filtering, check out the Automod plugin! `), + legacy: true, }, dependencies: [LogsPlugin], diff --git a/backend/src/plugins/SelfGrantableRoles/commands/RoleAddCmd.ts b/backend/src/plugins/SelfGrantableRoles/commands/RoleAddCmd.ts index 0568ea41..8fa9cca4 100644 --- a/backend/src/plugins/SelfGrantableRoles/commands/RoleAddCmd.ts +++ b/backend/src/plugins/SelfGrantableRoles/commands/RoleAddCmd.ts @@ -43,6 +43,7 @@ export const RoleAddCmd = selfGrantableRolesCmd({ pluginData, msg.channel, `<@!${msg.author.id}> Unknown ${args.roleNames.length === 1 ? "role" : "roles"}`, + { users: [msg.author.id] }, ); lock.unlock(); return; @@ -87,6 +88,7 @@ export const RoleAddCmd = selfGrantableRolesCmd({ pluginData, msg.channel, `<@!${msg.author.id}> Got an error while trying to grant you the roles`, + { users: [msg.author.id] }, ); return; } @@ -118,7 +120,9 @@ export const RoleAddCmd = selfGrantableRolesCmd({ messageParts.push("couldn't recognize some of the roles"); } - sendSuccessMessage(pluginData, msg.channel, `<@!${msg.author.id}> ${messageParts.join("; ")}`); + sendSuccessMessage(pluginData, msg.channel, `<@!${msg.author.id}> ${messageParts.join("; ")}`, { + users: [msg.author.id], + }); lock.unlock(); }, diff --git a/backend/src/plugins/SelfGrantableRoles/commands/RoleRemoveCmd.ts b/backend/src/plugins/SelfGrantableRoles/commands/RoleRemoveCmd.ts index 0ba51766..7a4bb83b 100644 --- a/backend/src/plugins/SelfGrantableRoles/commands/RoleRemoveCmd.ts +++ b/backend/src/plugins/SelfGrantableRoles/commands/RoleRemoveCmd.ts @@ -51,12 +51,14 @@ export const RoleRemoveCmd = selfGrantableRolesCmd({ msg.channel, `<@!${msg.author.id}> Removed ${removedRolesStr.join(", ")} ${removedRolesWord};` + ` couldn't recognize the other roles you mentioned`, + { users: [msg.author.id] }, ); } else { sendSuccessMessage( pluginData, msg.channel, `<@!${msg.author.id}> Removed ${removedRolesStr.join(", ")} ${removedRolesWord}`, + { users: [msg.author.id] }, ); } } catch { @@ -64,6 +66,7 @@ export const RoleRemoveCmd = selfGrantableRolesCmd({ pluginData, msg.channel, `<@!${msg.author.id}> Got an error while trying to remove the roles`, + { users: [msg.author.id] }, ); } } else { @@ -71,6 +74,7 @@ export const RoleRemoveCmd = selfGrantableRolesCmd({ pluginData, msg.channel, `<@!${msg.author.id}> Unknown ${args.roleNames.length === 1 ? "role" : "roles"}`, + { users: [msg.author.id] }, ); } diff --git a/backend/src/plugins/Spam/SpamPlugin.ts b/backend/src/plugins/Spam/SpamPlugin.ts index ddf45a0b..87dc50ad 100644 --- a/backend/src/plugins/Spam/SpamPlugin.ts +++ b/backend/src/plugins/Spam/SpamPlugin.ts @@ -51,6 +51,7 @@ export const SpamPlugin = zeppelinGuildPlugin()({ Basic spam detection and auto-muting. For more advanced spam filtering, check out the Automod plugin! `), + legacy: true, }, dependencies: [LogsPlugin], diff --git a/backend/src/plugins/Utility/commands/EmojiInfoCmd.ts b/backend/src/plugins/Utility/commands/EmojiInfoCmd.ts index 3f7d094b..7a60eda6 100644 --- a/backend/src/plugins/Utility/commands/EmojiInfoCmd.ts +++ b/backend/src/plugins/Utility/commands/EmojiInfoCmd.ts @@ -1,7 +1,7 @@ import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; -import { customEmojiRegex } from "../../../utils"; import { getEmojiInfoEmbed } from "../functions/getEmojiInfoEmbed"; +import { getCustomEmojiId } from "../functions/getCustomEmojiId"; import { utilityCmd } from "../types"; export const EmojiInfoCmd = utilityCmd({ @@ -11,17 +11,17 @@ export const EmojiInfoCmd = utilityCmd({ permission: "can_emojiinfo", signature: { - emoji: ct.string({ required: false }), + emoji: ct.string({ required: true }), }, async run({ message, args, pluginData }) { - const emojiIdMatch = args.emoji.match(customEmojiRegex); - if (!emojiIdMatch?.[2]) { + const emojiId = getCustomEmojiId(args.emoji); + if (!emojiId) { sendErrorMessage(pluginData, message.channel, "Emoji not found"); return; } - const embed = await getEmojiInfoEmbed(pluginData, emojiIdMatch[2]); + const embed = await getEmojiInfoEmbed(pluginData, emojiId); if (!embed) { sendErrorMessage(pluginData, message.channel, "Emoji not found"); return; diff --git a/backend/src/plugins/Utility/commands/InfoCmd.ts b/backend/src/plugins/Utility/commands/InfoCmd.ts index 9c4b55d3..5dcf3456 100644 --- a/backend/src/plugins/Utility/commands/InfoCmd.ts +++ b/backend/src/plugins/Utility/commands/InfoCmd.ts @@ -2,26 +2,20 @@ import { Snowflake } from "discord.js"; import { getChannelId, getRoleId } from "knub/dist/utils"; import { commandTypeHelpers as ct } from "../../../commandTypes"; import { sendErrorMessage } from "../../../pluginUtils"; -import { - customEmojiRegex, - isValidSnowflake, - noop, - parseInviteCodeInput, - resolveInvite, - resolveUser, -} from "../../../utils"; +import { isValidSnowflake, noop, parseInviteCodeInput, resolveInvite, resolveUser } from "../../../utils"; +import { getUserInfoEmbed } from "../functions/getUserInfoEmbed"; import { canReadChannel } from "../../../utils/canReadChannel"; import { resolveMessageTarget } from "../../../utils/resolveMessageTarget"; import { getChannelInfoEmbed } from "../functions/getChannelInfoEmbed"; -import { getEmojiInfoEmbed } from "../functions/getEmojiInfoEmbed"; import { getGuildPreview } from "../functions/getGuildPreview"; import { getInviteInfoEmbed } from "../functions/getInviteInfoEmbed"; import { getMessageInfoEmbed } from "../functions/getMessageInfoEmbed"; import { getRoleInfoEmbed } from "../functions/getRoleInfoEmbed"; +import { getEmojiInfoEmbed } from "../functions/getEmojiInfoEmbed"; +import { getCustomEmojiId } from "../functions/getCustomEmojiId"; +import { utilityCmd } from "../types"; import { getServerInfoEmbed } from "../functions/getServerInfoEmbed"; import { getSnowflakeInfoEmbed } from "../functions/getSnowflakeInfoEmbed"; -import { getUserInfoEmbed } from "../functions/getUserInfoEmbed"; -import { utilityCmd } from "../types"; export const InfoCmd = utilityCmd({ trigger: "info", @@ -139,9 +133,9 @@ export const InfoCmd = utilityCmd({ // 8. Emoji if (userCfg.can_emojiinfo) { - const emojiIdMatch = value.match(customEmojiRegex); - if (emojiIdMatch?.[2]) { - const embed = await getEmojiInfoEmbed(pluginData, emojiIdMatch[2]); + const emojiId = getCustomEmojiId(value); + if (emojiId) { + const embed = await getEmojiInfoEmbed(pluginData, emojiId); if (embed) { message.channel.send({ embeds: [embed] }); return; diff --git a/backend/src/plugins/Utility/commands/NicknameCmd.ts b/backend/src/plugins/Utility/commands/NicknameCmd.ts index 3dc803a2..75f6699d 100644 --- a/backend/src/plugins/Utility/commands/NicknameCmd.ts +++ b/backend/src/plugins/Utility/commands/NicknameCmd.ts @@ -1,6 +1,7 @@ +import { Util } from "discord.js"; import { commandTypeHelpers as ct } from "../../../commandTypes"; -import { canActOn, sendSuccessMessage } from "../../../pluginUtils"; import { errorMessage } from "../../../utils"; +import { canActOn, sendSuccessMessage } from "../../../pluginUtils"; import { utilityCmd } from "../types"; export const NicknameCmd = utilityCmd({ @@ -11,10 +12,19 @@ export const NicknameCmd = utilityCmd({ signature: { member: ct.resolvedMember(), - nickname: ct.string({ catchAll: true }), + nickname: ct.string({ catchAll: true, required: false }), }, async run({ message: msg, args, pluginData }) { + if (!args.nickname) { + if (!args.member.nickname) { + msg.channel.send(`<@!${args.member.id}> does not have a nickname`); + } else { + msg.channel.send(`The nickname of <@!${args.member.id}> is **${Util.escapeBold(args.nickname)}**`); + } + return; + } + if (msg.member.id !== args.member.id && !canActOn(pluginData, msg.member, args.member)) { msg.channel.send(errorMessage("Cannot change nickname: insufficient permissions")); return; @@ -29,9 +39,7 @@ export const NicknameCmd = utilityCmd({ const oldNickname = args.member.nickname || ""; try { - await args.member.edit({ - nick: args.nickname, - }); + await args.member.setNickname(args.nickname ?? null); } catch { msg.channel.send(errorMessage("Failed to change nickname")); return; diff --git a/backend/src/plugins/Utility/commands/NicknameResetCmd.ts b/backend/src/plugins/Utility/commands/NicknameResetCmd.ts index 91894a40..f86a33c9 100644 --- a/backend/src/plugins/Utility/commands/NicknameResetCmd.ts +++ b/backend/src/plugins/Utility/commands/NicknameResetCmd.ts @@ -19,10 +19,13 @@ export const NicknameResetCmd = utilityCmd({ return; } + if (!args.member.nickname) { + msg.channel.send(errorMessage("User does not have a nickname")); + return; + } + try { - await args.member.edit({ - nick: "", - }); + await args.member.setNickname(null); } catch { msg.channel.send(errorMessage("Failed to reset nickname")); return; diff --git a/backend/src/plugins/Utility/commands/PingCmd.ts b/backend/src/plugins/Utility/commands/PingCmd.ts index 842d3742..2ddc1623 100644 --- a/backend/src/plugins/Utility/commands/PingCmd.ts +++ b/backend/src/plugins/Utility/commands/PingCmd.ts @@ -5,7 +5,7 @@ import { utilityCmd } from "../types"; const { performance } = require("perf_hooks"); export const PingCmd = utilityCmd({ - trigger: "ping", + trigger: ["ping", "pong"], description: "Test the bot's ping to the Discord API", permission: "can_ping", diff --git a/backend/src/plugins/Utility/functions/getCustomEmojiId.ts b/backend/src/plugins/Utility/functions/getCustomEmojiId.ts new file mode 100644 index 00000000..3fdce097 --- /dev/null +++ b/backend/src/plugins/Utility/functions/getCustomEmojiId.ts @@ -0,0 +1,6 @@ +const customEmojiRegex = /(?:?/i; + +export function getCustomEmojiId(str: string): string | null { + const emojiIdMatch = str.match(customEmojiRegex); + return emojiIdMatch?.[1] ?? null; +} diff --git a/backend/src/plugins/ZeppelinPluginBlueprint.ts b/backend/src/plugins/ZeppelinPluginBlueprint.ts index 7a1dd44a..9bcd3417 100644 --- a/backend/src/plugins/ZeppelinPluginBlueprint.ts +++ b/backend/src/plugins/ZeppelinPluginBlueprint.ts @@ -27,6 +27,7 @@ export interface ZeppelinGuildPluginBlueprint ({ + items: this.plugins.filter(plugin => !plugin.info.legacy).map(plugin => ({ + label: plugin.info.prettyName || plugin.name, + to: `/docs/plugins/${plugin.name}`, + })), + }, + { + label: "Legacy Plugins", + items: this.plugins.filter(plugin => plugin.info.legacy).map(plugin => ({ label: plugin.info.prettyName || plugin.name, to: `/docs/plugins/${plugin.name}`, })),