• 症状:群里 @ 后,FSM 循环 CHAT_LIST→click→WRONG_CHAT→BACK 死磕,
    最终撞 MAX_STATE_TRANSITIONS=6 cap 放弃。
    
    根因:进群聊页后 obn (id=/obn) 文本是 "宜创AI全员群(60)"(带成员数后缀),
    而 broadcast 传的 toUserDisplayName 是 "宜创AI全员群"(从 chatroom.displayname
    查的,不带后缀),严格 equals 永远 false → 永远判定 WRONG_CHAT。
    
    修法:classifyState 拿到 obn.text 后用正则剥掉末尾 (数字),再与目标做
    equals 比对。两种格式都接受(带 / 不带后缀都能匹配)。
    
    实测(22:23:00 后):
      classifyState: obn.text=宜创AI全员群(19) normalized=宜创AI全员群
      FSM[0] state=TARGET_CHAT      ← 一步到位(不再 BACK 死磕)
      Quote bubble matched [5]
      Long-press → 引用 → @ 火星人 → 写文本 → send
      整条 3 秒走完
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 旧代码的 onReceive postDelayed 块里嵌套 if + ad-hoc relaunch 逻辑,
    覆盖不到几种边界情形:
    - 微信停在非"会话"主 tab(通讯录/发现/我),CLEAR_TASK 不重置 LauncherUI
      内部 fragment,导致 chat list 扫到 0 个 kbq 后只能 fallback 搜索
    - 在错误聊天页(用户上次跟别人聊到一半),CLEAR_TASK 也保留 fragment
    - 在子页(搜索/创建群/朋友圈等),需要 BACK 才能退出
    
    新实现:状态机
      WeChatState ∈ {TARGET_CHAT, WRONG_CHAT, CHAT_LIST, NO_CHAT_VIEW, OUTSIDE_WECHAT}
    
      classifyState(root): O(1) 检测 bkk / obn / kbq / pkg.name → 5 状态
      driveStateMachine(n): switch dispatch + postDelayed 递归
        - TARGET_CHAT  → performTypeAndSend (终态)
        - WRONG_CHAT   → GLOBAL_ACTION_BACK → 再 classify
        - CHAT_LIST    → findChatListRow + click cj1 → 再 classify (→ TARGET_CHAT)
        - NO_CHAT_VIEW → 优先 findBottomNavTab("微信") click(Y 最大者),fallback BACK
        - OUTSIDE_WECHAT → startActivity LauncherUI → 再 classify
      MAX_STATE_TRANSITIONS = 6 防死循环
    
    新增辅助:
      findChatListRow(root, name): kbq strict equals → 回溯找 cj1 ancestor
      findBottomNavTab(root, text): findByText + 选 Y 坐标最大的 clickable 祖先
        (区分底部 nav 与标题栏 / 联系人列表里的同名 text)
    
    删除:enterChatViaListOrSearch (被状态机吸收)
    
    实测(从群聊页起步,FSM[0]→[3] 共 8s):
      FSM[0] state=WRONG_CHAT target=火星人 → BACK
      FSM[1] state=WRONG_CHAT target=火星人 → BACK
      FSM[2] state=CHAT_LIST  target=火星人 → click cj1
      FSM[3] state=TARGET_CHAT target=火星人 → performTypeAndSend ✅
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • #1 主进入路径改造(用户提议)— A11y 在不在目标聊天页时,
       优先扫主界面 chat list 直接点击进入,而不是走"搜索 → 点结果"。
       理由:搜索 "丁鹏、🍋火星人、火星人" 这种群名会被 WeChat 解读成
       "创建包含这 3 人的新群"建议,clickFirstSearchResult 误点进了
       MMCreateChatroomUI,然后 bkk 不出现,整条发送链路挂掉。
    
       新流程:
       - HermesAccessibilityService.enterChatViaListOrSearch:
         扫 root 下所有 ID_CHATLIST_NAME (kbq) 节点
         文本严格 equals pendingToUserDisplayName 时
         向上找 ID_CHATLIST_ROW (cj1) clickable 容器并 ACTION_CLICK
         等 2.2s 后检查 bkk 出现就 performTypeAndSend
       - 找不到匹配(chat 不在可见列表 / 不在 LauncherUI)回退到 performSearch
       - 实测:群在 chat list 第一/二行可定位,整段比搜索快 ~3s
    
    #2 @ 选人面板修复
       - auto-reply.js: senderName 是 wxid("yibooo"),但群 @ 面板显示
         的是昵称("火星人"),传 wxid 进 picker 永远找不到。改成
         getContactNickname(senderName) 翻译后再传给 broadcast 的 atSender。
       - HermesAccessibilityService.pickMentionMemberAndContinue:
         @ 失败 fallback 时旧 edit 节点引用已过期(IPC backed,600ms
         postDelayed 后失效),SET_TEXT 静默 false 整条发送挂掉。
         fallback 路径重新 getRootInActiveWindow + findInnerEditText
         拿 fresh edit 节点再 type 内容。
    
    实测(msgId 1556 type=57):
      ChatList scan: 10 kbq names → matched 丁鹏、小火子、火星人、延墨...
      Clicking row → Chat UI loaded
      Quote bubble @小火子 测试一下 → long-press → 引用
      @ mention typing → Clicking @ member: 火星人
      Append "延墨好!欢迎欢迎..." → Clicking send button
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 新增功能:
    - HermesAccessibilityService 支持广播 extras quoteText / atSender
    - quoteText: 在聊天页找文本含此 substring 的 bkl 气泡 → 长按 → 点「引用」
      菜单项 → 输入回复文本 → 点发送
    - atSender: 在输入框先打「@」触发选人面板 → 找匹配成员点击 → 追加回复
    
    实测路径完整跑通(msgId 1409 验证):
    1. 广播触发 A11y service
    2. 进入聊天页("already in correct chat" 或经过搜索)
    3. findBubbleByPartialText 在 bkl 列表里倒序匹配
    4. dispatchGesture 派发 800ms 合成长按手势(ACTION_LONG_CLICK 在 WeChat
       GestureDetector 实现上不触发菜单,必须用 gesture 派发)
    5. 等 1.2s 后用 findClickableByTextAllWindows 搜「引用」 — 关键:菜单在
       popup window 不在 active window,必须遍历 getWindows() 才能找到
    6. 点击引用 → 引用 chip 注入到输入框 → 写文本 → 点发送
    7. DB 写入的 message 是 type=57 (微信内部引用回复消息类型)
    
    关键依赖(Android AccessibilityService):
    - accessibility_service_config.xml 必须设 canPerformGestures="true"
      否则 dispatchGesture 不回调 onCompleted
    
    群聊路径:
    - auto-reply.js handleMessage 在 replyType=group-at/group-quote 时自动
      从触发消息 content 解析 bubbleText 当 quoteText,senderName 当 atSender
    - android-wechat-sender.js sendTextMessageBroadcast 接受 options.quoteText
      / options.atSender,拼到 am broadcast --es 参数里
    
    测试覆盖(在 Pixel 实测):
    - ✅ 私聊回归:plain text 路径不影响(msgId 1408)
    - ✅ 引用回复:私聊场景已验证(msgId 1409 type=57)
    - ⏸ 群聊 + @ 群成员:代码已写完,需要真实群消息触发才能测
    
    APK 已重编 + 用 debug.keystore 签 + adb install -r 升级(A11y 权限保留)。
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • - auto-reply.js 接入 hermes-agent OpenAI-compatible API (:8642)
    - 新增 android-wechat-tracker-sqlcipher.js 替代 LSPosed 日志 tail
    - 新增 android-wechat-sender-frida.js 替代 am broadcast 发送
    - 更新 keepalive.sh、README、HermesWeChatModule.java
    - 添加 .gitignore SQLite 变体规则
    - 新增 docs: 息屏发送尝试记录与屏幕自动唤醒方案
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • Mac side (lemonPCRM):
    - Express server with /api/master/chat SSE endpoint
    - MasterSession multi-round orchestration
    - AgentManager + ToolRegistry
    - ClaudeCliProtocol ACP implementation
    
    Pixel side (wechat_plain/scripts/):
    - hermes_wechat_daemon.py: standalone daemon
      - SQLCipher DB decryption with WAL checkpoint fix
      - Direct LLM API calls (Kimi/Anthropic/OpenAI)
      - am broadcast to trigger LSPosed send
      - Auto wake WeChat to foreground before poll/send
    - lsposed-wechat-sendmsg: LSPosed module
      - sendTextMessageDirect via WeChat internal API
      - u01.r1.a -> builder -> m1.a() full chain
      - Background thread NetScene submission
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »