3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-06-06 15:45:03 +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

2
.nvmrc
View file

@ -1 +1 @@
18
22

View file

@ -29,7 +29,7 @@
"migrate-rollback-prod": "npm run migrate-rollback",
"migrate-rollback-dev": "npm run build && npm run migrate-rollback",
"validate-active-configs": "node --enable-source-maps dist/validateActiveConfigs.js > ../config-errors.txt",
"export-config-json-schema": "node --enable-source-maps dist/exportSchemas.js > ../config-schema.json",
"export-config-json-schema": "node --enable-source-maps dist/exportSchemas.js",
"test": "npm run build && npm run run-tests",
"run-tests": "ava",
"test-watch": "tsc-watch --build --onSuccess \"npx ava\""
@ -41,7 +41,7 @@
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"deep-diff": "^1.0.2",
"discord.js": "^14.14.1",
"discord.js": "^14.19.3",
"dotenv": "^4.0.0",
"emoji-regex": "^8.0.0",
"escape-string-regexp": "^1.0.5",
@ -49,7 +49,7 @@
"fp-ts": "^2.0.1",
"humanize-duration": "^3.15.0",
"js-yaml": "^4.1.0",
"knub": "^32.0.0-next.21",
"knub": "^32.0.0-next.23",
"knub-command-manager": "^9.1.0",
"last-commit-log": "^2.1.0",
"lodash-es": "^4.17.21",
@ -76,7 +76,7 @@
"uuid": "^9.0.0",
"yawn-yaml": "github:dragory/yawn-yaml#string-number-fix-build",
"zlib-sync": "^0.1.7",
"zod": "^3.7.2"
"zod": "^3.25.17"
},
"devDependencies": {
"@types/cors": "^2.8.5",

View file

@ -1,4 +1,4 @@
import { EventEmitter } from "events";
import { EventEmitter } from "node:events";
import { CooldownManager } from "knub";
import { RegExpWorker, TimeoutError } from "regexp-worker";
import { MINUTES, SECONDS } from "./utils.js";

View file

@ -46,7 +46,7 @@ export class SimpleCache<T = any> {
});
if (this.maxItems && this.store.size > this.maxItems) {
const keyToDelete = this.store.keys().next().value;
const keyToDelete = this.store.keys().next().value!;
this.store.delete(keyToDelete);
}
}

View file

