- q_contacts: replaced ad-hoc `gh_*`/`biz_*` prefix filter with
`chat_type_of == "private"`. The old filter leaked groups
(`@chatroom`), folded entries (`brandsessionholder` /
`@placeholder_foldgroup`), verified service accounts
(`verify_flag != 0`), and internal `@xxx` system accounts into
`wx contacts` output.
- q_search: parallelized the per-message-DB blocking phase via
`JoinSet::spawn_blocking`. Previously the `for (db_path, ...) in
by_path { ... .await }` loop ran one DB at a time; users with N
message_*.db shards paid N× latency. Each DB now runs concurrently
on the blocking pool; total latency collapses to a single slow DB.
- q_new_messages: fixed `new_state` reset path so first-run + truncated
sessions don't lock `since_ts` at `fallback_ts` forever. Old code
always wrote `state[uname] = old_since_ts || fallback_ts` for changed
sessions, then advanced only those that appeared in `all_msgs`. On
first run (state=None) truncated sessions ended up with
`state[uname] = now-86400` and stayed there across calls — every
subsequent call re-scanned a window that grew with elapsed time.
New logic separates three cases:
* in_results → advance to returned_max (incremental fetch)
* truncated + state → keep prev since_ts (retry next call)
* truncated + none → advance to session_ts (avoid lock-in; old
messages remain reachable via `wx history`).
* feat: expose url field for link/appmsg messages
Extract <url> from appmsg XML in type-49 messages and append it as
a 'url' field in history/search output. The field is omitted when
the message has no valid URL (non-link types, empty, non-http).
* fix: normalize appmsg urls across query outputs
---------
Co-authored-by: tsinghu <tsinghu@tencent.com>
Co-authored-by: jackwener <jakevingoo@gmail.com>
* feat: support group nicknames
* fix(group): keep duplicate nickname senders separate in stats
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
Was: Arc<std::sync::RwLock<Names>>; each dispatch clone_names() copied
4 HashMaps (~100KB for a user with 2700 contacts) and used std RwLock
which blocks the tokio worker thread during the clone.
Now: Arc<tokio::sync::RwLock<Arc<Names>>>; dispatch takes the read
guard, does Arc::clone (pointer bump), drops the guard, then spawns
the query work. Names is immutable after daemon startup; Arc is ideal.
Smoke tested: `wx sessions --json` returns correct data including
chat_type; 8 concurrent clients finish in 12ms.
Root cause: `wx init` does two conceptually-separate things in one
privileged process: (1) scan WeChat memory for keys (needs root) and
(2) write ~/.wx-cli/{all_keys,config}.json (needs only user). When
run under sudo, the files inherit root ownership, so later the daemon
(forked as the user) can't create daemon.sock/log/pid → silent 15s
timeout.
Also: all_keys.json is the raw AES key; 0644 leaked it to every user
on the system.
Fix in init.rs: after the scan completes, immediately setgid+setuid
back to \$SUDO_UID/\$SUDO_GID and set umask 0o077 before any file I/O.
Files are then created as the real user with 0600 by default. Migrate
old broken installs by chown+chmod-recursive before the setuid call.
Fix in transport.rs: pre-check that ~/.wx-cli/ is writable before
spawning daemon; on EACCES print a clear "sudo chown -R ..." hint
instead of the useless "daemon 启动超时" message.
Server uses interprocess::local_socket, but client was using
std::fs::OpenOptions("\\.\pipe\wx-cli-daemon") which fails to
connect to pipes created by interprocess's tokio listener.
Use the same interprocess client API on both sides for consistency.
Verified with: cargo check --target x86_64-pc-windows-gnu (mingw-w64).
Three related bugs caused "wx init" and daemon startup to fail on Windows:
1. init.rs: create ~/.wx-cli/ before writing all_keys.json (was created
only before config.json, so first write failed with ENOENT)
2. transport.rs (Windows): daemon.log was always empty because stderr
was never redirected, and log file open silently fell back to null
when parent dir didn't exist. Now mirror the Unix version: create
parent dir, try_clone to redirect both stdout and stderr.
3. server.rs (Windows): interprocess GenericNamespaced auto-prepends
\\.\pipe\ on Windows. Passing the full path caused a double-prefixed
pipe name that clients (using raw \\.\pipe\wx-cli-daemon) could
never connect to, leading to the 15s startup timeout.
- server.rs: add handle_connection_windows for named pipe connections
- transport.rs: import CommandExt trait for creation_flags on Windows
- release.yml: mkdir -p before binary copy to npm bin dirs