Graila/send target open fix - #171
Open
fieldyyuyang wants to merge 14 commits into
Open
Conversation
…_dir (issue thisnick#153) find_account_dir only scanned the main WeChat PID's /proc/<pid>/fd; when the DB fds are held by a helper/renderer process it returned None, so login never persisted logged_in_user and /api/chats returned empty. Add: scan all WeChat-related PIDs, then a filesystem fallback over xwechat_files/* (account dirs with core DBs, newest mtime wins). Redacted diagnostic logging + unit tests for the pure helpers. login.rs: loud warn on detection give-up (control flow unchanged to preserve the ws/login contract).
…in our build network) The build network has no proxy inside the Docker build, so curl-from-github for noVNC and sqlcipher truncates. Vendor both tarballs into the build context (COPY) instead; pre-fetch commands documented in the Dockerfile. WeChat .deb (Tencent CDN) still downloads in-build fine. gitignore the vendored binaries + staged rust source.
…-nobell Recover the data plane when WeChat is logged in but session.logged_in_user was never persisted (issue thisnick#153). Two real login paths (phone-confirm, and connecting /api/ws/login while already logged in) bypass the LoginPlan DetectingUser phase, so find_account_dir is never called and /api/chats etc. return empty. - POST /api/sync/rescan (token-protected): resolve wechat pid -> patched find_account_dir_with_method -> persist logged_in_user -> trigger key extraction. Reuses the exact helpers the LoginPlan uses. Redacted JSON + logs (no wxid/path/key/token). - find_account_dir_with_method: expose detection method for observability. - x11vnc -nobell in entrypoint.sh + sessions/manager.rs: suppress the noVNC browser bell WeChat triggers on notifications. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The WeChat version-update popup is a separate top-level X window that the
a11y dismiss_popup (scoped to the main window tree) does not catch; left
open it obscures the UI and disrupts login/rescan/adapter.
- tools/ui_popups::close_known_popups: enumerate visible windows via the
no-shell exec_command xdotool wrapper, match a tight whitelist (name
Weixin/微信/WeChat + geometry ~500-650x350-500) guarded by 'a larger main
Weixin window also present', click only the top-right close X once each,
re-verify. Redacted logs/response (no titles/screenshots/chat data).
- POST /api/ui/close-known-popups (token-protected) returns
{status, popup_detected, popup_closed, closed_count, windows_seen,
popup_type, warning?}.
- /api/sync/rescan calls the closer first so popups can't block detection.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The frida chat-select fast-path hard-fails on WeChat builds without a
BUILD_PROFILE (e.g. 4.1.1.7 / 7b3f07cc), killing send at 'No action selected'.
Add a version-robust fallback that opens a chat the way a human does:
- tools/ui_open_chat::open_chat_a11y_search: resolve chatId -> display name via
the decrypted DB (get_chat_by_username), then drive WeChat's search box via
a11y-dump + xdotool (focus, clear, type name, click first result), and confirm
via the message composer (Send button) being present. No frida offsets; no
shell interpolation. Redacted logs/response (never name/id/content).
- POST /api/ui/open-chat (token-protected): {chatId, dryRun} -> redacted
{status, method, *_present, result_clicked, open_confirmed, error}, with
specific error codes.
- SendMessagePlan Opening: on chat-select failure, fall back to a11y search and
return a specific error instead of generic 'No action selected'. Frida hook
kept as the fast-path when a profile exists.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
WeChat's composer send button is 'Send(S)' on EN clients but '发送(S)' on ZH clients (the EMDE locale). Hardcoding the English name made open-confirmation (AW-FORK-7 open_confirmed=false) and the send Confirming phase false-negative. - ia/selectors::is_send_button_name(name): match 'send' (any case) OR '发送'; shared helper + unit test. - Use it in plans/send_message::find_edit_send_pair, plans/chat_open find_edit_near_send, and tools/ui_open_chat::chat_is_open (which now also logs redacted editable/send-button counts). Dropped the English-only Send(S) query_selector gate in send_message. No behavior change beyond locale. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AW-FORK-7B showed open_confirmed=false because the open clicked the wrong list: WeChat 4.1.1 renders search results as a SEPARATE deeper list (depth ~15, few items) vs the main chat-list (shallow depth, many items). The old find_first_result picked the global topmost list-item -> a main-chat-list row, so the target never opened. - find_search_box_node: locate the search box WITH its tree depth (prefer FOCUSED editable). - select_result_first_item: pick the first row of a list that is deeper than the search box, has a modest item count (<10), and sits at/below the search box — excludes the main chat-list. Redacted diagnostics: lists_count / candidate_lists / selected_depth / selected_items. - keyboard fallback: if no results list is distinguishable, or a click didn't confirm, press Down+Return (search box focused) to open the first result. Logs keyboard_fallback=true/false. No names/ids/content logged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AW-FORK-8 produced 2 outgoing messages from 1 authorized send: the send-path
open used the a11y keyboard fallback (Down+Return) which, with the composer
focused, injected a stray message before the real one.
- OpenChatOptions { dry_run, allow_keyboard_fallback, send_safe } +
open_chat_a11y_search_with_options. Endpoint keeps fallback
(OpenChatOptions::endpoint); send path uses OpenChatOptions::send_safe
(open_chat_a11y_search_send_safe): click-only, NEVER presses Return, fails
closed (keyboard_fallback_suppressed / open_not_confirmed_send_safe).
- SendMessagePlan Opening now requires fb.open_confirmed (not mere
result_clicked) and uses the send-safe open.
- SendMessagePlanState.send_action_executed guard: a plan run emits at most one
send action (send_action_guard_triggered). composer_not_found log added.
- Redacted logs only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AW-FORK-8C: the standalone open-chat keyboard fallback (Down+Return) sent a stray when a composer was focused; and the send-safe open could not complete when the chat was already open (candidate_lists=0). - focused_editable_is_search_box + safe_keyboard_open: NEVER press Return unless the FOCUSED editable node is the search box. Both keyboard-fallback sites (including the standalone endpoint) route through it; on unsafe focus it returns error keyboard_fallback_unsafe_focus and does not press Return. - SendMessagePlan Opening: if mainWindow=chat_open and a composer pair is already present, skip the a11y re-search entirely (already_chat_open skip_reopen) — this is the AW-FORK-8C state and avoids both the flaky re-search and re-typing into a focused composer. Send path still forbids keyboard fallback. - Doc note on /api/ui/open-chat: do not use dryRun:false as a pre-send precheck. - Redacted logs only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AW-FORK-19: SendMessagePlan no longer reuses whatever chat is already open on a send. The `already_chat_open => skip_reopen` shortcut assumed any present composer was the target and mis-routed a group send into a still-open private chat (AW-FORK-18E). Now every send opens the intended target by name via the send-safe a11y search (click-only, never Return) and fails closed if the target open can't be confirmed. Decision routed through decide_send_open_policy() with a regression test: composer-present-alone => ForceOpenTarget. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AW-FORK-20: when a send-safe open runs while ANOTHER chat is already open, the post-type search-results list can't be distinguished from the main chat list (candidate_lists=0) and the open fails closed (AW-FORK-19C). Before searching, if a chat is open, press Escape to return to the no-chat-open layout where the results list is detectable, then re-locate the search box. Escape never sends and we still never press Enter or type into a composer; if it can't clear, the candidate_lists=0 guard still fails closed (no mis-route). Gated via needs_pre_search_unfocus(send_safe, chat_open) with unit tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AW-FORK-21: when select_result_first_item finds no candidate search-results list (candidate_lists=0, the chat-open layout that fails closed in AW-FORK-19C/20B), log each list's depth/rect/item-count and which of the 3 heuristic conditions (deeper-than-search, items<10, first-at-or-below-search) excluded it, plus search_depth/search_y. Sanitized (numbers/booleans only). Diagnostic only — no behaviour change to the selection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AW-FORK-21B: find_search_box_node now selects the TOPMOST editable (the WeChat search field at the top-left) via choose_search_editable(), instead of focused.or(topmost). While a chat is open the bottom composer is EDITABLE+FOCUSED; the old logic picked it, so the resolved name was typed into the composer and no search results appeared (candidate_lists=0, AW-FORK-21). Now a focused bottom composer never wins. Adds search_box_policy=topmost_editable log + unit tests (topmost beats focused-bottom; focused doesn't auto-win). Send-safe invariants unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AW-FORK-22: the open path now clicks the search-result row whose normalized name EXACTLY matches the resolved target (select_result_matching_target + pick_matching_row), instead of the blind first row that strayed a group send to a wrong private chat (§63). System chats (File Transfer / Weixin Team / 文件传输助手 / 微信团队) are denylisted. No unique match => fail closed (no click, no keyboard fallback) in both send-safe and endpoint modes; the keyboard fallback is removed. Adds normalize_name/is_denied_system_chat/pick_matching_row + unit tests (exact-not-first-row, denylist-skipped, no-match/ambiguous fail closed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Aug 12, 2026
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.
No description provided.