@ -1,97 +1,90 @@
import express from "express";
import z from "zod";
import z from "zod/v4";
import { availableGuildPlugins } from "../plugins/availablePlugins.js";
import { ZeppelinGuildPluginInfo } from "../types.js";
import { indentLines } from "../utils.js";
import { notFound } from "./responses.js";
function isZodObject(schema: z.ZodTypeAny): schema is z.ZodObject<any> {
return schema._def.typeName === "ZodObject";
function isZodObject(schema: z.ZodType): schema is z.ZodObject<any> {
return schema.def.type === "object";
}
function isZodRecord(schema: z.ZodTypeAny): schema is z.ZodRecord<any> {
return schema._def.typeName === "ZodRecord";
function isZodRecord(schema: z.ZodType): schema is z.ZodRecord<any> {
return schema.def.type === "record";
}
function isZodEffects(schema: z.ZodTypeAny): schema is z.ZodEffects<any, any> {
return schema._def.typeName === "ZodEffects";
function isZodOptional(schema: z.ZodType): schema is z.ZodOptional<any> {
return schema.def.type === "optional";
}
function isZodOptional(schema: z.ZodTypeAny): schema is z.ZodOptional<any> {
return schema._def.typeName === "ZodOptional";
function isZodArray(schema: z.ZodType): schema is z.ZodArray<any> {
return schema.def.type === "array";
}
function isZodArray(schema: z.ZodTypeAny): schema is z.ZodArray<any> {
return schema._def.typeName === "ZodArray";
function isZodUnion(schema: z.ZodType): schema is z.ZodUnion<any> {
return schema.def.type === "union";
}
function isZodUnion(schema: z.ZodTypeAny): schema is z.ZodUnion<any> {
return schema._def.typeName === "ZodUnion";
function isZodNullable(schema: z.ZodType): schema is z.ZodNullable<any> {
return schema.def.type === "nullable";
}
function isZodNullable(schema: z.ZodTypeAny): schema is z.ZodNullable<any> {
return schema._def.typeName === "ZodNullable";
function isZodDefault(schema: z.ZodType): schema is z.ZodDefault<any> {
return schema.def.type === "default";
}
function isZodDefault(schema: z.ZodTypeAny): schema is z.ZodDefault<any> {
return schema._def.typeName === "ZodDefault";
function isZodLiteral(schema: z.ZodType): schema is z.ZodLiteral<any> {
return schema.def.type === "literal";
}
function isZodLiteral(schema: z.ZodTypeAny): schema is z.ZodLiteral<any> {
return schema._def.typeName === "ZodLiteral";
function isZodIntersection(schema: z.ZodType): schema is z.ZodIntersection<any, any> {
return schema.def.type === "intersection";
}
function isZodIntersection(schema: z.ZodTypeAny): schema is z.ZodIntersection<any, any> {
return schema._def.typeName === "ZodIntersection";
}
function formatZodConfigSchema(schema: z.ZodTypeAny) {
function formatZodConfigSchema(schema: z.ZodType) {
if (isZodObject(schema)) {
return (
`{\n` +
Object.entries(schema._def.shape())
.map(([k, value]) => indentLines(`${k}: ${formatZodConfigSchema(value as z.ZodTypeAny)}`, 2))
.map(([k, value]) => indentLines(`${k}: ${formatZodConfigSchema(value as z.ZodType)}`, 2))
.join("\n") +
"\n}"
);
}
if (isZodRecord(schema)) {
return "{\n" + indentLines(`[string]: ${formatZodConfigSchema(schema._def.valueType)}`, 2) + "\n}";
}
if (isZodEffects(schema)) {
return formatZodConfigSchema(schema._def.schema);
return "{\n" + indentLines(`[string]: ${formatZodConfigSchema(schema.valueType as z.ZodType)}`, 2) + "\n}";
}
if (isZodOptional(schema)) {
return `Optional<${formatZodConfigSchema(schema._def.innerType)}>`;
return `Optional<${formatZodConfigSchema(schema.def.innerType)}>`;
}
if (isZodArray(schema)) {
return `Array<${formatZodConfigSchema(schema._def.type)}>`;
return `Array<${formatZodConfigSchema(schema.def.element)}>`;
}
if (isZodUnion(schema)) {
return schema._def.options.map((t) => formatZodConfigSchema(t)).join(" | ");
}
if (isZodNullable(schema)) {
return `Nullable<${formatZodConfigSchema(schema._def.innerType)}>`;
return `Nullable<${formatZodConfigSchema(schema.def.innerType)}>`;
}
if (isZodDefault(schema)) {
return formatZodConfigSchema(schema._def.innerType);
return formatZodConfigSchema(schema.def.innerType);
}
if (isZodLiteral(schema)) {
return schema._def.value;
return schema.def.values;
}
if (isZodIntersection(schema)) {
return [formatZodConfigSchema(schema._def.left), formatZodConfigSchema(schema._def.right)].join(" & ");
return [formatZodConfigSchema(schema.def.left as z.ZodType), formatZodConfigSchema(schema.def.right as z.ZodType)].join(" & ");
}
if (schema._def.typeName === "ZodString") {
if (schema.def.type === "string") {
return "string";
}
if (schema._def.typeName === "ZodNumber") {
if (schema.def.type === "number") {
return "number";
}
if (schema._def.typeName === "ZodBoolean") {
if (schema.def.type === "boolean") {
return "boolean";
}
if (schema._def.typeName === "ZodNever") {
if (schema.def.type === "never") {
return "never";
}
return "unknown";

View file

@ -1,7 +1,7 @@
import { ApiPermissions } from "@zeppelinbot/shared/apiPermissions.js";
import express, { Request, Response } from "express";
import moment from "moment-timezone";
import { z } from "zod";
import { z } from "zod/v4";
import { GuildCases } from "../../data/GuildCases.js";
import { Case } from "../../data/entities/Case.js";
import { MINUTES } from "../../utils.js";

View file

@ -1,5 +1,5 @@
import { BaseConfig, ConfigValidationError, GuildPluginBlueprint, PluginConfigManager } from "knub";
import { ZodError } from "zod";
import { ZodError } from "zod/v4";
import { availableGuildPlugins } from "./plugins/availablePlugins.js";
import { zZeppelinGuildConfig } from "./types.js";
import { formatZodIssue } from "./utils/formatZodIssue.js";

View file

@ -1,7 +1,7 @@
import dotenv from "dotenv";
import fs from "fs";
import path from "path";
import { z } from "zod";
import { z } from "zod/v4";
import { rootDir } from "./paths.js";
const envType = z.object({

View file

@ -1,21 +1,89 @@
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
import { z } from "zod/v4";
import { availableGuildPlugins } from "./plugins/availablePlugins.js";
import { zZeppelinGuildConfig } from "./types.js";
import { deepPartial } from "./utils/zodDeepPartial.js";
import fs from "node:fs";
const basePluginOverrideCriteriaSchema = z.strictObject({
channel: z
.union([z.string(), z.array(z.string())])
.nullable()
.optional(),
category: z
.union([z.string(), z.array(z.string())])
.nullable()
.optional(),
level: z
.union([z.string(), z.array(z.string())])
.nullable()
.optional(),
user: z
.union([z.string(), z.array(z.string())])
.nullable()
.optional(),
role: z
.union([z.string(), z.array(z.string())])
.nullable()
.optional(),
thread: z
.union([z.string(), z.array(z.string())])
.nullable()
.optional(),
is_thread: z.boolean().nullable().optional(),
thread_type: z.literal(["public", "private"]).nullable().optional(),
extra: z.any().optional(),
});
const pluginOverrideCriteriaSchema = basePluginOverrideCriteriaSchema.extend({
get zzz_dummy_property_do_not_use() {
return pluginOverrideCriteriaSchema.optional();
},
get all() {
return z.array(pluginOverrideCriteriaSchema).optional();
},
get any() {
return z.array(pluginOverrideCriteriaSchema).optional();
},
get not() {
return pluginOverrideCriteriaSchema.optional();
},
});
const outputPath = process.argv[2];
if (!outputPath) {
console.error("Output path required");
process.exit(1);
}
const partialConfigs = new Map<any, z.ZodType>();
function getPartialConfig(configSchema: z.ZodType) {
if (!partialConfigs.has(configSchema)) {
partialConfigs.set(configSchema, deepPartial(configSchema));
}
return partialConfigs.get(configSchema)!;
}
function overrides(configSchema: z.ZodType): z.ZodType {
const partialConfig = getPartialConfig(configSchema);
return pluginOverrideCriteriaSchema.extend({
config: partialConfig,
});
}
const pluginSchemaMap = availableGuildPlugins.reduce((map, pluginInfo) => {
map[pluginInfo.plugin.name] = pluginInfo.docs.configSchema;
map[pluginInfo.plugin.name] = z.object({
config: pluginInfo.docs.configSchema.optional(),
overrides: overrides(pluginInfo.docs.configSchema).optional(),
});
return map;
}, {});
const fullSchema = zZeppelinGuildConfig.omit({ plugins: true }).merge(
z.strictObject({
plugins: z.strictObject(pluginSchemaMap).partial(),
}),
);
const fullSchema = zZeppelinGuildConfig.omit({ plugins: true }).extend({
plugins: z.strictObject(pluginSchemaMap).partial().optional(),
});
const jsonSchema = zodToJsonSchema(fullSchema);
const jsonSchema = z.toJSONSchema(fullSchema, { io: "input", cycles: "ref" });
console.log(JSON.stringify(jsonSchema, null, 2));
fs.writeFileSync(outputPath, JSON.stringify(jsonSchema, null, 2), { encoding: "utf8" });
process.exit(0);

View file

@ -12,7 +12,6 @@ import {
TextChannel,
ThreadChannel,
} from "discord.js";
import { EventEmitter } from "events";
import { Knub, PluginError, PluginLoadError, PluginNotLoadedError } from "knub";
import moment from "moment-timezone";
import { performance } from "perf_hooks";
@ -252,9 +251,8 @@ connect().then(async () => {
GatewayIntentBits.GuildVoiceStates,
],
});
// FIXME: TS doesn't see Client as a child of EventEmitter for some reason
// If you're here because of an error from TS 5.5.2, see https://github.com/discordjs/discord.js/issues/10358
(client as unknown as EventEmitter).setMaxListeners(200);
client.setMaxListeners(200);
const safe429DecayInterval = 5 * SECONDS;
const safe429MaxCount = 5;

View file

@ -9,7 +9,7 @@ export class MigrateUsernamesToNewHistoryTable1556909512501 implements Migration
const migratedUsernames = new Set();
await new Promise(async (resolve) => {
await new Promise<void>(async (resolve) => {
const stream = await queryRunner.stream("SELECT CONCAT(user_id, '-', username) AS `key` FROM username_history");
stream.on("data", (row: any) => {
migratedUsernames.add(row.key);

View file

@ -3,16 +3,27 @@
*/
import {
BaseChannel,
BitField,
BitFieldResolvable,
ChatInputCommandInteraction,
CommandInteraction,
GuildMember,
InteractionEditReplyOptions,
InteractionReplyOptions,
InteractionResponse,
Message,
MessageCreateOptions,
MessageEditOptions,
MessageFlags,
MessageFlagsString,
ModalSubmitInteraction,
PermissionsBitField,
SendableChannels,
TextBasedChannel,
User,
} from "discord.js";
import { AnyPluginData, BasePluginData, CommandContext, ExtendedMatchParams, GuildPluginData, helpers } from "knub";
import { AnyPluginData, BasePluginData, CommandContext, ExtendedMatchParams, GuildPluginData, helpers, PluginConfigManager } from "knub";
import { isStaff } from "./staff.js";
import { TZeppelinKnub } from "./types.js";
import { Tail } from "./utils/typeUtils.js";
@ -49,57 +60,124 @@ export async function hasPermission(
return helpers.hasPermission(config, permission);
}
export type GenericCommandSource = Message | CommandInteraction | ModalSubmitInteraction;
export function isContextInteraction(
context: TextBasedChannel | Message | User | ChatInputCommandInteraction,
): context is ChatInputCommandInteraction {
return "commandId" in context && !!context.commandId;
context: GenericCommandSource,
): context is CommandInteraction | ModalSubmitInteraction {
return (context instanceof CommandInteraction || context instanceof ModalSubmitInteraction);
}
export function isContextMessage(
context: TextBasedChannel | Message | User | ChatInputCommandInteraction,
context: GenericCommandSource,
): context is Message {
return "content" in context || "embeds" in context;
return (context instanceof Message);
}
export async function getContextChannel(
context: TextBasedChannel | Message | User | ChatInputCommandInteraction,
): Promise<TextBasedChannel> {
context: GenericCommandSource,
): Promise<TextBasedChannel | null> {
if (isContextInteraction(context)) {
// context is ChatInputCommandInteraction
return context.channel!;
} else if ("username" in context) {
// context is User
return await (context as User).createDM();
} else if ("send" in context) {
// context is TextBaseChannel
return context as TextBasedChannel;
} else {
// context is Message
return context.channel;
}
if (context instanceof Message) {
return context.channel;
}
throw new Error("Unknown context type");
}
export function getContextChannelId(
context: GenericCommandSource,
): string | null {
return context.channelId;
}
export async function fetchContextChannel(context: GenericCommandSource) {
if (!context.guild) {
throw new Error("Missing context guild");
}
const channelId = getContextChannelId(context);
if (!channelId) {
throw new Error("Missing context channel ID");
}
return (await context.guild.channels.fetch(channelId))!;
}
function flagsWithEphemeral<
TFlags extends string,
TType extends number | bigint
>(flags: BitFieldResolvable<TFlags, any>, ephemeral: boolean): BitFieldResolvable<
TFlags | Extract<MessageFlagsString, "Ephemeral">,
TType | MessageFlags.Ephemeral
> {
if (!ephemeral) {
return flags;
}
return new BitField(flags).add(MessageFlags.Ephemeral) as any;
}
export type ContextResponseOptions = MessageCreateOptions & InteractionReplyOptions & InteractionEditReplyOptions;
export type ContextResponse = Message | InteractionResponse;
export async function sendContextResponse(
context: TextBasedChannel | Message | User | ChatInputCommandInteraction,
response: string | Omit<MessageCreateOptions, "flags"> | InteractionReplyOptions,
context: GenericCommandSource,
content: string | ContextResponseOptions,
ephemeral = false,
): Promise<Message> {
if (isContextInteraction(context)) {
const options = { ...(typeof response === "string" ? { content: response } : response), fetchReply: true };
const options = { ...(typeof content === "string" ? { content: content } : content), fetchReply: true };
return (
context.replied
? context.followUp(options)
: context.deferred
? context.editReply(options)
: context.reply(options)
) as Promise<Message>;
if (context.replied) {
return context.followUp({
...options,
flags: flagsWithEphemeral(options.flags, ephemeral),
});
}
if (context.deferred) {
return context.editReply(options);
}
const replyResult = await context.reply({
...options,
flags: flagsWithEphemeral(options.flags, ephemeral),
withResponse: true,
});
return replyResult.resource!.message!;
}
if (typeof response !== "string" && "ephemeral" in response) {
delete response.ephemeral;
const contextChannel = await fetchContextChannel(context);
if (!contextChannel?.isSendable()) {
throw new Error("Context channel does not exist or is not sendable");
}
return contextChannel.send(content);
}
return (await getContextChannel(context)).send(response as string | Omit<MessageCreateOptions, "flags">);
export type ContextResponseEditOptions = MessageEditOptions & InteractionEditReplyOptions;
export function editContextResponse(
response: ContextResponse,
content: string | ContextResponseEditOptions,
): Promise<ContextResponse> {
return response.edit(content);
}
export async function deleteContextResponse(response: ContextResponse): Promise<void> {
await response.delete();
}
export async function getConfigForContext<TPluginData extends BasePluginData<any>>(config: PluginConfigManager<TPluginData>, context: GenericCommandSource): Promise<TPluginData["_pluginType"]["config"]> {
if (context instanceof ChatInputCommandInteraction) {
// TODO: Support for modal interactions (here and Knub)
return config.getForInteraction(context);
}
const channel = await getContextChannel(context);
const member = isContextMessage(context) && context.inGuild() ? await resolveMessageMember(context) : null;
return config.getMatchingConfig({
channel,
member,
});
}
export function getBaseUrl(pluginData: AnyPluginData<any>) {
@ -147,4 +225,6 @@ export function makePublicFn<TPluginData extends BasePluginData<any>, T extends
};
}
// ???
export function resolveMessageMember(message: Message<true>) {
return Promise.resolve(message.member || message.guild.members.fetch(message.author.id));
}

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";

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