Keep the full control-character range limited to decoded styled-card text. Preserve member-level error isolation and cover whitespace, Unicode text, cached output, and restart behavior. Refs #680
72 lines
3.9 KiB
Markdown
72 lines
3.9 KiB
Markdown
# Contact profiles
|
||
|
||
`get_friend_list` and `get_friends_with_category` preserve their existing result
|
||
shapes and add optional public profile fields to each friend. By default they
|
||
only read the paginated roster: they do not issue a profile request for every
|
||
friend. Both accept `with_profile: true` to explicitly query additional details.
|
||
Large lists may require a longer client timeout.
|
||
|
||
Explicit enrichment uses a five-minute cache belonging to the current account,
|
||
deduplicates simultaneous queries and limits aggregate profile concurrency to
|
||
four. Values already supplied by the roster take precedence, including empty
|
||
strings, zero and false. An enrichment failure fails the action after draining
|
||
the current batch; it is logged, is not cached and is not automatically retried.
|
||
A later call may retry it. The ordinary flat list retains its existing logged
|
||
fallback to cached friends when the roster cannot be refreshed.
|
||
|
||
Friend rows contain `user_id`, `nickname`, `remark`, and `category_id` when
|
||
available. Public fields also appear in `get_stranger_info` when QQ supplies them:
|
||
|
||
| Fields | Meaning |
|
||
| --- | --- |
|
||
| `long_nick`, `sex`, `age`, `level` | Signature, gender, age and QQ level |
|
||
| `birthday_year`, `birthday_month`, `birthday_day` | Birthday; a hidden year is omitted |
|
||
| `phone_num`, `email` | Contact details made available by the profile |
|
||
| `country`, `province`, `city`, `college` | Location and school |
|
||
| `is_qq_vip`, `is_vip`, `is_years_vip`, `vip_level` | Ordinary membership, super membership (existing meaning of `is_vip`), annual membership and level |
|
||
| `privilege_icons` | `jump_url` if supplied, plus `open` and `closed` icon lists |
|
||
|
||
Each privilege entry may contain `type`, `level`, `flag`, `is_big`, `jump_url`,
|
||
`icon_url` and `deluxe_icon_url`. The two icon URLs can coexist. Flags retain
|
||
their server values; they are not guessed to mean annual or premium membership.
|
||
Local downloaded icon paths are not part of the response.
|
||
|
||
Missing data is omitted rather than replaced with false, zero or an empty list.
|
||
An explicitly returned empty icon-list body produces `open: [], closed: []`;
|
||
unavailable icon data omits `privilege_icons`. Existing required stranger fields
|
||
and aliases retain their earlier defaults. Malformed optional birthdays are
|
||
logged without recording their values; malformed text or privilege payloads
|
||
fail decoding.
|
||
|
||
SDK calls keep the existing options argument:
|
||
|
||
```ts
|
||
const friends = await client.getFriendList({ withProfile: true, timeoutMs: 120_000 });
|
||
const categories = await client.call('get_friends_with_category', { with_profile: true });
|
||
```
|
||
|
||
The exported `FriendInfo`, `StrangerInfo`, `PublicProfileFields` and
|
||
`ProfilePrivilege` types describe these fields. Returned availability depends
|
||
on QQ and the account's access; requesting enrichment does not guarantee every
|
||
field is present.
|
||
|
||
## Group member cards
|
||
|
||
Styled cards are decoded before their text is normalized. Within successfully
|
||
decoded UTF-8 text, SnowLuma removes C0 control characters (`U+0000`–`U+001F`)
|
||
except tab, line feed and carriage return, and removes DEL (`U+007F`). Spaces,
|
||
Unicode formatting characters and emoji sequences are preserved. Raw plain text
|
||
and explicitly literal display names retain strict control-character validation;
|
||
invalid UTF-8 is rejected in both formats.
|
||
|
||
`get_group_member_list` and `get_group_member_info` keep returning member data
|
||
when an individual group card cannot be decoded. The last valid card for that
|
||
member in the same group and account is retained; if none is known, the OneBot
|
||
`card` field is an empty string. An explicitly empty card from QQ clears a
|
||
previous value.
|
||
|
||
Unreadable cards are logged with the group, member, byte length and decoding
|
||
reason. The persistent cache distinguishes an unknown card from a confirmed
|
||
empty one, so later roster refreshes can recover it after restart. The existing
|
||
60-second roster cache and `no_cache: true` refresh option still apply.
|