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:
parent
595e1a0556
commit
09eb8e92f2
189 changed files with 1244 additions and 900 deletions
2
.nvmrc
2
.nvmrc
|
@ -1 +1 @@
|
|||
18
|
||||
22
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import z from "zod";
|
||||
import z from "zod/v4";
|
||||
import {
|
||||
convertDelayStringToMS,
|
||||
nonNullish,
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import z from "zod";
|
||||
import z from "zod/v4";
|
||||
import { zBoundedCharacters } from "../../../utils.js";
|
||||
import { automodAction } from "../helpers.js";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import z from "zod";
|
||||
import z from "zod/v4";
|
||||
import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError.js";
|
||||
import {
|
||||
convertDelayStringToMS,
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import z from "zod";
|
||||
import z from "zod/v4";
|
||||
import { automodTrigger } from "../helpers.js";
|
||||
|
||||
interface AntiraidLevelTriggerResult {}
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import z from "zod";
|
||||
import z from "zod/v4";
|
||||
import { automodTrigger } from "../helpers.js";
|
||||
|
||||
// tslint:disable-next-line
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import z from "zod";
|
||||
import z from "zod/v4";
|
||||
import { automodTrigger } from "../helpers.js";
|
||||
|
||||
interface ExampleMatchResultType {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import z from "zod";
|
||||
import z from "zod/v4";
|
||||
import { convertDelayStringToMS, zDelayString } from "../../../utils.js";
|
||||
import { automodTrigger } from "../helpers.js";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import z from "zod";
|
||||
import z from "zod/v4";
|
||||
import { automodTrigger } from "../helpers.js";
|
||||
|
||||
const configSchema = z.strictObject({});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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),
|
||||
});
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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();
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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?
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const zGuildAccessMonitorConfig = z.strictObject({});
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { BasePluginType } from "knub";
|
||||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const zGuildInfoSaverConfig = z.strictObject({});
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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({});
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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}> | `);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue