Files
SnowLuma/packages/core/src/bridge/apis/qzone.ts
T
TanTan fa9c73363d
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
SnowLuma v2.4.2 source snapshot
2026-11-13 09:02:00 +08:00

325 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { JsonObject } from '@snowluma/common/json';
import {
commentQzoneMsg,
deleteQzoneMsg,
getQzoneFeeds,
getQzoneMsgDetail,
getQzoneMsgList,
getQzoneProfile,
publishQzoneMsg,
replyQzoneComment,
setQzoneBlack,
setQzoneLike,
setQzoneProfile,
updateQzoneMsgRight,
uploadQzoneImageFromSource,
type QzoneCommentResult,
type QzoneFeedsPage,
type QzoneMsgDetailResult,
type QzoneMsgListResult,
type QzoneProfile,
type QzoneProfileUpdate,
type QzonePublishResult,
type QzoneReplyTarget,
type QzoneUpdateRightResult,
type QzoneUploadImageResult,
} from '@snowluma/protocol/web/qzone';
import {
getQzonePublicCapabilities,
getQzoneHomeFeeds,
getQzoneFeedDetail,
type QzoneFeedDetail,
getQzoneShareArk,
type QzoneShareArk,
getQzonePublicFeeds,
getQzonePublicFeedDetail,
getQzonePublicComments,
getQzonePublicReplies,
setQzonePublicUpvote,
setQzonePublicLike,
setQzonePublicCommentLike,
commentQzonePublicFeed,
replyQzonePublicFeed,
deleteQzonePublicComment,
deleteQzonePublicReply,
setQzonePublicFollow,
forwardQzonePublicFeed,
type QzonePublicContent,
type QzonePublicFeedsPage,
} from '@snowluma/protocol/web/qzone-public';
import type { BridgeContext } from '../bridge-context';
/**
* Personal QQ-Zone (个人空间) web API: 说说, public recommendations, likes,
* comments and image upload over the cookie-backed Qzone web interfaces,
* reusing the same `getCookies('qzone.qq.com')` plumbing as GroupAlbumApi.
* Distinct from the group-album surface, which lives on GroupAlbumApi.
*/
export class QzoneApi {
constructor(private readonly ctx: BridgeContext) {}
async getProfile(targetUin?: number): Promise<QzoneProfile> {
const cookieObject = await this.ctx.apis.web.getCookies('qzone.qq.com');
return getQzoneProfile(cookieObject, this.ctx.identity.uin, targetUin?.toString());
}
async setProfile(profile: QzoneProfileUpdate): Promise<void> {
const cookieObject = await this.ctx.apis.web.getCookies('qzone.qq.com');
await setQzoneProfile(cookieObject, this.ctx.identity.uin, profile);
}
async getPublicCapabilities(): Promise<JsonObject> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return getQzonePublicCapabilities(cookies, this.ctx.identity.uin);
}
async getPublicFeeds(cursor?: string): Promise<QzonePublicFeedsPage> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return getQzonePublicFeeds(cookies, this.ctx.identity.uin, cursor);
}
async getHomeFeeds(targetUin?: number, cursor?: string): Promise<QzonePublicFeedsPage> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return getQzoneHomeFeeds(cookies, this.ctx.identity.uin, targetUin?.toString(), cursor);
}
async getFeedDetail(tid: string): Promise<QzoneFeedDetail> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return getQzoneFeedDetail(cookies, tid);
}
async getShareArk(feed: JsonObject): Promise<QzoneShareArk> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return getQzoneShareArk(cookies, feed);
}
async getPublicFeedDetail(feed: JsonObject): Promise<JsonObject> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return getQzonePublicFeedDetail(cookies, feed);
}
async getPublicComments(feed: JsonObject, cursor: string): Promise<JsonObject> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return getQzonePublicComments(cookies, feed, cursor);
}
async getPublicReplies(
feed: JsonObject,
comment: JsonObject,
cursor = '',
pageSize = 20,
): Promise<JsonObject> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return getQzonePublicReplies(cookies, feed, comment, cursor, pageSize);
}
async setPublicUpvote(feed: JsonObject, enable = true): Promise<JsonObject> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return setQzonePublicUpvote(cookies, feed, enable);
}
async setPublicLike(feed: JsonObject, enable = true): Promise<JsonObject> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return setQzonePublicLike(cookies, feed, enable);
}
async setPublicCommentLike(
feed: JsonObject,
comment: JsonObject,
enable = true,
reply?: JsonObject,
): Promise<JsonObject> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return setQzonePublicCommentLike(cookies, feed, comment, enable, reply);
}
async commentPublicFeed(feed: JsonObject, content: QzonePublicContent): Promise<JsonObject> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
const user = {
uin: this.ctx.identity.uin,
uid: this.ctx.identity.selfUid ?? '',
nick: this.ctx.identity.nickname,
};
return commentQzonePublicFeed(cookies, feed, user, content);
}
async replyPublicFeed(
feed: JsonObject,
comment: JsonObject,
content: QzonePublicContent,
reply?: JsonObject,
): Promise<JsonObject> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
const user = {
uin: this.ctx.identity.uin,
uid: this.ctx.identity.selfUid ?? '',
nick: this.ctx.identity.nickname,
};
return replyQzonePublicFeed(cookies, feed, user, comment, content, reply);
}
async deletePublicComment(feed: JsonObject, comment: JsonObject): Promise<JsonObject> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return deleteQzonePublicComment(cookies, feed, comment);
}
async deletePublicReply(
feed: JsonObject,
comment: JsonObject,
reply: JsonObject,
): Promise<JsonObject> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return deleteQzonePublicReply(cookies, feed, comment, reply);
}
async setPublicFollow(feed: JsonObject, enable = true): Promise<JsonObject> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return setQzonePublicFollow(cookies, feed, enable);
}
async forwardPublicFeed(feed: JsonObject, reason = ''): Promise<JsonObject> {
const cookies = await this.ctx.apis.web.getCookies('qzone.qq.com');
return forwardQzonePublicFeed(cookies, feed, reason);
}
/**
* 获取说说列表。`targetUin` 省略时取机器人自己的空间。
* `pos` 为起始偏移,`num` 为本页数量(服务端上限约 20/页)。
*/
async getMsgList(targetUin?: number, pos = 0, num = 20): Promise<QzoneMsgListResult> {
const uin = targetUin && targetUin > 0 ? targetUin.toString() : this.ctx.identity.uin;
const cookieObject = await this.ctx.apis.web.getCookies('qzone.qq.com');
return getQzoneMsgList(cookieObject, uin, pos, num);
}
/** 获取说说详情和一页评论。省略 targetUin 时使用当前账号。 */
async getMsgDetail(
tid: string,
targetUin?: number,
pos = 0,
num = 20,
): Promise<QzoneMsgDetailResult> {
const uin = targetUin === undefined ? this.ctx.identity.uin : String(targetUin);
const cookieObject = await this.ctx.apis.web.getCookies('qzone.qq.com');
return getQzoneMsgDetail(cookieObject, uin, tid, pos, num);
}
/**
* 获取好友动态。页码调用从首页顺序定位;续页优先传上一页的 cursor。
* 始终以机器人自己的身份拉取好友动态。
*/
async getFeeds(pageNum = 1, count = 10, cursor?: string): Promise<QzoneFeedsPage> {
const cookieObject = await this.ctx.apis.web.getCookies('qzone.qq.com');
return getQzoneFeeds(cookieObject, this.ctx.identity.uin, pageNum, count, cursor);
}
/**
* 从来源上传图片到 QQ 空间。
* `source` 支持: file:// 本地路径 | http(s):// URL | base64:// base64数据
*/
async uploadImageFromSource(source: string): Promise<QzoneUploadImageResult> {
const cookieObject = await this.ctx.apis.web.getCookies('qzone.qq.com');
return uploadQzoneImageFromSource(cookieObject, this.ctx.identity.uin, source);
}
/**
* 发表说说,返回新说说的 tid。始终发到机器人自己的空间。
* `richType` / `richval`: 带图说说时传 richType=1 和 richval 字符串
* (多图用 `\t` 连接多个 richval)。纯文字说说省略这两个参数。
* `ugcRight` / `targetUins`: 查看权限及其作用名单(ugcRight=16/128 时必填)。
*/
async publish(
content: string,
richType?: number,
richval?: string,
ugcRight = 1,
targetUins?: string,
): Promise<QzonePublishResult> {
const cookieObject = await this.ctx.apis.web.getCookies('qzone.qq.com');
return publishQzoneMsg(
cookieObject,
this.ctx.identity.uin,
content,
richType,
richval,
ugcRight,
targetUins,
);
}
/** 删除机器人自己空间的一条说说(按 tid)。 */
async delete(tid: string): Promise<void> {
const cookieObject = await this.ctx.apis.web.getCookies('qzone.qq.com');
await deleteQzoneMsg(cookieObject, this.ctx.identity.uin, tid);
}
/**
* 修改机器人自己空间一条已发说说的查看权限(按 tid)。
* `ugcRight` / `targetUins` 含义同 publish(16/128 时 targetUins 必填)。
*/
async updateRight(
tid: string,
ugcRight: number,
targetUins?: string,
): Promise<QzoneUpdateRightResult> {
const cookieObject = await this.ctx.apis.web.getCookies('qzone.qq.com');
return updateQzoneMsgRight(cookieObject, this.ctx.identity.uin, tid, ugcRight, targetUins);
}
/**
* 给一条说说点赞/取消赞。`targetUin` 省略时点赞机器人自己空间的说说;
* 点赞好友说说时传好友 uin(tid 来自 get_qzone_feeds / get_qzone_msg_list)。
*/
async like(
tid: string,
targetUin: number | undefined,
like: boolean,
abstime = 0,
): Promise<void> {
const selfUin = this.ctx.identity.uin;
const owner = targetUin && targetUin > 0 ? targetUin.toString() : selfUin;
const cookieObject = await this.ctx.apis.web.getCookies('qzone.qq.com');
await setQzoneLike(cookieObject, selfUin, owner, tid, like, abstime);
}
/**
* 评论一条说说。`targetUin` 为说说所属 QQ 号(省略=机器人自己空间);
* 始终以机器人身份发表评论。返回新评论 id(尽力而为)。
* `richType` / `richval`: 带图评论时传 richType=1 和图片直链 URL
* (从 uploadImageFromSource 的 url 字段获取)。
* `reply` 指定父评论及可选回复对象;楼中楼回复仅支持文字。
*/
async comment(
tid: string,
content: string,
targetUin: number | undefined,
richType?: number,
richval?: string,
reply?: QzoneReplyTarget,
): Promise<QzoneCommentResult> {
if (reply !== undefined && (richType !== undefined || richval !== undefined)) {
throw new Error('images are not supported for Qzone comment replies');
}
const selfUin = this.ctx.identity.uin;
const owner = targetUin && targetUin > 0 ? targetUin.toString() : selfUin;
const cookieObject = await this.ctx.apis.web.getCookies('qzone.qq.com');
if (reply !== undefined) {
return replyQzoneComment(cookieObject, selfUin, owner, tid, content, reply);
}
return commentQzoneMsg(cookieObject, selfUin, owner, tid, content, richType, richval);
}
/**
* 拉黑或解除拉黑某人(修改机器人自身 QQ 空间黑名单)。
* `ban=true` 拉黑,`ban=false` 解除拉黑。
*/
async setBlack(targetUin: number, ban: boolean): Promise<void> {
const selfUin = this.ctx.identity.uin;
const cookieObject = await this.ctx.apis.web.getCookies('qzone.qq.com');
await setQzoneBlack(cookieObject, selfUin, targetUin.toString(), ban);
}
}