fix(weixin): Batch-5 polish — SSRF allowlist, qrcode dep, session retry, macOS SSL, signature alignment#11634
Merged
Merged
Conversation
iLink context_token has a limited TTL. When no user message has arrived for an extended period (e.g. overnight), cron-initiated pushes fail with errcode -14 (session timeout). Tested that iLink accepts sends without context_token as a degraded fallback, so we now automatically strip the expired token and retry once. This keeps scheduled push messages (weather, digests, etc.) working reliably without requiring a user message to refresh the session first. Changes: - _send_text_chunk() catches iLinkDeliveryError with session-expired errcode (-14) and retries without context_token - Stale tokens are cleared from ContextTokenStore on session expiry - All 34 existing weixin tests pass
- Use certifi CA bundle for aiohttp SSL in qr_login(), start(), and send_weixin_direct() to fix SSL verification failures against Tencent's iLink server on macOS (Homebrew OpenSSL lacks system certs) - Fix QR code data: encode qrcode_img_content (full liteapp URL) instead of raw hex token — WeChat needs the full URL to resolve the scan - Render ASCII QR on refresh so the user can re-scan without restarting - Improve error message on QR render failure to show the actual exception Tested on macOS (Apple Silicon, Homebrew Python 3.13)
Contributor
|
This was referenced Apr 17, 2026
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Batch-5 Weixin polish — five contributor PRs salvaged together. Security, packaging, resilience, and platform-compat fixes.
Included contributor work
◆ #9222 @memosr —
fix(security): validate WeChat media URLs against CDN allowlist to prevent SSRFCommit
95e15e10. Adds_WEIXIN_CDN_ALLOWLISTfrozenset (7 known WeChat CDN hosts) and_assert_weixin_cdn_url()validation before any media download viafull_url. Complements the existingis_safe_url()check on_download_remote_media— defense in depth. Rejectsfile://, non-WeChat hosts, and unparseable URLs.◆ #9504 @anthhub —
fix(packaging): include qrcode in messaging extraCommit
877674e3. Addsqrcode>=7.0,<8toproject.optional-dependencies.messagingsohermes-agent[messaging]can render terminal QR codes for Weixin setup without a follow-uppip install qrcode. Adds metadata regression test. Updates weixin docs. Closes #9431.◆ #9928 @jinzheng8115 —
fix(weixin): retry send without context_token on iLink session expiryCommit
942086d8. When_send_text_chunkdetects an iLinkerrcode: -14(session expired), strips the stalecontext_token, clears it fromContextTokenStore, and retries once. Fixes silent drop of cron-initiated pushes (weather reports, digests, etc.) after overnight idle. Also makes_send_messagereturn the API response dict so callers can inspect errcode.◆ #8730 @shenuu —
fix(weixin): macOS SSL cert, QR data, and refresh renderingCommit
c1b0efbb. Three fixes:certifiCA bundle for SSL verification — Homebrew Python on macOS Apple Silicon can't verifyilinkai.weixin.qq.comagainst the default cert store. Refactored into_make_ssl_connector()helper with graceful fallback when certifi is unavailable.qrcode_img_content(liteapp URL) overqrcode(hex token) when encoding the QR ASCII — only the URL is scannable by WeChat.◆ #10342 @xiayh0107 —
Fix Weixin media uploads and refresh lockfileCommit
d8ab47f9. Alignssend_document()signature withBasePlatformAdapter— addsfile_name,reply_toparams and**kwargsfor forward-compat. Tightenssend_image_file()body to use keyword args. Adds regression tests covering keyword-arg acceptance and theupload_full_urlPOST behavior with hex-encoded AES key.Note: Much of #10342's original scope (the
send_image_fileparameter rename, MEDIA extraction, voice fallback, markdown preservation) was already landed via Batches 1/3. What survived here is thesend_documentbase-class alignment + the upload_full_url test, which weren't covered elsewhere.Conflict resolutions
Five resolution points (all mechanical, all verified):
_send_text_chunk()in weixin.py — fix(weixin): retry send without context_token on iLink session expiry #9928'sresp = await _send_message(self._session, ...)vs currentawait _send_message(self._send_session, ...). Kept PR's return capture + HEAD's Batch-4 session name:resp = await _send_message(self._send_session, ...).SSL connector in
qr_login()/connect()/send_weixin_direct()— fix(weixin): macOS SSL cert verification, QR scan data, and refresh rendering #8730'saiohttp.ClientSession(connector=certifi_ctx)vs HEAD'saiohttp.ClientSession(trust_env=True)(would have silently regressed proxy env var handling). Refactored into a module-level_make_ssl_connector()helper used at all 4 ClientSession creation sites withtrust_env=Truepreserved:Helper gracefully returns
Noneif certifi is unavailable (aiohttp then uses default ctx, still honorsSSL_CERT_FILEviatrust_env).send_image_file()body — Fix Weixin media uploads #10342 rewrote with keyword args +del reply_to, kwargs. Accepted PR's more thorough version.send_document()body — Fix Weixin media uploads #10342'sself._sessioncheck vs HEAD'sself._send_session. Kept HEAD's Batch-4 split + added PR'sdel file_name, reply_to, metadata, kwargs.Test fixture alignment — Batch-1's
TestWeixinSendImageFileParameterNameasserted the OLD positional call signature; Fix Weixin media uploads #10342 changedsend_image_fileto callsend_document(chat_id=chat_id, ...)with keyword args and caption=None default. Updated tests to match (chat_id as keyword, caption=None when not passed). Fix Weixin media uploads #10342'sTestWeixinOutboundMediamocked only_sessionbut code uses_send_sessionafter Batch-4 — added_send_session = sessioncompanion.Verification
_WEIXIN_CDN_ALLOWLISTpresent, contains 7 WeChat CDN hosts_assert_weixin_cdn_url()blocksevil.com, blocksfile://scheme, passes legitimatenovac2c.cdn.weixin.qq.com_make_ssl_connector()returns a TCPConnector in async context (graceful None fallback sync-calls tested separately — the helper is only called from async contexts in production)SESSION_EXPIRED_ERRCODE = -14present_send_messagereturn annotation updated toDict[str, Any]send_documentsignature now matches base class: chat_id, file_path, caption, file_name, reply_to, metadata, **kwargsAUTHOR_MAP additions
memosr_email@gmail.com → memosranthhub@163.com → anthhubshenuu@gmail.com → shenuuxiayh17@gmail.com → xiayh0107On merge
Will rebase-merge to preserve per-commit authorship across 5 contributors, then close #9222, #9504, #9928, #8730, #10342 with credit pointing here.
This closes out the Weixin/QQBot PR queue after Batches 1-5.