QQ reports a per-member kick outcome in the command body. A zero
outcome means the member was removed; treat that as success instead
of a false failure. Still reject a non-zero outcome so callers do
not see success when the kick did not take effect.
Fixes#413
set_group_member_permissions was still rejected after the previous
command switch. Linux QQ writes these capabilities with the same
group-detail command as the other admin settings.
Closes#392
set_group_member_permissions was still rejected after the privilege
flag read was corrected. Album, temporary-session, and create-group
writes use a different 0x89A variant than invite policy.
Closes#392
The group-detail reply reports whether new members can see history as
a simple yes/no value. The write still flips that setting; the read
no longer expects a multi-bit field.
Group detail and ext-info can now return the join option, search flags,
invite/permission/history bits, and robot-add switch so setters have a
matching read path.
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
Reject malformed varints, out-of-bounds fields, and invalid groups before they escape their parent frame. Aggregate unknown-field metadata and decode signed int32 values while preserving SnowLuma's existing outbound encoding bytes.
The 0x8FC_2 body omitted uinName (tag 7), so the server accepted the request
(errorCode 0) but silently never applied the title — looking like the feature
was unsupported. Both NapCat (Oidb.0x8FC_2: uinName=title) and Lagrange
(0x8FC body @7 UinName=title) set it; mirror them by sending uinName = title.
No RE needed — two independent packet-based implementations agree on the field.
Note: setting a special title is group-owner-only, so on-target validation
requires a group the bot owns.
All recovered from QQNT wrapper.linux.node via IDA — NapCat ships these as
kernel-only with no portable packet wire, so each cmd + protobuf was traced
from the binary (worker ctor cmd/subcmd via BaseOidbWorker::DoSendOidbReq;
field tags from the per-feature EncodeRequest). All uin-form OIDB (envelope
reserved=1, matching the NT worker flag=1). One commit for easy rollback.
New OIDB services + byte-oracle tests (fixed-wire, mutation-resistant):
- 0x9130_0 getBuddyRecommendContactArkJson → share_peer/send_ark_share (user)
- 0x8b7_5 getGroupRecommendContactArkJson → share_peer/send_ark_share (group),
share_group_ex/send_group_ark_share (NapCat's getArkJsonGroupShare
was NOT RE'd; routed to this confident equivalent — documented)
- 0xd69_0 getDoubtBuddyReq → get_doubt_friends_add_request
- 0xd69_0 approvalDoubtBuddyReq → set_doubt_friends_add_request (approve only)
- 0xf00_3 modifyGroupExtInfoV2 → set_group_robot_add_option (EXTInfo tags
30/31; GroupExtFilter is client-side gating, not on the wire)
NOT shipped (kernel-.so-side, no static cmd recoverable from this binary —
honest, no guessed packets): get_robot_uin_range (trpc method-routed),
create_collection/get_collection_list (Weiyun multi-step), trans_group_file.
Doubt-list string field NAMES (nick/source/msg) are medium-confidence
(generic serializer); uid+reqTime are high. READ path, so a mislabel is
cosmetic, never a wire/ban risk. Wired through bridge friend/contacts/
groupAdmin apis; proto-defs + per-file subpath exports registered.
set_group_whole_ban false (unmute) failed with `OIDB error 1007` while
enabling worked (issue #70). OIDB (0x89A, 0) is shared by several commands
(SetSearch / SetAddOption / mute-all) disambiguated by body shape. Unmute
sends muteState.state=0, but proton omits zero scalars (proto3), leaving an
empty `muteState` the server can't classify. Enable sends 0xFFFFFFFF, which
is non-zero and survives — hence the asymmetry.
Add a `pb_optional<N, T>` marker (proto3 explicit presence): a scalar so
marked serialises even at its zero/default value. It is opt-in per field via
a NEW `explicitPresence` flag set ONLY by this marker — never reused from the
TS-`?`-derived `isOptional` (true for nearly every field). The encoder routes
explicit-presence scalars to a dedicated block; every `pb<>` / `pb_repeated<>`
field takes the untouched branches, so existing wire output is byte-identical
(proton's 99 existing tests, incl. byte-level round-trips, stay green).
Mark OidbMuteAllState.state `pb_optional<17, uint_32>` so unmute now puts
field 17 (=0) on the wire. New tests: proton pb_optional encode/round-trip
(incl. that sibling pb<> fields still omit zero), and a byte-level assertion
that mute-all unmute carries the state field.
Note: proton ships as a built Vite plugin (dist, gitignored); rebuild it
(`pnpm --filter @snowluma/proton build`) for the change to reach consumers.
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.