diff --git a/backend/src/configValidator.ts b/backend/src/configValidator.ts index eb115d46..b2e2fc55 100644 --- a/backend/src/configValidator.ts +++ b/backend/src/configValidator.ts @@ -1,9 +1,8 @@ -import { ConfigValidationError, PluginConfigManager } from "knub"; -import moment from "moment-timezone"; +import { BaseConfig, ConfigValidationError, PluginConfigManager } from "knub"; import { ZodError } from "zod"; import { ZeppelinPlugin } from "./plugins/ZeppelinPlugin"; import { guildPlugins } from "./plugins/availablePlugins"; -import { ZeppelinGuildConfig, zZeppelinGuildConfig } from "./types"; +import { zZeppelinGuildConfig } from "./types"; import { formatZodIssue } from "./utils/formatZodIssue"; const pluginNameToPlugin = new Map(); @@ -17,14 +16,7 @@ export async function validateGuildConfig(config: any): Promise { return validationResult.error.issues.map(formatZodIssue).join("\n"); } - const guildConfig = config as ZeppelinGuildConfig; - - if (guildConfig.timezone) { - const validTimezones = moment.tz.names(); - if (!validTimezones.includes(guildConfig.timezone)) { - return `Invalid timezone: ${guildConfig.timezone}`; - } - } + const guildConfig = config as BaseConfig; if (guildConfig.plugins) { for (const [pluginName, pluginOptions] of Object.entries(guildConfig.plugins)) { diff --git a/backend/src/plugins/Common/CommonPlugin.ts b/backend/src/plugins/Common/CommonPlugin.ts index 9f4a6d82..62ee3628 100644 --- a/backend/src/plugins/Common/CommonPlugin.ts +++ b/backend/src/plugins/Common/CommonPlugin.ts @@ -98,7 +98,7 @@ export const CommonPlugin = zeppelinGuildPlugin()({ body: string, allowedMentions?: MessageMentionOptions, responseInteraction?: ModalSubmitInteraction, - ephemeral = false, + ephemeral = true, ): Promise => { const emoji = getErrorEmoji(pluginData); const formattedBody = errorMessage(body, emoji); diff --git a/backend/src/plugins/ContextMenus/actions/mute.ts b/backend/src/plugins/ContextMenus/actions/mute.ts index 9861a1d2..6e69b16f 100644 --- a/backend/src/plugins/ContextMenus/actions/mute.ts +++ b/backend/src/plugins/ContextMenus/actions/mute.ts @@ -69,7 +69,6 @@ async function muteAction( try { const result = await mutes.muteUser(target, durationMs, reason, reason, { caseArgs }); - const messageResultText = result.notifyResult.text ? ` (${result.notifyResult.text})` : ""; const muteMessage = `Muted **${result.case!.user_name}** ${ durationMs ? `for ${humanizeDuration(durationMs)}` : "indefinitely" diff --git a/backend/src/plugins/ModActions/commands/cases/CasesModMsgCmd.ts b/backend/src/plugins/ModActions/commands/cases/CasesModMsgCmd.ts index 717ea531..debeff45 100644 --- a/backend/src/plugins/ModActions/commands/cases/CasesModMsgCmd.ts +++ b/backend/src/plugins/ModActions/commands/cases/CasesModMsgCmd.ts @@ -32,7 +32,7 @@ export const CasesModMsgCmd = modActionsMsgCmd({ msg, args.mod, null, - msg.author, + msg.member, args.notes, args.warns, args.mutes, diff --git a/backend/src/plugins/ModActions/commands/cases/CasesSlashCmd.ts b/backend/src/plugins/ModActions/commands/cases/CasesSlashCmd.ts index 5207d76f..7833cfab 100644 --- a/backend/src/plugins/ModActions/commands/cases/CasesSlashCmd.ts +++ b/backend/src/plugins/ModActions/commands/cases/CasesSlashCmd.ts @@ -33,7 +33,7 @@ export const CasesSlashCmd = { interaction, options.mod, options.user, - interaction.user, + interaction.member, options.notes, options.warns, options.mutes, diff --git a/backend/src/plugins/ModActions/commands/cases/CasesUserMsgCmd.ts b/backend/src/plugins/ModActions/commands/cases/CasesUserMsgCmd.ts index 967ae160..5640080d 100644 --- a/backend/src/plugins/ModActions/commands/cases/CasesUserMsgCmd.ts +++ b/backend/src/plugins/ModActions/commands/cases/CasesUserMsgCmd.ts @@ -45,7 +45,7 @@ export const CasesUserMsgCmd = modActionsMsgCmd({ msg, args.mod, user, - msg.author, + msg.member, args.notes, args.warns, args.mutes, diff --git a/backend/src/plugins/ModActions/commands/warn/WarnMsgCmd.ts b/backend/src/plugins/ModActions/commands/warn/WarnMsgCmd.ts index cd8533b6..d6175356 100644 --- a/backend/src/plugins/ModActions/commands/warn/WarnMsgCmd.ts +++ b/backend/src/plugins/ModActions/commands/warn/WarnMsgCmd.ts @@ -24,7 +24,7 @@ export const WarnMsgCmd = modActionsMsgCmd({ async run({ pluginData, message: msg, args }) { const user = await resolveUser(pluginData.client, args.user); if (!user.id) { - pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`); + await pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found`); return; } @@ -33,9 +33,9 @@ export const WarnMsgCmd = modActionsMsgCmd({ if (!memberToWarn) { const _isBanned = await isBanned(pluginData, user.id); if (_isBanned) { - pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User is banned`); + await pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User is banned`); } else { - pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found on the server`); + await pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, `User not found on the server`); } return; @@ -43,7 +43,7 @@ export const WarnMsgCmd = modActionsMsgCmd({ // Make sure we're allowed to warn this member if (!canActOn(pluginData, msg.member, memberToWarn)) { - pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot warn: insufficient permissions"); + await pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, "Cannot warn: insufficient permissions"); return; } @@ -62,7 +62,7 @@ export const WarnMsgCmd = modActionsMsgCmd({ try { contactMethods = readContactMethodsFromArgs(args); } catch (e) { - pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, e.message); + await pluginData.getPlugin(CommonPlugin).sendErrorMessage(msg, e.message); return; } diff --git a/backend/src/plugins/ModActions/commands/warn/WarnSlashCmd.ts b/backend/src/plugins/ModActions/commands/warn/WarnSlashCmd.ts index c34e91e0..43f796f2 100644 --- a/backend/src/plugins/ModActions/commands/warn/WarnSlashCmd.ts +++ b/backend/src/plugins/ModActions/commands/warn/WarnSlashCmd.ts @@ -45,7 +45,7 @@ export const WarnSlashCmd = { const attachments = retrieveMultipleOptions(NUMBER_ATTACHMENTS_CASE_CREATION, options, "attachment"); if ((!options.reason || options.reason.trim() === "") && attachments.length < 1) { - pluginData + await pluginData .getPlugin(CommonPlugin) .sendErrorMessage(interaction, "Text or attachment required", undefined, undefined, true); @@ -57,9 +57,9 @@ export const WarnSlashCmd = { if (!memberToWarn) { const _isBanned = await isBanned(pluginData, options.user.id); if (_isBanned) { - pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `User is banned`); + await pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `User is banned`); } else { - pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `User not found on the server`); + await pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, `User not found on the server`); } return; @@ -67,7 +67,7 @@ export const WarnSlashCmd = { // Make sure we're allowed to warn this member if (!canActOn(pluginData, interaction.member, memberToWarn)) { - pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, "Cannot warn: insufficient permissions"); + await pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, "Cannot warn: insufficient permissions"); return; } @@ -79,7 +79,7 @@ export const WarnSlashCmd = { if (options.mod) { if (!canActAsOther) { - pluginData + await pluginData .getPlugin(CommonPlugin) .sendErrorMessage(interaction, "You don't have permission to act as another moderator"); return; @@ -92,7 +92,7 @@ export const WarnSlashCmd = { try { contactMethods = readContactMethodsFromArgs(options) ?? undefined; } catch (e) { - pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, e.message); + await pluginData.getPlugin(CommonPlugin).sendErrorMessage(interaction, e.message); return; } diff --git a/backend/src/plugins/ModActions/functions/actualCommands/actualCasesCmd.ts b/backend/src/plugins/ModActions/functions/actualCommands/actualCasesCmd.ts index 1ad7b813..ae92affa 100644 --- a/backend/src/plugins/ModActions/functions/actualCommands/actualCasesCmd.ts +++ b/backend/src/plugins/ModActions/functions/actualCommands/actualCasesCmd.ts @@ -145,9 +145,9 @@ async function casesModCmd( expand: boolean | null, ) { const casesPlugin = pluginData.getPlugin(CasesPlugin); - const caseFilters = { type: In(typesToShow), is_hidden: !!hidden }; + const casesFilters = { type: In(typesToShow), is_hidden: !!hidden }; - const totalCases = await casesPlugin.getTotalCasesByMod(modId ?? author.id, caseFilters); + const totalCases = await casesPlugin.getTotalCasesByMod(modId ?? author.id, casesFilters); if (totalCases === 0) { pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, `No cases by **${modName}**`); @@ -159,7 +159,7 @@ async function casesModCmd( if (expand) { // Expanded view (= individual case embeds) - const cases = totalCases > 8 ? [] : await casesPlugin.getRecentCasesByMod(modId ?? author.id, 8, 0, caseFilters); + const cases = totalCases > 8 ? [] : await casesPlugin.getRecentCasesByMod(modId ?? author.id, 8, 0, casesFilters); sendExpandedCases(pluginData, context, totalCases, cases); return; @@ -174,7 +174,7 @@ async function casesModCmd( modId ?? author.id, casesPerPage, (page - 1) * casesPerPage, - caseFilters, + casesFilters, ); const lines = await asyncMap(cases, (c) => casesPlugin.getCaseSummary(c, true, author.id)); @@ -212,7 +212,7 @@ export async function actualCasesCmd( context: Message | ChatInputCommandInteraction, modId: string | null, user: GuildMember | User | UnknownUser | null, - author: User, + author: GuildMember, notes: boolean | null, warns: boolean | null, mutes: boolean | null, @@ -253,6 +253,6 @@ export async function actualCasesCmd( } user - ? casesUserCmd(pluginData, context, author, modId!, user, modName, typesToShow, hidden, expand) - : casesModCmd(pluginData, context, author, modId!, mod!, modName, typesToShow, hidden, expand); + ? casesUserCmd(pluginData, context, author.user, modId!, user, modName, typesToShow, hidden, expand) + : casesModCmd(pluginData, context, author.user, modId!, mod ?? author, modName, typesToShow, hidden, expand); } diff --git a/backend/src/plugins/ModActions/functions/actualCommands/actualMassUnbanCmd.ts b/backend/src/plugins/ModActions/functions/actualCommands/actualMassUnbanCmd.ts index bfb57111..a980445c 100644 --- a/backend/src/plugins/ModActions/functions/actualCommands/actualMassUnbanCmd.ts +++ b/backend/src/plugins/ModActions/functions/actualCommands/actualMassUnbanCmd.ts @@ -4,6 +4,7 @@ import { waitForReply } from "knub/helpers"; import { CaseTypes } from "../../../../data/CaseTypes"; import { LogType } from "../../../../data/LogType"; import { getContextChannel, sendContextResponse } from "../../../../pluginUtils"; +import { MINUTES } from "../../../../utils"; import { CasesPlugin } from "../../../Cases/CasesPlugin"; import { CommonPlugin } from "../../../Common/CommonPlugin"; import { LogsPlugin } from "../../../Logs/LogsPlugin"; @@ -45,8 +46,8 @@ export async function actualMassUnbanCmd( // We'll create our own cases below and post a single "mass unbanned" log instead userIds.forEach((userId) => { // Use longer timeouts since this can take a while - ignoreEvent(pluginData, IgnoredEventType.Unban, userId, 120 * 1000); - pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_UNBAN, userId, 120 * 1000); + ignoreEvent(pluginData, IgnoredEventType.Unban, userId, 2 * MINUTES); + pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_UNBAN, userId, 2 * MINUTES); }); // Show a loading indicator since this can take a while diff --git a/backend/src/plugins/ModActions/functions/actualCommands/actualWarnCmd.ts b/backend/src/plugins/ModActions/functions/actualCommands/actualWarnCmd.ts index 15a54cfe..ac43d8df 100644 --- a/backend/src/plugins/ModActions/functions/actualCommands/actualWarnCmd.ts +++ b/backend/src/plugins/ModActions/functions/actualCommands/actualWarnCmd.ts @@ -37,7 +37,7 @@ export async function actualWarnCmd( { confirmText: "Yes", cancelText: "No", restrictToId: authorId }, ); if (!reply) { - pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Warn cancelled by moderator"); + await pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Warn cancelled by moderator"); return; } } @@ -53,13 +53,16 @@ export async function actualWarnCmd( }); if (warnResult.status === "failed") { - pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, "Failed to warn user"); + const failReason = warnResult.error ? `: ${warnResult.error}` : ""; + + await pluginData.getPlugin(CommonPlugin).sendErrorMessage(context, `Failed to warn user${failReason}`); + return; } const messageResultText = warnResult.notifyResult.text ? ` (${warnResult.notifyResult.text})` : ""; - pluginData + await pluginData .getPlugin(CommonPlugin) .sendSuccessMessage( context, diff --git a/backend/src/plugins/ModActions/functions/updateCase.ts b/backend/src/plugins/ModActions/functions/updateCase.ts index 78b5d6f4..08f11230 100644 --- a/backend/src/plugins/ModActions/functions/updateCase.ts +++ b/backend/src/plugins/ModActions/functions/updateCase.ts @@ -6,6 +6,7 @@ import { CasesPlugin } from "../../Cases/CasesPlugin"; import { CommonPlugin } from "../../Common/CommonPlugin"; import { LogsPlugin } from "../../Logs/LogsPlugin"; import { ModActionsPluginType } from "../types"; +import { handleAttachmentLinkDetectionAndGetRestriction } from "./attachmentLinkReaction"; import { formatReasonWithMessageLinkForAttachments } from "./formatReasonForAttachments"; export async function updateCase( @@ -33,6 +34,10 @@ export async function updateCase( return; } + if (await handleAttachmentLinkDetectionAndGetRestriction(pluginData, context, note)) { + return; + } + const formattedNote = await formatReasonWithMessageLinkForAttachments(pluginData, note, context, attachments); const casesPlugin = pluginData.getPlugin(CasesPlugin); diff --git a/backend/src/plugins/ModActions/functions/warnMember.ts b/backend/src/plugins/ModActions/functions/warnMember.ts index f87ce408..8ba8ee38 100644 --- a/backend/src/plugins/ModActions/functions/warnMember.ts +++ b/backend/src/plugins/ModActions/functions/warnMember.ts @@ -1,7 +1,6 @@ import { GuildMember, Snowflake } from "discord.js"; import { GuildPluginData } from "knub"; import { CaseTypes } from "../../../data/CaseTypes"; -import { getContextChannel, isContextInteraction } from "../../../pluginUtils"; import { TemplateParseError, TemplateSafeValueContainer, renderTemplate } from "../../../templateFormatter"; import { UserNotificationResult, createUserNotificationError, notifyUser, resolveUser, ucfirst } from "../../../utils"; import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects"; @@ -52,12 +51,7 @@ export async function warnMember( } if (!notifyResult.success) { - const contextIsNotInteraction = - warnOptions.retryPromptContext && !isContextInteraction(warnOptions.retryPromptContext); - const contextChannel = contextIsNotInteraction ? await getContextChannel(warnOptions.retryPromptContext!) : null; - const isValidChannel = contextIsNotInteraction && pluginData.guild.channels.resolve(contextChannel!.id); - - if (!warnOptions.retryPromptContext || !isValidChannel) { + if (!warnOptions.retryPromptContext) { return { status: "failed", error: "Failed to message user", diff --git a/backend/src/types.ts b/backend/src/types.ts index 409c4dc6..cf0967c3 100644 --- a/backend/src/types.ts +++ b/backend/src/types.ts @@ -1,29 +1,12 @@ -import { BaseConfig, Knub } from "knub"; +import { Knub } from "knub"; import z from "zod"; import { zSnowflake } from "./utils"; -export interface ZeppelinGuildConfig extends BaseConfig { - success_emoji?: string; - error_emoji?: string; - - // Deprecated - timezone?: string; - date_formats?: any; -} - export const zZeppelinGuildConfig = z.strictObject({ // From BaseConfig prefix: z.string().optional(), levels: z.record(zSnowflake, z.number()).optional(), plugins: z.record(z.string(), z.unknown()).optional(), - - // From ZeppelinGuildConfig - success_emoji: z.string().optional(), - error_emoji: z.string().optional(), - - // Deprecated - timezone: z.string().optional(), - date_formats: z.unknown().optional(), }); export type TZeppelinKnub = Knub; diff --git a/backend/src/utils/waitForInteraction.ts b/backend/src/utils/waitForInteraction.ts index c2cc659b..0cdd0437 100644 --- a/backend/src/utils/waitForInteraction.ts +++ b/backend/src/utils/waitForInteraction.ts @@ -36,11 +36,11 @@ export async function waitForButtonConfirm( const sendMethod = () => { return contextIsInteraction ? context.replied - ? context.followUp - : context.reply + ? context.followUp.bind(context) + : context.reply.bind(context) : "send" in context - ? context.send - : context.channel.send; + ? context.send.bind(context) + : context.channel.send.bind(context.channel); }; const extraParameters = contextIsInteraction ? { fetchReply: true } : {}; const message = await sendMethod()({ ...toPost, components: [row], ...extraParameters });