Commit Graph
11 Commits
Author SHA1 Message Date
DrSmoothl 7c0519f75f lint 2026-10-12 15:43:34 +08:00
Airline233 884db940df feat(qzone): add set_qzone_msg_right to change a feed's visibility (#186)
* feat(qzone): add set_qzone_msg_right to change a feed's visibility

Add updateQzoneMsgRight in the protocol layer, porting php-qzone's
updateRight: fetch the feed via emotion_cgi_msgdetail_v6, rebuild the
full publish-shaped payload (richval/pic_bo/con), then override
ugc_right/allow_uins and POST to emotion_cgi_update. Expose it through
QzoneApi.updateRight and the OneBot set_qzone_msg_right action, with the
same ugc_right/target_uins validation as send_qzone_msg. Covered by
protocol, core and onebot tests.

* fix(qzone): make qzoneParse more stable and fixed a test example
2026-07-02 22:38:05 +08:00
Airline233 2f374ea8c9 feat(qzone): support post visibility rights when publishing qzone posts (#178)
Add ugc_right and target_uins to the qzone publish path across protocol,
bridge, and the send_qzone_msg OneBot action, regenerate the MCP catalog,
and cover restricted visibility (16/128) with validation tests.
2026-06-30 23:57:39 +08:00
Airline233 62eb8fd763 feat: Qzone支持上传图片(发说说、评论附图) (#152)
* feat(core,onebot,protocol): support publish shuoshuo and comment with image

* Fix: Accidentally changed notes
2026-06-30 01:47:21 +08:00
DrSmoothl 95f24ffc4a feat(qzone): comment_qzone — comment on a 说说 via web API
commentQzoneMsg POSTs a form body to taotao.qzone.qq.com's
emotion_cgi_re_feeds (proxied through h5.qzone), keyed by topicId
<hostUin>_<tid>__1, as the bot (uin=self) on target_uin's feed (defaults
to the bot's own space). Returns the new comment id best-effort — the
response field name varies (commentid/commentId) so a missing id with
code 0 is still success. Throws on non-zero code/subcode; rejects empty
tid/content pre-flight. WRITE — rate-limit (风控).

topicId shape + comment-id field are extrapolated from community impls,
pending a live capture (labeled in the helper comment).

- core: QzoneApi.comment; onebot: comment_qzone action; regen catalog.
- tests: re_feeds form lock (topicId/uin/hostUin/content/format=fs),
  commentid + camelCase commentId fallback + missing-id success, empty
  tid/content preflight, non-zero code throw, bridge owner-defaulting.
2026-06-20 02:05:56 +08:00
DrSmoothl 870504ae3a fix(qzone): thread abstime into like, flag unlike endpoint as unverified
Adversarial review (NEEDS-FIXES) of the like/unlike slice:
- Missing abstime: every real dolike impl (QLiker.py, CSDN) sends the
  target feed's post time. Threaded abstime through setQzoneLike →
  QzoneApi.like → like_qzone/unlike_qzone actions (optional, default 0;
  pass the feed's real time for reliability). Test asserts it.
- internal_unlike_app is the conventional paired CGI but NO public bot
  impl exercises unlike, so it's best-guess. Labeled UNVERIFIED/pending a
  live capture in the helper comment, the unlike action summary, and the
  unlike test — instead of pinning it as confirmed truth (avoiding the
  re-codify-a-wrong-constant trap the last 3 slices hit).
- Documented the success-signal (code/subcode) as extrapolated from
  sibling CGIs (dolike may use a succ/fail token instead).

The confirmed mechanics (dolike CGI, opuin=liker, unikey/curkey mood
shape, appid=311, fid=tid) are noted as such.
2026-06-20 02:02:25 +08:00
DrSmoothl d497f1e6c7 feat(qzone): like_qzone / unlike_qzone — like/unlike a 说说 via web API
setQzoneLike POSTs a form body to w.qzone.qq.com's internal_dolike_app
(like) or internal_unlike_app (unlike), proxied through h5.qzone, keyed by
the feed's mood unikey/curkey (http://user.qzone.qq.com/<uin>/mood/<tid>)
and fid=tid, as the bot (opUin). target_uin selects whose 说说 (defaults
to the bot's own); scope is 说说 (mood, appid 311). Throws on non-zero
code/subcode. Rejects empty tid pre-flight. WRITE — rate-limit (风控).

- core: QzoneApi.like(tid, targetUin?, like); onebot: like_qzone +
  unlike_qzone actions; regen catalog.
- tests: dolike vs unlike CGI selection, unikey/curkey/fid/opuin form
  lock, empty-tid preflight, code+subcode throws, and bridge owner-
  defaulting (self vs friend) + like-flag threading.
2026-06-20 01:56:23 +08:00
DrSmoothl a704f12945 feat(qzone): delete_qzone_msg — delete a 说说 by tid via web API
deleteQzoneMsg POSTs a form-urlencoded tid to taotao.qzone.qq.com's
emotion_cgi_delete_v6 (proxied through h5.qzone), on the bot's own space.
Success has no positive payload beyond code 0, so the contract is
throw-on-non-zero-code (foreign/unknown tid or auth failure); the server
only deletes feeds the hostUin owns. Rejects empty tid pre-flight.

- core: QzoneApi.delete; onebot: delete_qzone_msg action; regen catalog.
- tests: tid form-body/url/headers lock, empty-tid preflight, non-zero
  code throw, and bridge self-space threading.
2026-06-20 01:47:50 +08:00
DrSmoothl 9f2eb37232 feat(qzone): send_qzone_msg — publish a text 说说 via web API
First Qzone WRITE path. publishQzoneMsg POSTs a form-urlencoded body to
taotao.qzone.qq.com's emotion_cgi_publish_v6 (proxied through h5.qzone),
g_tk in the query, on the bot's own space. Returns the server-assigned
tid (the handle for later delete/comment/like) + post time.

- Throws on transport failure, non-zero Qzone code (content rejected /
  rate-limited), or a success body with no tid — never reports a publish
  as succeeded without the feed id. Rejects empty content pre-flight.
- 带图 (image) publishing is a later slice (needs the upload CGI first).
- Action carries a风控/限流 caveat: publishing is an active action and
  high frequency is rate-limited by Qzone, same as sending messages.

- core: QzoneApi.publish; onebot: send_qzone_msg action; regen catalog.
- tests: form-body/url/headers lock, empty-content preflight, non-zero
  code throw, missing-tid throw, and bridge self-space threading.
2026-06-20 01:40:06 +08:00
DrSmoothl 094dc12f0c feat(qzone): get_qzone_feeds — read friend feeds (好友动态) via web API
Second read-only Qzone slice. getQzoneFeeds over ic2.qzone.qq.com's
feeds3_html_more CGI, same cookie/g_tk plumbing + JSONP-tolerant parse +
throw-on-auth-failure contract as get_qzone_msg_list. Surfaces the stable
structured fields (uin/nickname/time/appid/key) and passes the
pre-rendered html blob through verbatim — deep HTML→segment parsing is
left to callers. Paginated via page_num (1-based) + count.

- protocol/web/qzone.ts: getQzoneFeeds + mapFeeds.
- core: QzoneApi.getFeeds (always the bot's own identity).
- onebot: get_qzone_feeds action; regenerated MCP catalog.
- tests: feeds mapping, HTTP url/params, empty-feed→[], auth-throw,
  and bridge pageNum/count threading.

Exact CGI field names / pagination cursor to be re-confirmed against a
live capture (same posture as the msglist helper).
2026-06-20 01:31:36 +08:00
DrSmoothl 0083a8fb07 test(qzone): cover empty-space happy path + bridge target_uin defaulting
Adversarial review of the get_qzone_msg_list slice flagged two real
coverage gaps (verdict was SHIP, no correctness defects):
- protocol: empty space ({code:0,msglist:[]}) must return [] not throw —
  the exact distinction the throw-on-auth-failure contract promises.
- core: QzoneApi.getMsgList target_uin defaulting + the >0 guard, the
  only place the string(identity.uin)/number(param) boundary is crossed.
2026-06-20 01:28:57 +08:00