diff --git a/src/bilibiliclient/comment/comment.ts b/src/bilibiliclient/comment/comment.ts index 148ce7f..c8c10aa 100644 --- a/src/bilibiliclient/comment/comment.ts +++ b/src/bilibiliclient/comment/comment.ts @@ -1,16 +1,18 @@ +import { decodeHtmlEntities } from "../../htmlparser"; + export const BilibiliClientCommentMethods = { // 获取评论区内容 async getReplies(this: any, type: string, oid: string, pn: number = 1, ps: number = 10, sort: number = 1) { const url = `https://api.bilibili.com/x/v2/reply?type=${type}&oid=${oid}&pn=${pn}&ps=${ps}&sort=${sort}`; const response = await this.getRequest(url); - return response.data.data; + return decodeReplyMessages(response.data.data); }, // 获取二级评论区内容 async getSecReplies(this: any, type: string, oid: string, root: string, pn: number = 1, ps: number = 10) { const url = `https://api.bilibili.com/x/v2/reply/reply?type=${type}&oid=${oid}&pn=${pn}&ps=${ps}&root=${root}`; const response = await this.getRequest(url); - return response.data.data; + return decodeReplyMessages(response.data.data); }, // 点赞评论 @@ -44,4 +46,22 @@ export const BilibiliClientCommentMethods = { const response = await this.postRequest(url, body, "application/x-www-form-urlencoded"); return response.data; }, -}; \ No newline at end of file +}; + +/** + * 评论内容中的引号、大小于号等字符会以 HTML 实体形式返回(例如 "、<、>)。 + * 在进入 UI 前统一解码,避免 QuickApp 的 text 组件把实体直接显示成乱码。 + */ +function decodeReplyMessages(replyData: any): any { + if (!replyData || !Array.isArray(replyData.replies)) { + return replyData; + } + + replyData.replies.forEach((reply: any) => { + if (reply && reply.content && typeof reply.content.message === "string") { + reply.content.message = decodeHtmlEntities(reply.content.message); + } + }); + + return replyData; +} diff --git a/src/htmlparser.ts b/src/htmlparser.ts index f1c3d0b..9555c35 100644 --- a/src/htmlparser.ts +++ b/src/htmlparser.ts @@ -18,13 +18,35 @@ const htmlEntities: { [key: string]: string } = { // 可以在这里扩展其他常用的 HTML 实体 }; -// 处理转义符,将   等转义符转换为对应的字符 -function decodeEntities(text: string): string { - return text.replace(/&([^;]+);/g, (match, entity) => { - return htmlEntities[entity] || match; // 如果实体存在,则替换;否则保留原样 +// 处理 HTML 实体,将评论或文章接口返回的转义符还原为可显示字符。 +// 同时支持十进制和十六进制数字实体,避免只处理少数几个命名实体。 +export function decodeHtmlEntities(text: string): string { + return text.replace(/&(#(?:x[0-9a-f]+|\d+)|[a-z][a-z0-9]+);/gi, (match, entity) => { + if (entity.charAt(0) === '#') { + const isHex = entity.charAt(1).toLowerCase() === 'x'; + const codePoint = parseInt(entity.slice(isHex ? 2 : 1), isHex ? 16 : 10); + if (!isNaN(codePoint) && codePoint >= 0 && codePoint <= 0x10ffff) { + // String.fromCodePoint 在部分 QuickApp 运行时不可用,手动处理代理项。 + if (codePoint <= 0xffff) { + return String.fromCharCode(codePoint); + } + const offset = codePoint - 0x10000; + return String.fromCharCode( + 0xd800 + (offset >> 10), + 0xdc00 + (offset & 0x3ff) + ); + } + return match; + } + + return htmlEntities[entity.toLowerCase()] || match; }); } +function decodeEntities(text: string): string { + return decodeHtmlEntities(text); +} + // 移除 HTML 标签,保留纯文本 function stripHtmlTags(html: string): string { return html.replace(/<\/?[^>]+(>|$)/g, "");