fix: read WeChat identity from account settings

This commit is contained in:
2026-09-08 02:54:32 +08:00
parent e169cba8be
commit da9f0a4549
2 changed files with 46 additions and 14 deletions
+5 -4
View File
@@ -44,13 +44,14 @@
## 本轮多账号绑定验证
- Windows API 实例在锁屏/非交互 SSH 会话下返回 1 个脱敏 PID+HWND 目标和 1 个 `Unbound` 数据库账号;身份字段保持空值,未猜测绑定
- 缺少 `accountId` 的 sessions/contacts/messages 请求分别返回 HTTP 400 `AccountIdRequired`
- `doctor`/`inspect-ui`/`smoke` 在当前会话返回 `SessionLocked`/`WindowNotFound`;未把锁屏结果当作绑定成功证据
- 使用 `tscon 1 /dest:console` 将 Rogee 会话重新接入 console 后,WeChat/UIA 可用;未发送消息
- `/api/v1/ui-targets` 成功读取 `i-Rogee` / `Rogee`,并通过临时 manage 权限完成 PID 8912、HWND 590928 绑定;随后已恢复原始只读凭据
- `/api/v1/accounts` 返回 `Bound`,带 accountId 的 sessions 返回 HTTP 200;缺少 accountId 的 sessions/contacts/messages 仍返回 HTTP 400 `AccountIdRequired`
- 已配置 AC/DC 不息屏、不睡眠、不休眠、屏保关闭,并设置 `NoLockScreen=1`;配置仅禁止自动锁屏,不阻止用户手动锁定。
## 未完成/不宣称
- 未执行发送、联系人/群管理、朋友圈、语音等写操作;`send-text``group-at-all` 和 deferred 项保持禁用。
- 未完成窗口绑定成功和 60 分钟/20 条消息监听、断线补齐和多客户端慢消费者真机验收;SSE 与服务端监听源已实现,默认配置 `EnableListenerEvents=false`。自动化高频探针已中止,不作为验收证据;后续真机消息验证上限 3 条。
- 未完成第二个账号/第二个窗口隔离、60 分钟/20 条消息监听、断线补齐和多客户端慢消费者真机验收;SSE 与服务端监听源已实现,默认配置 `EnableListenerEvents=false`。自动化高频探针已中止,不作为验收证据;后续真机消息验证上限 3 条。
- 未完成外部主机直接访问验收(防火墙未开放);不将 HTTP 鉴权描述为网络加密。
- 远程验证任务已停止并清理;不再自动发送测试消息,后续真机消息验证上限为 3 条。
@@ -98,17 +98,45 @@ public static partial class WechatChatClient
}
public static Task<WechatAccountSnapshot> GetMyInfoAsync(CancellationToken cancellationToken = default) =>
WithMainWindowAsync(async (main, _) =>
WithMainWindowAsync(async (main, automation) =>
{
var accountButton = main.FindAllDescendants(cf => cf.ByControlType(ControlType.Button))
.FirstOrDefault(button => string.IsNullOrWhiteSpace(SafeAutomationId(button)) && !string.IsNullOrWhiteSpace(SafeName(button)))
?? throw new WxAgentException(WxAgentErrorCode.ControlNotFound, "Account button was not found.");
var displayName = SafeName(accountButton);
accountButton.Click();
await Task.Delay(200, cancellationToken).ConfigureAwait(false);
var values = main.FindAllDescendants(cf => cf.ByControlType(ControlType.Text))
.Select(SafeName).Where(value => !string.IsNullOrWhiteSpace(value)).Distinct(StringComparer.Ordinal).ToArray();
return new WechatAccountSnapshot(displayName, ValueAfter(values, "微信号"), ValueAfter(values, "地区"));
var more = FindByAutomationId(main, "MainView.main_tabbar.tabbar_setting")
?? main.FindAllDescendants(cf => cf.ByControlType(ControlType.Button))
.FirstOrDefault(button => IsActionable(button) && string.Equals(SafeName(button), "更多", StringComparison.Ordinal));
more?.Click();
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
var desktop = automation.GetDesktop();
var menu = desktop.FindAllChildren(cf => cf.ByControlType(ControlType.Window))
.FirstOrDefault(window => string.Equals(SafeName(window), "Weixin", StringComparison.Ordinal));
var settingsButton = (menu ?? desktop).FindAllDescendants(cf => cf.ByControlType(ControlType.Button))
.FirstOrDefault(button => IsActionable(button) && string.Equals(SafeName(button), "设置", StringComparison.Ordinal));
settingsButton?.Click();
AutomationElement? settings = null;
for (var attempt = 0; attempt < 30 && settings is null; attempt++)
{
settings = desktop.FindAllChildren(cf => cf.ByControlType(ControlType.Window))
.FirstOrDefault(window => string.Equals(SafeAutomationId(window), "PreferenceWindow", StringComparison.Ordinal)
|| string.Equals(SafeName(window), "设置", StringComparison.Ordinal));
if (settings is null) await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
if (settings is null) throw new WxAgentException(WxAgentErrorCode.ControlNotFound, "Account settings window was not found.");
try
{
var values = settings.FindAllDescendants(cf => cf.ByControlType(ControlType.Text))
.Select(SafeName).Where(value => !string.IsNullOrWhiteSpace(value)).Distinct(StringComparer.Ordinal).ToArray();
var accountIndex = Array.FindIndex(values, value => string.Equals(value, "账号", StringComparison.Ordinal));
var accountValues = accountIndex >= 0
? values.Skip(accountIndex + 1).TakeWhile(value => value is not ("登录方式" or "存储" or "退出登录")).ToArray()
: values;
var wechatId = ValueAfter(values, "微信号") ?? accountValues.FirstOrDefault(LooksLikeWechatId);
var displayName = ValueAfter(values, "昵称") ?? accountValues.FirstOrDefault(value => !string.Equals(value, wechatId, StringComparison.Ordinal)
&& value is not ("退出登录" or "自动"));
if (string.IsNullOrWhiteSpace(wechatId) && string.IsNullOrWhiteSpace(displayName))
throw new WxAgentException(WxAgentErrorCode.ControlNotFound, "Account identity was not found in settings.");
return new WechatAccountSnapshot(displayName ?? wechatId ?? string.Empty, wechatId, ValueAfter(values, "地区"));
}
finally { settings.AsWindow().Close(); }
}, cancellationToken);
/// <summary>Reads non-group contact rows, up to maxCount. Use the paged API for completeness and account selection.</summary>
@@ -630,6 +658,9 @@ public static partial class WechatChatClient
return values.Length == 0 ? null : new WechatNewFriendSnapshot(values[0], values.Length > 1 ? values[^1] : "unknown");
}
private static bool LooksLikeWechatId(string value) =>
value.Length is >= 6 and <= 20 && char.IsLetter(value[0]) && value.All(character => char.IsLetterOrDigit(character) || character is '_' or '-');
private static string? ValueAfter(IReadOnlyList<string> values, string label)
{
for (var index = 0; index < values.Count - 1; index++)