mirror of
https://github.com/wechat-article/wechat-article-exporter.git
synced 2026-06-09 17:12:41 +08:00
186 lines
8.8 KiB
TypeScript
186 lines
8.8 KiB
TypeScript
import { formatTimeStamp } from '#shared/utils/helpers';
|
|
import { getCommentCache } from '~/store/v2/comment';
|
|
import { getCommentReplyCache } from '~/store/v2/comment_reply';
|
|
import { getMetadataCache } from '~/store/v2/metadata';
|
|
|
|
/**
|
|
* 从文章 HTML 中提取 comment_id。
|
|
* 文本分享与普通图文的页面结构不同,因此需要兼容多种写法。
|
|
*/
|
|
export function extractCommentId(html: string): string | null {
|
|
const patterns = [
|
|
// 普通图文: var comment_id = 'xxx' || '0';
|
|
/var comment_id = '(?<comment_id>\d+)' \|\| '0';/,
|
|
// 文本分享等: d.comment_id = xml ? getXmlValue('comment_id.DATA') : 'xxx';
|
|
/comment_id:\s*JsDecode\('(?<comment_id>\d+)'\)/,
|
|
// 有些模板里以 JsDecode 形式写在配置里
|
|
/d\.comment_id\s*=\s*xml \? getXmlValue\('comment_id\.DATA'\) : '(?<comment_id>\d+)';/,
|
|
// 兜底: window.segment_comment_id = 'xxx';
|
|
/window\.comment_id\s*=\s*'(?<comment_id>\d+)'/,
|
|
];
|
|
|
|
for (const pattern of patterns) {
|
|
const match = html.match(pattern);
|
|
if (match) {
|
|
// 优先使用命名分组,否则退回第一个分组
|
|
if ('groups' in match && match.groups && match.groups.comment_id) {
|
|
return match.groups.comment_id;
|
|
}
|
|
if (match[1]) {
|
|
return match[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 渲染文章的评论内容
|
|
* @param url 文章链接
|
|
*/
|
|
export async function renderComments(url: string) {
|
|
let commentHTML = '';
|
|
let elected_comments = [];
|
|
|
|
const commentCache = await getCommentCache(url);
|
|
if (commentCache) {
|
|
const commentResponse = commentCache.data;
|
|
if (Array.isArray(commentResponse)) {
|
|
elected_comments = commentResponse.flatMap(response => response.elected_comment);
|
|
} else if (commentResponse) {
|
|
elected_comments = commentResponse.elected_comment;
|
|
}
|
|
|
|
if (elected_comments.length > 0) {
|
|
// 评论总数
|
|
const metadata = await getMetadataCache(url);
|
|
commentHTML += '<div style="max-width: 667px;margin: 0 auto;padding: 10px 10px 80px;">';
|
|
commentHTML += `<p style="font-size: 15px;color: #949494;">留言 ${metadata?.commentNum}</p>`;
|
|
commentHTML += '<div style="margin-top: -10px;">';
|
|
|
|
// 留言列表
|
|
for (const comment of elected_comments) {
|
|
commentHTML += '<div style="margin-top: 25px;"><div style="display: flex;">';
|
|
if ([1, 2].includes(comment.identity_type)) {
|
|
commentHTML += `<img src="${comment.logo_url}" style="display: block;width: 30px;height: 30px;border-radius: 50%;margin-right: 8px;" alt="">`;
|
|
} else {
|
|
commentHTML += `<img src="${comment.logo_url}" style="display: block;width: 30px;height: 30px;border-radius: 2px;margin-right: 8px;" alt="">`;
|
|
}
|
|
commentHTML += '<div style="flex: 1;"><p style="display: flex;line-height: 16px;margin-block: 5px;">';
|
|
commentHTML += `<span style="margin-right: 5px;font-size: 15px;color: #949494;">${comment.nick_name}</span>`;
|
|
if (comment.is_from_friend === 1) {
|
|
commentHTML += `<span style="margin-right: 5px;font-size: 12px;color: #00BA5A;">朋友</span>`;
|
|
}
|
|
if (comment.ip_wording) {
|
|
commentHTML += `<span style="margin-right: 5px;font-size: 12px;color: #b5b5b5;">${comment.ip_wording?.province_name}</span>`;
|
|
} else {
|
|
commentHTML += `<span style="margin-right: 5px;font-size: 12px;color: #00BA5A;">作者</span>`;
|
|
}
|
|
commentHTML += `<span style="font-size: 12px;color: #b5b5b5;">${formatTimeStamp(comment.create_time)}</span>`;
|
|
commentHTML += '<span style="flex: 1;"></span><span style="display: inline-flex;align-items: center;">';
|
|
commentHTML += `<span class="sns_opr_btn sns_praise_btn" style="font-size: 12px;color: #8b8a8a;">${comment.like_num || ''}</span>`;
|
|
commentHTML += '</span></p>';
|
|
commentHTML += `<p style="font-size: 15px;color: #333;white-space: pre-line;margin-block: .5em;">${comment.content}</p>`;
|
|
if (comment.multi_info && comment.multi_info.pictures && comment.multi_info.pictures.length > 0) {
|
|
commentHTML += `<p>${comment.multi_info.pictures.map((pic: any) => '<img src="' + pic.url + '" style="max-width: 100%;" alt="">').join('')}</p>`;
|
|
}
|
|
if (comment.author_like_status === 1) {
|
|
commentHTML += '<p style="font-size: 12px;color: #00BA5A;margin-block: .5em;">作者赞过</p>';
|
|
}
|
|
commentHTML += '</div></div>';
|
|
|
|
// 留言回复列表
|
|
let reply_list = [];
|
|
const commentReplyResponse = await getCommentReplyCache(url, comment.content_id);
|
|
if (
|
|
commentReplyResponse &&
|
|
commentReplyResponse.data &&
|
|
commentReplyResponse.data.reply_list.reply_list.length > 0
|
|
) {
|
|
reply_list = commentReplyResponse.data.reply_list.reply_list;
|
|
} else if (comment.reply_new && comment.reply_new.reply_list.length > 0) {
|
|
reply_list = comment.reply_new.reply_list;
|
|
}
|
|
commentHTML += '<div style="padding-left: 38px;">';
|
|
reply_list
|
|
.sort((a: any, b: any) => a.create_time - b.create_time)
|
|
.forEach((reply: any) => {
|
|
commentHTML += '<div style="display: flex;margin-top: 15px;">';
|
|
if ([1, 2].includes(reply.identity_type)) {
|
|
commentHTML += `<img src="${reply.logo_url}" style="display: block;width: 23px;height: 23px;border-radius: 50%;margin-right: 8px;" alt="">`;
|
|
} else {
|
|
commentHTML += `<img src="${reply.logo_url}" style="display: block;width: 23px;height: 23px;border-radius: 2px;margin-right: 8px;" alt="">`;
|
|
}
|
|
commentHTML += '<div style="flex: 1;"><p style="display: flex;line-height: 16px;margin-block: 5px;">';
|
|
commentHTML += `<span style="margin-right: 5px;font-size: 15px;color: #949494;">${reply.nick_name}</span>`;
|
|
|
|
if (reply.is_from_friend === 1) {
|
|
commentHTML += `<span style="margin-right: 5px;font-size: 12px;color: #00BA5A;">朋友</span>`;
|
|
}
|
|
if (reply.ip_wording) {
|
|
commentHTML += `<span style="margin-right: 5px;font-size: 12px;color: #b5b5b5;">${reply.ip_wording?.province_name}</span>`;
|
|
} else {
|
|
commentHTML += `<span style="margin-right: 5px;font-size: 12px;color: #00BA5A;">作者</span>`;
|
|
}
|
|
commentHTML += `<span style="font-size: 12px;color: #b5b5b5;">${formatTimeStamp(reply.create_time)}</span>`;
|
|
commentHTML +=
|
|
'<span style="flex: 1;"></span><span style="display: inline-flex;align-items: center; font-size: 12px;color: #b5b5b5;">';
|
|
commentHTML += `<span class="sns_opr_btn sns_praise_btn" style="font-size: 12px;color: #8b8a8a;">${reply.reply_like_num || ''}</span>`;
|
|
commentHTML += '</span></p>';
|
|
commentHTML += `<p style="font-size: 15px;color: #333;white-space: pre-line;margin-block: .5em;">${reply.to_nick_name ? '回复 ' + reply.to_nick_name + ':' : ''} ${reply.content}</p>`;
|
|
if (reply.multi_info && reply.multi_info.pictures && reply.multi_info.pictures.length > 0) {
|
|
commentHTML += `<p>${reply.multi_info.pictures.map((pic: any) => '<img src="' + pic.url + '" style="max-width: 100%;" alt="">').join('')}</p>`;
|
|
}
|
|
if (reply.author_like_status === 1) {
|
|
commentHTML += '<p style="font-size: 12px;color: #00BA5A;margin-block: .5em;">作者赞过</p>';
|
|
}
|
|
commentHTML += '</div></div>';
|
|
});
|
|
commentHTML += '</div>';
|
|
|
|
commentHTML += '</div>';
|
|
}
|
|
|
|
commentHTML += '</div></div>';
|
|
}
|
|
}
|
|
|
|
return commentHTML;
|
|
}
|
|
|
|
// 从本地缓存获取文章的评论数据
|
|
export async function getArticleComments(url: string) {
|
|
let elected_comments = [];
|
|
const commentCache = await getCommentCache(url);
|
|
if (commentCache) {
|
|
const commentResponse = commentCache.data;
|
|
if (Array.isArray(commentResponse)) {
|
|
elected_comments = commentResponse.flatMap(response => response.elected_comment);
|
|
} else if (commentResponse) {
|
|
elected_comments = commentResponse.elected_comment;
|
|
}
|
|
|
|
if (elected_comments.length > 0) {
|
|
for (const comment of elected_comments) {
|
|
// 留言回复列表
|
|
let reply_list = [];
|
|
const commentReplyResponse = await getCommentReplyCache(url, comment.content_id);
|
|
if (
|
|
commentReplyResponse &&
|
|
commentReplyResponse.data &&
|
|
commentReplyResponse.data.reply_list.reply_list.length > 0
|
|
) {
|
|
reply_list = commentReplyResponse.data.reply_list.reply_list;
|
|
} else if (comment.reply_new && comment.reply_new.reply_list.length > 0) {
|
|
reply_list = comment.reply_new.reply_list;
|
|
}
|
|
reply_list.sort((a: any, b: any) => a.create_time - b.create_time);
|
|
comment.$reply_list = reply_list;
|
|
}
|
|
}
|
|
}
|
|
|
|
return elected_comments;
|
|
}
|