Inbound person lookup used to be split across decoders, the packet
pipeline, and Bridge closures. Missing identities were filled from
unrelated packet headers, and "who is this person" was coupled to
roster refresh and pending-request enrichment.
Identity now owns both directions of resolution. The hot path stays
synchronous and emits unresolved events on a miss. Only the paths
that already waited before dispatch perform a network lookup, and
that lookup is a profile fetch by UID rather than a member-list
refresh. Pending-request comments and approval flags stay outside
Identity.
Decoders no longer invent a UIN from a group routing header. Private
chat routing still uses a hop's own wire UIN when the hop is named
that way. Inbound misses stay availability-first: they do not throw
and do not mark Identity degraded.
get_group_info / get_group_list mapped FE5 tag 31 and 88D tag 59 as
shutUpAllTimestamp, then treated any >0 value as muted. Official
wrapper.node maps those tags to internal 60259; the real expire
(60027 / groupShutupExpireTime) is FE5 tag 10 and 88D tag 45
(request mask 45). After a group had been muted, 60259 often stayed
nonzero so group_all_shut stuck at -1. Read the 60027 tags and treat
all-mute as expire > now (0xFFFFFFFF is permanent).
Fixes#356
send_group_msg / send_private_msg with a file segment that only has
file:// (no name/filename) used url.split('/').pop() as the upload
name, so percent-encoded CJK paths uploaded as
%E6%B5%8B%E8%AF%95%E6%96%87%E4%BB%B6.xlsx. Derive the fallback through
guessFileNameFromUrl, which decodeURIComponent's the path basename
(invalid encodings are left as-is). An explicit name still wins.
Fixes#354
WebUI access was a single shared password. This adds optional
Authenticator TOTP as a second factor, with one-time recovery codes
and SSH as the last-resort disable path.
Login stays two HTTP posts with no pending token: a correct password
and enabled 2FA without a second factor returns { needsTotp: true };
the client then resubmits { password, totp } or { password, recoveryCode }.
The TOTP secret is wrapped with a password-derived AES-256-GCM key.
Eight XXXX-XXXX recovery codes are stored as SHA-256 hashes and each
code is consumed once. Enabling or disabling 2FA invalidates other
sessions; disabling still requires password plus TOTP or a recovery
code. SNOWLUMA_DEV_MODE hides the feature.
The login page uses a dedicated second-factor screen (Interior OTP
cells for the 6-digit code; the same cell UI for recovery codes,
grouped as XXXX-XXXX). Settings expose enroll/confirm/disable and
one-time recovery-code display.
Fixes#351
upload_group_file of ~2 GiB files failed after a highway idle timeout
because the next decoded error_code=102902 was treated as a fatal
server reject. QQ's own BDH client (multi_uploader.cc NeedDelayRetry)
treats 102902 / 302902 as backpressure: wait, switch sender, and
re-PUT the same offset. Mirror that — drop the socket, wait 250 ms,
increment retryTimes, and retry the chunk up to 8 times. Definitive
codes such as 921 still fail immediately.
Fixes#352
Restore the interaction user lists returned by get_profile_like and keep self and other-user lookups consistent with the current client behavior.
Resolve missing account identities when needed, surface lookup failures, and update the public action catalog with the completed response shape. Add regression coverage for request selection, response decoding, identity enrichment, and malformed results.
Fixes#349
Deliver actionable invitations even when the sender account is unavailable instead of failing the full polling batch.
Isolate malformed records, keep sibling requests flowing, and distinguish multiple anonymous invitations by their request handles.
Add regression coverage for repeated polls, batch isolation, and anonymous-request deduplication.
Add typed group, pending-state, and per-inbox limit options to group-system message queries while preserving existing call sites.
Regenerate the shared action catalog so SDK and MCP consumers receive consistent validation rules and updated request-flag guidance.
Report pending group invitations through the standard OneBot request event even when their origin does not provide a live notification.
Prevent duplicate delivery when the same invitation is observed more than once, stop outstanding work with the account lifecycle, and preserve previously supported invitation behavior.
Expand group-system message queries and surface incomplete lookups instead of reporting misleading empty or not-found results.
Correct group-request retrieval so requester identities and action metadata remain usable across client and web-originated invitations.
Keep existing live-event behavior compatible, ensure returned requests carry the correct information for approval or rejection, and reject malformed request identifiers instead of emitting unusable flags.
Add regression coverage for both request inboxes and the supported invitation categories.
Recover invalidated native send bindings from same-account receive evidence. Track outbound connection loss in status health and refresh the six public native artifacts from native build 94f2b63.
Closes#342
Keep release files at the archive root and reject product-named directory components before CI packages artifacts.
Use neutral directories for media staging, WebUI uploads, stream storage, and MCP downloads so packaged and runtime layouts remain consistent.
Return an empty result when a successful, completed group essence response omits its message list instead of reporting a malformed response.
Keep upstream errors and incomplete pagination responses explicit, and cover omitted and null list variants with regression tests.
Fixes#320.
Prevent ordinary messages and forwarded nodes from combining video content with visible sibling segments or additional videos. Invalid combinations now fail before parsing side effects, media work, or bridge sends, while exact empty text placeholders remain compatible with a single video.
Cover group, private, temporary-session, CQ string, nested forward, cached forward, and protocol-builder paths. Preserve the existing standalone window-shake behavior and keep valid single-video messages unchanged.
Accept the standard action, event, and universal paths when a forward WebSocket server listens at the root endpoint.
Keep authentication, custom endpoints, embedded listeners, and rejected-path reporting compatible while assigning each accepted connection the correct role.
Add real-listener regression coverage for standard paths, trailing slashes, query authentication, invalid paths, and existing lifecycle behavior.
Fixes#317
Return the mute expiry timestamp from group member list and detail actions, using zero when no active mute is available.
Keep the public action descriptions and generated MCP catalog aligned, and cover muted, unmuted, and unavailable member responses.
Refs #314
Report whether group-wide mute is enabled through the existing group list and group information responses.
Keep standard and extended queries aligned, support explicit refreshes in the SDK, and update the generated action documentation without adding another endpoint.
Preserve established compatibility values, isolate cached non-member results by account, and add coverage across the request, mapping, action, and client layers.
Closes#313
Add read-only actions for catalog retrieval, per-face lookup, search, and super-face classification. Publish matching SDK methods and generated MCP schemas, including valid face ID zero.
Persist and validate the shared system face catalog, refresh it once when the first account connects, and coalesce concurrent loads. Resolve outgoing face metadata from the catalog so startup races, unknown IDs, and incomplete entries fail before sending.
Route private file publication through the common send path so successful uploads retain their send receipt and emit a complete self-sent event.
Return action failures when publication fails while preserving upload_file=false as a non-publishing operation.
Keep passive replies associated with the correct conversation when the same contact is shared across groups. Active temporary conversations remain unsupported.
Fixes#308
Improve compatibility for accounts restored after a restart.
Keep existing accounts working normally while restoring full availability for affected sessions.
Keep forward nodes out of ordinary message parameters and expose a node-only root builder for forward send and upload actions.
Lock nested forward content to one valid shape, clear stale form values when switching actions, and add regression coverage for the capability rules.
Update the packaged runtime files from the verified release pipeline.
Align all supported platform bundles with the validated account recovery release.
Refs #305
Refs #307