Skip to content

Port feedback_cog from deprecated repo - #56

Merged
shamikkarkhanis merged 9 commits into
developfrom
feature/capr-22-port-feedback_cog-from-deprecated-repo
Feb 6, 2026
Merged

Port feedback_cog from deprecated repo#56
shamikkarkhanis merged 9 commits into
developfrom
feature/capr-22-port-feedback_cog-from-deprecated-repo

Conversation

@GreenJonathan

@GreenJonathan GreenJonathan commented Feb 3, 2026

Copy link
Copy Markdown

CAPR-22

Ports the feedback_cog functionality from the deprecated repository to the new capy-discord codebase.

Changes:

  • Implemented feedback collection system
  • Used ModelModal and BaseView patterns for UI
  • Added Pydantic schemas in exts/feedback/_schemas.py
  • Added timeout and error handling

Summary by Sourcery

Introduce a ticket-based feedback submission system using a shared ticket base cog and UI modal flow.

New Features:

  • Add a feedback ticket cog with a slash command that submits feedback via a modal form into a configured channel.
  • Define a reusable ticket base cog with reaction-based status tracking and a feedback button view for modal submission.
  • Introduce Pydantic schemas for ticket forms, starting with a FeedbackForm model.

Enhancements:

  • Extend configuration to include a ticket feedback channel ID for routing feedback submissions.
  • Update pre-commit configuration to run type checks via uv run for the ty hook.

Chores:

  • Add initial tickets extension package structure and supporting IDE project files.

@sourcery-ai

sourcery-ai Bot commented Feb 3, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Ports a generic, reaction-based ticket/feedback submission system into the new codebase, built around a reusable TicketBase cog with a Pydantic-backed modal form, plus a concrete Feedback cog wired to a configurable channel and improved error/timeout handling, and a minor pre-commit tweak.

Sequence diagram for feedback submission via modal and ticket creation

sequenceDiagram
    actor User
    participant Discord_Client
    participant Discord_API
    participant Feedback_Cog
    participant TicketBase
    participant FeedbackButtonView
    participant ModelModal
    participant Feedback_Channel

    User->>Discord_Client: Trigger slash command feedback
    Discord_Client->>Discord_API: Send interaction
    Discord_API->>Feedback_Cog: Dispatch app command feedback
    Feedback_Cog->>Feedback_Cog: feedback(interaction)
    Feedback_Cog->>TicketBase: _show_feedback_button(interaction)
    TicketBase->>FeedbackButtonView: __init__(schema_cls, callback, modal_title)
    TicketBase->>FeedbackButtonView: reply(interaction, content, ephemeral False)
    FeedbackButtonView-->>User: Button Open_Survey visible

    User->>Discord_Client: Click Open_Survey button
    Discord_Client->>Discord_API: Send button interaction
    Discord_API->>FeedbackButtonView: on_click open_modal(interaction)
    FeedbackButtonView->>ModelModal: __init__(model_cls FeedbackForm, callback _handle_ticket_submit, title)
    FeedbackButtonView->>Discord_API: interaction.response.send_modal(ModelModal)
    Discord_API-->>User: Show feedback modal

    User->>Discord_Client: Submit modal with title and description
    Discord_Client->>Discord_API: Submit modal data
    Discord_API->>ModelModal: Validate data with FeedbackForm
    ModelModal->>TicketBase: _handle_ticket_submit(interaction, validated_data)
    TicketBase->>TicketBase: _validate_and_get_text_channel(interaction)
    TicketBase->>Feedback_Channel: send(embed)
    Feedback_Channel-->>TicketBase: message
    loop Add status reactions
        TicketBase->>Feedback_Channel: add_reaction(emoji)
    end
    TicketBase->>Discord_API: Send success followup or response
    Discord_API-->>User: Ephemeral success message
Loading

Sequence diagram for reaction-based feedback ticket status updates

sequenceDiagram
    actor Staff
    participant Discord_Client
    participant Discord_API
    participant TicketBase
    participant Ticket_Channel

    Staff->>Discord_Client: React to feedback ticket message
    Discord_Client->>Discord_API: Send RawReactionActionEvent
    Discord_API->>TicketBase: on_raw_reaction_add(payload)
    TicketBase->>TicketBase: _should_process_reaction(payload)
    alt Reaction not in configured channel or from bot or unknown emoji
        TicketBase-->>TicketBase: Return without processing
    else Valid reaction
        TicketBase->>Ticket_Channel: get_channel(payload.channel_id)
        TicketBase->>Ticket_Channel: fetch_message(payload.message_id)
        Ticket_Channel-->>TicketBase: message
        TicketBase->>TicketBase: _is_ticket_embed(message)
        alt Not a ticket embed
            TicketBase-->>TicketBase: Return
        else Is ticket embed
            TicketBase->>TicketBase: _update_ticket_status(message, emoji, payload)
            TicketBase->>Ticket_Channel: remove_reaction(payload.emoji, payload.member)
            TicketBase->>Ticket_Channel: edit(embed with new status and color)
        end
    end
Loading

Class diagram for the new ticket-based feedback system

classDiagram
    class TicketBase {
        - bot : commands.Bot
        - schema_cls : BaseModel
        - status_emoji : dict~str, str~
        - command_config : dict~str, Any~
        - color_config : dict~str, Any~
        - reaction_footer : str
        - log : logging.Logger
        + __init__(bot, schema_cls, status_emoji, command_config, color_config, reaction_footer) void
        + _show_feedback_button(interaction) void
        + _validate_and_get_text_channel(interaction) TextChannel | None
        + _build_ticket_embed(data, submitter) discord.Embed
        + _handle_ticket_submit(interaction, validated_data) void
        + _should_process_reaction(payload) bool
        + _is_ticket_embed(message) bool
        + _update_ticket_status(message, emoji, payload) void
        + on_raw_reaction_add(payload) void
    }

    class FeedbackButtonView {
        - schema_cls : BaseModel
        - callback : Callable~discord.Interaction, BaseModel, Any~
        - modal_title : str
        + __init__(schema_cls, callback, modal_title) void
        + open_modal(interaction, _button) void
    }

    class Feedback {
        - log : logging.Logger
        + __init__(bot) void
        + feedback(interaction) void
    }

    class FeedbackForm {
        + title : str
        + description : str
    }

    class Settings {
        + prefix : str
        + token : str
        + ticket_feedback_channel_id : int
    }

    TicketBase ..> FeedbackButtonView : creates
    TicketBase ..> FeedbackForm : uses_as_schema
    TicketBase ..> Settings : reads_channel_id

    FeedbackButtonView --|> BaseView
    TicketBase --|> commands.Cog
    Feedback --|> TicketBase
    Feedback ..> FeedbackForm : configures_schema

    Settings ..> Feedback : configures_request_channel_id
Loading

File-Level Changes

Change Details Files
Introduce a reusable ticket/feedback base cog with button-triggered modal form, ticket embed creation, and reaction-based status tracking.
  • Add FeedbackButtonView using BaseView and ModelModal to open a Pydantic-backed modal from a button press.
  • Implement TicketBase cog that wires a Pydantic schema into modal handling, validates the target channel from config, builds/sends ticket embeds, and logs errors.
  • Handle status updates via on_raw_reaction_add listener that filters relevant reactions and updates embed color/footer accordingly based on a status_emoji mapping.
capy_discord/exts/tickets/_base.py
Add a concrete Feedback cog that uses TicketBase and a Pydantic schema to collect and route feedback to a configured channel.
  • Define Feedback cog that passes FeedbackForm schema, command metadata, status emoji mapping, and color configuration into TicketBase.
  • Expose a /feedback slash command that shows the feedback button view and wraps it in broad exception handling with user-facing error messages.
  • Wire cog setup via an async setup function for extension loading.
capy_discord/exts/tickets/feedback.py
Define Pydantic schema for the feedback form used by the modal UI.
  • Create FeedbackForm model with validated title and description fields and descriptive metadata suitable for auto-generated modal inputs.
capy_discord/exts/tickets/_schemas.py
Add configuration and package/module plumbing for the ticket system.
  • Extend Settings with ticket_feedback_channel_id to configure the feedback target channel.
  • Create tickets package init for the new extension namespace.
  • Add IDE project files and ignore configuration (non-runtime impact).
capy_discord/config.py
capy_discord/exts/tickets/__init__.py
.idea/.gitignore
.idea/discord-bot.iml
.idea/modules.xml
.idea/vcs.xml
Adjust pre-commit type-checking hook to run via uv.
  • Change ty pre-commit hook entry to use uv run ty check instead of calling ty check directly.
.pre-commit-config.yaml

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The new .idea project files (.gitignore, discord-bot.iml, modules.xml, vcs.xml) look like IDE artifacts and probably shouldn’t be committed; consider removing them from the repo and adding the .idea/ directory to the global/project .gitignore instead.
  • In TicketBase._build_ticket_embed and the logging in _handle_ticket_submit, you’re using type: ignore[attr-defined] to access title/description on BaseModel; consider tightening the typing (e.g., a Protocol or generic type bound with those attributes) so these fields are statically known rather than suppressed.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `.idea` project files (`.gitignore`, `discord-bot.iml`, `modules.xml`, `vcs.xml`) look like IDE artifacts and probably shouldn’t be committed; consider removing them from the repo and adding the `.idea/` directory to the global/project `.gitignore` instead.
- In `TicketBase._build_ticket_embed` and the logging in `_handle_ticket_submit`, you’re using `type: ignore[attr-defined]` to access `title`/`description` on `BaseModel`; consider tightening the typing (e.g., a `Protocol` or generic type bound with those attributes) so these fields are statically known rather than suppressed.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown
Member

few things:

  • don't include IDE artifacts such as .idea files (add to .gitignore)
  • Extract Generic UI: Move FeedbackButtonView from _base.py to capy_discord/ui/views.py as ModalLauncherView. This allows any cog to launch a ModelModal with a configurable button (label, emoji, style).
  • Standardize Embeds: Refactor TicketBase to use standard factory functions from ui/embeds.py (success_embed, ignored_embed, unmarked_embed) instead of custom color configurations.
  • If you need additional colors, add them to the ui/embed.py like all the other colors.
  • Global Error Handling: Remove the broad try/except block in feedback.py to ensure all errors are caught and logged consistently by the bot's central error handler.

@shamikkarkhanis

Copy link
Copy Markdown
Member

Few more things after looking into this more.

  1. Define a TicketSchema base class in _schemas.py with title and description fields to provide a typed contract for all ticket cogs and remove the need for type: ignore.
  2. Move all status emojis and labels to capy_discord/exts/tickets/init.py to centralize the ticket system's visual configuration and eliminate magic strings.
  3. Use asyncio.gather to add reactions in parallel immediately after sending the message, reducing the time the ticket exists in an interactive "dead zone."
  4. Filter model_fields to only count those explicitly intended for the UI (excluding internal or hidden fields) before validating the Discord 5-row modal limit

@shamikkarkhanis
shamikkarkhanis merged commit b76722b into develop Feb 6, 2026
4 checks passed
@shamikkarkhanis
shamikkarkhanis deleted the feature/capr-22-port-feedback_cog-from-deprecated-repo branch February 6, 2026 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants