const got = require('@/utils/got'); const cheerio = require('cheerio'); const iconv = require('iconv-lite'); const { parseDate } = require('@/utils/parse-date'); const timezone = require('@/utils/timezone'); const config = require('@/config').value; module.exports = async (ctx) => { const getPageUrl = (tid, authorId, page = 1, hash = '') => `https://nga.178.com/read.php?tid=${tid}&page=${page}${authorId ? `&authorid=${authorId}` : ''}&rand=${Math.random() * 1000}#${hash}`; const getPage = async (tid, authorId, pageId = 1) => { const link = getPageUrl(tid, authorId, pageId); const timestamp = Math.floor(Date.now() / 1000); let cookieString = `guestJs=${timestamp};`; if (config.nga.uid && config.nga.cid) { cookieString = `ngaPassportUid=${config.nga.uid}; ngaPassportCid=${config.nga.cid};`; } const response = await got(link, { responseType: 'buffer', headers: { Cookie: cookieString, }, }); const htmlString = iconv.decode(response.data, 'gbk'); return cheerio.load(htmlString); }; const getLastPageId = async (tid, authorId) => { const $ = await getPage(tid, authorId); const nav = $('#pagebtop'); const match = nav.html().match(/\{0:'\/read\.php\?tid=(\d+).*?',1:(\d+),.*?\}/); return match ? match[2] : 1; }; const deepReplace = (str, pattern, replace) => { // 对于可能存在嵌套的样式一路 replace 到最深处 while (str.match(pattern)) { str = str.replace(pattern, replace); } return str; }; const formatContent = (str) => { // 简单样式 str = deepReplace(str, /\[(b|u|i|del|code|sub|sup)\](.+?)\[\/\1\]/g, '<$1>$2'); str = str .replace(/\[dice\](.+?)\[\/dice\]/g, 'ROLL : $1') .replace(/\[color=(.+?)\](.+?)\[\/color\]/g, '$2') .replace(/\[font=(.+?)\](.+?)\[\/font\]/g, '$2') .replace(/\[size=(.+?)\](.+?)\[\/size\]/g, '$2') .replace(/\[align=(.+?)\](.+?)\[\/align\]/g, '$2'); // 列表 str = deepReplace(str, /\[\*\](.+?)(?=\[\*\]|\[\/list\])/g, '
  • $1
  • '); str = deepReplace(str, /\[list\](.+?)\[\/list\]/g, ''); // 图片 str = str.replace(/\[img\](.+?)\[\/img\]/g, (m, src) => ``); // 折叠 str = deepReplace(str, /\[collapse(?:=(.+?))?\](.+?)\[\/collapse\]/g, '
    $1$2
    '); // 引用 str = deepReplace(str, /\[quote\](.+?)\[\/quote\]/g, '
    $1
    ') .replace(/\[@(.+?)\]/g, '@$1') .replace(/\[uid=(\d+)\](.+?)\[\/uid\]/g, '@$2') .replace(/\[tid=(\d+)\](.+?)\[\/tid\]/g, '$2') .replace(/\[pid=(\d+),(\d+),(\d+)\](.+?)\[\/pid\]/g, (m, pid, tid, page, str) => { const url = `https://nga.178.com/read.php?tid=${tid}&page=${page}#pid${pid}Anchor`; return `${str}`; }); // 链接 str = str.replace(/\[url=(.+?)\](.+?)\[\/url\]/g, '$2'); // 分割线 str = str.replace(/\[h\](.+?)\[\/h\]/g, '

    $1

    '); return str; }; const tid = ctx.params.tid; const authorId = ctx.params.authorId || undefined; const pageId = await getLastPageId(tid, authorId); const $ = await getPage(tid, authorId, pageId); const title = $('title').text() || ''; const posterMap = JSON.parse( $('script') .text() .match(/commonui\.userInfo\.setAll\((.*)\)$/m)[1] ); const authorName = authorId ? posterMap[authorId].username : undefined; const items = $('#m_posts_c') .children() .filter('table') .map((ind, post_) => { const post = $(post_); const posterId = post .find('.posterinfo a') .first() .attr('href') .match(/&uid=(-?\d+)$/)[1]; const poster = authorName || posterMap[posterId].username; const content = post.find('.postcontent').first(); const description = formatContent(content.html()); const postId = content.attr('id'); const link = getPageUrl(tid, authorId, pageId, postId); const pubDate = timezone(parseDate(post.find('.postInfo > span').first().text(), 'YYYY-MM-DD HH:mm'), +8); return { title: cheerio.load(description).text(), author: poster, link, description, pubDate, guid: postId, }; }); let rssTitle; if (authorName) { rssTitle = `NGA ${authorName} ${title}`; } else { rssTitle = `NGA ${title}`; } ctx.state.data = { title: rssTitle, link: getPageUrl(tid, authorId, pageId), item: items.get(), }; };