-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Background
The !infraction command implementation handles a Discord server’s system for warnings, strikes, and mutes. It uses Discord entities (roles, channels, permissions) to manage user infractions.
The system:
- Assigns incremental roles (
Warned,Strike 1,Strike 2,Strike 3,Strike 4) to offenders. - Mutes them for certain durations (automatically unmuting when time is up).
- Expires infractions after a certain number of days, removing roles automatically.
- Logs all infractions and warnings in dedicated channels (
infraction-log,bot-stuff,meta). - Persists the current infractions by pinning messages in the
infraction-log.
The current workflow is:
-
Initialization:
- Ensures the server has
Muted,Warned, andStrikeroles. - Loads existing infractions by scanning pinned messages in
infraction-log. - Schedules periodic checks (every 15 seconds) for:
- Unmuting users when enough time has passed.
- Removing expired infractions and associated roles.
- Ensures the
Mutedrole has correct channel permissions (preventing sending messages, reactions, etc.).
- Ensures the server has
-
Command Execution (
!infraction):- Available only to moderators.
- Accepts either a direct
discordIdor amessageLinkto identify the offender. - Determines the user’s current infraction state (none, warning, strike 1..4).
- Escalates the infraction level (warn → strike 1 → strike 2 → strike 3 → strike 4).
- Assigns the corresponding role, sets mute durations, pins infraction records, and sends notifications.
-
Role & Infraction Tracking:
- Each user’s highest infraction is stored in-memory with a timestamp (
assignedAt). - The pinned message in
infraction-logalso reflects the user’s current infraction state. - On each scheduled check:
- If the user no longer has the infraction role (manually removed), the system cleans up its record.
- If an infraction’s expiration time is exceeded, the user’s infraction role is removed, and the user is notified.
- If a user’s mute time has expired, the
Mutedrole is removed.
- Each user’s highest infraction is stored in-memory with a timestamp (
Problem
-
Scalability & Maintainability
- The current implementation relies on pinned messages as a makeshift database. Over time, this could become unwieldy or fail if pinned messages are lost or updated.
- The system uses in-memory arrays (
infractionsandinfractionData) for core data. If the bot restarts, the data must be reconstructed from pinned messages, risking data loss or inconsistent states if pinned messages are out of sync.
-
Reliability
- If a user manually modifies roles or pinned messages are changed, the logic must guess what happened. This can lead to incorrect internal data.
- The periodic checks might be inefficient if the server grows large, leading to performance overhead.
-
Extensibility
- Adding more infraction levels, different penalties (kicks, bans), or advanced expiration logic is currently difficult.
- The
initExistingInfractionDatarequires certain roles named exactly “warned”, “strike 1”, etc. It's brittle and not easily configurable for other servers or changes in role names.
Solution
To implement a more robust, extensible infraction system in a new codebase, we propose:
-
Dedicated infraction interface/service
- Create a dedicated interface that represents the ways the infraction system can be interacted with.
- In the implementation, replace the pinned-message approach with a dedicated storage layer (e.g., OwlCore.ComponentModel.Settings). Each record can track:
- User ID
- Current infraction level
- Timestamps for when the infraction was issued
- Timestamps for when the infraction/mute expires
- Whenever an infraction is assigned or updated, persist these changes. On bot startup, restore from the persisted data.
-
Command Structure
- We want to make improvements now, but we need a smooth transition to MVP first.
- For now, we'll move to slash commands and match the existing codebase as much as possible.
-
Scheduled Task for Cleanup
- Maintain a single scheduled job (e.g., every hour) that:
- Iterates through all active infractions in storage.
- Checks if they have expired and removes roles as needed.
- Checks if it’s time to unmute a user and removes the
Mutedrole if applicable.
- Send notifications on role removals or un-mutes. This approach is more predictable (and reduces overhead vs. every 15 seconds).
- Maintain a single scheduled job (e.g., every hour) that:
-
Discord Roles & Permissions Management
- On bot startup, ensure the existence of needed roles (muted, warned, strikes, etc.). If missing, create them (or bail out with an error).
- Update channel permissions for the
Mutedrole if needed—optionally store these channel permissions in config to handle future expansions (voice channels, text channels, etc.).
-
Error Handling & Logging
- Provide clear logs or error messages if a role is missing or if the user is not found.
- Optionally, log infraction changes in a specific channel (like
bot-stuff) or a dedicated log.
-
Migration Strategy
- To transition from the existing pinned-message approach:
- Run a one-time script on initialization that parses pinned messages and infers the user’s infraction level.
- Migrate those into the new database schema.
- Mark pinned messages as “migrated” (or unpin them) to avoid confusion.
- Once the data is in the new system, rely on that system exclusively for ongoing reads and writes.
- To transition from the existing pinned-message approach:
By re-implementing the core logic in an Infraction Service with persistent storage and a well-defined config, you’ll have a more stable, extensible, and maintainable solution for handling infractions in the Discord server.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status
Status