Files
TanTan f954252a34
Build NapCat Artifacts / Type Check & Test (push) Waiting to run
Release Tag / validate-version (push) Waiting to run
NapCatQQ source snapshot
2026-11-13 09:01:47 +08:00

147 lines
4.9 KiB
TypeScript

import {
normalizeQzoneMediaImages,
normalizeQzoneRichContent,
type QzoneComment,
type QzoneFeedReference,
} from './qzone-rpc';
export interface QzoneReplyItem {
reply_id: string | null;
user_id: string | null;
uid: string | null;
nickname: string | null;
time: number | null;
content: string | null;
images: string[] | null;
like_count: number | null;
liked: boolean | null;
target_user_id: string | null;
target_uid: string | null;
target_nickname: string | null;
reply: QzoneComment;
}
export interface QzoneCommentItem {
comment_id: string | null;
user_id: string | null;
uid: string | null;
nickname: string | null;
time: number | null;
content: string | null;
images: string[] | null;
like_count: number | null;
liked: boolean | null;
reply_num: number | null;
has_more_replies: boolean | null;
replies: QzoneReplyItem[];
comment: QzoneComment;
}
export interface QzoneCommentPage {
feed_ref: QzoneFeedReference;
comment_num: number | null;
comments: QzoneCommentItem[];
has_more: boolean;
next_cursor: string | null;
raw: Record<string, unknown>;
}
export interface QzoneReplyPage {
feed_ref: QzoneFeedReference;
comment_id: string;
replies: QzoneReplyItem[];
reply_num: number | null;
has_more: boolean;
next_cursor: string | null;
raw: Record<string, unknown>;
}
function object (value: unknown): Record<string, unknown> | undefined {
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
const prototype = Object.getPrototypeOf(value);
return prototype === Object.prototype || prototype === null ? value as Record<string, unknown> : undefined;
}
function identifier (value: unknown): string | null {
if (typeof value === 'string') return value.trim().length > 0 && value.length <= 8192 ? value : null;
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? String(value) : null;
}
function userId (value: unknown): string | null {
if (typeof value === 'string' && /^\d+$/.test(value) && /[1-9]/.test(value)) return value;
return typeof value === 'number' && Number.isSafeInteger(value) && value > 0 ? String(value) : null;
}
function count (value: unknown): number | null {
if (typeof value !== 'number' && !(typeof value === 'string' && /^\d+$/.test(value))) return null;
const result = Number(value);
return Number.isSafeInteger(result) && result >= 0 ? result : null;
}
function nickname (value: unknown): string | null {
return typeof value === 'string' ? value : null;
}
function likeState (value: unknown): boolean | null {
return value === 1 ? true : value === 0 ? false : null;
}
function author (value: unknown): { user_id: string | null; uid: string | null; nickname: string | null; } {
const user = object(value);
const uid = user?.['uid'];
return {
user_id: userId(user?.['uin']),
uid: typeof uid === 'string' ? identifier(uid) : null,
nickname: nickname(user?.['nick']),
};
}
export function normalizeQzoneReply (value: QzoneComment): QzoneReplyItem {
const reply = object(value);
if (!reply) throw new Error('QQ空间回复数据格式异常');
const user = author(reply['user']);
const target = author(reply['target_user']);
const like = object(reply['like']);
return {
reply_id: identifier(reply['id']),
...user,
// The renderer uses time in Unix seconds. create_time is not this wire field.
time: count(reply['time']),
content: normalizeQzoneRichContent(reply['content']),
images: normalizeQzoneMediaImages(reply['media_items']),
like_count: count(like?.['count']),
liked: likeState(like?.['status']),
target_user_id: target.user_id,
target_uid: target.uid,
target_nickname: target.nickname,
reply: value,
};
}
export function normalizeQzoneComment (value: QzoneComment): QzoneCommentItem {
const comment = object(value);
if (!comment) throw new Error('QQ空间评论数据格式异常');
const user = author(comment['user']);
const like = object(comment['like']);
const inline = comment['replys'];
if (inline !== undefined && inline !== null && !Array.isArray(inline)) throw new Error('QQ空间内联回复列表格式异常');
const replies = (inline ?? []) as unknown[];
return {
comment_id: identifier(comment['id']),
...user,
time: count(comment['time']),
content: normalizeQzoneRichContent(comment['content']),
images: normalizeQzoneMediaImages(comment['media_items']),
like_count: count(like?.['count']),
liked: likeState(like?.['status']),
reply_num: count(comment['reply_num']),
has_more_replies: typeof comment['has_more_reply'] === 'boolean' ? comment['has_more_reply'] : null,
// These are only the inline replies returned on this page, not an exhaustiveness claim.
replies: replies.map((reply) => {
if (!object(reply)) throw new Error('QQ空间内联回复数据格式异常');
return normalizeQzoneReply(reply as QzoneComment);
}),
comment: value,
};
}