SnowLuma v2.4.2 source snapshot
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

This commit is contained in:
TanTan committed 2026-11-13 09:02:00 +08:00
commit fa9c73363d
1367 files changed
+355735

No files matched your search

+324
View File
@@ -0,0 +1,324 @@
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);
}
}