feat(notification): add WeChat Work app message notification channel#493
feat(notification): add WeChat Work app message notification channel#493chqchshj wants to merge 32 commits into
Conversation
Add WeComAppClient for sending notifications via WeCom application API (corpid + corpsecret + agentid), as opposed to the existing webhook bot. Features: - Access token caching with auto-refresh - TextCard message format with clickable links - Auto-retry on token expiration (40014/42001) - Configurable target users (touser, defaults to @ALL) Env vars: - WECOM_APP_CORPID - WECOM_APP_SECRET - WECOM_APP_AGENTID - WECOM_APP_TOUSER
There was a problem hiding this comment.
Code Review
This pull request adds support for WeChat Work App notifications by implementing a new WeComAppClient, updating configuration settings, and integrating the client into the notification factory. Key feedback involves improving security by escaping HTML in messages and masking secrets in the UI, fixing missing configuration logic in the service layer, and refining type hints and return values for consistency with the base notification class.
| f"<div class=\"normal\">💰 价格: {message.price}</div>", | ||
| f"<div class=\"normal\">📝 原因: {message.reason}</div>", |
There was a problem hiding this comment.
User-provided data such as price and reason should be HTML-escaped before being embedded in the textcard description. WeChat Work's textcard supports a subset of HTML, and unescaped characters (like < or &) in the product data could break the message structure or lead to injection issues.
| f"<div class=\"normal\">💰 价格: {message.price}</div>", | |
| f"<div class=\"normal\">📝 原因: {message.reason}</div>", | |
| f"<div class=\"normal\">💰 价格: {html.escape(message.price)}</div>", | |
| f"<div class=\"normal\">📝 原因: {html.escape(message.reason)}</div>", |
| "GOTIFY_TOKEN": "gotify_token", | ||
| "BARK_URL": "bark_url", | ||
| "WX_BOT_URL": "wx_bot_url", | ||
| "WECOM_APP_CORPID": "wecom_app_corpid", |
There was a problem hiding this comment.
| "BARK_URL": "bark_url", | ||
| "WX_BOT_URL": "wx_bot_url", | ||
| "WECOM_APP_CORPID": "wecom_app_corpid", | ||
| "WECOM_APP_SECRET": "wecom_app_secret", |
| wx_bot_url: Optional[str] = _env_field(None, "WX_BOT_URL") | ||
| wecom_app_corpid: Optional[str] = _env_field(None, "WECOM_APP_CORPID") | ||
| wecom_app_secret: Optional[str] = _env_field(None, "WECOM_APP_SECRET") | ||
| wecom_app_agentid: Optional[str] = _env_field(None, "WECOM_APP_AGENTID") |
There was a problem hiding this comment.
It is recommended to use Optional[int] for the wecom_app_agentid field. This allows Pydantic to automatically handle type conversion and validation from environment variables, ensuring the value is a valid integer as required by the WeChat Work API.
| wecom_app_agentid: Optional[str] = _env_field(None, "WECOM_APP_AGENTID") | |
| wecom_app_agentid: Optional[int] = _env_field(None, "WECOM_APP_AGENTID") |
| import asyncio | ||
| import time |
| self, | ||
| corpid: str | None = None, | ||
| corpsecret: str | None = None, | ||
| agentid: str | None = None, |
| self._token_expires_at = now + data.get("expires_in", 7200) - 300 | ||
| return self._access_token | ||
|
|
||
| async def send(self, product_data: Dict, reason: str) -> None: |
There was a problem hiding this comment.
| else: | ||
| raise RuntimeError( | ||
| f"企微应用消息发送失败: {result.get('errmsg', '未知错误')}" | ||
| ) |
There was a problem hiding this comment.
- HTML-escape user data in textcard description (XSS prevention) - Add WECOM_APP_SECRET to SECRET_NOTIFICATION_FIELDS (mask in UI) - Add wecom_app fields to build_notification_settings_response - Return bool from send() to match base class contract - Accept int|str for agentid parameter - Import html module
Summary
Add WeComAppClient for sending notifications via WeCom (企业微信) application message API, as opposed to the existing group bot webhook.
Motivation
The existing
wecomchannel uses group bot webhooks, which only support posting to group chats. Application messages (wecom_app) push directly to individual users via the WeCom app, which is more suitable for personal monitoring alerts.Changes
src/infrastructure/external/notification_clients/wecom_app_client.py— new clientsrc/infrastructure/config/settings.py— add 4 env fieldssrc/infrastructure/external/notification_clients/factory.py— register clientsrc/infrastructure/external/notification_clients/__init__.py— exportsrc/services/notification_config_service.py— field map, channel detection, status flagsConfiguration
Features
wecombot channel (separate channel keywecom_app)