Inbox groups and operator sightings are no longer treated as current
membership after a restart. Members who actually joined stay on the
roster until they leave.
A join now counts the member as in the group right away. A leave drops
them from the live roster without forgetting who they are. Other
sightings still do not mean the person is in the group.
After identity persistence is suspended, later observations were still
logged one-by-one. That filled Docker logs and made the WebUI log
stream unusable while the bot kept receiving messages. Report the skip
once, then at most once a minute with a count. Console write failures
must not throw into message handling.
Decoder used to write Identity: decodeGroupAdmin patched member roles
and decodeFriendMessage stashed invite-card msgseq. That split
observation with IncomingPacketPipeline.rememberEventIdentity and put
pending-application state on Identity.
Decoder now only returns events. Invite-card groupUin/msgseq ride on
FriendMessage; the live pipeline writes them to ContactsApi through
an optional port. Role self-heal for #93 stays in
rememberEventIdentity. History and forward decode do not write.
Identity no longer stores invite-card sequences.
Live #93 and #125 behavior is unchanged.
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.
Request and retain the group remark information needed by group list and group detail queries while preserving the existing safeguards for large rosters.
Synchronize confirmed group remark changes with the local identity roster and persistent cache. Remote failures leave local state untouched, and missing roster entries remain observable without creating phantom groups.
Add regression coverage for query mapping, request scope, persistence, failed updates, unknown groups, and closed identity stores.
Fixes#304
Validate the service result before reporting friend remark changes as successful. Rejected, incomplete, or contradictory confirmations now reach callers instead of being reported as completed.
Keep confirmed remarks synchronized in the identity cache and consume cross-client remark updates without exposing a new OneBot event. Local cache failures remain observable without turning an already completed remote change into a retryable failure.
Preserve friend remarks exactly across restarts and add request, response, persistence, receive-pipeline, and action-level regression coverage.
Fixes#303.
Fetch QQ's versioned robot UIN ranges, classify and persist member status, and surface is_robot from both group member actions.
Preserve unknown legacy cache entries and fail visibly when classification is unavailable.
Closes#265
The server never pushes a member-decrease back to the member that left of its
own accord, so set_group_leave produced no notice.group_decrease — downstream
kept a 'zombie' group in its cache, and a later set_group_add_request failed
with 'matching group request not found'.
bridge.leave() now, after the 0x1097_1 OIDB succeeds, synthesizes a self
group_member_leave (user_id = operator_id = self, sub_type 'leave') through the
event bus — converted to notice.group_decrease by the existing path — and
calls identity.forgetGroup() so get_group_list / member lookups drop it
immediately (in-memory + DB inactive).
Kick / disband already arrive via the GroupMemberDecrease push (PkgType 34).
Limitation: a MANUAL leave in the NTQQ client isn't covered — SL is packet-
based with no kernel callback, and a voluntary self-leave isn't pushed back;
only kernel-based clients (NapCat/LLOneBot) catch that.
Validated by unit tests (bridge leave emits the event + forgets the group).
When the bot is invited to a group via a private-chat "qun.invite" ark
card, set_group_add_request failed: handleGroupAddRequest re-derived the
approval tuple from fetchGroupRequests (0x10c8_2), but for a self-invite the
only sequence the server accepts at 0x10c8_1 is the msgseq embedded in the
card's mqqapi://group/invite_join jumpUrl, applied with eventType=2 /
filtered=false. The MSF invite push (PkgType 87) never carries that msgseq,
so approval returned 120161001 'handle async message fail'.
- friend-message decoder: parse the private invite card's jumpUrl and stash
groupUin -> msgseq on the per-instance IdentityService.
- ContactsApi.getGroupInviteCardSequence(groupId) exposes it.
- handleGroupAddRequest: for an invite flag, if a card msgseq is cached,
approve with setAddRequest(groupId, msgseq, 2, approve, reason, false);
otherwise fall back to the existing fetchGroupRequests path.
Validated on-target (Docker): private invite card -> set_group_add_request
approve -> 0x10c8_1 ok -> bot joined the group (get_group_member_info shows
role=member, fresh join_time); no 120161001.
Remove the in-tree @snowluma/sqlite wrapper and vendored better-sqlite3 prebuilts; migrate consumers to node:sqlite's synchronous API. Replaced imports and types (Database/Statement -> DatabaseSync/StatementSync), updated PRAGMA calls to use exec, and adjusted code in onebot (media/message/reaction stores) and protocol (identity service). Vite config no longer bundles sqlite native binaries and references to the sqlite workspace were removed. Also delete the packages/sqlite package and its native prebuild artifacts, and add a couple of unrelated AST improvements in proton: use a module-level shared wrapper cache and add an inheritance cache to avoid repeated scans. Minor import reordering/cleanup applied where needed.
The package previously named "bridge" houses the protocol-byte layer
(OIDB cmds, msg-push decoders, highway uploaders, element-builder,
event bus). The name "bridge" suggested transport/lifecycle, which it
isn't — that role lives in core/hook and core/bridge/manager. Rename
to match what's actually inside.
This is a pure rename: zero logic changes, all 73 import sites updated
mechanically and TS-verified. core/onebot dependencies key updated.
ESLint override path adjusted for the moved oidb-services directory.
Rename detection threshold: git log --find-renames=20% recommended
when following history across this commit — content-replacement
(@snowluma/bridge → @snowluma/protocol) dropped per-file similarity
below git's default 50% on import-heavy files.