Skip to content

fix(cron+weixin): Batch-4 salvage — origin fallback dict + Weixin session split + multi-target deliver - #11594

Merged
teknium1 merged 5 commits into
mainfrom
hermes/hermes-7078c790
Apr 17, 2026
Merged

fix(cron+weixin): Batch-4 salvage — origin fallback dict + Weixin session split + multi-target deliver#11594
teknium1 merged 5 commits into
mainfrom
hermes/hermes-7078c790

Conversation

@teknium1

@teknium1 teknium1 commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Batch-4 salvage — cron origin-fallback correctness + Weixin session architecture fix + cron multi-target delivery. Three contributor PRs salvaged together in FULL (no feature dropping this time), with per-commit authorship preserved across all three contributors.

Included contributor work (ALL preserved, no scope trimming)

#10091 @LLQWQfix(gateway/weixin): split poll/send sessions, reuse live adapter
Commit 02777741 — the full PR, landed first to establish the architectural base:

  • weixin.py: split aiohttp.ClientSession into _poll_session and _send_session; add _LIVE_ADAPTERS registry so send_weixin_direct() reuses the connected gateway adapter instead of spawning a competing iLink session under the same bot token (fixes silent message loss when gateway is running, closes [Bug]: Weixin messages from cron/send_message are silently dropped when gateway is running #10089)
  • cron/scheduler.py: refactors _resolve_delivery_target into _resolve_single_delivery_target(job, deliver_value) + _resolve_delivery_targets(job) → List[dict], supporting comma-separated multi-target deliver values (e.g. "feishu,weixin"); delays pconfig.enabled check so live adapters work without gateway config entries
  • tools/send_message_tool.py: synthesizes PlatformConfig from WEIXIN_* env vars when gateway config lacks a weixin entry; falls back to WEIXIN_HOME_CHANNEL for home-channel resolution
  • tests/gateway/test_weixin.py: mock updates for _send_session

#9193 @briandevansfix(cron): restore origin fallback for feishu home channels
Two clean commits (cherry-picked on top of #10091):

  • 8635577e — narrow fix adding feishu/wecom/weixin to the origin-fallback tuple
  • ca85534aarchitectural fix adds a module-level _HOME_TARGET_ENV_VARS dict mapping platform → its actual env var name, plus a _get_home_target_chat_id() helper. Replaces the buggy f"{platform.upper()}_HOME_CHANNEL" pattern that silently missed platforms with non-generic env var names like MATRIX_HOME_ROOM
  • Applied inside fix(weixin): split poll/send sessions and reuse live adapter to prevent message loss #10091's renamed _resolve_single_delivery_target() — the dict/helper are at module scope and work regardless of the function rename

Closes #8848.

#11317 @Xowiekfix(cron): broaden deliver=origin home-channel fallback
Completeness intent picked up via my Teknium commit 3668766b — extends _HOME_TARGET_ENV_VARS to cover the 3 remaining origin-fallback-eligible platforms that have home-channel env vars in gateway/config.py:

  • emailEMAIL_HOME_ADDRESS (non-standard suffix)
  • dingtalkDINGTALK_HOME_CHANNEL
  • qqbotQQ_HOME_CHANNEL (non-standard prefix: QQ_ not QQBOT_)

Note: whatsapp, homeassistant, webhook, wecom_callback from #11317's proposed list don't have _HOME_CHANNEL env vars defined anywhere in gateway/config.py, so they can't meaningfully participate in origin fallback until those are added upstream.

Conflict resolutions

Three resolution points (all mechanical, all verified):

  1. _send_file() in weixin.py — Batch-3 fix: route Weixin voice replies to real file attachments #9425 added force_file_attachment param; fix(weixin): split poll/send sessions and reuse live adapter to prevent message loss #10091 renamed _session → _send_session. Kept both changes in one signature.

  2. send_voice() in weixin.py — lingering self._session check (added in Batch-3 fix: route Weixin voice replies to real file attachments #9425 after fix(weixin): split poll/send sessions and reuse live adapter to prevent message loss #10091 was written) updated to self._send_session to stay consistent with the rename.

  3. cron/scheduler.pyfix(cron): include feishu/wecom/weixin in origin fallback #9193's ca85534a header conflict with fix(weixin): split poll/send sessions and reuse live adapter to prevent message loss #10091's rename. Kept _resolve_single_delivery_target(job, deliver_value) signature from fix(weixin): split poll/send sessions and reuse live adapter to prevent message loss #10091; the _get_home_target_chat_id() helper from fix(cron): include feishu/wecom/weixin in origin fallback #9193 was added above it. All the actual fallback-iteration logic merged cleanly because it's inside the function body (same lines, just different function name).

  4. tests/gateway/test_weixin.py — three test sites added in Batch-1 (TestWeixinSendImageFileParameterName) and Batch-3 (TestWeixinVoiceSending) mocked only adapter._session but fix(weixin): split poll/send sessions and reuse live adapter to prevent message loss #10091 made the send paths check self._send_session. Added adapter._send_session = adapter._session companion setter at each.

Verification

Full suite: 430 tests pass across test_weixin, test_qqbot, test_unauthorized_dm_behavior, test_platform_base, test_send_image_file, test_send_retry, test_scheduler (cron), test_send_message_tool, test_url_safety. Cron subtree: 174 passed, 4 skipped.

Integration checks (runtime, real imports):

  • _HOME_TARGET_ENV_VARS dict complete: 14 platforms with correct env var names
  • Multi-target deliver works: "feishu,weixin" resolves to 2 targets via _resolve_delivery_targets()
  • Backward-compat _resolve_delivery_target() returns first target (existing callers unchanged)
  • QQ_HOME_CHANNEL resolves to qqbot platform (non-standard prefix handled)
  • MATRIX_HOME_ROOM resolves to matrix platform (non-standard suffix handled)
  • EMAIL_HOME_ADDRESS resolves to email platform (non-standard suffix handled)
  • _LIVE_ADAPTERS dict exists; _poll_session/_send_session attrs initialize to None; legacy _session attribute removed
  • send_weixin_direct() references _LIVE_ADAPTERS.get() with send_session.closed guard

py_compile OK on all 5 touched files.

AUTHOR_MAP additions

  • brian@bde.io → briandevans
  • hubin_ll@qq.com → LLQWQ
  • (xowiekk@gmail.com already in AUTHOR_MAP)

On merge

Will rebase-merge to preserve per-commit authorship across 3 contributors (LLQWQ ×1, briandevans ×2, me ×2), then close #9193, #11317, #10091 with credit pointing here.

LLQWQ and others added 5 commits April 17, 2026 05:39
… cron & send_message

- gateway/platforms/weixin.py:
  - Split aiohttp.ClientSession into _poll_session and _send_session
  - Add _LIVE_ADAPTERS registry so send_weixin_direct() reuses the connected gateway adapter instead of creating a competing session
  - Fixes silent message loss when gateway is running (iLink token contention)

- cron/scheduler.py:
  - Support comma-separated deliver values (e.g. 'feishu,weixin') for multi-target delivery
  - Delay pconfig/enabled check until standalone fallback so live adapters work even when platform is not in gateway config

- tools/send_message_tool.py:
  - Synthesize PlatformConfig from WEIXIN_* env vars when gateway config lacks a weixin entry
  - Fall back to WEIXIN_HOME_CHANNEL env var for home channel resolution

- tests/gateway/test_weixin.py:
  - Update mocks to include _send_session
…x Weixin test mocks

Cron origin fallback extension (builds on #9193's _HOME_TARGET_ENV_VARS):
adds the three remaining origin-fallback-eligible platforms that have
home channel env vars configured in gateway/config.py but use non-generic
env var names:

- email    → EMAIL_HOME_ADDRESS   (non-standard suffix)
- dingtalk → DINGTALK_HOME_CHANNEL
- qqbot    → QQ_HOME_CHANNEL      (non-standard prefix: QQ_ not QQBOT_)

Picks up the completeness intent of @Xowiek's PR #11317 using the
architecturally-correct dict-based lookup from #9193, so platforms with
non-standard env var names actually resolve instead of silently missing.
Extended the parametrized regression test to cover the new three.

Weixin test mock alignment (builds on #10091's _send_session split):
Three test sites added in Batch 1 (TestWeixinSendImageFileParameterName)
and Batch 3 (TestWeixinVoiceSending) mocked only adapter._session, but
#10091 switched the send paths to check self._send_session. Added the
companion setter so the tests stay green with the session split in place.
@teknium1
teknium1 force-pushed the hermes/hermes-7078c790 branch from ce02944 to 5a160d6 Compare April 17, 2026 12:43
@teknium1 teknium1 changed the title fix(cron+weixin): Batch-4 salvage — origin fallback dict + Weixin session split fix(cron+weixin): Batch-4 salvage — origin fallback dict + Weixin session split + multi-target deliver Apr 17, 2026
@teknium1
teknium1 merged commit fdf42d6 into main Apr 17, 2026
5 checks passed
@teknium1
teknium1 deleted the hermes/hermes-7078c790 branch April 17, 2026 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants