Skip to content

Commit a3fd95f

Browse files
committed
feat: slowmode
1 parent 16dba20 commit a3fd95f

File tree

5 files changed

+122
-1
lines changed

5 files changed

+122
-1
lines changed

.github/workflows/deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
- name: Setup Node.js
1616
uses: actions/setup-node@v4
1717
with:
18-
node-version: "20"
18+
node-version: "22"
1919
cache: "npm"
2020

2121
- name: Install dependencies

src/commands/help.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ const commands: CommandInfo[] = [
6767
value: "View the seraphic archives of purged messages",
6868
category: "moderator",
6969
},
70+
{
71+
name: "/slowmode [seconds] [reason]",
72+
value: "Impose restraint upon the flow of messages",
73+
category: "moderator",
74+
},
7075
// Basic
7176
{
7277
name: "/avatar [@user]",

src/commands/slowmode.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import {
2+
ChatInputCommandInteraction,
3+
SlashCommandBuilder,
4+
GuildMember,
5+
EmbedBuilder,
6+
ChannelType,
7+
TextChannel,
8+
MessageFlags,
9+
} from "discord.js";
10+
import { ModerationLogger } from "../utils/moderationLogger.js";
11+
12+
export const data = new SlashCommandBuilder()
13+
.setName("slowmode")
14+
.setDescription("Impose restraint upon the flow of messages")
15+
.addIntegerOption((option) =>
16+
option
17+
.setName("seconds")
18+
.setDescription("Duration of slowmode in seconds (0-21600, 0 to disable)")
19+
.setRequired(true)
20+
.setMinValue(0)
21+
.setMaxValue(21600),
22+
)
23+
.addStringOption((option) =>
24+
option
25+
.setName("reason")
26+
.setDescription("Reason for imposing restraint")
27+
.setRequired(false),
28+
);
29+
30+
export async function execute(
31+
interaction: ChatInputCommandInteraction,
32+
executor: GuildMember,
33+
): Promise<void> {
34+
const seconds = interaction.options.getInteger("seconds")!;
35+
const reason =
36+
interaction.options.getString("reason") || "Maintaining sacred order";
37+
38+
if (interaction.channel?.type !== ChannelType.GuildText) {
39+
await interaction.reply({
40+
content: "**RESTRAINT CAN ONLY BE IMPOSED IN GUILD CHANNELS!**",
41+
flags: MessageFlags.Ephemeral,
42+
});
43+
return;
44+
}
45+
46+
if (!interaction.guild) {
47+
await interaction.reply({
48+
content: "**THIS HOLY COMMAND CAN ONLY BE USED IN THE SACRED HALLS!**",
49+
flags: MessageFlags.Ephemeral,
50+
});
51+
return;
52+
}
53+
54+
const channel = interaction.channel as TextChannel;
55+
56+
try {
57+
await channel.setRateLimitPerUser(seconds, reason);
58+
59+
await ModerationLogger.addEntry({
60+
type: "slowmode" as any,
61+
userId: "N/A",
62+
userTag: "N/A",
63+
moderatorId: executor.id,
64+
moderatorTag: executor.user.tag,
65+
reason: `${seconds === 0 ? "Disabled" : "Set"} slowmode in #${channel.name}${seconds > 0 ? ` (${seconds}s)` : ""}`,
66+
guildId: interaction.guild.id,
67+
duration: seconds > 0 ? seconds : undefined,
68+
});
69+
70+
const embed = new EmbedBuilder()
71+
.setColor(seconds === 0 ? "#00FF00" : "#FFD700")
72+
.setTitle(seconds === 0 ? "💨 RESTRAINT LIFTED" : "⛈️ RESTRAINT IMPOSED")
73+
.setDescription(
74+
seconds === 0
75+
? `**The flow of messages in ${channel} hath been restored to normal pace!**`
76+
: `**Restraint hath been imposed upon ${channel}! The alters are not happy.**`,
77+
)
78+
.addFields(
79+
{
80+
name: "ENFORCER OF RESTRAINT",
81+
value: `${executor.user.tag}`,
82+
inline: true,
83+
},
84+
{
85+
name: "CHANNEL",
86+
value: `${channel}`,
87+
inline: true,
88+
},
89+
{
90+
name: seconds === 0 ? "STATUS" : "RESTRAINT DURATION",
91+
value: seconds === 0 ? "Slowmode disabled" : `${seconds} seconds between messages`,
92+
inline: true,
93+
},
94+
{ name: "REASON FOR RESTRAINT", value: reason, inline: false },
95+
{
96+
name: "DIVINE WISDOM",
97+
value: seconds === 0
98+
? "The faithful may once again speak freely in the sacred halls"
99+
: "Patience brings wisdom, and wisdom brings understanding",
100+
inline: false,
101+
},
102+
)
103+
.setTimestamp();
104+
105+
await interaction.reply({ embeds: [embed] });
106+
} catch (error) {
107+
console.error("Error setting slowmode:", error);
108+
await interaction.reply({
109+
content: "**THE DIVINE POWERS HAVE BEEN THWARTED! The restraint could not be imposed!**",
110+
flags: MessageFlags.Ephemeral,
111+
});
112+
}
113+
}

src/utils/commandLoader.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import * as link from "../commands/link.js";
1414
import * as unlink from "../commands/removelink.js";
1515
import * as checklink from "../commands/checklink.js";
1616
import * as synctop5 from "../commands/synctop5.js";
17+
import * as slowmode from "../commands/slowmode.js";
1718

1819
export interface Command {
1920
data: any;
@@ -39,6 +40,7 @@ export function loadCommands(): Collection<string, Command> {
3940
unlink,
4041
checklink,
4142
synctop5,
43+
slowmode,
4244
];
4345

4446
for (const command of commandModules) {

src/utils/rolePermissions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const COMMAND_PERMISSIONS: Record<string, PermissionLevel> = {
2020
clear: PermissionLevel.MODERATOR,
2121
archives: PermissionLevel.MODERATOR,
2222
sins: PermissionLevel.MODERATOR,
23+
slowmode: PermissionLevel.MODERATOR,
2324

2425
// Basic
2526
help: PermissionLevel.BASIC,

0 commit comments

Comments
 (0)