Skip to content

fix: preserve webhook callback responses - #9115

Merged
Soulter merged 1 commit into
masterfrom
codex/fix-webhook-platform-responses
Jul 2, 2026
Merged

Soulter merged 1 commit into
masterfrom
codex/fix-webhook-platform-responses

Conversation

@Soulter

@Soulter Soulter commented Jul 2, 2026 •

Copy link
Copy Markdown
Member

修复 QQ、微信客服等平台 Webhook 下无法正常校验的问题。

Summary by Sourcery

Preserve platform-specific webhook callback responses in unified webhook routes and tighten WeCom-related proactive messaging error handling while adjusting QQ Official webhook validation flow.

New Features:

  • Allow platform webhook endpoints to return raw adapter responses including plain strings and (body, status, headers) tuples for protocol compatibility.
  • Expose proactive messaging capability for WeCom, WeCom AI Bot, and Weixin Official Account adapters via updated metadata flags.

Bug Fixes:

  • Permit QQ Official webhook validation callbacks to succeed without signature verification while still enforcing signatures for regular events.

Enhancements:

  • Normalize adapter webhook callback results into FastAPI-compatible responses via a shared webhook_response_from_result helper.
  • Change WeCom and WeCom AI Bot proactive send flows to raise explicit errors when configuration is missing or the channel does not support proactive messaging instead of failing silently with logs.

Tests:

  • Add unit tests covering webhook_response_from_result behavior for plain strings, tuple responses, and JSON dictionaries.
  • Extend dashboard webhook tests to assert preservation of raw plain and tuple responses from platform callbacks.
  • Update QQ Official webhook signature tests to reflect the new validation-callback behavior.

