Skip to content

Commit 94678e5

Browse files
committed
feat: wip automod
1 parent 934954c commit 94678e5

File tree

2 files changed

+316
-0
lines changed

2 files changed

+316
-0
lines changed

src/mod/automod/automod.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { ApplicationCommandOptionType } from 'discord.js'
2+
import {
3+
SleetSlashCommand,
4+
SleetSlashCommandGroup,
5+
SleetSlashSubcommand,
6+
} from 'sleetcord'
7+
8+
// TODO for automod:
9+
// - Add a way to view the automod rules
10+
// - Include way to search
11+
// - Include pagination
12+
// - Add a way to remove automod rules
13+
// - Add a way to add automod rules
14+
// - Rules should have:
15+
// - Bot-set type
16+
// - User-customizable name
17+
// - User-customizable description (shown on trigger, optional)
18+
// - User-customizable arguments (varies per rule)
19+
// - Each rule should (somehow) define what it needs to generate the slash commands
20+
// - Each rule should be able to parse the DB row and create itself (on load)
21+
// - Could use the type as a discriminator to determine which rule to create
22+
// - Each rule should be able to serialize itself into a Prisma-compatible payload
23+
// - Add a way to edit automod rules?
24+
// - How? Good way to handle multiple types? Generate slash commands like add?
25+
26+
const automod_add_content_repeat_rule = new SleetSlashSubcommand(
27+
{
28+
name: 'content-repeat-rule',
29+
description: 'Filter out message content repeats',
30+
options: [
31+
{
32+
name: 'max-repeats',
33+
description: 'The maximum number of repeats allowed',
34+
type: ApplicationCommandOptionType.Integer,
35+
required: true,
36+
min_value: 2,
37+
max_value: 100,
38+
},
39+
],
40+
},
41+
{
42+
async run(interaction) {
43+
await interaction.reply('ok')
44+
},
45+
},
46+
)
47+
48+
const automod_add = new SleetSlashCommandGroup({
49+
name: 'add',
50+
description: 'Add a new rule to automod',
51+
options: [automod_add_content_repeat_rule],
52+
})
53+
54+
const automod_remove = new SleetSlashSubcommand(
55+
{
56+
name: 'remove',
57+
description: 'Remove a rule from automod',
58+
options: [
59+
{
60+
name: 'rule',
61+
description: 'The rule to remove',
62+
type: ApplicationCommandOptionType.String,
63+
},
64+
],
65+
},
66+
{
67+
async run(interaction) {
68+
await interaction.reply('ok')
69+
},
70+
},
71+
)
72+
73+
const automod_view = new SleetSlashSubcommand(
74+
{
75+
name: 'view',
76+
description: 'View the automod rules',
77+
options: [
78+
{
79+
name: 'name',
80+
description: 'Search rules by name',
81+
type: ApplicationCommandOptionType.String,
82+
},
83+
{
84+
name: 'type',
85+
description: 'Search rules by type',
86+
type: ApplicationCommandOptionType.String,
87+
},
88+
],
89+
},
90+
{
91+
async run(interaction) {
92+
await interaction.reply('ok')
93+
},
94+
},
95+
)
96+
97+
export const automod = new SleetSlashCommand({
98+
name: 'automod',
99+
description: "Manage the bot's automod",
100+
options: [automod_add, automod_remove, automod_view],
101+
})

src/mod/automod/filters/test.ts

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
import { ApplicationCommandOptionType } from 'discord.js'
2+
3+
type PrimitiveFromOptionType<T extends ApplicationCommandOptionType> =
4+
T extends ApplicationCommandOptionType.String
5+
? string
6+
: T extends ApplicationCommandOptionType.Integer
7+
? number
8+
: T extends ApplicationCommandOptionType.Boolean
9+
? boolean
10+
: T extends ApplicationCommandOptionType.User
11+
? string
12+
: T extends ApplicationCommandOptionType.Channel
13+
? string
14+
: T extends ApplicationCommandOptionType.Role
15+
? string
16+
: T extends ApplicationCommandOptionType.Mentionable
17+
? string
18+
: never
19+
20+
type Callback<Item> = Item extends { name: infer Name; type: infer Type }
21+
? Name extends PropertyKey
22+
? Type extends ApplicationCommandOptionType
23+
? Record<Name, PrimitiveFromOptionType<Type>>
24+
: never
25+
: never
26+
: never
27+
28+
type Reducer<T extends unknown[], Acc = object> = T extends []
29+
? Acc
30+
: T extends [infer Head, ...infer Tail]
31+
? Reducer<Tail, Acc & Callback<Head>>
32+
: never
33+
34+
const getOutput = <
35+
Type extends ApplicationCommandOptionType,
36+
Name extends string,
37+
Item extends { name: Name; type: Type },
38+
Input extends Item[],
39+
>(
40+
input: Readonly<[...Input]>,
41+
) =>
42+
input.reduce<Reducer<Input>>(
43+
// biome-ignore lint/performance/noAccumulatingSpread: TODO: this is still WIP
44+
(output, record) => ({ ...output, [record.name]: record.type }),
45+
{} as Reducer<Input>,
46+
)
47+
48+
const foo = [
49+
{
50+
name: 'max-repeats',
51+
description: 'The maximum number of repeats allowed',
52+
type: ApplicationCommandOptionType.Integer,
53+
required: true,
54+
min_value: 2,
55+
max_value: 100,
56+
},
57+
{
58+
name: 'min-repeats',
59+
description: 'The min number of repeats allowed',
60+
type: ApplicationCommandOptionType.Integer,
61+
required: true,
62+
},
63+
{
64+
name: 'cooldown',
65+
description: 'The cooldown between each repeat',
66+
type: ApplicationCommandOptionType.Integer,
67+
},
68+
{
69+
name: 'strict',
70+
description: 'Whether or not to be strict',
71+
type: ApplicationCommandOptionType.Boolean,
72+
},
73+
] as const
74+
75+
const a = getOutput(foo)
76+
console.log(a)
77+
78+
// -----------------------------------------------------------------------------------------------
79+
80+
// type ApplicationCommandOptionData = SleetSlashCommandBody['options']
81+
82+
// interface ApplicationCommandOptionChoiceData<T extends string | number> {
83+
// name: string
84+
// value: T
85+
// }
86+
87+
// export type ExtractArrayType<T> = ((a: T) => never) extends (
88+
// a: (infer H)[],
89+
// ) => never
90+
// ? H
91+
// : never
92+
93+
// export type MaybePromise<T> = T | Promise<T>
94+
95+
// type LengthOfReadonly<T extends Readonly<unknown[]>> = T['length']
96+
// type HeadOfReadonly<T extends Readonly<unknown[]>> = T extends [] ? never : T[0]
97+
// type TailOfReadonly<T extends Readonly<unknown[]>> = ((
98+
// ...array: T
99+
// ) => never) extends (head: never, ...tail: infer Tail_) => never
100+
// ? Tail_
101+
// : never
102+
103+
// type MapChoicesToValues<
104+
// T extends readonly ApplicationCommandOptionChoiceData[],
105+
// > = {
106+
// [K in keyof T]: T[K] extends ApplicationCommandOptionChoiceData
107+
// ? T[K]['value']
108+
// : never
109+
// }[number]
110+
111+
// type HasChoices = {
112+
// choices: readonly [
113+
// ApplicationCommandOptionChoiceData,
114+
// ...ApplicationCommandOptionChoiceData[],
115+
// ]
116+
// }
117+
118+
// type CommandInteractionOptionResolverReturn<
119+
// T extends keyof CommandInteractionOptionResolver,
120+
// > = CommandInteractionOptionResolver[T] extends Function
121+
// ? // @ts-expect-error this works, it just doesn't narrow the type here
122+
// NonNullable<ReturnType<CommandInteractionOptionResolver[T]>>
123+
// : never
124+
125+
// export type OptionsMap = {
126+
// STRING: CommandInteractionOptionResolverReturn<'getString'>
127+
// 3: CommandInteractionOptionResolverReturn<'getString'>
128+
// INTEGER: CommandInteractionOptionResolverReturn<'getInteger'>
129+
// 4: CommandInteractionOptionResolverReturn<'getInteger'>
130+
// BOOLEAN: CommandInteractionOptionResolverReturn<'getBoolean'>
131+
// 5: CommandInteractionOptionResolverReturn<'getBoolean'>
132+
// USER:
133+
// | CommandInteractionOptionResolverReturn<'getMember'>
134+
// | CommandInteractionOptionResolverReturn<'getUser'>
135+
// 6:
136+
// | CommandInteractionOptionResolverReturn<'getMember'>
137+
// | CommandInteractionOptionResolverReturn<'getUser'>
138+
// CHANNEL: CommandInteractionOptionResolverReturn<'getChannel'>
139+
// 7: CommandInteractionOptionResolverReturn<'getChannel'>
140+
// ROLE: CommandInteractionOptionResolverReturn<'getRole'>
141+
// 8: CommandInteractionOptionResolverReturn<'getRole'>
142+
// MENTIONABLE:
143+
// | CommandInteractionOptionResolverReturn<'getMember'>
144+
// | CommandInteractionOptionResolverReturn<'getRole'>
145+
// | CommandInteractionOptionResolverReturn<'getUser'>
146+
// 9:
147+
// | CommandInteractionOptionResolverReturn<'getMember'>
148+
// | CommandInteractionOptionResolverReturn<'getRole'>
149+
// | CommandInteractionOptionResolverReturn<'getUser'>
150+
// NUMBER: CommandInteractionOptionResolverReturn<'getInteger'>
151+
// 10: CommandInteractionOptionResolverReturn<'getInteger'>
152+
// ATTACHMENT: CommandInteractionOptionResolverReturn<'getAttachment'>
153+
// 11: CommandInteractionOptionResolverReturn<'getAttachment'>
154+
// }
155+
156+
// type ChannelsMap = {
157+
// 0: TextChannel
158+
// 1: never // DM
159+
// 2: VoiceChannel
160+
// 3: never // Group DM
161+
// 4: CategoryChannel
162+
// 5: NewsChannel
163+
// 10: ThreadChannel
164+
// 11: ThreadChannel
165+
// 12: ThreadChannel
166+
// 13: StageChannel
167+
// 14: never // Directory
168+
// 15: ForumChannel // Forum
169+
// }
170+
171+
// type MapChannelTypesToChannels<T extends ReadonlyArray<ChannelType>> = {
172+
// [K in keyof T]: T[K] extends ChannelType ? ChannelsMap[T[K]] : never
173+
// }[number]
174+
175+
// type OptionToValue<T extends ApplicationCommandOptionData> = T extends {
176+
// transformer: (value: any) => unknown
177+
// }
178+
// ? Awaited<ReturnType<T['transformer']>>
179+
// : T extends HasChoices
180+
// ? MapChoicesToValues<T['choices']>
181+
// : T extends {
182+
// channelTypes: ReadonlyArray<ChannelType>
183+
// }
184+
// ?
185+
// | MapChannelTypesToChannels<T['channelTypes']>
186+
// | APIInteractionDataResolvedChannel
187+
// : OptionsMap[T['type']]
188+
189+
// export type CommandOptionsObject<T extends OptionsDataArray> = {
190+
// [Key in T[number]['name']]: Extract<
191+
// T[number],
192+
// { name: Key }
193+
// >['required'] extends true
194+
// ? OptionToValue<Extract<T[number], { name: Key }>>
195+
// : OptionToValue<Extract<T[number], { name: Key }>> | null
196+
// }
197+
198+
// type MapOptionToAutocompleteName<T extends ApplicationCommandOptionData> =
199+
// T extends { autocomplete: true }
200+
// ? T extends {
201+
// onAutocomplete: Function
202+
// }
203+
// ? never
204+
// : T['name']
205+
// : never
206+
207+
// export type MapOptionsToAutocompleteNames<
208+
// T extends readonly ApplicationCommandOptionData[],
209+
// > = LengthOfReadonly<T> extends 0
210+
// ? never
211+
// : LengthOfReadonly<T> extends 1
212+
// ? MapOptionToAutocompleteName<HeadOfReadonly<T>>
213+
// :
214+
// | MapOptionToAutocompleteName<HeadOfReadonly<T>>
215+
// | MapOptionsToAutocompleteNames<TailOfReadonly<T>>

0 commit comments

Comments
 (0)