3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-06-08 00:05:01 +00:00

feat: update to djs 14.19.3, node 22, zod 4

This commit is contained in:
Dragory 2025-05-22 22:35:48 +00:00
parent 595e1a0556
commit 09eb8e92f2
No known key found for this signature in database
189 changed files with 1244 additions and 900 deletions

View file

@ -1,5 +1,5 @@
import { BasePluginType } from "knub";
import z from "zod";
import z from "zod/v4";
import { GuildLogs } from "../../data/GuildLogs.js";
import { GuildSavedMessages } from "../../data/GuildSavedMessages.js";
import { SavedMessage } from "../../data/entities/SavedMessage.js";

View file

@ -17,8 +17,8 @@ export async function deleteNextItem(pluginData: GuildPluginData<AutoDeletePlugi
scheduleNextDeletion(pluginData);
const channel = pluginData.guild.channels.cache.get(itemToDelete.message.channel_id as Snowflake);
if (!channel || channel.type === ChannelType.GuildCategory) {
// Channel was deleted, ignore
if (!channel || !("messages" in channel)) {
// Channel does not exist or does not support messages, ignore
return;
}

View file

@ -1,5 +1,5 @@
import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand, pluginUtils } from "knub";
import z from "zod";
import z from "zod/v4";
import { GuildAutoReactions } from "../../data/GuildAutoReactions.js";
import { GuildLogs } from "../../data/GuildLogs.js";
import { GuildSavedMessages } from "../../data/GuildSavedMessages.js";

View file

@ -1,5 +1,5 @@
import { PermissionFlagsBits, Snowflake } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { nonNullish, unique, zSnowflake } from "../../../utils.js";
import { canAssignRole } from "../../../utils/canAssignRole.js";
import { getMissingPermissions } from "../../../utils/getMissingPermissions.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { zBoundedCharacters } from "../../../utils.js";
import { CountersPlugin } from "../../Counters/CountersPlugin.js";
import { LogsPlugin } from "../../Logs/LogsPlugin.js";

View file

@ -1,5 +1,5 @@
import { Snowflake } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { LogType } from "../../../data/LogType.js";
import {
createTypedTemplateSafeValueContainer,

View file

@ -1,5 +1,5 @@
import { AnyThreadChannel } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { noop } from "../../../utils.js";
import { automodAction } from "../helpers.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import {
convertDelayStringToMS,
nonNullish,

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { nonNullish, unique, zBoundedCharacters } from "../../../utils.js";
import { LogsPlugin } from "../../Logs/LogsPlugin.js";
import { automodAction } from "../helpers.js";

View file

@ -1,6 +1,6 @@
import { PermissionsBitField, PermissionsString } from "discord.js";
import { U } from "ts-toolbelt";
import z from "zod";
import z from "zod/v4";
import { TemplateParseError, TemplateSafeValueContainer, renderTemplate } from "../../../templateFormatter.js";
import { isValidSnowflake, keys, noop, zBoundedCharacters } from "../../../utils.js";
import {

View file

@ -1,5 +1,5 @@
import { GuildTextBasedChannel, Snowflake } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { LogType } from "../../../data/LogType.js";
import { noop } from "../../../utils.js";
import { automodAction } from "../helpers.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { zBoundedCharacters } from "../../../utils.js";
import { automodAction } from "../helpers.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { asyncMap, nonNullish, resolveMember, unique, zBoundedCharacters, zSnowflake } from "../../../utils.js";
import { CaseArgs } from "../../Cases/types.js";
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { isTruthy, unique } from "../../../utils.js";
import { LogsPlugin } from "../../Logs/LogsPlugin.js";
import { automodAction } from "../helpers.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError.js";
import {
convertDelayStringToMS,

View file

@ -1,5 +1,5 @@
import { GuildFeature } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { automodAction } from "../helpers.js";
export const PauseInvitesAction = automodAction({

View file

@ -1,5 +1,5 @@
import { PermissionFlagsBits, Snowflake } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { nonNullish, unique, zSnowflake } from "../../../utils.js";
import { canAssignRole } from "../../../utils/canAssignRole.js";
import { getMissingPermissions } from "../../../utils/getMissingPermissions.js";

View file

@ -1,5 +1,5 @@
import { GuildTextBasedChannel, MessageCreateOptions, PermissionsBitField, Snowflake, User } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { TemplateParseError, TemplateSafeValueContainer, renderTemplate } from "../../../templateFormatter.js";
import {
convertDelayStringToMS,

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { MAX_COUNTER_VALUE, MIN_COUNTER_VALUE } from "../../../data/GuildCounters.js";
import { zBoundedCharacters } from "../../../utils.js";
import { CountersPlugin } from "../../Counters/CountersPlugin.js";

View file

@ -1,5 +1,5 @@
import { ChannelType, GuildTextBasedChannel, Snowflake } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { convertDelayStringToMS, isDiscordAPIError, zDelayString, zSnowflake } from "../../../utils.js";
import { LogsPlugin } from "../../Logs/LogsPlugin.js";
import { automodAction } from "../helpers.js";

View file

@ -1,5 +1,5 @@
import { ChannelType, GuildTextThreadCreateOptions, ThreadAutoArchiveDuration, ThreadChannel } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { TemplateParseError, TemplateSafeValueContainer, renderTemplate } from "../../../templateFormatter.js";
import { MINUTES, convertDelayStringToMS, noop, zBoundedCharacters, zDelayString } from "../../../utils.js";
import { savedMessageToTemplateSafeSavedMessage, userToTemplateSafeUser } from "../../../utils/templateSafeObjects.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { asyncMap, nonNullish, resolveMember, unique, zBoundedCharacters, zSnowflake } from "../../../utils.js";
import { CaseArgs } from "../../Cases/types.js";
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { MINUTES, SECONDS } from "../../utils.js";
export const RECENT_SPAM_EXPIRY_TIME = 10 * SECONDS;

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { SavedMessage } from "../../../data/entities/SavedMessage.js";
import { humanizeDurationShort } from "../../../humanizeDuration.js";
import { getBaseUrl } from "../../../pluginUtils.js";

View file

@ -1,5 +1,5 @@
import { GuildPluginData } from "knub";
import z, { ZodTypeAny } from "zod";
import z, { ZodTypeAny } from "zod/v4";
import { Awaitable } from "../../utils/typeUtils.js";
import { AutomodContext, AutomodPluginType } from "./types.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { automodTrigger } from "../helpers.js";
interface AntiraidLevelTriggerResult {}

View file

@ -1,5 +1,5 @@
import { Snowflake } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { verboseChannelMention } from "../../../utils.js";
import { automodTrigger } from "../helpers.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { automodTrigger } from "../helpers.js";
// tslint:disable-next-line:no-empty-interface

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { automodTrigger } from "../helpers.js";
// tslint:disable-next-line

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { automodTrigger } from "../helpers.js";
interface ExampleMatchResultType {

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { automodTrigger } from "../helpers.js";
// tslint:disable-next-line:no-empty-interface

View file

@ -1,6 +1,6 @@
import { escapeInlineCode, Snowflake } from "discord.js";
import { extname } from "path";
import z from "zod";
import z from "zod/v4";
import { asSingleLine, messageSummary, verboseChannelMention } from "../../../utils.js";
import { automodTrigger } from "../helpers.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { getInviteCodesInString, GuildInvite, isGuildInvite, resolveInvite, zSnowflake } from "../../../utils.js";
import { getTextMatchPartialSummary } from "../functions/getTextMatchPartialSummary.js";
import { MatchableTextType, matchMultipleTextTypesOnMessage } from "../functions/matchMultipleTextTypesOnMessage.js";

View file

@ -1,5 +1,5 @@
import { escapeInlineCode } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { allowTimeout } from "../../../RegExpRunner.js";
import { phishermanDomainIsSafe } from "../../../data/Phisherman.js";
import { getUrlsInString, zRegex } from "../../../utils.js";

View file

@ -1,5 +1,5 @@
import { escapeInlineCode } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { asSingleLine, messageSummary, verboseChannelMention } from "../../../utils.js";
import { automodTrigger } from "../helpers.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { allowTimeout } from "../../../RegExpRunner.js";
import { zRegex } from "../../../utils.js";
import { mergeRegexes } from "../../../utils/mergeRegexes.js";

View file

@ -1,5 +1,5 @@
import escapeStringRegexp from "escape-string-regexp";
import z from "zod";
import z from "zod/v4";
import { normalizeText } from "../../../utils/normalizeText.js";
import { stripMarkdown } from "../../../utils/stripMarkdown.js";
import { getTextMatchPartialSummary } from "../functions/getTextMatchPartialSummary.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { convertDelayStringToMS, zDelayString } from "../../../utils.js";
import { automodTrigger } from "../helpers.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { convertDelayStringToMS, zDelayString } from "../../../utils.js";
import { RecentActionType } from "../constants.js";
import { findRecentSpam } from "../functions/findRecentSpam.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { automodTrigger } from "../helpers.js";
const configSchema = z.strictObject({});

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { automodTrigger } from "../helpers.js";
// tslint:disable-next-line:no-empty-interface

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { automodTrigger } from "../helpers.js";
// tslint:disable-next-line:no-empty-interface

View file

@ -1,5 +1,5 @@
import { Snowflake } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { renderUsername, zSnowflake } from "../../../utils.js";
import { consumeIgnoredRoleChange } from "../functions/ignoredRoleChanges.js";
import { automodTrigger } from "../helpers.js";

View file

@ -1,5 +1,5 @@
import { Snowflake } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { renderUsername, zSnowflake } from "../../../utils.js";
import { consumeIgnoredRoleChange } from "../functions/ignoredRoleChanges.js";
import { automodTrigger } from "../helpers.js";

View file

@ -1,5 +1,5 @@
import { User, escapeBold, type Snowflake } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { renderUsername } from "../../../utils.js";
import { automodTrigger } from "../helpers.js";

View file

@ -1,5 +1,5 @@
import { User, escapeBold, type Snowflake } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { renderUsername } from "../../../utils.js";
import { automodTrigger } from "../helpers.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { convertDelayStringToMS, zDelayString } from "../../../utils.js";
import { RecentActionType } from "../constants.js";
import { findRecentSpam } from "../functions/findRecentSpam.js";

View file

@ -1,5 +1,5 @@
import { User, escapeBold, type Snowflake } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { renderUsername } from "../../../utils.js";
import { automodTrigger } from "../helpers.js";

View file

@ -1,5 +1,5 @@
import { User, escapeBold, type Snowflake } from "discord.js";
import z from "zod";
import z from "zod/v4";
import { renderUsername } from "../../../utils.js";
import { automodTrigger } from "../helpers.js";

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { automodTrigger } from "../helpers.js";
// tslint:disable-next-line:no-empty-interface

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { automodTrigger } from "../helpers.js";
// tslint:disable-next-line:no-empty-interface

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { automodTrigger } from "../helpers.js";
// tslint:disable-next-line:no-empty-interface

View file

@ -1,6 +1,6 @@
import { GuildMember, GuildTextBasedChannel, PartialGuildMember, ThreadChannel, User } from "discord.js";
import { BasePluginType, CooldownManager, pluginUtils } from "knub";
import z from "zod";
import z from "zod/v4";
import { Queue } from "../../Queue.js";
import { RegExpRunner } from "../../RegExpRunner.js";
import { GuildAntiraidLevels } from "../../data/GuildAntiraidLevels.js";

View file

@ -1,5 +1,5 @@
import { BasePluginType, globalPluginEventListener, globalPluginMessageCommand } from "knub";
import z from "zod";
import z from "zod/v4";
import { AllowedGuilds } from "../../data/AllowedGuilds.js";
import { ApiPermissionAssignments } from "../../data/ApiPermissionAssignments.js";
import { Configs } from "../../data/Configs.js";

View file

@ -1,4 +1,4 @@
import { escapeCodeBlock, MessageCreateOptions, MessageEditOptions } from "discord.js";
import { escapeCodeBlock, InteractionEditReplyOptions, InteractionReplyOptions, MessageCreateOptions, MessageEditOptions } from "discord.js";
import { GuildPluginData } from "knub";
import moment from "moment-timezone";
import { CaseTypes } from "../../../data/CaseTypes.js";
@ -14,7 +14,7 @@ export async function getCaseEmbed(
caseOrCaseId: Case | number,
requestMemberId?: string,
noOriginalCaseLink?: boolean,
): Promise<MessageCreateOptions & MessageEditOptions> {
): Promise<MessageCreateOptions & MessageEditOptions & InteractionReplyOptions & InteractionEditReplyOptions> {
const theCase = await pluginData.state.cases.with("notes").find(resolveCaseId(caseOrCaseId));
if (!theCase) {
throw new Error("Unknown case");

View file

@ -1,6 +1,6 @@
import { BasePluginType } from "knub";
import { U } from "ts-toolbelt";
import z from "zod";
import z from "zod/v4";
import { CaseNameToType, CaseTypes } from "../../data/CaseTypes.js";
import { GuildArchives } from "../../data/GuildArchives.js";
import { GuildCases } from "../../data/GuildCases.js";

View file

@ -1,5 +1,5 @@
import { BasePluginType } from "knub";
import z from "zod";
import z from "zod/v4";
import { RegExpRunner } from "../../RegExpRunner.js";
import { GuildLogs } from "../../data/GuildLogs.js";
import { GuildSavedMessages } from "../../data/GuildSavedMessages.js";

View file

@ -1,5 +1,5 @@
import { guildPlugin } from "knub";
import z from "zod";
import z from "zod/v4";
import { CommonPlugin } from "../Common/CommonPlugin.js";
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin.js";
import { ArchiveChannelCmd } from "./commands/ArchiveChannelCmd.js";

View file

@ -1,16 +1,18 @@
import {
Attachment,
ChatInputCommandInteraction,
InteractionResponse,
Message,
MessageCreateOptions,
MessageMentionOptions,
ModalSubmitInteraction,
SendableChannels,
TextBasedChannel,
User,
} from "discord.js";
import { PluginOptions, guildPlugin } from "knub";
import { logger } from "../../logger.js";
import { isContextInteraction, sendContextResponse } from "../../pluginUtils.js";
import { GenericCommandSource, isContextInteraction, sendContextResponse } from "../../pluginUtils.js";
import { errorMessage, successMessage } from "../../utils.js";
import { getErrorEmoji, getSuccessEmoji } from "./functions/getEmoji.js";
import { CommonPluginType, zCommonConfig } from "./types.js";
@ -34,101 +36,39 @@ export const CommonPlugin = guildPlugin<CommonPluginType>()({
getErrorEmoji,
sendSuccessMessage: async (
context: TextBasedChannel | Message | User | ChatInputCommandInteraction,
context: GenericCommandSource | SendableChannels,
body: string,
allowedMentions?: MessageMentionOptions,
responseInteraction?: ModalSubmitInteraction,
responseInteraction?: never,
ephemeral = true,
): Promise<Message | undefined> => {
) => {
const emoji = getSuccessEmoji(pluginData);
const formattedBody = successMessage(body, emoji);
const content: MessageCreateOptions = allowedMentions
const content = allowedMentions
? { content: formattedBody, allowedMentions }
: { content: formattedBody };
if (responseInteraction) {
await responseInteraction
.editReply({ content: formattedBody, embeds: [], components: [] })
.catch((err) => logger.error(`Interaction reply failed: ${err}`));
return;
if ("isSendable" in context) {
return context.send(content);
}
if (!isContextInteraction(context)) {
// noinspection TypeScriptValidateJSTypes
return sendContextResponse(context, { ...content }) // Force line break
.catch((err) => {
const channelInfo =
"guild" in context && context.guild ? `${context.id} (${context.guild.id})` : context.id;
logger.warn(`Failed to send success message to ${channelInfo}): ${err.code} ${err.message}`);
return undefined;
});
}
const replyMethod = context.replied || context.deferred ? "editReply" : "reply";
return context[replyMethod]({
content: formattedBody,
embeds: [],
components: [],
fetchReply: true,
ephemeral,
}).catch((err) => {
logger.error(`Context reply failed: ${err}`);
return undefined;
}) as Promise<Message>;
return sendContextResponse(context, content, ephemeral);
},
sendErrorMessage: async (
context: TextBasedChannel | Message | User | ChatInputCommandInteraction,
context: GenericCommandSource | SendableChannels,
body: string,
allowedMentions?: MessageMentionOptions,
responseInteraction?: ModalSubmitInteraction,
responseInteraction?: never,
ephemeral = true,
): Promise<Message | undefined> => {
) => {
const emoji = getErrorEmoji(pluginData);
const formattedBody = errorMessage(body, emoji);
const content: MessageCreateOptions = allowedMentions
const content = allowedMentions
? { content: formattedBody, allowedMentions }
: { content: formattedBody };
if (responseInteraction) {
await responseInteraction
.editReply({ content: formattedBody, embeds: [], components: [] })
.catch((err) => logger.error(`Interaction reply failed: ${err}`));
return;
if ("isSendable" in context) {
return context.send(content);
}
if (!isContextInteraction(context)) {
// noinspection TypeScriptValidateJSTypes
return sendContextResponse(context, { ...content }) // Force line break
.catch((err) => {
const channelInfo =
"guild" in context && context.guild ? `${context.id} (${context.guild.id})` : context.id;
logger.warn(`Failed to send error message to ${channelInfo}): ${err.code} ${err.message}`);
return undefined;
});
}
const replyMethod = context.replied || context.deferred ? "editReply" : "reply";
return context[replyMethod]({
content: formattedBody,
embeds: [],
components: [],
fetchReply: true,
ephemeral,
}).catch((err) => {
logger.error(`Context reply failed: ${err}`);
return undefined;
}) as Promise<Message>;
return sendContextResponse(context, content, ephemeral);
},
storeAttachmentsAsMessage: async (attachments: Attachment[], backupChannel?: TextBasedChannel | null) => {
@ -142,8 +82,13 @@ export const CommonPlugin = guildPlugin<CommonPluginType>()({
"Cannot store attachments: no attachment storing channel configured, and no backup channel passed",
);
}
if (!channel.isSendable()) {
throw new Error(
"Passed attachment storage channel is not sendable",
);
}
return channel!.send({
return channel.send({
content: `Storing ${attachments.length} attachment${attachments.length === 1 ? "" : "s"}`,
files: attachments.map((a) => a.url),
});

View file

@ -1,5 +1,5 @@
import { BasePluginType } from "knub";
import z from "zod";
import z from "zod/v4";
export const zCommonConfig = z.strictObject({
success_emoji: z.string(),

View file

@ -1,5 +1,5 @@
import { BasePluginType, CooldownManager, guildPluginEventListener } from "knub";
import z from "zod";
import z from "zod/v4";
import { GuildLogs } from "../../data/GuildLogs.js";
import { zBoundedCharacters, zSnowflake } from "../../utils.js";

View file

@ -18,7 +18,7 @@ export async function cleanAction(
amount: number,
target: string,
targetMessage: Message,
targetChannel: string,
targetChannelId: string,
interaction: ModalSubmitInteraction,
) {
const executingMember = await pluginData.guild.members.fetch(interaction.user.id);
@ -28,12 +28,20 @@ export async function cleanAction(
});
const utility = pluginData.getPlugin(UtilityPlugin);
if (!userCfg.can_use || !(await utility.hasPermission(executingMember, targetChannel, "can_clean"))) {
if (!userCfg.can_use || !(await utility.hasPermission(executingMember, targetChannelId, "can_clean"))) {
await interaction
.editReply({ content: "Cannot clean: insufficient permissions", embeds: [], components: [] })
.catch((err) => logger.error(`Clean interaction reply failed: ${err}`));
return;
}
const targetChannel = await pluginData.guild.channels.fetch(targetChannelId);
if (!targetChannel?.isTextBased()) {
await interaction
.editReply({ content: "Cannot clean: target channel is not a text channel", embeds: [], components: [] })
.catch((err) => logger.error(`Clean interaction reply failed: ${err}`));
return;
}
await interaction
.editReply({
@ -43,7 +51,21 @@ export async function cleanAction(
})
.catch((err) => logger.error(`Clean interaction reply failed: ${err}`));
await utility.clean({ count: amount, channel: targetChannel, "response-interaction": interaction }, targetMessage);
const fetchMessagesResult = await utility.fetchChannelMessagesToClean(targetChannel, {
count: amount,
beforeId: targetMessage.id,
});
if ("error" in fetchMessagesResult) {
interaction.editReply(fetchMessagesResult.error);
return;
}
if (fetchMessagesResult.messages.length > 0) {
await utility.cleanMessages(targetChannel, fetchMessagesResult.messages, interaction.user);
interaction.editReply(`Cleaned ${fetchMessagesResult.messages.length} ${fetchMessagesResult.messages.length === 1 ? "message" : "messages"}`);
} else {
interaction.editReply("No messages to clean");
}
}
export async function launchCleanActionModal(

View file

@ -1,6 +1,6 @@
import { APIEmbed, Awaitable } from "discord.js";
import { BasePluginType } from "knub";
import z from "zod";
import z from "zod/v4";
import { GuildCases } from "../../data/GuildCases.js";
export const zContextMenusConfig = z.strictObject({

View file

@ -1,7 +1,7 @@
import { EventEmitter } from "events";
import { PluginOptions, guildPlugin } from "knub";
import { GuildCounters } from "../../data/GuildCounters.js";
import { CounterTrigger, parseCounterConditionString } from "../../data/entities/CounterTrigger.js";
import { buildCounterConditionString, CounterTrigger, getReverseCounterComparisonOp, parseCounterConditionString } from "../../data/entities/CounterTrigger.js";
import { makePublicFn } from "../../pluginUtils.js";
import { MINUTES, convertDelayStringToMS } from "../../utils.js";
import { CommonPlugin } from "../Common/CommonPlugin.js";
@ -89,7 +89,7 @@ export const CountersPlugin = guildPlugin<CountersPluginType>()({
const { state, guild } = pluginData;
state.counters = new GuildCounters(guild.id);
state.events = new EventEmitter();
state.events = new EventEmitter() as any;
state.counterTriggersByCounterId = new Map();
const activeTriggerIds: number[] = [];
@ -107,7 +107,8 @@ export const CountersPlugin = guildPlugin<CountersPluginType>()({
// Initialize triggers
for (const [triggerName, trigger] of Object.entries(counter.triggers)) {
const parsedCondition = parseCounterConditionString(trigger.condition)!;
const parsedReverseCondition = parseCounterConditionString(trigger.reverse_condition)!;
const rawReverseCondition = trigger.reverse_condition || buildCounterConditionString(getReverseCounterComparisonOp(parsedCondition[0]), parsedCondition[1]);
const parsedReverseCondition = parseCounterConditionString(rawReverseCondition)!;
const counterTrigger = await state.counters.initCounterTrigger(
dbCounter.id,
triggerName,
@ -167,6 +168,6 @@ export const CountersPlugin = guildPlugin<CountersPluginType>()({
}
}
state.events.removeAllListeners();
(state.events as any).removeAllListeners();
},
});

View file

@ -13,5 +13,5 @@ export function getPrettyNameForCounterTrigger(
}
const trigger = counter.triggers[triggerName];
return trigger ? trigger.pretty_name || trigger.name : "Unknown Counter Trigger";
return trigger ? trigger.pretty_name || triggerName : "Unknown Counter Trigger";
}

View file

@ -1,6 +1,6 @@
import { EventEmitter } from "events";
import { BasePluginType, pluginUtils } from "knub";
import z from "zod";
import z from "zod/v4";
import { GuildCounters, MAX_COUNTER_VALUE, MIN_COUNTER_VALUE } from "../../data/GuildCounters.js";
import {
CounterTrigger,
@ -18,10 +18,6 @@ const MAX_TRIGGERS_PER_COUNTER = 5;
export const zTrigger = z
.strictObject({
// Dummy type because name gets replaced by the property key in transform()
name: z
.never()
.optional()
.transform(() => ""),
pretty_name: zBoundedCharacters(0, 100).nullable().default(null),
condition: zBoundedCharacters(1, 64).refine((str) => parseCounterConditionString(str) !== null, {
message: "Invalid counter trigger condition",
@ -31,28 +27,9 @@ export const zTrigger = z
message: "Invalid counter trigger reverse condition",
})
.optional(),
})
.transform((val, ctx) => {
const ruleName = String(ctx.path[ctx.path.length - 1]).trim();
let reverseCondition = val.reverse_condition;
if (!reverseCondition) {
const parsedCondition = parseCounterConditionString(val.condition)!;
reverseCondition = buildCounterConditionString(
getReverseCounterComparisonOp(parsedCondition[0]),
parsedCondition[1],
);
}
return {
...val,
name: ruleName,
reverse_condition: reverseCondition,
};
});
const zTriggerFromString = zBoundedCharacters(0, 100).transform((val, ctx) => {
const ruleName = String(ctx.path[ctx.path.length - 1]).trim();
const parsedCondition = parseCounterConditionString(val);
if (!parsedCondition) {
ctx.addIssue({
@ -62,7 +39,6 @@ const zTriggerFromString = zBoundedCharacters(0, 100).transform((val, ctx) => {
return z.NEVER;
}
return {
name: ruleName,
pretty_name: null,
condition: buildCounterConditionString(parsedCondition[0], parsedCondition[1]),
reverse_condition: buildCounterConditionString(

View file

@ -1,5 +1,5 @@
import { GuildPluginData } from "knub";
import z from "zod";
import z from "zod/v4";
import { canActOn } from "../../../pluginUtils.js";
import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter.js";
import { resolveMember, zSnowflake } from "../../../utils.js";

View file

@ -1,5 +1,5 @@
import { GuildPluginData } from "knub";
import z from "zod";
import z from "zod/v4";
import { CaseTypes } from "../../../data/CaseTypes.js";
import { renderTemplate, TemplateSafeValueContainer } from "../../../templateFormatter.js";
import { zBoundedCharacters, zSnowflake } from "../../../utils.js";

View file

@ -1,6 +1,6 @@
import { Snowflake } from "discord.js";
import { GuildPluginData } from "knub";
import z from "zod";
import z from "zod/v4";
import { TemplateSafeValueContainer } from "../../../templateFormatter.js";
import { convertDelayStringToMS, noop, zDelayString, zSnowflake } from "../../../utils.js";
import { ActionError } from "../ActionError.js";

View file

@ -1,6 +1,6 @@
import { Snowflake } from "discord.js";
import { GuildPluginData } from "knub";
import z from "zod";
import z from "zod/v4";
import { TemplateSafeValueContainer } from "../../../templateFormatter.js";
import { zSnowflake } from "../../../utils.js";
import { ActionError } from "../ActionError.js";

View file

@ -1,6 +1,6 @@
import { Snowflake, TextChannel } from "discord.js";
import { GuildPluginData } from "knub";
import z from "zod";
import z from "zod/v4";
import { TemplateSafeValueContainer, renderTemplate } from "../../../templateFormatter.js";
import { zBoundedCharacters, zSnowflake } from "../../../utils.js";
import { ActionError } from "../ActionError.js";

View file

@ -1,6 +1,6 @@
import { Snowflake, VoiceChannel } from "discord.js";
import { GuildPluginData } from "knub";
import z from "zod";
import z from "zod/v4";
import { canActOn } from "../../../pluginUtils.js";
import { TemplateSafeValueContainer, renderTemplate } from "../../../templateFormatter.js";
import { resolveMember, zSnowflake } from "../../../utils.js";

View file

@ -1,6 +1,6 @@
import { PermissionsBitField, PermissionsString, Snowflake } from "discord.js";
import { GuildPluginData } from "knub";
import z from "zod";
import z from "zod/v4";
import { TemplateSafeValueContainer } from "../../../templateFormatter.js";
import { zSnowflake } from "../../../utils.js";
import { ActionError } from "../ActionError.js";

View file

@ -38,7 +38,7 @@ export async function runEvent(
} catch (e) {
if (e instanceof ActionError) {
if (event.trigger.type === "command") {
void pluginData.state.common.sendErrorMessage((eventData.msg as Message).channel, e.message);
void pluginData.state.common.sendErrorMessage(eventData.msg, e.message);
} else {
// TODO: Where to log action errors from other kinds of triggers?
}

View file

@ -1,5 +1,5 @@
import { BasePluginType, pluginUtils } from "knub";
import z from "zod";
import z from "zod/v4";
import { zBoundedCharacters, zBoundedRecord } from "../../utils.js";
import { CommonPlugin } from "../Common/CommonPlugin.js";
import { zAddRoleAction } from "./actions/addRoleAction.js";

View file

@ -1,3 +1,3 @@
import { z } from "zod";
import { z } from "zod/v4";
export const zGuildAccessMonitorConfig = z.strictObject({});

View file

@ -1,5 +1,5 @@
import { globalPlugin } from "knub";
import z from "zod";
import z from "zod/v4";
import { Configs } from "../../data/Configs.js";
import { reloadChangedGuilds } from "./functions/reloadChangedGuilds.js";
import { GuildConfigReloaderPluginType } from "./types.js";

View file

@ -1,5 +1,5 @@
import { BasePluginType } from "knub";
import { z } from "zod";
import { z } from "zod/v4";
import { Configs } from "../../data/Configs.js";
import Timeout = NodeJS.Timeout;

View file

@ -1,6 +1,6 @@
import { Guild } from "discord.js";
import { guildPlugin, guildPluginEventListener } from "knub";
import z from "zod";
import z from "zod/v4";
import { AllowedGuilds } from "../../data/AllowedGuilds.js";
import { ApiPermissionAssignments } from "../../data/ApiPermissionAssignments.js";
import { MINUTES } from "../../utils.js";

View file

@ -1,5 +1,5 @@
import { BasePluginType } from "knub";
import { z } from "zod";
import { z } from "zod/v4";
export const zGuildInfoSaverConfig = z.strictObject({});

View file

@ -1,5 +1,5 @@
import { guildPlugin } from "knub";
import z from "zod";
import z from "zod/v4";
import { GuildMemberCache } from "../../data/GuildMemberCache.js";
import { makePublicFn } from "../../pluginUtils.js";
import { SECONDS } from "../../utils.js";

View file

@ -1,5 +1,5 @@
import { BasePluginType } from "knub";
import { z } from "zod";
import { z } from "zod/v4";
import { GuildMemberCache } from "../../data/GuildMemberCache.js";
export const zGuildMemberCacheConfig = z.strictObject({});

View file

@ -1,5 +1,5 @@
import { PluginOptions, guildPlugin } from "knub";
import z from "zod";
import z from "zod/v4";
import { Queue } from "../../Queue.js";
import { Webhooks } from "../../data/Webhooks.js";
import { makePublicFn } from "../../pluginUtils.js";

View file

@ -1,6 +1,6 @@
import { WebhookClient } from "discord.js";
import { BasePluginType } from "knub";
import { z } from "zod";
import { z } from "zod/v4";
import { Queue } from "../../Queue.js";
import { Webhooks } from "../../data/Webhooks.js";

View file

@ -10,7 +10,7 @@ export const ListFollowCmd = locateUserCmd({
permission: "can_alert",
async run({ message: msg, pluginData }) {
const alerts = await pluginData.state.alerts.getAlertsByRequestorId(msg.member.id);
const alerts = await pluginData.state.alerts.getAlertsByRequestorId(msg.author.id);
if (alerts.length === 0) {
void pluginData.state.common.sendErrorMessage(msg, "You have no active alerts!");
return;
@ -41,7 +41,7 @@ export const DeleteFollowCmd = locateUserCmd({
},
async run({ message: msg, args, pluginData }) {
const alerts = await pluginData.state.alerts.getAlertsByRequestorId(msg.member.id);
const alerts = await pluginData.state.alerts.getAlertsByRequestorId(msg.author.id);
alerts.sort(sorter("expires_at"));
if (args.num > alerts.length || args.num <= 0) {

View file

@ -13,6 +13,6 @@ export const WhereCmd = locateUserCmd({
},
async run({ message: msg, args, pluginData }) {
sendWhere(pluginData, args.member, msg.channel, `<@${msg.member.id}> | `);
sendWhere(pluginData, args.member, msg.channel, `<@${msg.author.id}> | `);
},
});

View file

@ -1,5 +1,5 @@
import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand, pluginUtils } from "knub";
import z from "zod";
import z from "zod/v4";
import { GuildVCAlerts } from "../../data/GuildVCAlerts.js";
import { CommonPlugin } from "../Common/CommonPlugin.js";

View file

@ -1,5 +1,5 @@
import { CooldownManager, PluginOptions, guildPlugin } from "knub";
import DefaultLogMessages from "../../data/DefaultLogMessages.json" assert { type: "json" };
import DefaultLogMessages from "../../data/DefaultLogMessages.json" with { type: "json" };
import { GuildArchives } from "../../data/GuildArchives.js";
import { GuildCases } from "../../data/GuildCases.js";
import { GuildLogs } from "../../data/GuildLogs.js";

View file

@ -49,7 +49,6 @@ export const LogsGuildBanRemoveEvt = logsEvt({
user.id,
);
const mod = relevantAuditLogEntry?.executor ?? null;
logMemberUnban(pluginData, {
mod,
userId: user.id,

View file

@ -1,4 +1,4 @@
import { User } from "discord.js";
import { PartialUser, User } from "discord.js";
import { GuildPluginData } from "knub";
import { LogType } from "../../../data/LogType.js";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter.js";
@ -8,7 +8,7 @@ import { LogsPluginType } from "../types.js";
import { log } from "../util/log.js";
export interface LogMemberBanData {
mod: User | UnknownUser | null;
mod: User | UnknownUser | PartialUser | null;
user: User | UnknownUser;
caseNumber: number;
reason: string;

View file

@ -1,4 +1,4 @@
import { Snowflake, User } from "discord.js";
import { PartialUser, Snowflake, User } from "discord.js";
import { GuildPluginData } from "knub";
import { LogType } from "../../../data/LogType.js";
import { createTypedTemplateSafeValueContainer } from "../../../templateFormatter.js";
@ -8,7 +8,7 @@ import { LogsPluginType } from "../types.js";
import { log } from "../util/log.js";
export interface LogMemberUnbanData {
mod: User | UnknownUser | null;
mod: User | UnknownUser | PartialUser | null;
userId: Snowflake;
caseNumber: number;
reason: string;

View file

@ -1,5 +1,5 @@
import { BasePluginType, CooldownManager, guildPluginEventListener } from "knub";
import { z } from "zod";
import { z } from "zod/v4";
import { RegExpRunner } from "../../RegExpRunner.js";
import { GuildArchives } from "../../data/GuildArchives.js";
import { GuildCases } from "../../data/GuildCases.js";
@ -29,6 +29,7 @@ const MAX_BATCH_TIME = 5000;
// A bit of a workaround so we can pass LogType keys to z.enum()
const logTypes = Object.keys(LogType) as [keyof typeof LogType, ...Array<keyof typeof LogType>];
const zLogFormats = z.record(z.enum(logTypes), zMessageContent);
type TLogFormats = z.infer<typeof zLogFormats>;
const zLogChannel = z.strictObject({
include: z.array(zBoundedCharacters(1, 255)).default([]),
@ -42,7 +43,7 @@ const zLogChannel = z.strictObject({
excluded_threads: z.array(zSnowflake).nullable().default(null),
exclude_bots: z.boolean().default(false),
excluded_roles: z.array(zSnowflake).nullable().default(null),
format: zLogFormats.default({}),
format: zLogFormats.default({} as TLogFormats),
timestamp_format: z.string().nullable().default(null),
include_embed_timestamp: z.boolean().nullable().default(null),
});

View file

@ -1,5 +1,5 @@
import { BasePluginType, guildPluginEventListener, guildPluginMessageCommand, pluginUtils } from "knub";
import z from "zod";
import z from "zod/v4";
import { GuildSavedMessages } from "../../data/GuildSavedMessages.js";
import { CommonPlugin } from "../Common/CommonPlugin.js";

View file

@ -31,8 +31,10 @@ export const AddCaseMsgCmd = modActionsMsgCmd({
return;
}
const member = msg.member || await msg.guild.members.fetch(msg.author.id);
// The moderator who did the action is the message author or, if used, the specified -mod
let mod = msg.member;
let mod = member;
if (args.mod) {
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
pluginData.state.common.sendErrorMessage(msg, "You don't have permission to use -mod");
@ -52,7 +54,7 @@ export const AddCaseMsgCmd = modActionsMsgCmd({
actualAddCaseCmd(
pluginData,
msg,
msg.member,
member,
mod,
[...msg.attachments.values()],
user,

View file

@ -41,8 +41,10 @@ export const BanMsgCmd = modActionsMsgCmd({
return;
}
const member = msg.member || await msg.guild.members.fetch(msg.author.id);
// The moderator who did the action is the message author or, if used, the specified -mod
let mod = msg.member;
let mod = member;
if (args.mod) {
if (!(await hasPermission(pluginData, "can_act_as_other", { message: msg }))) {
pluginData.state.common.sendErrorMessage(msg, "You don't have permission to use -mod");
@ -67,7 +69,7 @@ export const BanMsgCmd = modActionsMsgCmd({
args["time"] ? args["time"] : null,
args.reason || "",
[...msg.attachments.values()],
msg.member,
member,
mod,
contactMethods,
);

View file

@ -19,7 +19,7 @@ export async function actualCaseCmd(
}
const casesPlugin = pluginData.getPlugin(CasesPlugin);
const embed = await casesPlugin.getCaseEmbed(theCase.id, authorId);
const content = await casesPlugin.getCaseEmbed(theCase.id, authorId);
void sendContextResponse(context, { ...embed, ephemeral: show !== true });
void sendContextResponse(context, content, show !== true);
}

View file

@ -1,4 +1,5 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes.js";
import { resolveMessageMember } from "../../../../pluginUtils.js";
import { modActionsMsgCmd } from "../../types.js";
import { actualCasesCmd } from "./actualCasesCmd.js";
@ -29,12 +30,13 @@ export const CasesModMsgCmd = modActionsMsgCmd({
],
async run({ pluginData, message: msg, args }) {
const member = await resolveMessageMember(msg);
return actualCasesCmd(
pluginData,
msg,
args.mod,
null,
msg.member,
member,
args.notes,
args.warns,
args.mutes,

View file

@ -1,4 +1,5 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes.js";
import { resolveMessageMember } from "../../../../pluginUtils.js";
import { resolveMember, resolveUser, UnknownUser } from "../../../../utils.js";
import { modActionsMsgCmd } from "../../types.js";
import { actualCasesCmd } from "./actualCasesCmd.js";
@ -40,13 +41,15 @@ export const CasesUserMsgCmd = modActionsMsgCmd({
pluginData.state.common.sendErrorMessage(msg, `User not found`);
return;
}
const member = await resolveMessageMember(msg);
return actualCasesCmd(
pluginData,
msg,
args.mod,
user,
msg.member,
member,
args.notes,
args.warns,
args.mutes,

View file

@ -41,8 +41,8 @@ async function sendExpandedCases(
const casesPlugin = pluginData.getPlugin(CasesPlugin);
for (const theCase of cases) {
const embed = await casesPlugin.getCaseEmbed(theCase.id);
await sendContextResponse(context, { ...embed, ephemeral: !show });
const content = await casesPlugin.getCaseEmbed(theCase.id);
await sendContextResponse(context, content, !show);
}
}

View file

@ -1,4 +1,5 @@
import { commandTypeHelpers as ct } from "../../../../commandTypes.js";
import { resolveMessageMember } from "../../../../pluginUtils.js";
import { trimLines } from "../../../../utils.js";
import { modActionsMsgCmd } from "../../types.js";
import { actualDeleteCaseCmd } from "./actualDeleteCaseCmd.js";
@ -18,6 +19,7 @@ export const DeleteCaseMsgCmd = modActionsMsgCmd({
},
async run({ pluginData, message, args }) {
actualDeleteCaseCmd(pluginData, message, message.member, args.caseNumber, args.force);
const member = await resolveMessageMember(message);
actualDeleteCaseCmd(pluginData, message, member, args.caseNumber, args.force);
},
});

Some files were not shown because too many files have changed in this diff Show more