Skip to content

fix(discord): declare fields on Discord message components - #10710

Open
rudycelekli wants to merge 2 commits into
AstrBotDevs:masterfrom
rudycelekli:fix/discord-components-fields
Open

rudycelekli wants to merge 2 commits into
AstrBotDevs:masterfrom
rudycelekli:fix/discord-components-fields

Conversation

@rudycelekli

@rudycelekli rudycelekli commented Oct 23, 2026 •

Copy link
Copy Markdown

Addresses the component construction part of #7965.

The Discord-specific message components (DiscordEmbed, DiscordButton, DiscordReference, DiscordView, and the legacy DiscordViewComponent) extend BaseMessageComponent, which is a pydantic model. Their __init__ assigned attributes such as self.title without declaring them as fields and without calling super().__init__(), so every construction failed:

>>> DiscordEmbed(title="test")
ValueError: "DiscordEmbed" object has no field "title"

As a result, plugins cannot send embeds, buttons or views through MessageChain at all, even though DiscordPlatformEvent already knows how to render them.

Modifications / 改动点

  • astrbot/core/platform/sources/discord/components.py: declare the attributes as model fields and pass them to super().__init__(). The constructor signatures and the to_discord_embed() / to_discord_view() logic are unchanged.
  • astrbot/core/platform/sources/discord/discord_platform_event.py: same fix for DiscordViewComponent (view is declared as Any because discord.ui.View is not a pydantic type).
  • tests/unit/test_discord_components.py: construct each component, put an embed in a MessageChain and convert it, and build a view with a normal button and a link button.

The other points in the issue (duck-typed component detection and the respond-stage empty-chain check for embed-only chains) are separate behavior changes and are not part of this PR.

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

$ uv run pytest tests/unit/test_discord_components.py tests/test_discord_adapter.py -q
43 passed

ruff format / ruff check pass.


Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc. / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。
  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above. / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。
  • 📚 No WebUI navigation, page structure or labels changed, so no documentation update is needed. / 未改动 WebUI 入口、页面结构或术语,无需更新文档。
  • 🤓 I have ensured that no new dependencies are introduced. / 我确保没有引入新依赖库。
  • 😮 My changes do not introduce malicious code. / 我的更改没有引入恶意代码。

Summary by Sourcery

Restore functional Discord message component construction and validate its rendering through message chains and views.

Bug Fixes:

  • Fix construction of Discord message components so embeds, buttons, references, views, and legacy view components can be instantiated and rendered correctly.

Enhancements:

  • Define Discord component attributes as validated Pydantic model fields while preserving their existing conversion behavior.

Tests:

  • Add coverage for component construction, embed conversion, button and view rendering, validation, and serialization.

Review follow-up (2026-10-26)

Changed components to list[BaseMessageComponent], matching the constructor contract. Added invalid-item controls (integer, string, None) and a serialization control ensuring concrete button fields survive the typed list. Uses the project's Pydantic v1 model semantics. Before: 3 failed / 4 passed; after: all 7 tests in tests/unit/test_discord_components.py pass. Whole-repository Ruff lint/format checks pass. Local Python 3.13.5 validation; hosted Python 3.12 CI remains separate. No Discord network calls.

Co-authored-by: Cursor <cursoragent@cursor.com>

@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 found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="astrbot/core/platform/sources/discord/components.py" line_range="116" />
<code_context>
     """Discord视图组件,包含按钮和选择菜单"""

     type: str = "discord_view"
+    components: list = []
+    timeout: float | None = None

</code_context>
<issue_to_address>
**Invalid view items are silently ignored**

When a caller supplies a list containing values that are not message components, pydantic validates only that `components` is a list, so non-component entries are accepted; `to_discord_view()` ignores every entry that is not a `DiscordButton`, and callers can get a view missing the items they supplied.

Declare the field as `list[BaseMessageComponent]` so model validation matches the constructor contract.
</issue_to_address>

Sourcery assessment

Approval pending. 1 finding to address first.

Blocking findings: astrbot/core/platform/sources/discord/components.py:116


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread astrbot/core/platform/sources/discord/components.py Outdated

@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.

Sourcery assessment

Approved.

This branch has not been deployed

No deployments
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.

1 participant