Skip to content

feature/capr-38-who-is#130

Open
YC-5002 wants to merge 65 commits intodevelopfrom
feature/capr-38-who-is
Open

feature/capr-38-who-is#130
YC-5002 wants to merge 65 commits intodevelopfrom
feature/capr-38-who-is

Conversation

@YC-5002
Copy link

@YC-5002 YC-5002 commented Mar 17, 2026

  • feat(onboarding): add in-memory setup and member verification flow
  • init(onboarding)
  • Changed onboarding to setup and added new features to change in /setup config
  • Changed onboarding to setup
  • Fix
  • Extended profile form, added whois cog to learn about other users

Summary by Sourcery

Introduce a configurable in-memory guild onboarding and setup system, expand user profiles into a multi-step flow with richer fields, and add a whois command for viewing other members’ profiles.

New Features:

  • Add a /setup command suite with in-memory guild configuration, onboarding flows, and grace-period enforcement for new members.
  • Introduce a multi-step profile editor that separates identity and details, including minor and personal description fields.
  • Add a /whois command that lets users view another member’s stored profile information.

Bug Fixes:

  • Improve command error handling for both slash and prefix commands by mapping common failures to clear user-facing messages.
  • Ensure telemetry value serialization always produces JSON-safe primitives, including for non-primitive types and non-string dict keys.

Enhancements:

  • Share the in-memory profile store across cogs via the bot instance to allow cross-feature profile access.
  • Extend profile embeds to include new minor and description fields for both self and whois views.
  • Refine modal and form callback typing and view message typing for better async handling and interaction updates.

Build:

  • Update GitHub Actions workflows to the latest docker login, metadata, setup-buildx, and build-push actions.

Tests:

  • Add tests covering onboarding setup behavior, role/channel/config updates, grace-period kicking, and profile modal launching behavior.

Chores:

  • Introduce onboarding/setup and whois extension packages and supporting schemas/views for better structure around member verification flows.