@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. area:platform The bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on. labels Jul 2, 2026
@Soulter
Soulter merged commit 1e3b12a into master Jul 2, 2026
20 checks passed
@Soulter
Soulter deleted the codex/fix-webhook-platform-responses branch July 2, 2026 16:25

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors webhook response handling to preserve raw platform responses, updates proactive messaging support across several adapters (WeCom, WeChat Official Account, and WeCom AI Bot) to raise exceptions instead of logging warnings on failure, and updates associated tests. The review feedback highlights critical issues where calling super().send_by_session before raising an exception in the WeCom and WeChat Official Account adapters could trigger unintended side-effects for failed messages. Additionally, it is recommended to catch UnicodeDecodeError alongside JSONDecodeError in the QQ Official Webhook server to prevent unhandled 500 errors on invalid payloads.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines 274 to 281
if hasattr(self.client, "kf_message"):
logger.warning("企业微信客服模式不支持 send_by_session 主动发送。")
await super().send_by_session(session, message_chain)
return
raise Exception("企业微信客服模式不支持 send_by_session 主动发送。")
if not self.agent_id:
logger.warning(
await super().send_by_session(session, message_chain)
raise Exception(
f"send_by_session 失败:无法为会话 {session.session_id} 推断 agent_id。",
)

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.

high

Calling await super().send_by_session(session, message_chain) before raising an exception is problematic. If the platform does not support proactive messages or if the agent_id is missing, the message sending has failed. Calling super().send_by_session will trigger base platform side-effects (such as recording the message in the database or history) even though the message was never actually sent, leading to inconsistent state.

The exception should be raised immediately without calling super().send_by_session.

        if hasattr(self.client, "kf_message"):
            raise Exception("企业微信客服模式不支持 send_by_session 主动发送。")
        if not self.agent_id:
            raise Exception(
                f"send_by_session 失败:无法为会话 {session.session_id} 推断 agent_id。",
            )

Comment on lines 397 to +398
await super().send_by_session(session, message_chain)
raise Exception("微信公众号不支持发送主动消息")

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.

high

Similar to the WeCom adapter, calling await super().send_by_session(session, message_chain) before raising an exception will trigger base platform side-effects (such as recording the message in the database or history) even though the message was never actually sent.

Since proactive messages are not supported for WeChat Official Accounts, the exception should be raised directly without calling super().send_by_session.

Suggested change
await super().send_by_session(session, message_chain)
raise Exception("微信公众号不支持发送主动消息")
raise Exception("微信公众号不支持发送主动消息")

Comment on lines 179 to 181
try:
msg = json.loads(body.decode("utf-8"))
except json.JSONDecodeError:

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.

medium

If the webhook payload contains invalid UTF-8 bytes, calling body.decode("utf-8") will raise a UnicodeDecodeError before json.loads is executed. Since only json.JSONDecodeError is caught, this will result in an unhandled exception and a 500 Internal Server Error.

Catching UnicodeDecodeError alongside json.JSONDecodeError ensures that any invalid encoding payloads are gracefully rejected with a 400 Bad Request.

Suggested change
try:
msg = json.loads(body.decode("utf-8"))
except json.JSONDecodeError:
try:
msg = json.loads(body.decode("utf-8"))
except (json.JSONDecodeError, UnicodeDecodeError):

result = await result
return _response_from_result(result)
return webhook_response_from_result(result)

@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, and left some high level feedback:

  • In the WeCom and Weixin adapters, send_by_session now calls super().send_by_session and then raises, which means the message is still sent but reported as an error; consider either raising before doing any fallback send or using a non-exception signal so the caller can distinguish truly unsupported proactive messaging from successful fallback delivery.
  • The support_proactive_message flags for WeCom, WeCom AI Bot, and Weixin Official Account are set to True even though the adapters immediately raise on proactive sends in some modes; it may be clearer to align these flags with actual proactive support or introduce a more nuanced capability indicator.
  • For webhook error handling you now wrap PlatformServiceError into a generic JSON error via webhook_response_from_result; if different platforms need protocol-specific error formats, consider letting adapters return their own structured error responses instead of a uniform {"error": ...} payload.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the WeCom and Weixin adapters, `send_by_session` now calls `super().send_by_session` and then raises, which means the message is still sent but reported as an error; consider either raising before doing any fallback send or using a non-exception signal so the caller can distinguish truly unsupported proactive messaging from successful fallback delivery.
- The `support_proactive_message` flags for WeCom, WeCom AI Bot, and Weixin Official Account are set to `True` even though the adapters immediately raise on proactive sends in some modes; it may be clearer to align these flags with actual proactive support or introduce a more nuanced capability indicator.
- For webhook error handling you now wrap `PlatformServiceError` into a generic JSON error via `webhook_response_from_result`; if different platforms need protocol-specific error formats, consider letting adapters return their own structured error responses instead of a uniform `{"error": ...}` payload.

## Individual Comments

### Comment 1
<location path="astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py" line_range="404-408" />
<code_context>
             "wecom 适配器",
             id=self.config.get("id", "wecom"),
             support_streaming_message=False,
-            support_proactive_message=False,
+            support_proactive_message=True,
         )

</code_context>
<issue_to_address>
**suggestion (bug_risk):** `support_proactive_message=True` conflicts with an implementation that always raises in `send_by_session`.

With `support_proactive_message=True`, higher-level code will assume proactive sends are supported, but `send_by_session` still always raises. Either keep this flag `False` or update `send_by_session` so it can successfully send in at least some cases, keeping the advertised capability consistent with actual behavior.

```suggestion
            "微信公众平台 适配器",
            id=self.config.get("id", "weixin_official_account"),
            support_streaming_message=False,
            support_proactive_message=False,
        )
```
</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 404 to 408
id=self.config.get("id", "weixin_official_account"),
support_streaming_message=False,
support_proactive_message=False,
support_proactive_message=True,
)

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.

suggestion (bug_risk): support_proactive_message=True conflicts with an implementation that always raises in send_by_session.

With support_proactive_message=True, higher-level code will assume proactive sends are supported, but send_by_session still always raises. Either keep this flag False or update send_by_session so it can successfully send in at least some cases, keeping the advertised capability consistent with actual behavior.

Suggested change
id=self.config.get("id", "weixin_official_account"),
support_streaming_message=False,
support_proactive_message=False,
support_proactive_message=True,
)
"微信公众平台 适配器",
id=self.config.get("id", "weixin_official_account"),
support_streaming_message=False,
support_proactive_message=False,
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:platform The bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on. size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants