
Modulus
PausedModulus is a fully-featured Discord moderation bot built with a clean structural architecture. It includes slash commands, case tracking, role-aware permission resolution, and moderation tooling — designed to be reliable, easy to extend, and simple to maintain.
Status
Development is currently paused. The bot is offline and undergoing extensive maintenance, and no new features are being actively worked on at this time.
Command structure
Every command is a self-contained file that exports a SlashCommandBuilder and an execute function. Commands handle their own permission checks, role hierarchy validation, and error replies — the loader picks them up at startup with no extra registration needed.
1const { SlashCommandBuilder, PermissionsBitField } = require("discord.js")
2const { resolveModerationAccess } = require("../../../../shared/utils/discord/permissionResolver")
3const { createBanCase } = require("../../../../core/services/moderation/ban.service")
4const logModerationAction = require("../../../../shared/utils/logging/logModerationAction")
5
6module.exports = {
7 data: new SlashCommandBuilder()
8 .setName("ban")
9 .setDescription("Ban a user with advanced options")
10 .addUserOption(o => o.setName("user").setDescription("User to ban").setRequired(true))
11 .addStringOption(o => o.setName("reason").setDescription("Reason for the ban").setAutocomplete(true))
12 .addIntegerOption(o =>
13 o.setName("delete_messages")
14 .setDescription("Delete recent messages")
15 .addChoices(
16 { name: "Do not delete", value: 0 },
17 { name: "Last 1 day", value: 1 },
18 { name: "Last 7 days", value: 7 }
19 )
20 )
21 .addBooleanOption(o => o.setName("dm").setDescription("Send DM to the user"))
22 .addAttachmentOption(o => o.setName("proof").setDescription("Optional proof attachment")),
23
24 async execute(interaction) {
25 await interaction.deferReply()
26
27 const targetUser = interaction.options.getUser("user")
28 const reason = interaction.options.getString("reason") || "No reason provided"
29 const deleteDays = interaction.options.getInteger("delete_messages") ?? 0
30
31 const access = await resolveModerationAccess({
32 guildId: interaction.guild.id,
33 member: interaction.member,
34 requiredDiscordPerms: [PermissionsBitField.Flags.BanMembers]
35 })
36
37 if (!access.allowed) return replyError(access.reason)
38
39 await interaction.guild.members.ban(targetUser.id, { reason, deleteMessageDays: deleteDays })
40
41 const caseResult = await createBanCase({
42 guildId: interaction.guild.id,
43 targetId: targetUser.id,
44 moderatorId: interaction.user.id,
45 reason
46 })
47
48 await logModerationAction({
49 guild: interaction.guild,
50 action: "ban",
51 userId: targetUser.id,
52 moderatorId: interaction.user.id,
53 reason,
54 caseId: caseResult.caseId || null
55 })
56
57 return interaction.editReply({ embeds: [embed({ punishment: "ban", state: "applied" })] })
58 }
59}Case tracking
Every moderation action creates a persistent case in the database. Ban cases are written with an active flag so they can be deactivated on unban — giving each user a full, queryable history per guild.
1const createBanCase = async ({ guildId, targetId, moderatorId, reason, createdAt = Date.now() }) => {
2 const result = await db.execute(
3 `INSERT INTO moderation_cases
4 (guild_id, target_id, moderator_id, action, reason, created_at, active)
5 VALUES (?, ?, ?, 'ban', ?, ?, 1)`,
6 [guildId, targetId, moderatorId, reason || null, createdAt]
7 )
8 return { ok: true, caseId: result.insertId }
9}
10
11const createUnbanCase = async ({ guildId, targetId, moderatorId, reason, createdAt = Date.now() }) => {
12 return db.transaction(async tx => {
13 await tx.execute(
14 `UPDATE moderation_cases SET active = 0
15 WHERE guild_id = ? AND target_id = ? AND action = 'ban' AND active = 1`,
16 [guildId, targetId]
17 )
18 const result = await tx.execute(
19 `INSERT INTO moderation_cases
20 (guild_id, target_id, moderator_id, action, reason, created_at, active)
21 VALUES (?, ?, ?, 'unban', ?, ?, 0)`,
22 [guildId, targetId, moderatorId, reason || null, createdAt]
23 )
24 return { ok: true, caseId: result.insertId }
25 })
26}