dependabot bot and others added 30 commits February 13, 2026 12:25
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.15.0 to 0.15.1.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ruff@0.15.0...0.15.1)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.15.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
build(deps-dev): bump ruff from 0.15.0 to 0.15.1
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](docker/build-push-action@v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [uv](https://github.com/astral-sh/uv) from 0.10.1 to 0.10.4.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](astral-sh/uv@0.10.1...0.10.4)

---
updated-dependencies:
- dependency-name: uv
  dependency-version: 0.10.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.15.1 to 0.15.2.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ruff@0.15.1...0.15.2)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.15.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [pydantic-settings](https://github.com/pydantic/pydantic-settings) from 2.12.0 to 2.13.1.
- [Release notes](https://github.com/pydantic/pydantic-settings/releases)
- [Commits](pydantic/pydantic-settings@v2.12.0...v2.13.1)

---
updated-dependencies:
- dependency-name: pydantic-settings
  dependency-version: 2.13.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [ty](https://github.com/astral-sh/ty) from 0.0.15 to 0.0.18.
- [Release notes](https://github.com/astral-sh/ty/releases)
- [Changelog](https://github.com/astral-sh/ty/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ty@0.0.15...0.0.18)

---
updated-dependencies:
- dependency-name: ty
  dependency-version: 0.0.18
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
…s/checkout-6

build(deps): bump actions/checkout from 4 to 6
build(deps-dev): bump uv from 0.10.1 to 0.10.4
build(deps-dev): bump ruff from 0.15.1 to 0.15.2
…2.13.1

build(deps): bump pydantic-settings from 2.12.0 to 2.13.1
…/build-push-action-6

build(deps): bump docker/build-push-action from 5 to 6
Bumps [ty](https://github.com/astral-sh/ty) from 0.0.15 to 0.0.18.
- [Release notes](https://github.com/astral-sh/ty/releases)
- [Changelog](https://github.com/astral-sh/ty/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ty@0.0.15...0.0.18)

---
updated-dependencies:
- dependency-name: ty
  dependency-version: 0.0.18
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
build(deps-dev): bump ty from 0.0.15 to 0.0.18
Bumps [uv](https://github.com/astral-sh/uv) from 0.10.4 to 0.10.5.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](astral-sh/uv@0.10.4...0.10.5)

---
updated-dependencies:
- dependency-name: uv
  dependency-version: 0.10.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
build(deps-dev): bump uv from 0.10.4 to 0.10.5
Bumps [uv](https://github.com/astral-sh/uv) from 0.10.5 to 0.10.6.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](astral-sh/uv@0.10.5...0.10.6)

---
updated-dependencies:
- dependency-name: uv
  dependency-version: 0.10.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [ty](https://github.com/astral-sh/ty) from 0.0.18 to 0.0.19.
- [Release notes](https://github.com/astral-sh/ty/releases)
- [Changelog](https://github.com/astral-sh/ty/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ty@0.0.18...0.0.19)

---
updated-dependencies:
- dependency-name: ty
  dependency-version: 0.0.19
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.15.2 to 0.15.4.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ruff@0.15.2...0.15.4)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.15.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
build(deps-dev): bump ruff from 0.15.2 to 0.15.4
build(deps-dev): bump ty from 0.0.18 to 0.0.19
dependabot bot and others added 28 commits March 9, 2026 12:55
Bumps [uv](https://github.com/astral-sh/uv) from 0.10.6 to 0.10.9.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](astral-sh/uv@0.10.6...0.10.9)

---
updated-dependencies:
- dependency-name: uv
  dependency-version: 0.10.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [ty](https://github.com/astral-sh/ty) from 0.0.19 to 0.0.22.
- [Release notes](https://github.com/astral-sh/ty/releases)
- [Changelog](https://github.com/astral-sh/ty/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ty@0.0.19...0.0.22)

---
updated-dependencies:
- dependency-name: ty
  dependency-version: 0.0.22
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.15.4 to 0.15.6.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ruff@0.15.4...0.15.6)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.15.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
build(deps-dev): bump ruff from 0.15.4 to 0.15.6
build(deps-dev): bump ty from 0.0.19 to 0.0.22
build(deps-dev): bump uv from 0.10.6 to 0.10.9
…r/metadata-action-6

build(deps): bump docker/metadata-action from 5 to 6
…r/build-push-action-7

build(deps): bump docker/build-push-action from 6 to 7
…r/setup-buildx-action-4

build(deps): bump docker/setup-buildx-action from 3 to 4
build(deps): bump discord-py from 2.6.4 to 2.7.1
…r/login-action-4

build(deps): bump docker/login-action from 3 to 4
Bumps [uv](https://github.com/astral-sh/uv) from 0.10.9 to 0.10.10.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](astral-sh/uv@0.10.9...0.10.10)

---
updated-dependencies:
- dependency-name: uv
  dependency-version: 0.10.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [ty](https://github.com/astral-sh/ty) from 0.0.22 to 0.0.23.
- [Release notes](https://github.com/astral-sh/ty/releases)
- [Changelog](https://github.com/astral-sh/ty/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ty@0.0.22...0.0.23)

---
updated-dependencies:
- dependency-name: ty
  dependency-version: 0.0.23
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
build(deps-dev): bump ty from 0.0.22 to 0.0.23
Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.13.4 to 7.13.5.
- [Release notes](https://github.com/coveragepy/coveragepy/releases)
- [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst)
- [Commits](coveragepy/coveragepy@7.13.4...7.13.5)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.13.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
build(deps-dev): bump coverage from 7.13.4 to 7.13.5
build(deps-dev): bump uv from 0.10.9 to 0.10.10
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Mar 17, 2026

Reviewer's Guide

Implements a new in-memory guild setup and onboarding flow with verification, extends the profile system into a two-step modal with additional fields, introduces a whois-style profile lookup command, centralizes user-facing error handling, tightens UI callback typing, and updates CI Docker GitHub Actions along with serialization and tests.

Sequence diagram for two-step profile edit modal flow

sequenceDiagram
    actor User
    participant Discord
    participant ProfileCog as Profile
    participant Modal1 as ModelModalIdentity
    participant Modal2 as ModelModalDetails
    participant ProfileModalLauncherView
    participant Pydantic

    User->>Discord: /profile action=edit
    Discord-->>ProfileCog: handle_edit_action(interaction, action)
    ProfileCog->>ProfileCog: current_profile = profiles.get(user.id)
    ProfileCog->>ProfileCog: initial_data = current_profile.model_dump() or None
    ProfileCog->>ProfileCog: _open_profile_identity_modal(interaction, action, initial_data)
    ProfileCog->>Modal1: create ModelModal(model_cls=UserProfileIdentitySchema, callback=_handle_profile_identity_submit, title="Edit Your Profile (1/2)")
    ProfileCog->>Discord: interaction.response.send_modal(Modal1)

    User->>Discord: Submit identity fields
    Discord-->>Modal1: on_submit(interaction)
    Modal1->>Pydantic: validate UserProfileIdentitySchema
    Pydantic-->>Modal1: identity instance
    Modal1->>ProfileCog: _handle_profile_identity_submit(interaction, identity, action)
    ProfileCog->>ProfileCog: profile_data = current_profile.model_dump() or {}
    ProfileCog->>ProfileCog: profile_data.update(identity.model_dump())
    ProfileCog->>ProfileCog: create ProfileModalLauncherView(callback=_open_profile_details_modal)
    ProfileCog->>Discord: interaction.response.send_message("Step 1 of 2 complete…", view=ProfileModalLauncherView)

    User->>Discord: Click "Finish Profile"
    Discord-->>ProfileModalLauncherView: _button_callback(interaction)
    ProfileModalLauncherView->>ProfileCog: _open_profile_details_modal(interaction, action, profile_data)
    ProfileCog->>Modal2: create ModelModal(model_cls=UserProfileDetailsSchema, callback=_handle_profile_details_submit, title="Edit Your Profile (2/2)")
    ProfileCog->>Discord: interaction.response.send_modal(Modal2)

    User->>Discord: Submit details fields
    Discord-->>Modal2: on_submit(interaction)
    Modal2->>Pydantic: validate UserProfileDetailsSchema
    Pydantic-->>Modal2: details instance
    Modal2->>ProfileCog: _handle_profile_details_submit(interaction, details, profile_data)
    ProfileCog->>ProfileCog: combined_data = {**profile_data, **details.model_dump()}
    ProfileCog->>Pydantic: UserProfileSchema(**combined_data)
    alt Validation succeeds
        Pydantic-->>ProfileCog: profile instance
        ProfileCog->>ProfileCog: _handle_profile_submit(interaction, profile)
        ProfileCog->>ProfileCog: profiles[user.id] = profile
        ProfileCog->>Discord: respond (success embed, etc.)
    else ValidationError
        Pydantic-->>ProfileCog: ValidationError
        ProfileCog->>Discord: interaction.response.send_message(error_embed("Profile Validation Failed"), ephemeral=True)
    end
Loading

Updated class diagram for setup, profile, whois, and related schemas

classDiagram
    direction LR

    class Bot {
        +_format_missing_permissions(permissions list~str~) str
        +_get_app_command_error_message(error app_commands.AppCommandError) str
        +_get_prefix_error_message(error commands.CommandError) str
    }

    class Setup {
        +setup app_commands.Group
        -bot commands.Bot
        -_setup_store dict~int, GuildSetupConfig~
        -_user_state_store dict~str, UserOnboardingState~
        -_grace_tasks dict~str, asyncio.Task~None~~
        +_state_key(guild_id int, user_id int) str
        +_ensure_setup(guild_id int) GuildSetupConfig
        +_get_user_state(guild_id int, user_id int) UserOnboardingState
        +_cancel_grace_task(guild_id int, user_id int) None
        +_schedule_grace_period_check(guild_id int, user_id int) None
        +_get_bot_member(guild discord.Guild) discord.Member
        +_first_public_text_channel(guild discord.Guild) discord.TextChannel
        +_format_role_mentions(guild discord.Guild, role_ids list~int~) str
        +_format_channel_mention(guild discord.Guild, channel_id int) str
        +_missing_items(config GuildSetupConfig) list~str~
        +_build_setup_message(guild discord.Guild) str
        +_parse_role_ids(raw str, guild discord.Guild) list~int~
        +_send_log_message(guild discord.Guild, config GuildSetupConfig, message str) None
        +_mark_pending(guild_id int, user_id int) None
        +_mark_timed_out(guild_id int, user_id int) None
        +_enforce_grace_period(guild_id int, user_id int) None
        +_handle_accept(interaction discord.Interaction, target_user_id int) None
        +on_guild_join(guild discord.Guild) None
        +on_member_join(member discord.Member) None
        +setup_summary(interaction discord.Interaction) None
        +setup_roles(interaction discord.Interaction, admin_roles str, moderator_roles str, member_role discord.Role) None
        +setup_channels(interaction discord.Interaction, log_channel discord.TextChannel, announcement_channel discord.TextChannel, welcome_channel discord.TextChannel, support_channel discord.TextChannel) None
        +setup_onboarding(interaction discord.Interaction, enabled bool, welcome_dm_enabled bool, auto_kick_unverified bool, grace_period_hours int, log_events bool, rules_location str, message str) None
        +setup_reset(interaction discord.Interaction) None
    }

    class VerifyView {
        +target_user_id int
        -_on_accept Callable~discord.Interaction, int -\> Awaitable~None~~
        -_on_timeout_callback Callable~int -\> Awaitable~None~~
        +accept(interaction discord.Interaction, _button ui.Button) None
        +on_timeout() None
    }

    class Profile {
        -bot commands.Bot
        -profiles dict~int, UserProfileSchema~
        +handle_edit_action(interaction discord.Interaction, action str) None
        +_open_profile_identity_modal(interaction discord.Interaction, action str, initial_data dict~str, Any~) None
        +_handle_profile_identity_submit(interaction discord.Interaction, identity UserProfileIdentitySchema, action str) None
        +_open_profile_details_modal(interaction discord.Interaction, action str, profile_data dict~str, Any~) None
        +_handle_profile_details_submit(interaction discord.Interaction, details UserProfileDetailsSchema, profile_data dict~str, Any~) None
        +_handle_profile_submit(interaction discord.Interaction, profile UserProfileSchema) None
        +handle_show_action(interaction discord.Interaction) None
        +_create_profile_embed(user discord.User, profile UserProfileSchema) discord.Embed
    }

    class ProfileModalLauncherView {
        -_callback Callable~discord.Interaction -\> Any~
        +ProfileModalLauncherView(callback Callable~discord.Interaction -\> Any~, button_label str, button_emoji str, button_style discord.ButtonStyle)
        +_button_callback(interaction discord.Interaction) None
    }

    class WhoIs {
        -bot commands.Bot
        +profile(interaction discord.Interaction, member discord.Member) None
        +_create_profile_embed(member discord.Member, profile UserProfileSchema) discord.Embed
    }

    class BaseView {
        +message discord.InteractionMessage|discord.Message|None
        +on_error(interaction discord.Interaction, error Exception, item ui.Item) None
    }

    class ModelModal {
        +model_cls type~BaseModel~
        +callback Callable~discord.Interaction, BaseModel -\> Awaitable~Any~~
        +title str
        +initial_data dict~str, Any~
    }

    class UserProfileIdentitySchema {
        +preferred_name str
        +student_id str
        +school_email str
        +graduation_year int
        +major str
    }

    class UserProfileDetailsSchema {
        +minor str
        +description str
    }

    class UserProfileSchema {
        +preferred_name str
        +student_id str
        +school_email str
        +graduation_year int
        +major str
        +minor str
        +description str
    }

    class GuildSetupConfig {
        +enabled bool
        +admin_role_ids list~int~
        +moderator_role_ids list~int~
        +log_channel_id int
        +announcement_channel_id int
        +welcome_channel_id int
        +welcome_dm_enabled bool
        +auto_kick_unverified bool
        +grace_period_hours int
        +log_events bool
        +support_channel_id int
        +rules_location str
        +verification_acceptance str
        +member_role_id int
        +onboarding_message_template str
    }

    class UserOnboardingState {
        +status str
        +started_at_utc datetime
        +completed_at_utc datetime
        +attempts int
    }

    Bot <|-- Setup
    Bot <|-- Profile
    Bot <|-- WhoIs

    Setup o--> GuildSetupConfig
    Setup o--> UserOnboardingState
    Setup o--> VerifyView

    VerifyView --|> BaseView
    ProfileModalLauncherView --|> BaseView

    Profile o--> UserProfileSchema
    Profile o--> UserProfileIdentitySchema
    Profile o--> UserProfileDetailsSchema

    WhoIs o--> UserProfileSchema

    ModelModal <.. Profile : uses
    VerifyView <.. Setup : created_by
Loading

File-Level Changes

Change Details Files
Introduce in-memory guild setup and onboarding flow with verification and grace-period enforcement, plus dedicated views and schemas.
  • Add Setup/Onboarding cog with /setup command group for roles, channels, onboarding config, summary, and reset operations
  • Implement member join onboarding that posts a verification view, tracks per-user onboarding state, and optionally DMs hints
  • Add grace-period enforcement task that can auto-kick unverified members with logging to a configurable log channel
  • Define GuildSetupConfig and UserOnboardingState pydantic models to store in-memory config and lifecycle state
  • Implement VerifyView UI with an Accept Rules button restricted to the target user and timeout handling
capy_discord/exts/setup/setup.py
capy_discord/exts/setup/_views.py
capy_discord/exts/setup/_schemas.py
capy_discord/exts/setup/__init__.py
tests/capy_discord/exts/test_setup.py
Extend profile editing into a multi-step modal flow, share in-memory storage across cogs, and enrich profile data.
  • Refactor profile edit handler to open an identity modal followed by a details modal using a launcher button view
  • Introduce UserProfileIdentitySchema and UserProfileDetailsSchema and aggregate them into UserProfileSchema with new fields
  • Add minor and description fields to profile embeds and models, with safe defaults when not provided
  • Attach the in-memory profile store to the bot instance for cross-cog access
  • Add tests ensuring the profile create action opens a modal immediately
capy_discord/exts/profile/profile.py
capy_discord/exts/profile/_schemas.py
tests/capy_discord/exts/test_profile.py
Add whois-style profile lookup for other members based on the shared profile store.
  • Create WhoIs cog with a /whois slash command that fetches another member's profile from the shared store
  • Render a read-only profile embed including name, major, grad year, email, minor, and description, or show an error embed if missing
capy_discord/exts/whois/profile.py
Centralize and broaden user-facing error message handling for slash and prefix commands.
  • Add helper methods to Bot for mapping app-command and prefix-command errors to friendly messages, including permission and DM restrictions
  • Refactor on_tree_error and on_command_error to use the new helpers instead of directly inspecting UserFriendlyError
capy_discord/bot.py
Tighten typing for UI callbacks and views, and expand view message typing.
  • Change CallbackModal, RetryView, and ModelModal callback signatures to require awaitable callbacks
  • Allow BaseView.message to hold either an InteractionMessage or a standard Message for later edits
capy_discord/ui/modal.py
capy_discord/ui/forms.py
capy_discord/ui/views.py
Constrain telemetry value serialization to JSON-safe types and normalize non-primitive values to strings.
  • Introduce JsonPrimitive and JsonValue type aliases to describe serialized telemetry value shapes
  • Ensure dict keys are stringified and fall back to string conversion for unsupported value types in _serialize_value
capy_discord/exts/core/telemetry.py
Update GitHub Actions Docker-related actions to latest major versions for build and publish workflows.
  • Bump docker/login-action, docker/metadata-action, docker/setup-buildx-action, and docker/build-push-action in CI workflows
.github/workflows/build-deploy.yml
.github/workflows/publish.yml

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

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • In ProfileModalLauncherView, assigning the button callback via self.children[0] is brittle if additional items are ever added; consider storing the created button in a local variable and attaching the callback directly to that instance instead of relying on index ordering.
  • The change to Telemetry._serialize_value now coerces all non-list/dict values to str, which will also stringify plain primitives like ints and bools that were previously preserved as-is; if you rely on numeric/boolean JSON types downstream, consider adding an explicit branch to pass through JSON-serializable primitives untouched before falling back to str(value).
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `ProfileModalLauncherView`, assigning the button callback via `self.children[0]` is brittle if additional items are ever added; consider storing the created button in a local variable and attaching the callback directly to that instance instead of relying on index ordering.
- The change to `Telemetry._serialize_value` now coerces all non-list/dict values to `str`, which will also stringify plain primitives like ints and bools that were previously preserved as-is; if you rely on numeric/boolean JSON types downstream, consider adding an explicit branch to pass through JSON-serializable primitives untouched before falling back to `str(value)`.

## Individual Comments

### Comment 1
<location path="capy_discord/exts/core/telemetry.py" line_range="561-564" />
<code_context>
+    type JsonPrimitive = str | int | float | bool | None
+    type JsonValue = JsonPrimitive | list["JsonValue"] | dict[str, "JsonValue"]
+
+    def _serialize_value(self, value: object) -> JsonValue:
         """Convert complex Discord objects to simple serializable types.

</code_context>
<issue_to_address>
**issue (bug_risk):** Serializing all non-container values to strings may unintentionally change data semantics.

With this change, any value not matched earlier (including ints/floats/bools/None) is now coerced to `str`, so data that previously passed through as proper JSON primitives will be logged as strings. That can break downstream consumers or analytics that depend on numeric/boolean types. Consider returning JSON primitives (int/float/bool/None) as-is and only `str()`-casting truly unknown or complex objects.
</issue_to_address>

### Comment 2
<location path="capy_discord/exts/setup/setup.py" line_range="411-416" />
<code_context>
+            timeout=1800,
+        )
+
+        sent = await welcome_channel.send(
+            rendered,
+            allowed_mentions=discord.AllowedMentions(users=True, roles=False, everyone=False),
+            view=view,
+        )
+        view.message = sent
+        self._schedule_grace_period_check(member.guild.id, member.id)
+
</code_context>
<issue_to_address>
**issue:** The welcome message send in `on_member_join` is not guarded against HTTP errors, which can break onboarding silently.

Unlike `_send_log_message` and the setup checklist, this `welcome_channel.send(...)` isn’t wrapped in `try/except discord.HTTPException`. If the bot loses `Send Messages` permission or the channel is misconfigured, this await will raise, aborting the handler with no logging or fallback. Please catch `discord.HTTPException`, log a warning, and skip scheduling the grace-period task when the welcome message fails to send.
</issue_to_address>

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.

Comment on lines +561 to 564
def _serialize_value(self, value: object) -> JsonValue:
"""Convert complex Discord objects to simple serializable types.

Args:
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Serializing all non-container values to strings may unintentionally change data semantics.

With this change, any value not matched earlier (including ints/floats/bools/None) is now coerced to str, so data that previously passed through as proper JSON primitives will be logged as strings. That can break downstream consumers or analytics that depend on numeric/boolean types. Consider returning JSON primitives (int/float/bool/None) as-is and only str()-casting truly unknown or complex objects.

Comment on lines +411 to +416
sent = await welcome_channel.send(
rendered,
allowed_mentions=discord.AllowedMentions(users=True, roles=False, everyone=False),
view=view,
)
view.message = sent
Copy link
Contributor

Choose a reason for hiding this comment

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

issue: The welcome message send in on_member_join is not guarded against HTTP errors, which can break onboarding silently.

Unlike _send_log_message and the setup checklist, this welcome_channel.send(...) isn’t wrapped in try/except discord.HTTPException. If the bot loses Send Messages permission or the channel is misconfigured, this await will raise, aborting the handler with no logging or fallback. Please catch discord.HTTPException, log a warning, and skip scheduling the grace-period task when the welcome message fails to send.

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.

2 participants