Skip to content

Conversation

@kzndotsh
Copy link
Contributor

@kzndotsh kzndotsh commented Jan 22, 2026

Pull Request

Description

Provide a clear summary of your changes and reference any related issues. Include the motivation behind these changes and list any new dependencies if applicable.

If your PR is related to an issue, please include the issue number below:

Related Issue: Closes #

Type of Change:

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Performance improvement
  • Code refactoring
  • Test improvements

Guidelines

  • My code follows the style guidelines of this project (formatted with Ruff)

  • I have performed a self-review of my own code

  • I have commented my code, particularly in hard-to-understand areas

  • I have made corresponding changes to the documentation if needed

  • My changes generate no new warnings

  • I have tested this change

  • Any dependent changes have been merged and published in downstream modules

  • I have added all appropriate labels to this PR

  • I have followed all of these guidelines.

How Has This Been Tested? (if applicable)

Please describe how you tested your code. e.g describe what commands you ran, what arguments, and any config stuff (if applicable)

Screenshots (if applicable)

Please add screenshots to help explain your changes.

Additional Information

Please add any other information that is important to this PR.

Summary by Sourcery

Improve interaction handling, permissions, and performance across multiple bot modules while introducing configurable Discord intents and sequence repair tooling.

New Features:

  • Add a db fix-sequences maintenance command to automatically realign PostgreSQL sequences with table IDs.

Enhancements:

  • Refactor multiple commands to consistently handle both slash and prefix invocations with early defers and ephemeral followups.
  • Optimize help and permission systems by batching permission checks and command permission lookups to reduce database load.
  • Introduce configurable Discord gateway intents in configuration and wire them into bot startup.
  • Improve status role checks and activity rotation so they only run heavy operations on the first ready event and batch member processing.
  • Add HTTP client configuration for high-latency environments to better tune Discord REST and gateway behavior.
  • Add a utility for safer user resolution using the cache before REST calls and improve member/user lookup patterns in info-related commands.
  • Harden case creation by ignoring manual ID injection and improve AFK, levels, moderation, and wiki flows for better UX and error messaging.

Build:

  • Bump Python version to 3.13.11 across CI, Docker, pre-commit, and tooling configs.

Deployment:

  • Update container base image to python:3.13.11-slim for production builds.

Documentation:

  • Update docs to reference the new Python base image version and workflow matrix configuration.

Chores:

  • Add a mise.toml specifying Python 3.13.11 for local tooling alignment.
  • Wire a new db fix-sequences CLI subcommand into the scripts entrypoint for easier database maintenance.

- Updated the default Python version from 3.13 to 3.13.11 in the pre-commit configuration file to ensure compatibility with the latest features and improvements.
- Updated the Python version from 3.13.8 to 3.13.11 in multiple GitHub Actions workflows and the setup-python action to ensure consistency and compatibility with the latest features.
- Updated documentation to reflect the change of Python version from 3.13.8 to 3.13.11 across various files, ensuring consistency in the usage of the latest Python version in CI/CD configurations and Docker references.
- Introduced a new command `fix-sequences` that resets PostgreSQL sequences to the maximum ID value in their respective tables, addressing synchronization issues that can lead to duplicate key violations.
- Updated the main application to include the new command, enhancing database management capabilities.
- Updated the Tux bot instance creation to retrieve intents from the configuration, enhancing flexibility in bot behavior based on user-defined settings.
- This change allows for more granular control over the bot's event handling capabilities.
- Introduced a new method `send_after_defer` in the BaseCog class to streamline message sending after deferring, accommodating both slash and prefix commands.
- This method enhances flexibility by allowing for optional content, embeds, and files, while also managing ephemeral messages and author mentions.
- Improved error handling with logging for failed message sends, ensuring better debugging capabilities.
…tency environments

- Introduced a new flag `first_ready` to track the initial on_ready event, improving state management.
- Configured the HTTP client for high-latency environments early in the setup process to enhance performance and reliability during REST calls.
…er resolution

- Introduced the `get_user_safe` function to retrieve users from the bot's cache before making REST API calls, improving performance in high-latency environments.
- The function handles user fetching with error logging for better debugging and reliability.
- Introduced a new module to configure discord.py's HTTP client for optimal performance in high-latency, high-jitter environments.
- Enhanced connection pooling and DNS caching settings, including adjustments to DNS cache TTL and keepalive timeout.
- Implemented error handling and logging for configuration failures, ensuring graceful degradation and visibility into issues.
…ieval

- Refactored the command permission checking logic to support batch retrieval of permissions for multiple commands, improving efficiency.
- Introduced a new method `batch_get_command_permissions` to allow simultaneous permission checks for a list of command names, reducing database query overhead.
- Updated logging to use trace level for parent command permission usage, enhancing clarity during permission checks.
- Updated the case creation logic to filter out the 'id' key from additional kwargs, ensuring that the database auto-generates the ID.
- Added a warning log when 'id' is included in kwargs, enhancing clarity and preventing potential issues with manual ID assignment.
… processing

- Introduced caching for user permission rank to reduce database queries during help operations.
- Added a new method `_filter_commands_for_permission_check` to streamline the filtering of commands based on their permission requirements.
- Enhanced `batch_can_run_commands` to efficiently check permissions for multiple commands, improving performance and reducing overhead.
- Updated documentation for new methods and clarified permission checking logic.
…ocessing

- Enhanced the permission checking logic in the TuxHelp class to utilize batch processing for subcommands, significantly improving performance.
- Replaced individual permission checks with a batch check, reducing overhead and streamlining the command filtering process.
…rocessing

- Improved the permission filtering logic in HelpNavigation by replacing individual permission checks with a batch processing approach, enhancing performance.
- This change allows for faster permission validation for subcommands, streamlining the command filtering process.
- Introduced a new method `_send_embed` to streamline sending embeds for both slash and prefix commands, improving user experience.
- Added validation methods for emoji, threshold, and channel permissions, providing clear error messages through embeds when validation fails.
- Refactored the starboard command logic to utilize the new validation methods, ensuring better error handling and user feedback.
- Improved overall code organization and readability by consolidating validation logic.
- Enhanced the on_ready method to check user statuses only on the first startup, reducing unnecessary REST calls during reconnects.
- Implemented batch processing of members in chunks to improve performance and manage rate limits effectively.
- Added logging to track the processing of members and completion of status role checks for better visibility.
…ion commands

- Added early interaction deferral to acknowledge user commands before executing asynchronous tasks in various moderation commands, enhancing user experience.
- Updated response handling to utilize interaction follow-ups where applicable, ensuring consistent feedback for users across different moderation actions.
…d avatar commands

- Added early interaction deferral to the xkcd and avatar commands to acknowledge user interactions before executing asynchronous tasks, enhancing user experience.
- Updated response handling to utilize interaction follow-ups where applicable, ensuring consistent feedback for users across both comic retrieval and avatar display functionalities.
…ing for level commands

- Added early interaction deferral to level commands to acknowledge user interactions before executing asynchronous tasks, improving user experience.
- Updated response handling to utilize interaction follow-ups where applicable, ensuring consistent feedback for users across level-related commands.
…confirmation handling

- Added methods for error message handling and timeout duration validation to improve user feedback and command reliability.
- Implemented early interaction deferral to acknowledge user commands before executing asynchronous tasks, enhancing user experience.
- Refactored confirmation dialog handling to streamline user interactions and ensure consistent feedback across different command contexts.
… interaction handling

- Introduced new methods for building converters and trying entity resolutions, improving the command's ability to handle various Discord objects.
- Implemented early interaction deferral to acknowledge user commands before executing asynchronous tasks, enhancing user experience.
- Updated response handling to utilize interaction follow-ups for sending error messages and embeds, ensuring consistent feedback across different contexts.
- Refactored existing logic to streamline the command flow and improve code readability.
…ling for AFK, RemindMe, and Wiki commands

- Added early interaction deferral to AFK, RemindMe, and Wiki commands to acknowledge user interactions before executing asynchronous tasks, enhancing user experience.
- Updated response handling to utilize interaction follow-ups where applicable, ensuring consistent feedback for users across different command contexts.
- Added logic to ensure activity rotation starts only on the first ready event, preventing multiple tasks from starting on reconnects.
- Introduced a delay before starting the activity rotation to ensure the bot is fully connected, addressing potential disconnection issues.
…nfiguration

- Introduced a new BotIntents model to manage Discord bot gateway intents, including members, presences, and message content.
- Updated the Config class to include BOT_INTENTS, allowing for easy configuration of bot intents.
- Enhanced organization of configuration models by extracting intents-related settings into a dedicated model.
- Updated the Python version in .python-version and Containerfile to 3.13.11 for consistency.
- Added mise.toml to specify the Python version for tools, ensuring compatibility across environments.
- Updated the Python image reference in the Containerfile to remove the specific SHA256 digest, streamlining the image definition for easier updates in the future.
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 22, 2026

Reviewer's Guide

Refactors several Discord bot cogs to better support slash vs prefix behavior and high-latency environments, introduces batched permission checks and a sequence-fix CLI for the database, makes intents configurable, optimizes startup/on_ready handlers, and updates tooling/docs to Python 3.13.11.

Class diagram for permission system batching and help integration

classDiagram
    class PermissionSystem {
        +async get_command_permission(guild_id: int, command_name: str) PermissionCommand|None
        +async batch_get_command_permissions(guild_id: int, command_names: list[str]) dict[str, PermissionCommand|None]
        +async get_user_permission_rank(ctx: commands.Context[Any]) int
    }

    class HelpData {
        -ctx: commands.Context[Any]|None
        -command_mapping: dict[str, dict[str, commands.Command[Any,Any,Any]]]|None
        -_cached_user_rank: int|None
        +async can_run_command(command: commands.Command[Any,Any,Any]) bool
        +async batch_can_run_commands(commands_list: list[commands.Command[Any,Any,Any]]) dict[commands.Command[Any,Any,Any], bool]
        -_filter_commands_for_permission_check(commands_list: list[commands.Command[Any,Any,Any]]) tuple[dict[commands.Command[Any,Any,Any], bool], list[commands.Command[Any,Any,Any]]]
        -_uses_permission_system(command: commands.Command[Any,Any,Any]) bool
        -_is_owner_or_sysadmin() bool
        +find_command(command_name: str) commands.Command[Any,Any,Any]|None
        +find_parent_command(subcommand_name: str) commands.Command[Any,Any,Any]|None
    }

    class HelpNavigation {
        -data: HelpData
        -current_command_obj: commands.Command[Any,Any,Any]|None
        -current_command: str|None
        +async create_command_view() HelpView
    }

    class PermissionCommand {
        +guild_id: int
        +command_name: str
        +required_rank: int
    }

    HelpNavigation --> HelpData : uses
    HelpData --> PermissionSystem : uses
    PermissionSystem --> PermissionCommand : returns
Loading

Class diagram for configurable bot intents and HTTP configuration

classDiagram
    class Config {
        +BOT_INFO: BotInfo
        +BOT_INTENTS: BotIntents
        +USER_IDS: UserIds
        +DATABASE: DatabaseConfig
    }

    class BotIntents {
        +presences: bool
        +members: bool
        +message_content: bool
        +to_discord_intents() discord.Intents
    }

    class App {
        -config: Config
        +_create_bot_instance(owner_ids: set[int]) Tux
    }

    class Tux {
        +http: HTTPClient
        +first_ready: bool
        +guilds_registered: asyncio.Event
        +maintenance_mode: bool
        +guilds: list[discord.Guild]
        +async setup_hook() None
    }

    class HTTPClient {
        -_HTTPClient__session: aiohttp.ClientSession
        +ws_connect(url: str, **kwargs) Any
        +request(method: str, url: str, **kwargs) Any
    }

    class aiohttp.ClientSession {
        +connector: aiohttp.TCPConnector|None
    }

    class aiohttp.TCPConnector {
        +limit: int
        +limit_per_host: int
        +ttl_dns_cache: int
        +keepalive_timeout: float
    }

    class HttpConfigModule {
        +configure_discord_http_client(bot: Tux) None
    }

    Config --> BotIntents : owns
    App --> Config : uses
    App --> Tux : creates
    BotIntents --> "1" discord.Intents : builds
    Tux --> HTTPClient : has
    HTTPClient --> aiohttp.ClientSession : has
    aiohttp.ClientSession --> aiohttp.TCPConnector : has
    HttpConfigModule --> Tux : configures
Loading

Class diagram for new HTTP configuration helper and BaseCog messaging helper

classDiagram
    class BaseCog {
        +bot: Tux
        +db: DatabaseService
        +get_config(key: str, default: Any) Any
        +async send_after_defer(ctx: commands.Context[Tux], content: str|None, embed: discord.Embed|None, embeds: list[discord.Embed]|None, file: discord.File|None, files: list[discord.File]|None, ephemeral: bool, mention_author: bool) discord.Message|None
    }

    class HttpConfigModule {
        +configure_discord_http_client(bot: Tux) None
    }

    class Tux {
        +http: HTTPClient
        +first_ready: bool
        +async setup_hook() None
    }

    class HTTPClient {
        -_HTTPClient__session: aiohttp.ClientSession
    }

    class aiohttp.ClientSession {
        +connector: aiohttp.TCPConnector|None
    }

    class aiohttp.TCPConnector {
        +limit: int
        +limit_per_host: int
        +ttl_dns_cache: int
        +keepalive_timeout: float
    }

    BaseCog <|-- SomeCog
    BaseCog --> Tux : uses
    Tux --> HTTPClient : has
    HTTPClient --> aiohttp.ClientSession : has
    aiohttp.ClientSession --> aiohttp.TCPConnector : has
    HttpConfigModule --> Tux : configures
Loading

Flow diagram for on_ready startup, guild registration, and first_ready gating

flowchart TD
    A_bot_start["Bot start"] --> B_connect["discord.py connects to gateway"]
    B_connect --> C_on_ready_event["EventHandler.on_ready"]

    C_on_ready_event --> D_register_guilds["Register guilds in database"]
    D_register_guilds --> E_mark_registered["_guilds_registered = True"]
    E_mark_registered --> F_set_event["bot.guilds_registered.set()"]
    F_set_event --> G_first_ready_check{bot.first_ready?}

    G_first_ready_check -- "False (first time)" --> H_set_first["bot.first_ready = True"]
    H_set_first --> I_log_first["Log 'First on_ready event completed'"]

    G_first_ready_check -- "True (reconnect)" --> J_skip_first["Skip first_ready changes"]

    %% StatusRoles handler
    C_status_ready["StatusRoles.on_ready"] --> K_maintenance{bot.maintenance_mode?}
    K_maintenance -- "True" --> L_exit_status["Return (skip checks)"]
    K_maintenance -- "False" --> M_first_ready_status{bot.first_ready?}

    M_first_ready_status -- "False (first startup)" --> N_process_members["Process members in chunks per guild"]
    N_process_members --> O_chunk_loop["For each chunk of ~20 non-bot members"]
    O_chunk_loop --> P_gather["asyncio.gather(check_and_update_roles)"]
    P_gather --> Q_sleep["asyncio.sleep(0.1) between chunks"]
    Q_sleep --> O_chunk_loop
    O_chunk_loop --> R_done_status["Completed status role checks"]

    M_first_ready_status -- "True (not first)" --> S_skip_status["Log 'Skipping member check' and return"]

    %% Activity handler
    C_activity_ready["ActivityHandler.on_ready"] --> T_task_check{"_activity_task is None or done?"}
    T_task_check -- "True" --> U_sleep2["asyncio.sleep(2)"]
    U_sleep2 --> V_start_loop["Create task for _activity_loop()"]
    T_task_check -- "False" --> W_skip_activity["Do nothing (task already running)"]

    %% Synchronization via guilds_registered Event
    F_set_event --> C_status_ready
    F_set_event --> C_activity_ready
Loading

Flow diagram for db fix-sequences CLI command

flowchart TD
    A_cmd["User runs: scripts.db fix-sequences"] --> B_typer_entry["fix_sequences() (Typer command)"]
    B_typer_entry --> C_print_intro["Print intro and dry-run notice"]
    C_print_intro --> D_run_async["asyncio.run(_fix_sequences(dry_run))"]

    D_run_async --> E_create_service["Create DatabaseService(echo=False)"]
    E_create_service --> F_connect_db["connect(CONFIG.database_url)"]
    F_connect_db --> G_get_sequences["execute_query(_get_sequences, 'get_sequences')"]

    G_get_sequences --> H_any_sequences{Any sequences found?}
    H_any_sequences -- "No" --> I_print_none["Print 'No sequences found' and return"]
    H_any_sequences -- "Yes" --> J_iterate["Loop over each sequence"]

    J_iterate --> K_check_seq["execute_query(_check_and_fix_sequence, 'check_sequence')"]
    K_check_seq --> L_discord_check{"max_id >= DISCORD_SNOWFLAKE_MIN?"}

    L_discord_check -- "Yes" --> M_skip_discord["Print skip message (Discord ID)" ]
    M_skip_discord --> J_iterate

    L_discord_check -- "No" --> N_needs_fix{"next_value <= max_id?"}
    N_needs_fix -- "No" --> O_in_sync["Print sequence in sync"]
    O_in_sync --> J_iterate

    N_needs_fix -- "Yes" --> P_compute_new["new_value = max(max_id, 1)"]
    P_compute_new --> Q_dry_run{dry_run?}

    Q_dry_run -- "True" --> R_print_dry["Print '[DRY RUN] Would reset' message"]
    R_print_dry --> J_iterate

    Q_dry_run -- "False" --> S_reset_call["execute_query(_reset_sequence, 'reset_sequence')"]
    S_reset_call --> T_commit["setval(sequence, new_value, true) and commit"]
    T_commit --> U_inc_counter["Increment fixes_applied and print success for sequence"]
    U_inc_counter --> J_iterate

    J_iterate --> V_done_all["Loop finished"]
    V_done_all --> W_summary{fixes_applied > 0?}

    W_summary -- "Yes" --> X_print_fixed["print_success('Fixed N sequence(s)')"]
    W_summary -- "No and dry_run" --> Y_print_dry_all["Print 'All sequences are in sync (dry run)' "]
    W_summary -- "No and not dry_run" --> Z_print_ok["print_success('All sequences are already in sync')"]

    X_print_fixed --> AA_disconnect["disconnect()"]
    Y_print_dry_all --> AA_disconnect
    Z_print_ok --> AA_disconnect

    AA_disconnect --> AB_exit["Return from CLI"]
Loading

File-Level Changes

Change Details Files
Refactor self-timeout and AFK-related moderation flows to support interactions cleanly and reduce duplication.
  • Extract helpers for error messaging, duration validation, confirmation dialogs, DM notifications, and applying timeouts in self_timeout cog
  • Unify AFK clear and AFK commands to send via interaction followups when applicable
  • Ensure early ctx.defer(ephemeral=True) and correct ephemeral behavior in moderation flows involving AFK and self-timeouts
src/tux/modules/utility/self_timeout.py
src/tux/modules/utility/afk.py
src/tux/modules/moderation/clearafk.py
Improve hybrid command handling and interaction support across info, starboard, xkcd, levels, avatar, wiki, remindme and moderation cogs.
  • Add helpers to route responses between ctx.interaction.followup and ctx.send/ctx.reply, ensuring ephemeral messages for slash commands
  • Defer interactions early in many commands (cases, levels, polls, snippets, moderation actions, xkcd, wiki, remindme, avatar, level, afk) to avoid timeouts
  • Adjust embed sending and error paths to respect ephemeral and interaction contexts consistently
src/tux/modules/info/info.py
src/tux/modules/features/starboard.py
src/tux/modules/fun/xkcd.py
src/tux/modules/levels/levels.py
src/tux/modules/info/avatar.py
src/tux/modules/utility/wiki.py
src/tux/modules/utility/remindme.py
src/tux/modules/moderation/cases.py
src/tux/modules/moderation/pollban.py
src/tux/modules/moderation/pollunban.py
src/tux/modules/moderation/snippetban.py
src/tux/modules/moderation/snippetunban.py
src/tux/modules/moderation/ban.py
src/tux/modules/moderation/kick.py
src/tux/modules/moderation/tempban.py
src/tux/modules/moderation/timeout.py
src/tux/modules/moderation/untimeout.py
src/tux/modules/moderation/warn.py
src/tux/core/base_cog.py
Optimize help/permission system by batching permission checks and database queries.
  • Introduce batch_can_run_commands in HelpData with caching of user rank and filtering logic for commands needing checks
  • Add batch_get_command_permissions to PermissionSystem to resolve command and parent permissions in a single query
  • Refactor help navigation and help command to use batched permission checks for subcommands instead of per-command checks
src/tux/help/data.py
src/tux/core/permission_system.py
src/tux/help/navigation.py
src/tux/help/help.py
Improve startup/on_ready behavior and HTTP client configuration for high-latency environments.
  • Add bot.first_ready flag and use it in StatusRoles and Event handlers to only perform heavy member scans once
  • Batch StatusRoles member processing with asyncio.gather, chunking, and sleeps to reduce rate-limit pressure
  • Introduce http_config.configure_discord_http_client and call it from Tux.setup_hook to tune aiohttp connector properties where possible
  • Ensure activity handler only starts rotation once and waits briefly before starting the loop
src/tux/modules/features/status_roles.py
src/tux/services/handlers/event.py
src/tux/services/handlers/activity.py
src/tux/core/bot.py
src/tux/core/http_config.py
Make Discord intents configurable via settings and wire them into bot creation.
  • Add BotIntents model with presences, members, and message_content flags plus to_discord_intents helper
  • Expose BOT_INTENTS in global config and use it in app._create_bot_instance instead of Intents.all()
  • Update related config typing imports for discord when type-checking
src/tux/shared/config/models.py
src/tux/shared/config/settings.py
src/tux/core/app.py
Harden database case creation and add a CLI to repair out-of-sync PostgreSQL sequences.
  • Filter out id from case creation kwargs to prevent manual ID injection and log when ignored
  • Add scripts.db.fix_sequences command that discovers sequences, compares them with table max IDs, and optionally resets them (with dry-run mode), skipping Discord snowflake-based IDs
  • Register fix_sequences in db CLI entrypoint
src/tux/database/controllers/case.py
scripts/db/fix_sequences.py
scripts/db/__init__.py
Add utility for safe user fetching and optimize user-related info flows.
  • Add get_user_safe helper that checks cache before REST fetch and handles errors
  • Switch member info to use member.user for banner instead of fetch_user to avoid extra HTTP calls
src/tux/core/converters.py
src/tux/modules/info/info.py
Make various embeds/outputs interaction-aware and more robust in moderation and info flows.
  • Adjust guild, member, user, channel, role, emoji, sticker, message, invite, thread, event info handlers to send via interaction followup when applicable
  • Ensure cases views, modifications, and error paths use followup for interactions and distinguish between ephemeral and non-ephemeral cases
  • Improve error messaging for guild lookup and not-found cases in info command, using ephemeral slash responses
src/tux/modules/info/info.py
src/tux/modules/moderation/cases.py
Upgrade Python toolchain references from 3.13.8 to 3.13.11 and add mise support.
  • Bump PYTHON_VERSION in all GitHub workflows and custom setup-python composite action
  • Update Containerfile base image tag and related docs to 3.13.11-slim
  • Adjust pre-commit default python version and add mise.toml with python=3.13.11
  • Update docs references to match new Python version
.github/workflows/tests.yml
.github/workflows/ci.yml
.github/workflows/docker-push.yml
.github/workflows/docker.yml
.github/workflows/docs.yml
.github/workflows/security.yml
.github/actions/setup-python/action.yml
.pre-commit-config.yaml
Containerfile
docs/content/developer/best-practices/ci-cd.md
docs/content/reference/docker-production.md
docs/content/reference/renovate.md
docs/content/selfhost/config/database.md
docs/content/selfhost/manage/docker.md
mise.toml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

Caution

Review failed

The pull request is closed.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Bumps Python from 3.13.8 → 3.13.11 across CI/Docker/docs; adds a DB sequence-repair CLI and Alembic indexes; introduces TTL-based guild config caching; adds intent config and HTTP tuning; large refactors to interaction handling (defer + followup), permission batching, moderation services, UI/help changes, logging, and many new helpers.

Changes

Cohort / File(s) Summary
Python version & CI/Docker
.github/actions/setup-python/action.yml, .github/workflows/ci.yml, .github/workflows/docker-push.yml, .github/workflows/docker.yml, .github/workflows/docs.yml, .github/workflows/security.yml, .github/workflows/tests.yml, .python-version, Containerfile, docs/...
Updated Python references and Docker base image from 3.13.83.13.11 across actions, workflows, Containerfile, and docs.
Project tooling
mise.toml
Added mise.toml specifying Python 3.13.11.
DB repair, migrations & models
scripts/db/fix_sequences.py, scripts/db/__init__.py, src/tux/database/migrations/versions/..._add_case_perf_indexes.py, src/tux/database/models/models.py, src/tux/database/controllers/case.py
New fix-sequences Typer CLI (dry-run/apply) to sync Postgres sequences; added Alembic migration and model indexes for cases; prevent manual id passthrough in create_case.
Guild config caching
src/tux/shared/cache.py, src/tux/shared/__init__.py, src/tux/database/controllers/guild_config.py, src/tux/services/moderation/communication_service.py, src/tux/ui/views/config/dashboard.py
Added TTLCache and GuildConfigCacheManager; integrated cache into GuildConfigController and CommunicationService with cache invalidation on updates.
Bot intents & HTTP tuning
src/tux/shared/config/models.py, src/tux/shared/config/settings.py, src/tux/core/app.py, src/tux/core/bot.py, src/tux/core/http_config.py
New BotIntents config and BOT_INTENTS field; bot builds intents from config and sets explicit status; added configure_discord_http_client to tune aiohttp connector; added first_ready flag.
Core helpers & converters
src/tux/core/base_cog.py, src/tux/core/converters.py, src/tux/core/decorators.py
Added BaseCog.send_after_defer, get_user_safe (cache-first user resolver), and improved decorator logging/guarding with command-name context.
Permission batching & help/navigation
src/tux/core/permission_system.py, src/tux/help/data.py, src/tux/help/help.py, src/tux/help/navigation.py, src/tux/help/renderer.py
Added batch_get_command_permissions and HelpData.batch_can_run_commands; replaced many per-command permission awaits with batched checks; help renderer/navigation manage banner attachments and image handling.
Interaction handling & unified responders
many src/tux/modules/*, src/tux/modules/moderation/__init__.py
Widespread: early ctx.defer for interactions, use of interaction.followup.send (ephemeral) with fallbacks, and many new helpers (_respond, _send_response, validation and send helpers) to unify slash vs prefix behavior.
Moderation services & coordinator
src/tux/services/moderation/execution_service.py, src/tux/services/moderation/moderation_coordinator.py, src/tux/services/moderation/communication_service.py
ExecutionService: LRU + circuit cleanup and refined failure semantics. ModerationCoordinator: parallelized case creation, embed/mod-log flows and new helpers. CommunicationService: uses guild-config cache and conditional response routing.
Events, activity & status
src/tux/services/handlers/event.py, src/tux/services/handlers/activity.py, src/tux/modules/features/status_roles.py, src/tux/modules/features/starboard.py
first_ready handling, startup activity delay, batched member processing for status roles, starboard send/validation helper extraction.
Error handling & exceptions
src/tux/services/handlers/error/cog.py, tests/services/test_error_handler.py, src/tux/shared/exceptions.py
Error handler _log_error now accepts source context and logs richer context; tests updated; TuxPermissionDeniedError now also inherits commands.CheckFailure and app_commands.CheckFailure.
UI, tools & other large features
src/tux/modules/utility/ping.py, src/tux/modules/info/*, src/tux/modules/levels/*, src/tux/modules/moderation/*, src/tux/services/sentry/tracing.py, src/tux/services/hot_reload/*
Significant refactors: ping command UI view, info command converter/resolution flow, many moderation command defers/responses, Sentry wrapper preservation, and reduced hot-reload log verbosity.
Tests & small misc
tests/..., src/tux/core/*, various small modules
Small test updates, logging/verbosity tweaks, re-exports (TTLCache), and assorted minor API/type annotations and doc changes.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant CLI
participant DB
participant Console
CLI->>DB: query serial-backed sequences
DB-->>CLI: list of sequences (sequence, table, column)
loop per-sequence
CLI->>DB: SELECT last_value,is_called FROM sequence
DB-->>CLI: current_seq,is_called
CLI->>DB: SELECT MAX(column) FROM table
DB-->>CLI: max_id
alt needs fix (next_value <= max_id and not discord snowflake)
CLI->>Console: log planned reset (dry-run or apply)
opt apply
CLI->>DB: SELECT setval(sequence, new_value, true)
DB-->>CLI: confirmation
end
else
CLI->>Console: log skip
end
end
CLI->>Console: print summary

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • Misc refactorings and optimizations #1160 — Large overlapping PR touching Python version bumps, BaseCog send/deferral changes, error handler signature/logging, TTLCache and guild-config caching, and many of the same module refactors.
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Misc refactorings and optimizations' is vague and uses a non-descriptive term ('Misc') that does not convey meaningful information about the changeset. Provide a more specific title that highlights a primary change, such as 'Add configurable Discord intents and interaction handling improvements' or 'Refactor command interaction handling and add sequence maintenance tooling'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering the PR objectives, new features, enhancements, build changes, deployment updates, and documentation changes.
Docstring Coverage ✅ Passed Docstring coverage is 98.73% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/optimizations

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45d7d56 and 86cd9a1.

📒 Files selected for processing (6)
  • src/tux/help/navigation.py
  • src/tux/modules/moderation/slowmode.py
  • src/tux/modules/moderation/unban.py
  • src/tux/modules/utility/ping.py
  • src/tux/modules/utility/wiki.py
  • src/tux/services/moderation/moderation_coordinator.py

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 22, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

…eval

- Added a method to retrieve the current database revision from the alembic_version table, improving reliability over previous methods.
- Updated migration logic to utilize ScriptDirectory for fetching head revisions, ensuring accurate migration management.
- Improved logging for current and head revisions to aid in debugging and monitoring migration status.
- Modified logging messages to correctly use "cog" instead of "extension" for folder names other than "plugins".
- Ensured consistent terminology in log outputs for better clarity when reporting loaded plugins and cogs.
- Removed unnecessary logging during cache invalidation to enhance performance and reduce clutter.
- Updated the delete method to automatically invalidate the guild config cache upon successful deletion, improving cache management consistency.
- Added functionality to attach a help banner image when sending the main help embed, improving visual appeal.
- Ensured that no images are set on paginated and category embeds to prevent unwanted visuals during navigation.
- Updated embed editing logic to handle image attachments correctly, enhancing user experience during help command interactions.
…erral

- Added a decorator to require command permissions for the levels command, ensuring proper access control.
- Updated the deferral logic to check for interactions before deferring, improving responsiveness during command execution.
- Introduced a new `_respond` method in `ModerationCogBase` to streamline response messaging for both slash and prefix commands.
- Updated the `cases` command to accept a user or case number as an argument, improving flexibility in case retrieval.
- Enhanced error handling for user and case number lookups, providing clearer feedback when no matches are found.
- Refactored the `_view_cases_with_flags` method to accept a broader range of flag types, improving its versatility.
- Removed the alias "man" from the tldr command to simplify command usage and avoid potential confusion.
…tatistics

- Added methods to retrieve and format system resource statistics, including CPU and RAM usage.
- Implemented a human-readable uptime formatter for better clarity on bot uptime.
- Enhanced the command response to include detailed bot statistics such as guild and user counts, sharding information, and gateway intents.
- Improved the command's output format using a structured layout view for better user experience.
- Updated the query_wiki method to filter search results for English pages by excluding titles with language codes in parentheses.
- Increased the search limit to 50 results to improve the chances of finding relevant English entries.
- Improved logging to provide more detailed information during API requests and responses.
… handling

- Changed the actions list to use type hinting for better clarity.
- Simplified the logic for appending the "not sent to Sentry" action to ensure it is always included when applicable.
…service

- Updated logging messages to use more concise and clear language, enhancing readability.
- Changed several info logs to debug level for less verbosity during normal operation.
- Improved error handling messages for better clarity on failures and issues during extension reloads.
- Removed redundant logging statements to streamline the code and focus on essential information.
…n coordinator

- Enhanced exception handling during the sending of response and mod log embeds, ensuring that errors are logged with detailed context.
- Updated the logic for updating the mod log message ID to only proceed if the embed was successfully sent, improving reliability.
- Streamlined the code for clarity and maintainability, focusing on capturing and reporting errors effectively.
…umentation

- Introduced a `create_wrapper` function to streamline the wrapping of command callbacks, preserving function signatures and metadata for better compatibility with discord.py.
- Improved transaction handling by ensuring original callbacks are called with the correct arguments and context.
- Added checks to prevent double-wrapping of commands already instrumented with Sentry, enhancing reliability.
- Preserved original parameters and cog information to maintain functionality for bound methods and subcommands.
- Enhanced logging for better visibility into the command instrumentation process.
- Modified the _log_error method to include a mock source parameter for improved error logging context.
- Updated test cases to reflect the new method signature, ensuring proper testing of error handling with Sentry enabled and disabled.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/tux/services/moderation/moderation_coordinator.py (1)

188-199: Don’t swallow Discord action failures.

This except logs and then continues, which can create a case and emit “Completed moderation action” even when actions failed. It also bypasses the centralized error handler described in _execute_actions. Prefer propagating the failure and canceling the case task so partial actions don’t look successful.

🛠️ Suggested fix
             try:
                 await self._execute_actions(ctx, case_type, user, actions)
                 logger.success(
                     f"Successfully executed Discord actions for {case_type.value}",
                 )
             except Exception as e:
                 logger.error(
                     f"Failed to execute Discord actions for {case_type.value}: {e}",
                     exc_info=True,
                 )
+                case_task.cancel()
+                raise
# Add at top of file if you want to await the cancellation cleanly
# import contextlib
src/tux/modules/moderation/slowmode.py (3)

82-117: Prevent misleading parsing when a channel fails to resolve.

If a user supplies both a channel and a delay (e.g., slowmode general 10) and the channel cannot be resolved, the current logic ignores the provided delay and instead treats the channel string as the delay, leading to confusing errors. Consider failing fast with a clear “channel not found” response when delay is provided but target_channel is None.

Proposed fix
         target_channel = (
             await self._resolve_channel(ctx, channel_or_delay)
             if channel_or_delay
             else None
         )
+        if delay is not None and channel_or_delay is not None and target_channel is None:
+            await Slowmode._send_response(
+                ctx,
+                f"Channel not found: {channel_or_delay}",
+            )
+            return
 
         # If first argument was a channel, use second argument as delay
         delay_value = delay if target_channel else channel_or_delay

292-357: Don’t expose raw exception details to users.

Both _get_slowmode and _set_slowmode currently echo exception messages to users. Prefer a generic user-facing message while logging the exception details for diagnostics. As per coding guidelines, avoid leaking internal errors to end users and log with context.

Proposed fix
-        except Exception as error:
-            logger.error(f"Failed to get slowmode: {error}")
-            await Slowmode._send_response(ctx, f"Failed to get slowmode: {error}")
+        except Exception:
+            logger.exception("Failed to get slowmode")
+            await Slowmode._send_response(
+                ctx,
+                "Failed to get slowmode. Please try again later.",
+            )
@@
-        except discord.HTTPException as error:
-            await Slowmode._send_response(ctx, f"Failed to set slowmode: {error}")
-            logger.error(f"Failed to set slowmode: {error}")
+        except discord.HTTPException:
+            logger.exception("Failed to set slowmode")
+            await Slowmode._send_response(
+                ctx,
+                "Failed to set slowmode. Please try again later.",
+            )

41-53: Add a cooldown/rate limit decorator for the slowmode command.

The command lacks rate limiting despite src/tux/modules/**/*.py guidelines requiring cooldowns for Discord commands. Add a @commands.cooldown() decorator or implement a shared rate-limiting policy to prevent abuse.

src/tux/modules/moderation/unban.py (1)

100-100: Remove the unused second element from the actions tuple or implement exception handling for it.

The tuple (lambda: guild.unban(user, reason=final_reason), type(None)) includes a second element that is never used. In _execute_actions (line 372 of moderation_coordinator.py), the tuple is unpacked as (action, _expected_type) but _expected_type is immediately discarded and never referenced. Either the second element should be removed from the tuple format, or the error handling logic should be implemented to actually use it.

🤖 Fix all issues with AI agents
In `@src/tux/help/navigation.py`:
- Around line 287-295: filtered_cmds is computed from
self.current_command_obj.commands using can_run_map but the pagination still
uses self.subcommand_pages built from the unfiltered list (see
handle_command_select), so users can see commands they cannot run; fix by
repaginating from filtered_cmds before rendering: after building filtered_cmds
(from self.data.batch_can_run_commands and self.current_command_obj.commands)
regenerate self.subcommand_pages (or pass a new paginated list) so
handle_command_select and the initial embed/dropdown use the filtered_cmds list
rather than the original commands list.

In `@src/tux/modules/utility/ping.py`:
- Around line 70-73: The cpu_percent(interval=...) calls in the async ping()
command (and the same pattern later around the block at lines 257-260) block the
event loop; wrap these blocking calls in asyncio.to_thread so the work runs in a
threadpool instead of blocking the async flow. Locate the cpu usage checks
(references: self._process.cpu_percent(interval=None) and
self._process.cpu_percent(interval=0.3)) inside the ping() implementation and
replace the direct calls with await asyncio.to_thread(self._process.cpu_percent,
<interval>) (keeping the same intervals/logic), and apply the identical change
to the other cpu_percent usage block so all blocking psutil calls run off the
event loop.
- Line 249: Guard the slash-only ephemeral defer by wrapping the existing await
ctx.defer(ephemeral=True) with an if ctx.interaction: check so prefix
(non-interaction) invocations don't pass the ephemeral kwarg; locate and update
the command method that calls ctx.defer. Also make the blocking cpu_percent call
in _get_system_stats non-blocking by running self._process.cpu_percent(0.3) in
an executor (e.g., asyncio.get_event_loop().run_in_executor or
asyncio.to_thread) and await the result, replacing the direct call to
self._process.cpu_percent.

In `@src/tux/modules/utility/wiki.py`:
- Around line 103-133: The language_pattern is too broad and excludes any title
with parentheses early in the title; update the filter in wiki.py by narrowing
language_pattern to match only known translation language markers instead of any
parentheses — e.g., replace the generic re.compile(r"^[^/]*\([^)]+\)") with a
regex that only matches a whitelist of language names/markers (like
Italiano|Español|Français|Deutsch|Português|Русский|日本語|中文|한국어|Polski|Türkçe|العربية|فارسی|हिन्दी)
anchored to the start or immediately before a slash, and keep the rest of the
logic in the loop that checks titles (the block that iterates search_results and
uses language_pattern.search(title) to decide whether to skip) so only known
translation tags are filtered out, avoiding false negatives for valid English
pages.
- Around line 182-184: The code calls await ctx.defer(ephemeral=True)
unconditionally which raises when ctx.interaction is None (prefix invocations);
update each occurrence (the defer at the line with ctx.defer and the other
occurrences around lines 208-210) to first check if ctx.interaction is truthy
and only then call await ctx.defer(ephemeral=True) — i.e., replace the
unconditional defer with a guarded defer (if ctx.interaction: await
ctx.defer(ephemeral=True)) so prefix/non-interaction invocations don't error.
♻️ Duplicate comments (1)
src/tux/modules/moderation/cases.py (1)

154-156: Potential double‑respond when deferring before ViewMenu.start().
If ViewMenu.start() uses interaction.response.send_message, deferring earlier can trigger InteractionResponded. Please confirm ReactionMenu’s behavior for deferred interactions and adjust (e.g., avoid defer for menu flows or ensure followup is used).

reactionmenu ViewMenu.start interaction defer followup behavior

Also applies to: 715-760

🧹 Nitpick comments (13)
src/tux/services/sentry/tracing.py (3)

718-722: Redundant __wrapped__ check.

The functools.wraps decorator (line 694) always sets __wrapped__ on the wrapper function, so this condition will never be true. The defensive comment is appreciated, but the check can be removed.

♻️ Suggested simplification
-        # Ensure __wrapped__ is set explicitly (functools.wraps should do this, but be explicit)
-        # This allows discord.py's unwrap_function() to find the original callback
-        if not hasattr(wrapped, "__wrapped__"):
-            wrapped.__wrapped__ = original_callback
+        # functools.wraps sets __wrapped__, allowing discord.py's unwrap_function()
+        # to find the original callback for signature inspection

725-734: Fragile double-wrapping detection heuristic.

The logic assumes that double-wrapped callbacks already have Sentry instrumentation, but other decorators (custom validators, rate limiters, logging decorators) could also wrap callbacks. This could incorrectly skip Sentry instrumentation for legitimate commands.

Consider using a specific marker attribute to reliably detect Sentry-instrumented commands.

♻️ Suggested approach using a marker attribute
+    # Marker attribute to detect Sentry-instrumented callbacks
+    SENTRY_INSTRUMENTED_MARKER = "_sentry_instrumented"
+
     for cmd in bot.walk_commands():
-        # Skip if already wrapped by Sentry (check for our wrapper)
-        # Commands may already be wrapped by `@requires_command_permission`, which is fine
-        if hasattr(cmd.callback, "__wrapped__"):
-            # Check if it's already wrapped by Sentry (double-wrapped = permission + sentry)
-            inner = getattr(cmd.callback, "__wrapped__", None)
-            if inner and hasattr(inner, "__wrapped__"):
-                # Check if the inner wrapper is from Sentry by checking for our transaction
-                # This is a heuristic - if it's already double-wrapped, assume it's done
-                continue
+        # Skip if already instrumented by Sentry
+        if getattr(cmd.callback, SENTRY_INSTRUMENTED_MARKER, False):
+            continue

Then in create_wrapper, add the marker to the wrapped function:

        wrapped._sentry_instrumented = True  # type: ignore[attr-defined]
        return wrapped

725-792: Avoid walking commands twice for counting.

bot.walk_commands() is called twice: once in the loop and once for logging. Track the count during iteration to avoid redundant traversal.

♻️ Suggested fix
+    instrumented_count = 0
     for cmd in bot.walk_commands():
         # ... (existing loop body)
+        instrumented_count += 1

-    logger.info(f"Instrumented {len(list(bot.walk_commands()))} commands with Sentry.")
+    logger.info(f"Instrumented {instrumented_count} commands with Sentry.")
src/tux/core/setup/database_setup.py (2)

95-97: Add debug logging for the exception case.

Per coding guidelines, errors should be logged with context. While returning None for a missing alembic_version table is correct behavior, silently swallowing all exceptions (including connection errors, permission issues, etc.) makes troubleshooting harder.

♻️ Proposed fix
         except Exception:
             # Table doesn't exist or query failed - no migrations applied yet
+            logger.debug("Could not query alembic_version table (may not exist yet)")
             return None

128-128: Prefer asyncio.get_running_loop() in async context.

Since this method is async, there's guaranteed to be a running loop. Using get_running_loop() is more explicit about this assumption and is the preferred approach.

♻️ Proposed fix
-        loop = asyncio.get_event_loop()
+        loop = asyncio.get_running_loop()
src/tux/modules/levels/levels.py (1)

66-68: Inconsistent decorator order compared to sibling commands.

The set command uses @commands.guild_only()@requires_command_permission()@levels.command(...), while setxp, reset, and blacklist all use the opposite order: @requires_command_permission()@commands.guild_only()@levels.command(...).

For consistency and to avoid potential ordering-related bugs with check execution, align with the pattern used by the other subcommands.

Suggested fix
-    `@commands.guild_only`()
     `@requires_command_permission`()
+    `@commands.guild_only`()
     `@levels.command`(name="set", aliases=["s"])
     async def set(
src/tux/services/hot_reload/watcher.py (1)

17-23: Align _shorten_path docstring with NumPy style.

Project guidelines call for NumPy-format docstrings, so this helper should follow that format for consistency. As per coding guidelines, please update the docstring.

♻️ Suggested update
-def _shorten_path(path: Path, base: Path | None = None) -> str:
-    """Return path relative to base (default cwd) when possible, else the path as-is."""
+def _shorten_path(path: Path, base: Path | None = None) -> str:
+    """
+    Shorten a path for logging.
+
+    Parameters
+    ----------
+    path : Path
+        Path to shorten.
+    base : Path | None, optional
+        Base directory used for relativizing, by default None.
+
+    Returns
+    -------
+    str
+        Relative path when possible, otherwise the original path.
+    """
src/tux/services/moderation/moderation_coordinator.py (1)

530-565: Align new helper docstrings to NumPy format.

These helpers use single-line docstrings; please update them to NumPy style to match project guidelines. The Expires field addition looks good. As per coding guidelines, please keep all function docstrings in NumPy format.

♻️ Example docstring updates
 async def _send_response_embed(
     self,
     ctx: commands.Context[Tux],
     case: Case | None,
     user: discord.Member | discord.User,
     dm_sent: bool,
 ) -> None:
-    """Send the response embed for the moderation action."""
+    """
+    Send the response embed for the moderation action.
+
+    Parameters
+    ----------
+    ctx : commands.Context[Tux]
+        Command context.
+    case : Case | None
+        The moderation case, or None if creation failed.
+    user : discord.Member | discord.User
+        Target user.
+    dm_sent : bool
+        Whether DM was sent.
+    """
@@
 async def _send_mod_log_embed(
     self,
     ctx: commands.Context[Tux],
     case: Case,
     user: discord.Member | discord.User,
     dm_sent: bool,
 ) -> discord.Message | None:
-    """Send the response embed to the mod log channel."""
+    """
+    Send the response embed to the mod log channel.
+
+    Parameters
+    ----------
+    ctx : commands.Context[Tux]
+        Command context.
+    case : Case
+        The moderation case.
+    user : discord.Member | discord.User
+        Target user.
+    dm_sent : bool
+        Whether DM was sent.
+
+    Returns
+    -------
+    discord.Message | None
+        The created mod-log message, if sent.
+    """
src/tux/modules/moderation/__init__.py (2)

176-198: Consider adding a docstring note about the deferral precondition.

The _respond method assumes that when ctx.interaction exists, the interaction has already been deferred (since it calls followup.send). While the current callers in jail.py and unjail.py correctly defer before calling this method, the docstring should clarify this precondition for future maintainers.

📝 Suggested docstring enhancement
     async def _respond(
         self,
         ctx: commands.Context[Tux],
         message: str,
         *,
         ephemeral: bool = True,
     ) -> None:
         """Send a response message, handling both slash and prefix commands.

+        Note: For slash commands, this method expects the interaction to have
+        been deferred already (via `ctx.defer()`), as it uses `followup.send()`.
+
         Parameters
         ----------
         ctx : commands.Context[Tux]
             The command context.
         message : str
             The message content to send.
         ephemeral : bool, optional
             Whether the message should be ephemeral (slash commands only), by default True.
         """

194-198: Consider delegating to send_after_defer to reduce duplication.

BaseCog.send_after_defer (lines 216-294 in src/tux/core/base_cog.py) provides similar functionality with additional features (embeds, files). The _respond method could delegate to it for consistency:

♻️ Optional refactor to reduce duplication
-        if ctx.interaction:
-            await ctx.interaction.followup.send(message, ephemeral=ephemeral)
-        else:
-            await ctx.reply(message, mention_author=False)
+        await self.send_after_defer(ctx, message, ephemeral=ephemeral, mention_author=False)

This would centralize the dispatch logic and ensure consistent behavior if send_after_defer is enhanced in the future.

src/tux/modules/moderation/unjail.py (1)

36-51: Consider moving get_jail_role to ModerationCogBase to eliminate duplication.

The get_jail_role method is identical in both jail.py (lines 45-60) and unjail.py (lines 36-51). Since both cogs inherit from ModerationCogBase, this method could be defined once in the base class.

♻️ Proposed refactor

Move to src/tux/modules/moderation/__init__.py in ModerationCogBase:

async def get_jail_role(self, guild: discord.Guild) -> discord.Role | None:
    """Get the jail role for the guild.

    Parameters
    ----------
    guild : discord.Guild
        The guild to get the jail role for.

    Returns
    -------
    discord.Role | None
        The jail role, or None if not found.
    """
    jail_role_id = await self.db.guild_config.get_jail_role_id(guild.id)
    return None if jail_role_id is None else guild.get_role(jail_role_id)

Then remove the duplicate implementations from jail.py and unjail.py.

src/tux/modules/moderation/unban.py (1)

144-150: Inconsistent response helper usage across moderation commands.

This file uses send_after_defer from BaseCog, while jail.py and unjail.py use _respond from ModerationCogBase. Both achieve similar goals but with different interfaces:

  • _respond(ctx, message, ephemeral=True) - simpler, message-only
  • send_after_defer(ctx, content, ephemeral=True, mention_author=False) - supports embeds, files

For consistency within the moderation module, consider using the same helper across all commands. Either:

  1. Use _respond here (simpler for text-only messages), or
  2. Update _respond to delegate to send_after_defer and use it everywhere
♻️ Option 1: Use _respond for consistency
-                await self.send_after_defer(
-                    ctx,
-                    f"Could not find '{username_or_id}' in the ban list. "
-                    "Try using the exact username or ID.",
-                    ephemeral=True,
-                    mention_author=False,
-                )
+                await self._respond(
+                    ctx,
+                    f"Could not find '{username_or_id}' in the ban list. "
+                    "Try using the exact username or ID.",
+                )
-            await self.send_after_defer(
-                ctx,
-                f"{user} is not banned.",
-                ephemeral=True,
-                mention_author=False,
-            )
+            await self._respond(ctx, f"{user} is not banned.")

Also applies to: 158-163

src/tux/help/navigation.py (1)

591-597: Consider centralizing the help banner path.

The same hardcoded banner path now appears in multiple places; a shared constant/helper would avoid drift and reduce repeated filesystem checks.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c4ea44 and 45d7d56.

⛔ Files ignored due to path filters (1)
  • assets/branding/help_banner.png is excluded by !**/*.png
📒 Files selected for processing (23)
  • src/tux/core/cog_loader.py
  • src/tux/core/setup/database_setup.py
  • src/tux/database/controllers/guild_config.py
  • src/tux/help/help.py
  • src/tux/help/navigation.py
  • src/tux/help/renderer.py
  • src/tux/modules/levels/levels.py
  • src/tux/modules/moderation/__init__.py
  • src/tux/modules/moderation/cases.py
  • src/tux/modules/moderation/clearafk.py
  • src/tux/modules/moderation/jail.py
  • src/tux/modules/moderation/slowmode.py
  • src/tux/modules/moderation/unban.py
  • src/tux/modules/moderation/unjail.py
  • src/tux/modules/tools/tldr.py
  • src/tux/modules/utility/ping.py
  • src/tux/modules/utility/wiki.py
  • src/tux/services/handlers/error/cog.py
  • src/tux/services/hot_reload/service.py
  • src/tux/services/hot_reload/watcher.py
  • src/tux/services/moderation/moderation_coordinator.py
  • src/tux/services/sentry/tracing.py
  • tests/services/test_error_handler.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/tux/help/help.py
  • src/tux/database/controllers/guild_config.py
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use strict type hints with Type | None syntax instead of Optional[Type] in Python
Use NumPy docstring format for documenting Python functions and classes
Prefer absolute imports, with relative imports allowed only within the same module
Organize imports in order: stdlib → third-party → local, with imports at the top of the file unless absolutely necessary
Use 88 character line length for Python code
Use snake_case for functions and variables, PascalCase for classes, UPPER_CASE for constants in Python
Ensure all type hints are complete for function signatures
Add docstrings for all public APIs
Keep Python files to a maximum of 1600 lines
Use async/await for I/O operations instead of blocking calls
Use custom exceptions for business logic errors
Log with context when handling errors
Do not store secrets in code, use environment variables for configuration
Validate all user inputs in Python code
One class or function per file when possible

**/*.py: Follow input validation patterns from security/validation.mdc
Follow error patterns from error-handling/patterns.mdc
Follow logging patterns using loguru from error-handling/logging.mdc
Follow Sentry integration patterns from error-handling/sentry.mdc
Follow user-facing error message patterns from error-handling/user-feedback.mdc
Follow Discord.py Components V2 rules from ui/cv2.mdc

Files:

  • src/tux/modules/utility/ping.py
  • src/tux/modules/moderation/__init__.py
  • src/tux/services/sentry/tracing.py
  • src/tux/services/hot_reload/service.py
  • src/tux/services/handlers/error/cog.py
  • src/tux/modules/moderation/jail.py
  • src/tux/help/navigation.py
  • src/tux/services/hot_reload/watcher.py
  • src/tux/modules/moderation/clearafk.py
  • src/tux/modules/utility/wiki.py
  • src/tux/modules/moderation/cases.py
  • src/tux/modules/levels/levels.py
  • src/tux/modules/moderation/unban.py
  • src/tux/help/renderer.py
  • src/tux/modules/tools/tldr.py
  • src/tux/core/setup/database_setup.py
  • src/tux/modules/moderation/unjail.py
  • src/tux/services/moderation/moderation_coordinator.py
  • tests/services/test_error_handler.py
  • src/tux/core/cog_loader.py
  • src/tux/modules/moderation/slowmode.py
src/tux/modules/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/tux/modules/**/*.py: Use hybrid commands (slash and traditional) for Discord bot commands
Implement role-based permissions for Discord commands
Implement cooldowns and rate limiting for Discord commands

Files:

  • src/tux/modules/utility/ping.py
  • src/tux/modules/moderation/__init__.py
  • src/tux/modules/moderation/jail.py
  • src/tux/modules/moderation/clearafk.py
  • src/tux/modules/utility/wiki.py
  • src/tux/modules/moderation/cases.py
  • src/tux/modules/levels/levels.py
  • src/tux/modules/moderation/unban.py
  • src/tux/modules/tools/tldr.py
  • src/tux/modules/moderation/unjail.py
  • src/tux/modules/moderation/slowmode.py
src/tux/services/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/tux/services/**/*.py: Use dependency injection in service classes
Keep service classes stateless where possible and use async/await for I/O
Use caching for frequently accessed data

Files:

  • src/tux/services/sentry/tracing.py
  • src/tux/services/hot_reload/service.py
  • src/tux/services/handlers/error/cog.py
  • src/tux/services/hot_reload/watcher.py
  • src/tux/services/moderation/moderation_coordinator.py
**/*service*.py

📄 CodeRabbit inference engine (.cursor/rules/rules.mdc)

Follow DatabaseService patterns from database/services.mdc

Files:

  • src/tux/services/hot_reload/service.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Use pytest markers (unit, integration, slow, database, async) for test categorization

Files:

  • tests/services/test_error_handler.py
**/test_*.py

📄 CodeRabbit inference engine (.cursor/rules/rules.mdc)

**/test_*.py: Follow test marker conventions from testing/markers.mdc
Maintain test coverage requirements from testing/coverage.mdc
Follow async testing patterns from testing/async.mdc

Files:

  • tests/services/test_error_handler.py
🧠 Learnings (9)
📚 Learning: 2025-12-30T22:45:12.307Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T22:45:12.307Z
Learning: Applies to src/tux/ui/**/*.py : Use rich embeds for Discord message formatting

Applied to files:

  • src/tux/modules/utility/ping.py
  • src/tux/modules/levels/levels.py
📚 Learning: 2026-01-12T20:42:11.193Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: .cursor/rules/rules.mdc:0-0
Timestamp: 2026-01-12T20:42:11.193Z
Learning: Applies to **/*interaction*.py : Follow interaction response patterns from `modules/interactions.mdc`

Applied to files:

  • src/tux/modules/moderation/__init__.py
  • src/tux/modules/moderation/jail.py
  • src/tux/modules/moderation/clearafk.py
  • src/tux/modules/moderation/cases.py
📚 Learning: 2025-12-30T22:45:12.307Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T22:45:12.307Z
Learning: Applies to **/*.py : Log with context when handling errors

Applied to files:

  • src/tux/services/handlers/error/cog.py
📚 Learning: 2026-01-12T20:42:11.193Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: .cursor/rules/rules.mdc:0-0
Timestamp: 2026-01-12T20:42:11.193Z
Learning: Applies to **/*.py : Follow logging patterns using loguru from `error-handling/logging.mdc`

Applied to files:

  • src/tux/services/handlers/error/cog.py
📚 Learning: 2026-01-12T20:42:11.193Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: .cursor/rules/rules.mdc:0-0
Timestamp: 2026-01-12T20:42:11.193Z
Learning: Applies to **/*.py : Follow error patterns from `error-handling/patterns.mdc`

Applied to files:

  • src/tux/services/handlers/error/cog.py
📚 Learning: 2026-01-12T20:42:11.193Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: .cursor/rules/rules.mdc:0-0
Timestamp: 2026-01-12T20:42:11.193Z
Learning: Applies to **/*.py : Follow user-facing error message patterns from `error-handling/user-feedback.mdc`

Applied to files:

  • src/tux/services/handlers/error/cog.py
📚 Learning: 2025-12-30T22:45:12.307Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T22:45:12.307Z
Learning: Applies to **/*.py : Use custom exceptions for business logic errors

Applied to files:

  • src/tux/services/handlers/error/cog.py
📚 Learning: 2025-12-30T22:45:12.307Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T22:45:12.307Z
Learning: Applies to src/tux/database/migrations/**/*.py : Use Alembic for database migrations

Applied to files:

  • src/tux/core/setup/database_setup.py
📚 Learning: 2025-12-30T22:45:12.307Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T22:45:12.307Z
Learning: Applies to src/tux/modules/**/*.py : Implement cooldowns and rate limiting for Discord commands

Applied to files:

  • src/tux/modules/moderation/slowmode.py
🧬 Code graph analysis (13)
src/tux/modules/moderation/__init__.py (2)
typings/typer/models.pyi (1)
  • Context (18-19)
src/tux/core/bot.py (1)
  • Tux (46-495)
src/tux/services/sentry/tracing.py (3)
src/tux/services/sentry/__init__.py (2)
  • is_initialized (157-159)
  • start_transaction (302-318)
src/tux/services/sentry/config.py (1)
  • is_initialized (159-167)
src/tux/ui/views/config/callbacks.py (1)
  • wrapped_callback (161-170)
src/tux/services/handlers/error/cog.py (1)
src/tux/services/handlers/error/config.py (1)
  • ErrorHandlerConfig (38-60)
src/tux/modules/moderation/jail.py (3)
src/tux/modules/moderation/unjail.py (1)
  • get_jail_role (36-51)
src/tux/modules/moderation/__init__.py (2)
  • _respond (177-198)
  • is_jailed (111-133)
src/tux/core/bot.py (1)
  • is_jailed (193-217)
src/tux/help/navigation.py (2)
src/tux/help/renderer.py (2)
  • create_base_embed (32-49)
  • create_main_embed (147-177)
src/tux/help/data.py (1)
  • batch_can_run_commands (305-390)
src/tux/services/hot_reload/watcher.py (1)
src/tux/services/hot_reload/file_utils.py (4)
  • has_changed (179-194)
  • validate_python_syntax (90-110)
  • remove_file (204-206)
  • get_extension_from_path (32-87)
src/tux/modules/utility/wiki.py (2)
src/tux/services/http_client.py (1)
  • get (172-198)
src/tux/services/sentry/utils.py (1)
  • capture_api_error (144-173)
src/tux/modules/moderation/cases.py (4)
src/tux/core/converters.py (3)
  • convert (44-82)
  • convert (92-117)
  • convert (128-176)
src/tux/modules/moderation/__init__.py (1)
  • _respond (177-198)
src/tux/database/controllers/case.py (2)
  • get_case_by_number (343-354)
  • get_cases_by_options (356-385)
src/tux/core/flags.py (1)
  • CasesViewFlags (429-481)
src/tux/modules/levels/levels.py (2)
src/tux/core/decorators.py (1)
  • requires_command_permission (30-202)
src/tux/database/controllers/levels.py (2)
  • get_level (194-204)
  • get_xp (182-192)
src/tux/modules/moderation/unban.py (1)
src/tux/core/base_cog.py (1)
  • send_after_defer (217-295)
src/tux/core/setup/database_setup.py (1)
src/tux/database/service.py (1)
  • session (173-200)
tests/services/test_error_handler.py (2)
tests/e2e/test_error_handling_e2e.py (1)
  • error_handler (27-29)
src/tux/services/handlers/error/cog.py (1)
  • _log_error (183-232)
src/tux/modules/moderation/slowmode.py (2)
src/tux/modules/info/info.py (2)
  • _send_response (117-167)
  • info (342-377)
src/tux/core/converters.py (3)
  • convert (44-82)
  • convert (92-117)
  • convert (128-176)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Seer Code Review
🔇 Additional comments (48)
src/tux/core/cog_loader.py (2)

386-389: LGTM!

The noun selection logic correctly uses "plugin" for the plugins category and "cog" otherwise. The pluralization handles both singular and plural cases appropriately.


565-569: LGTM!

The noun selection and pluralization logic is consistent with the implementation in _load_cog_group. The condition correctly identifies the "plugins" folder to use the appropriate terminology.

src/tux/services/sentry/tracing.py (1)

740-790: LGTM!

The cog and params preservation logic is well-implemented with helpful comments explaining the discord.py internals. The handling of subcommand cog inheritance from parent commands is a good edge case to cover.

src/tux/core/setup/database_setup.py (3)

11-13: LGTM!

The new imports for ScriptDirectory and text are properly organized and both are used in the code below.


116-126: LGTM!

The revision retrieval for logging purposes improves observability during startup. Using ScriptDirectory directly is a reliable approach, and the debug logs will help troubleshoot migration issues.


132-170: LGTM!

The migration execution approach is sound:

  • Running synchronously in an executor is appropriate for Alembic's synchronous API
  • The 30-second timeout with actionable error messages is helpful
  • Exception handling properly distinguishes between timeout, migration errors, and unexpected failures
src/tux/modules/levels/levels.py (5)

52-64: LGTM!

Adding @requires_command_permission() to the group is a sound approach for gating all subcommands by default while still allowing per-subcommand overrides via the fallback mechanism in the decorator.


88-131: LGTM on the defer/followup pattern.

The early deferral before async DB operations and conditional followup/send responses correctly handle both slash and prefix command invocations with appropriate ephemeral visibility.


133-198: LGTM!

The decorator order is consistent with sibling commands, and the defer/followup pattern for interaction handling is correctly implemented. Validating input before fetching old values is efficient.


200-238: LGTM!

The reset command follows the established patterns correctly: consistent decorator order, early deferral for interactions, and conditional followup/send for responses.


240-281: LGTM!

The blacklist command correctly implements the defer/followup pattern for hybrid command support, with consistent decorator ordering and appropriate ephemeral responses for administrative actions.

src/tux/modules/tools/tldr.py (1)

265-293: The review comment assumes a man alias was removed from the prefix command, but there is no evidence this alias ever existed. No references to a man alias appear anywhere in the codebase, and git history shows no removal of such an alias. The current @commands.command(name="tldr") decorator is the actual state of the code, not a change requiring deprecation.

Likely an incorrect or invalid review comment.

src/tux/services/hot_reload/service.py (3)

101-135: Logging adjustments look good.

The updated disable-warning and debounced scheduling log read well and keep the flow concise.


160-170: Success/failure logging is clear.

Emoji-prefixed success/failure logs are consistent and easy to scan in hot reload traces.


196-217: Reload path messaging is consistent.

The debug/error logs for load vs. reload are cohesive and preserve clarity.

src/tux/services/hot_reload/watcher.py (3)

41-72: cwd plumbing looks good.

Storing the cwd parameter and using it later for path shortening is clean and minimal.


102-149: Short-path logging updates are solid.

The filtered/unchanged/syntax error logs are concise and more readable with short paths.


160-165: Delete logging looks good.

The short-path delete log keeps the message compact while retaining useful context.

src/tux/services/moderation/moderation_coordinator.py (5)

153-169: Clean expiration normalization and logging.

Clear handling of duration vs explicit expires_at, and the case kwargs are built safely.


242-305: Nice resilience for parallel embeds.

Using return_exceptions=True plus conditional update makes the flow robust to partial failures.


407-426: Timeout handling looks solid.

Explicit timeout/cancellation handling keeps the flow predictable.


428-472: Helper extraction reads well.

The async case creation helper is clean and the logging context is helpful.


474-528: Shared base-embed builder is clean.

Nice consolidation and the failed-case status messaging is clear.

src/tux/modules/moderation/clearafk.py (1)

61-93: LGTM.
Clean and consistent update.

src/tux/services/handlers/error/cog.py (1)

183-233: LGTM.
Clearer logging and safer error-path handling.

Also applies to: 252-255

tests/services/test_error_handler.py (1)

94-116: LGTM.
Tests updated correctly for the new _log_error signature.

src/tux/modules/moderation/jail.py (1)

204-222: LGTM! Conditional defer and unified response pattern are correctly implemented.

The refactoring addresses the previous review feedback by:

  1. Using self._respond() to eliminate repeated branching logic for interaction vs. prefix commands
  2. Conditional deferral only when ctx.interaction exists, which correctly handles slash commands without unnecessarily affecting prefix commands

The _respond helper (from ModerationCogBase) properly calls followup.send() after defer for interactions, and ctx.reply() for prefix commands.

src/tux/modules/moderation/unjail.py (2)

173-186: LGTM! Consistent defer and response pattern.

The conditional defer and _respond usage aligns with the pattern established in jail.py, ensuring consistent behavior across moderation commands.


47-48: Type annotation updated correctly.

The return type annotation discord.Role | None follows the coding guidelines which specify using Type | None syntax instead of Optional[Type].

src/tux/modules/moderation/unban.py (2)

133-135: LGTM! Conditional defer pattern is consistent.

The defer logic matches the pattern used in jail.py and unjail.py.


52-52: Type annotation updated correctly.

Using discord.User | None instead of Optional[discord.User] follows the coding guidelines.

src/tux/help/renderer.py (5)

5-5: LGTM.


242-244: LGTM.


286-288: LGTM.


364-366: LGTM.


170-176: The banner attachment is properly handled on all paths. Both call sites of create_main_embed (in help.py and navigation.py) check for the banner file's existence and append it as a discord.File object to the request, matching the filename help_banner.png in the attachment URL. No missing attachment paths identified.

src/tux/help/navigation.py (12)

6-6: LGTM.


142-147: LGTM.


170-172: LGTM.


394-399: LGTM.


428-445: LGTM.


456-465: LGTM.


497-505: LGTM.


512-523: LGTM.


543-548: LGTM.


560-565: LGTM.


578-585: LGTM.


642-645: LGTM.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +287 to 295
# Batch check permissions for all subcommands at once (much faster)
can_run_map = await self.data.batch_can_run_commands(
list(self.current_command_obj.commands),
)
filtered_cmds = [
cmd
for cmd in self.current_command_obj.commands
if await self.data.can_run_command(cmd)
if can_run_map.get(cmd, False)
]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Paginated groups still expose unfiltered subcommands.

filtered_cmds is computed, but pagination for large groups still uses self.subcommand_pages that are built from the unfiltered list (in handle_command_select). That means the initial paginated embed/dropdown can still show commands the user can’t run.

Consider repaginating from the filtered list before rendering, and ensure the initial paginated embed uses the same filtered list.

🔧 Proposed fix (keep pagination in sync with filtered list)
@@
-        filtered_cmds = [
-            cmd
-            for cmd in self.current_command_obj.commands
-            if can_run_map.get(cmd, False)
-        ]
-        sorted_cmds = sorted(filtered_cmds, key=lambda x: x.name)
+        filtered_cmds = [
+            cmd
+            for cmd in self.current_command_obj.commands
+            if can_run_map.get(cmd, False)
+        ]
+        sorted_cmds = sorted(filtered_cmds, key=lambda x: x.name)
+        # Keep pagination in sync with the filtered list for large groups
+        if (
+            self.current_command_obj.name in {"jsk", "jishaku"}
+            or len(sorted_cmds) > 15
+        ):
+            self._paginate_subcommands(sorted_cmds, preserve_page=True)
@@
-        if isinstance(command, commands.Group) and (
-            command.name in {"jsk", "jishaku"} or len(command.commands) > 15
-        ):
-            # Initialize pagination for large groups
-            if not self.subcommand_pages:
-                sorted_cmds = sorted(command.commands, key=lambda x: x.name)
-                self._paginate_subcommands(sorted_cmds, preserve_page=False)
-            embed = await self._create_paginated_embed()
+        if isinstance(command, commands.Group) and (
+            command.name in {"jsk", "jishaku"} or len(command.commands) > 15
+        ):
+            # Initialize pagination for large groups using permission-filtered list
+            can_run_map = await self.data.batch_can_run_commands(list(command.commands))
+            filtered_cmds = [
+                cmd for cmd in command.commands if can_run_map.get(cmd, False)
+            ]
+            sorted_cmds = sorted(filtered_cmds, key=lambda x: x.name)
+            self._paginate_subcommands(sorted_cmds, preserve_page=False)
+            embed = await self._create_paginated_embed()
🤖 Prompt for AI Agents
In `@src/tux/help/navigation.py` around lines 287 - 295, filtered_cmds is computed
from self.current_command_obj.commands using can_run_map but the pagination
still uses self.subcommand_pages built from the unfiltered list (see
handle_command_select), so users can see commands they cannot run; fix by
repaginating from filtered_cmds before rendering: after building filtered_cmds
(from self.data.batch_can_run_commands and self.current_command_obj.commands)
regenerate self.subcommand_pages (or pass a new paginated list) so
handle_command_select and the initial embed/dropdown use the filtered_cmds list
rather than the original commands list.

…moderation coordinator

- Enhanced error handling by ensuring case tasks are canceled and exceptions are raised appropriately during Discord action execution.
- Updated action execution logic to handle both single-element and two-element tuples, improving code clarity and maintainability.
…ated for filtered commands

- Updated the pagination logic to always regenerate pages from filtered commands, ensuring users only see commands they can run.
- Removed the conditional check for empty subcommand pages to streamline the pagination process.
…ode commands

- Added a clear error response when a specified channel is not found during slowmode setup.
- Improved exception handling to log errors with context and provide user-friendly messages when slowmode actions fail.
- Updated the actions list in the Unban class to remove unnecessary type hinting, enhancing code clarity and maintainability.
- Ensured the unban action is executed correctly without altering the intended functionality.
…nteraction handling

- Updated the _get_system_stats method to be asynchronous, utilizing asyncio.to_thread for CPU usage calls to prevent blocking the event loop.
- Enhanced interaction handling by deferring responses early for slash commands, ensuring better user experience during latency checks.
- Updated the language pattern matching to only include known translation language markers, improving accuracy in identifying translated pages.
- Modified interaction handling to defer responses early for slash commands, ensuring a better user experience during API calls.
@kzndotsh kzndotsh merged commit 94e5433 into main Jan 23, 2026
21 of 23 checks passed
@kzndotsh kzndotsh deleted the refactor/optimizations branch January 23, 2026 10:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants