Fix automod route and schemas

This commit is contained in:
Rory& committed 2026-10-29 13:08:31 +01:00
1 parent 4960a62cc1
commit 316e27a08d
8 files changed
+74 -87

No files matched your search

Binary file not shown.
Binary file not shown.
+3 -1
View File
@@ -7,7 +7,9 @@
],
"includeRe": [
"^MessageComponentType\\..*",
"^InteractionCallbackType\\..*"
"^InteractionCallbackType\\..*",
"^AutomodRuleTriggerType\\..*",
"^AutomodRuleActionType\\..*"
],
"manual": [
"DefaultSchema",
@@ -47,7 +47,7 @@ router.get(
router.post(
"/",
route({
// requestBody: "AutomodRuleSchema",
requestBody: "AutomodRuleSchema",
permission: "MANAGE_GUILD",
responses: {
200: {
@@ -63,25 +63,26 @@ router.post(
}),
async (req: Request, res: Response) => {
const { guild_id } = req.params as { [key: string]: string };
if (req.user_id !== req.body.creator_id) throw new HTTPError("You can't create a rule for someone else", 403);
if (guild_id !== req.body.guild_id) throw new HTTPError("You can't create a rule for another guild", 403);
if (req.body.id) {
throw new HTTPError("You can't specify an ID for a new rule", 400);
}
if ("id" in req.body) throw new HTTPError("You can't specify an ID for a new rule", 400);
if ("user_id" in req.body && req.user_id !== req.body.creator_id) throw new HTTPError("You can't create a rule for someone else", 403);
if ("guild_id" in req.body && guild_id !== req.body.guild_id) throw new HTTPError("You can't create a rule for another guild", 403);
const data = req.body as AutomodRuleSchema;
const created = AutomodRule.create({
...{ exempt_roles: [], exempt_channels: [] },
...data,
creator: await User.findOneOrFail({
where: { id: data.creator_id },
where: { id: req.user_id },
}),
guild_id: req.params.guild_id as string,
});
created.position = await AutomodRule.count({ where: { guild_id } });
const savedRule = await AutomodRule.save(created);
console.log(savedRule);
return res.json(savedRule);
},
);
@@ -89,7 +90,7 @@ router.post(
router.patch(
"/:rule_id",
route({
// requestBody: "AutomodRuleSchema
requestBody: "AutomodRuleSchema",
permission: "MANAGE_GUILD",
responses: {
200: {
@@ -110,9 +111,13 @@ router.patch(
});
if (!rule) throw new HTTPError("Automod rule not found", 404);
if ("id" in req.body) throw new HTTPError("You can't specify an ID for a rule", 400);
if ("user_id" in req.body && req.user_id !== req.body.creator_id) throw new HTTPError("You can't modify a rule for someone else", 403);
if ("guild_id" in req.body && req.params.guild_id !== req.body.guild_id) throw new HTTPError("You can't modify a rule for another guild", 403);
const data = req.body as AutomodRuleSchema;
AutomodRule.merge(rule, data);
AutomodRule.merge<AutomodRule>(rule, data);
const savedRule = await AutomodRule.save(rule);
return res.json(savedRule);
},
+6 -3
View File
@@ -17,7 +17,7 @@
*/
import { BaseClass } from "./BaseClass";
import { Entity, JoinColumn, ManyToOne, Column } from "typeorm";
import { Entity, JoinColumn, ManyToOne, Column, RelationId } from "typeorm";
import { User } from "./User";
import { AutomodAction, AutomodRuleEventType, AutomodRuleTriggerMetadata, AutomodRuleTriggerType } from "@spacebar/schemas";
@@ -25,6 +25,10 @@ import { AutomodAction, AutomodRuleEventType, AutomodRuleTriggerMetadata, Automo
name: "automod_rules",
})
export class AutomodRule extends BaseClass {
@Column()
@RelationId((automod: AutomodRule) => automod.creator)
creator_id: string;
@JoinColumn({ name: "creator_id", foreignKeyConstraintName: "FK_automod_rule_creator_id" })
@ManyToOne(() => User, { onDelete: "CASCADE" })
creator: User;
@@ -57,8 +61,7 @@ export class AutomodRule extends BaseClass {
type: "jsonb",
nullable: true,
})
trigger_metadata?: // this is null for "Block suspected spam content"
AutomodRuleTriggerMetadata;
trigger_metadata?: AutomodRuleTriggerMetadata | undefined; // may be undefined for trigger types that have no metadata
@Column({
type: "jsonb",
+50 -19
View File
@@ -1,6 +1,6 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2025 Spacebar and Spacebar Contributors
Copyright (C) 2026 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
@@ -18,21 +18,59 @@
import { Snowflake } from "../../Identifiers";
export type AutomodRuleTriggerMetadata = AutomodMentionSpamRule | AutomodSuspectedSpamRule | AutomodCommonlyFlaggedWordsRule | AutomodCustomWordsRule;
interface BaseAutomodRuleSchema {
enabled: boolean;
event_type: AutomodRuleEventType;
exempt_channels?: Snowflake[];
exempt_roles?: Snowflake[];
name: string;
actions: AutomodAction[];
}
export class AutomodMentionSpamRule {
type WithTrigger<T, M> = BaseAutomodRuleSchema & {
trigger_type: T;
} & M extends never
? { trigger_metadata?: never }
: { trigger_metadata: M };
type KeywordRule = WithTrigger<AutomodRuleTriggerType.KEYWORD, AutomodCustomWordsRuleMetadata>;
type HarmfulLinkRule = WithTrigger<AutomodRuleTriggerType.HARMFUL_LINK, never>;
type SpamRule = WithTrigger<AutomodRuleTriggerType.SPAM, never>;
type KeywordPresetRule = WithTrigger<AutomodRuleTriggerType.KEYWORD_PRESET, AutomodKeywordPresetRuleMetadata>;
type MentionSpamRule = WithTrigger<AutomodRuleTriggerType.MENTION_SPAM, AutomodMentionSpamRuleMetadata>;
type UserProfileRule = WithTrigger<AutomodRuleTriggerType.USER_PROFILE, AutomodUserProfileRuleMetadata>;
type GuildPolicyRule = WithTrigger<AutomodRuleTriggerType.GUILD_POLICY, never>;
export type AutomodRuleSchema = KeywordRule | HarmfulLinkRule | SpamRule | KeywordPresetRule | MentionSpamRule | UserProfileRule | GuildPolicyRule;
export type AutomodRuleSchemaWithId = AutomodRuleSchema & {
id: Snowflake;
creator_id: Snowflake;
guild_id: Snowflake;
position: number;
exempt_roles?: Snowflake[];
};
export type AutomodRuleTriggerMetadata = AutomodCustomWordsRuleMetadata | AutomodKeywordPresetRuleMetadata | AutomodMentionSpamRuleMetadata | AutomodUserProfileRuleMetadata;
export interface AutomodMentionSpamRuleMetadata {
mention_total_limit: number;
mention_raid_protection_enabled: boolean;
}
export class AutomodSuspectedSpamRule {}
export class AutomodCommonlyFlaggedWordsRule {
export interface AutomodKeywordPresetRuleMetadata {
allow_list: string[];
presets: AutomodKeywordPresetType[];
}
export class AutomodCustomWordsRule {
export interface AutomodCustomWordsRuleMetadata {
allow_list: string[];
keyword_filter: string[];
regex_patterns: string[];
}
// the same as custom words, but separate to allow divergence
export interface AutomodUserProfileRuleMetadata {
allow_list: string[];
keyword_filter: string[];
regex_patterns: string[];
@@ -42,6 +80,7 @@ export enum AutomodRuleEventType {
MESSAGE_SEND = 1,
GUILD_MEMBER_EVENT = 2,
}
export enum AutomodRuleTriggerType {
KEYWORD = 1,
HARMFUL_LINK = 2,
@@ -68,27 +107,19 @@ export enum AutomodRuleActionType {
export type AutomodAction =
| {
type: AutomodRuleActionType.BLOCK_MESSAGE;
metadata: {
custom_message?: string;
};
metadata: { custom_message?: string };
}
| {
type: AutomodRuleActionType.SEND_ALERT_MESSAGE;
metadata: {
channel_id: Snowflake;
};
metadata: { channel_id: Snowflake };
}
| {
type: AutomodRuleActionType.TIMEOUT_USER;
metadata: {
duration_seconds: number;
};
metadata: { duration_seconds: number };
}
| {
type: AutomodRuleActionType.QUARANTINE_USER;
metadata: {
duration_seconds: number;
};
metadata: { duration_seconds: number };
};
// TODO
@@ -1,53 +0,0 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2024 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export interface AutomodMentionSpamRuleSchema {
mention_total_limit: number;
mention_raid_protection_enabled: boolean;
}
// export interface AutomodSuspectedSpamRuleSchema {}
export type AutomodSuspectedSpamRuleSchema = Record<string, never>; // hack to represent an empty object
export interface AutomodCommonlyFlaggedWordsRuleSchema {
allow_list: [string];
presets: [number];
}
export interface AutomodCustomWordsRuleSchema {
allow_list: [string];
keyword_filter: [string];
regex_patterns: [string];
}
export interface AutomodRuleSchema {
creator_id: string;
enabled: boolean;
event_type: number; // No idea...
exempt_channels: [string];
exempt_roles: [string];
guild_id: string;
name: string;
position: number;
trigger_type: number; //AutomodTriggerTypes
trigger_metadata: AutomodMentionSpamRuleSchema | AutomodSuspectedSpamRuleSchema | AutomodCommonlyFlaggedWordsRuleSchema | AutomodCustomWordsRuleSchema;
}
export interface AutomodRuleSchemaWithId extends AutomodRuleSchema {
id: string;
}
-1
View File
@@ -18,7 +18,6 @@
export * from "./ActivitySchema";
export * from "./ApplicationAuthorizeSchema";
export * from "./AutomodRuleSchema";
export * from "./BackupCodesChallengeSchema";
export * from "./BanCreateSchema";
export * from "./BanModeratorSchema";