Skip to content

Implement infraction command #6

@Arlodotexe

Description

@Arlodotexe

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:

  1. Assigns incremental roles (Warned, Strike 1, Strike 2, Strike 3, Strike 4) to offenders.
  2. Mutes them for certain durations (automatically unmuting when time is up).
  3. Expires infractions after a certain number of days, removing roles automatically.
  4. Logs all infractions and warnings in dedicated channels (infraction-log, bot-stuff, meta).
  5. Persists the current infractions by pinning messages in the infraction-log.

The current workflow is:

  1. Initialization:

    • Ensures the server has Muted, Warned, and Strike roles.
    • 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 Muted role has correct channel permissions (preventing sending messages, reactions, etc.).
  2. Command Execution (!infraction):

    • Available only to moderators.
    • Accepts either a direct discordId or a messageLink to 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.
  3. Role & Infraction Tracking:

    • Each user’s highest infraction is stored in-memory with a timestamp (assignedAt).
    • The pinned message in infraction-log also 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 Muted role is removed.

Problem

  1. 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 (infractions and infractionData) 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.
  2. 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.
  3. Extensibility

    • Adding more infraction levels, different penalties (kicks, bans), or advanced expiration logic is currently difficult.
    • The initExistingInfractionData requires 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:

  1. 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.
  2. 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.
  1. Scheduled Task for Cleanup

    • Maintain a single scheduled job (e.g., every hour) that:
      1. Iterates through all active infractions in storage.
      2. Checks if they have expired and removes roles as needed.
      3. Checks if it’s time to unmute a user and removes the Muted role if applicable.
    • Send notifications on role removals or un-mutes. This approach is more predictable (and reduces overhead vs. every 15 seconds).
  2. 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 Muted role if needed—optionally store these channel permissions in config to handle future expansions (voice channels, text channels, etc.).
  3. 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.
  4. Migration Strategy

    • To transition from the existing pinned-message approach:
      1. Run a one-time script on initialization that parses pinned messages and infers the user’s infraction level.
      2. Migrate those into the new database schema.
      3. 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.

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

No one assigned

    Type

    No type

    Projects

    Status

    Ready

    Status

    Ready

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions