<|start_filename|>lib/routes/lkong/forum.js<|end_filename|>
const got = require('@/utils/got');

module.exports = async (ctx) => {
    const id = ctx.params.id;
    const type_path = ctx.params.digest ? '/digest' : '';
    const type_name = ctx.params.digest ? ' 精华' : '';

    const response = await got({
        method: 'get',
        url: `http://lkong.cn/forum/index.php?mod=ajax&action=forumconfig_${id}`,
    });
    const forumconfig = response.data;

    const response_forum_item = await got({
        method: 'get',
        url: `http://lkong.cn/forum/index.php?mod=data&sars=forum/${id}${type_path}`,
    });

    const data = response_forum_item.data;

    const items = await Promise.all(
        data.data.map(async (i) => {
            const item = {
                title: i.subject,
                pubDate: new Date(i.dateline + ' +8').toUTCString(),
            };
            if (i.id.startsWith('thread_')) {
                const thread_id = i.id.substr('thread_'.length);

                const thread_list_url = `http://lkong.cn/thread/index.php?mod=data&sars=thread/${thread_id}`;
                const thread_config_url = `http://lkong.cn/forum/index.php?mod=ajax&action=threadconfig_${thread_id}`;
                let thread_list;
                let thread_config;

                const cache = await ctx.cache.get(thread_list_url);
                if (cache) {
                    thread_list = JSON.parse(cache);
                } else {
                    const response_thread_list = await got({
                        method: 'get',
                        url: thread_list_url,
                    });
                    thread_list = response_thread_list.data;
                    ctx.cache.set(thread_list_url, JSON.stringify(thread_list));
                }

                const cache1 = await ctx.cache.get(thread_config_url);
                if (cache1) {
                    thread_config = JSON.parse(cache1);
                } else {
                    const response_thread_config = await got({
                        method: 'get',
                        url: thread_config_url,
                    });
                    thread_config = response_thread_config.data;
                    ctx.cache.set(thread_config_url, JSON.stringify(thread_config));
                }

                item.link = `http://lkong.cn/thread/${thread_id}`;
                item.description = thread_list.data[0].message;
                item.title = thread_config.subject;
            }

            return item;
        })
    );

    ctx.state.data = {
        title: `龙空 ${forumconfig.name}${type_name}`,
        link: `http://lkong.cn/forum/${id}${type_path}`,
        description: forumconfig.blackboard,
        image: `http://img.lkong.cn/forumavatar/000/00/00/${id}_avatar_middle.jpg`,
        item: items,
    };
};


<|start_filename|>lib/routes/mhgui/comic.js<|end_filename|>
const { resolve } = require('url');
const cheerio = require('cheerio');
const got = require('@/utils/got');
const LZString = require('lz-string');

const getChapters = ($) =>
    $('.chapter-list > ul')
        .toArray()
        .reverse()
        .reduce((acc, curr) => acc.concat($(curr).children('li').toArray()), [])
        .map((ele) => {
            const a = $(ele).children('a');
            return {
                link: resolve('https://www.mhgui.com/', a.attr('href')),
                title: a.attr('title'),
                num: a.find('i').text(),
            };
        });

module.exports = async (ctx) => {
    const { id } = ctx.params;

    const { data } = await got.get(`https://www.mhgui.com/comic/${id}/`);
    const $ = cheerio.load(data);

    if ($('#__VIEWSTATE').length > 0) {
        const n = LZString.decompressFromBase64($('#__VIEWSTATE').val());
        if (n) {
            $('#erroraudit_show').replaceWith(n);
            $('#__VIEWSTATE').remove();
        }
    }

    const bookTitle = $('.book-title > h1').text();
    const bookIntro = $('#intro-all').text();
    const coverImgSrc = $('.book-cover img').attr('src');
    const chapters = getChapters($);

    const genResult = (chapter) => ({
        link: chapter.link,
        title: chapter.title,
        description: `
            <h1>${chapter.num}</h1>
            <img src="${coverImgSrc}" />
        `.trim(),
    });
    ctx.state.data = {
        title: `看漫画 - ${bookTitle}`,
        link: `https://www.mhgui.com/comic/${id}/`,
        description: bookIntro,
        item: chapters.map(genResult),
    };
};


<|start_filename|>lib/v2/qq/maintainer.js<|end_filename|>
module.exports = {
    '/live/:id': ['nczitzk'],
};


<|start_filename|>lib/routes/people/cpc/24h.js<|end_filename|>
const got = require('@/utils/got');
const cheerio = require('cheerio');
const iconv = require('iconv-lite');

module.exports = async (ctx) => {
    const rootUrl = 'http://cpc.people.com.cn';
    const currentUrl = `${rootUrl}/GB/87228/index.html`;
    const response = await got({
        method: 'get',
        url: currentUrl,
        responseType: 'buffer',
    });

    const $ = cheerio.load(iconv.decode(response.data, 'gbk'));

    const list = $('.fl ul li a')
        .slice(0, 10)
        .map((_, item) => {
            item = $(item);
            const link = item.attr('href');

            return {
                title: item.text(),
                link: `${link.indexOf('http') < 0 ? rootUrl : ''}${link}`,
            };
        })
        .get();

    const items = await Promise.all(
        list.map((item) =>
            ctx.cache.tryGet(item.link, async () => {
                const detailResponse = await got({
                    method: 'get',
                    url: item.link,
                    responseType: 'buffer',
                });
                const content = cheerio.load(iconv.decode(detailResponse.data, 'gbk'));

                item.description = content('.show_text').html();
                item.pubDate = new Date(content('meta[name="publishdate"]').attr('content')).toUTCString();

                return item;
            })
        )
    );

    ctx.state.data = {
        title: $('title').text(),
        link: currentUrl,
        item: items,
    };
};


<|start_filename|>lib/routes/weibo/user.js<|end_filename|>
const querystring = require('querystring');
const got = require('@/utils/got');
const weiboUtils = require('./utils');
const timezone = require('@/utils/timezone');
const { parseDate } = require('@/utils/parse-date');
const { fallback, queryToBoolean } = require('@/utils/readable-social');

module.exports = async (ctx) => {
    const uid = ctx.params.uid;
    let displayVideo = '1';
    if (ctx.params.routeParams) {
        if (ctx.params.routeParams === '1' || ctx.params.routeParams === '0') {
            displayVideo = ctx.params.routeParams;
        } else {
            const routeParams = querystring.parse(ctx.params.routeParams);
            displayVideo = fallback(undefined, queryToBoolean(routeParams.displayVideo), true) ? '1' : '0';
        }
    }

    const { data: containerData } = await got({
        method: 'get',
        url: `https://m.weibo.cn/profile/info?uid=${uid}`,
        headers: {
            Referer: 'https://m.weibo.cn/',
        },
    });
    const name = containerData.data.user.screen_name;
    const description = containerData.data.user.description;
    const profileImageUrl = containerData.data.user.profile_image_url;
    const containerid = containerData.data.more.match(/\d+/)[0];

    const response = await got({
        method: 'get',
        url: `https://m.weibo.cn/api/container/getIndex?containerid=${containerid}`,
        headers: {
            Referer: `https://m.weibo.cn/u/${uid}`,
            'MWeibo-Pwa': 1,
            'X-Requested-With': 'XMLHttpRequest',
        },
    });

    const resultItem = await Promise.all(
        response.data.data.cards
            .filter((item) => item.mblog)
            .map(async (item) => {
                const key = 'weibo:user:' + item.mblog.bid;
                const data = await ctx.cache.tryGet(key, () => weiboUtils.getShowData(uid, item.mblog.bid));

                if (data && data.text) {
                    item.mblog.text = data.text;
                    item.mblog.created_at = parseDate(data.created_at);
                    if (item.mblog.retweeted_status && data.retweeted_status) {
                        item.mblog.retweeted_status.created_at = data.retweeted_status.created_at;
                    }
                } else {
                    item.mblog.created_at = timezone(item.mblog.created_at, +8);
                }
                // 转发的长微博处理
                const retweet = item.mblog.retweeted_status;
                if (retweet && retweet.isLongText) {
                    const retweetData = await weiboUtils.getShowData(retweet.user.id, retweet.bid);
                    if (retweetData !== undefined && retweetData.text) {
                        item.mblog.retweeted_status.text = retweetData.text;
                    }
                }

                const link = `https://weibo.com/${uid}/${item.mblog.bid}`;
                const formatExtended = weiboUtils.formatExtended(ctx, item.mblog);
                let description = formatExtended.description;
                const formattedTitle = formatExtended.title;
                const title = formattedTitle;
                const pubDate = item.mblog.created_at;

                // 视频的处理
                if (displayVideo === '1') {
                    description = weiboUtils.formatVideo(description, item.mblog);
                }

                return {
                    title,
                    description,
                    link,
                    pubDate,
                    author: item.mblog.user.screen_name,
                };
            })
    );

    ctx.state.data = {
        title: `${name}的微博`,
        link: `http://weibo.com/${uid}/`,
        description,
        image: profileImageUrl,
        item: resultItem,
    };
};


<|start_filename|>lib/routes/universities/sjtu/jwc.js<|end_filename|>
const got = require('@/utils/got');
const cheerio = require('cheerio');
const url = require('url');
const iconv = require('iconv-lite');

const urlRoot = 'http://www.jwc.sjtu.edu.cn/web/sjtu/';

const transcode = (buffer) => {
    const data = iconv.decode(buffer, 'gb2312');
    return cheerio.load(data);
};

async function getFullArticle(link) {
    try {
        const response = await got({
            method: 'get',
            url: link,
            responseType: 'buffer',
        });
        const fullArtData = transcode(response.data);
        const fullArt = fullArtData('.main_r_co_fo').html();
        return fullArt;
    } catch (error) {
        return '<h1> 这条消息被 -1s 了 </h1>';
    }
}

module.exports = async (ctx) => {
    const type = ctx.params.type || 'students';
    const config = {
        students: {
            link: '198076.htm',
            section: '面向学生的通知',
        },
        news: {
            link: '198005.htm',
            section: '新闻中心',
        },
        notice: {
            link: '198392.htm',
            section: '通知通告',
        },
        operation: {
            link: '198042.htm',
            section: '教学运行',
        },
        affairs: {
            link: '198043.htm',
            section: '注册学务',
        },
        yjb: {
            link: '198044.htm',
            section: '研究办',
        },
        jgb: {
            link: '198046.htm',
            section: '教改办',
        },
        zhb: {
            link: '198047.htm',
            section: '综合办',
        },
        party: {
            link: '198097.htm',
            section: '工会与支部',
        },
    };

    const sectionLink = url.resolve(urlRoot, config[type].link);

    const response = await got({
        method: 'get',
        url: sectionLink,
        responseType: 'buffer',
    });

    const $ = transcode(response.data);

    const out = await Promise.all(
        $('.main_r_xuxian')
            .find('tr')
            .slice(0, 10)
            .map(async (i, e) => {
                const title = $(e)
                    .find('a')
                    .text()
                    .replace(/[\r\n]/g, ' ');
                const relativeLink = $(e).find('a').attr('href');
                const link = url.resolve(sectionLink, relativeLink);
                const date = new Date(
                    $(e)
                        .children('td')
                        .eq(-1)
                        .text()
                        .match(/\d{4}-\d{2}-\d{2}/)[0]
                );
                const timeZone = 8;
                const serverOffset = date.getTimezoneOffset() / 60;
                const pubDate = new Date(date.getTime() - 60 * 60 * 1000 * (timeZone + serverOffset)).toUTCString();
                const description = await ctx.cache.tryGet(link, () => getFullArticle(link));
                const single = {
                    title,
                    link,
                    pubDate,
                    description,
                };
                return Promise.resolve(single);
            })
            .get()
    );

    ctx.state.data = {
        title: '上海交通大学教务处 ' + config[type].section,
        link: sectionLink,
        item: out,
    };
};


<|start_filename|>lib/routes/ehentai/ehapi.js<|end_filename|>
const got = require('@/utils/got');
const cheerio = require('cheerio');
const config = require('@/config').value.ehentai;

const headers = {};
const has_cookie = config.ipb_member_id && config.ipb_pass_hash && config.sk;
if (has_cookie) {
    const { ipb_member_id, ipb_pass_hash, sk } = config;
    headers.cookie = `ipb_member_id=${ipb_member_id};ipb_pass_hash=${ipb_pass_hash};sk=${sk}`;
}

function ehgot(url) {
    return got({ method: 'get', url: `https://e-hentai.org/${url}`, headers });
}

async function parsePage(cache, data, get_bittorrent = false) {
    const $ = cheerio.load(data);
    const table = $('table[class="itg gltc"] tbody');
    if (!table) {
        return [];
    }

    async function parseTableRow(cache, element) {
        const el = $(element);
        const el_a = el.find('td[class="gl3c glname"] a');
        const el_img = el.find('td.gl2c div.glthumb div img');
        const title = el_a.find('div.glink').html();
        const thumbnail = el_img.attr('data-src') ? el_img.attr('data-src') : el_img.attr('src');
        const description = `<img src='${thumbnail}'>`;
        const pubDate = el.find('div[id^="posted_"]').html();
        const link = el_a.attr('href');
        if (title && link) {
            const item = { title, description, pubDate, link };
            if (get_bittorrent) {
                const el_down = el.find('td.gl2c div div.gldown');
                const bittorrent_page_url = el_down.find('a').attr('href');
                if (bittorrent_page_url) {
                    const bittorrent_url = await getBittorrent(cache, bittorrent_page_url);
                    if (bittorrent_url) {
                        item.enclosure_url = bittorrent_url;
                        item.enclosure_type = 'application/x-bittorrent';
                        item.bittorrent_page_url = bittorrent_page_url;
                    }
                }
            }
            return item;
        }
    }

    const item_Promises = [];
    table.children('tr').each((index, element) => {
        item_Promises.push(parseTableRow(cache, element));
    });
    const items_with_null = await Promise.all(item_Promises);

    const items = [];
    for (const item of items_with_null) {
        if (item) {
            items.push(item);
        }
    }
    return items;
}

let p = '';

function getBittorrent(cache, bittorrent_page_url) {
    return cache.tryGet(bittorrent_page_url, async () => {
        try {
            const response = await got({ method: 'get', url: bittorrent_page_url, headers });
            const $ = cheerio.load(response.data);
            const el_forms = $('form').get();
            let bittorrent_url = undefined;
            for (const el_form of el_forms) {
                const el_a = $(el_form).find('a');
                const onclick = el_a.attr('onclick');
                if (onclick) {
                    const match = onclick.match(/'(.*?)'/);
                    if (match) {
                        bittorrent_url = match[1];
                        const match_p = bittorrent_url.match(/torrent\?p=(.*?)$/);
                        if (match_p) {
                            p = match_p[1];
                        }
                        break;
                    }
                }
            }
            return bittorrent_url;
        } catch {
            return undefined;
        }
    });
}

function updateBittorrent_url(cache, items) {
    // 下种子文件需要动态密码，密码每几次请求就更新一次
    for (const item of items) {
        if (item.enclosure_url) {
            item.enclosure_url = item.enclosure_url.replace(/torrent\?p=.*$/, `torrent?p=${p}`);
            cache.set(item.bittorrent_page_url, item.enclosure_url);
        }
    }
    return items;
}

async function gatherItemsByPage(cache, url, get_bittorrent = false) {
    const response = await ehgot(url);
    const items = await parsePage(cache, response.data, get_bittorrent);
    return updateBittorrent_url(cache, items);
}

async function getFavoritesItems(cache, page, favcat, inline_set, get_bittorrent = false) {
    const response = await ehgot(`favorites.php?favcat=${favcat}&inline_set=${inline_set}`);
    if (parseInt(page) === 0) {
        const items = await parsePage(cache, response.data, get_bittorrent);
        return updateBittorrent_url(cache, items);
    }
    return gatherItemsByPage(cache, `favorites.php?page=${page}&favcat=${favcat}`, get_bittorrent);
}

function getSearchItems(cache, params, page = undefined, get_bittorrent = false) {
    if (page) {
        return gatherItemsByPage(cache, `?page=${page}&${params}`, get_bittorrent);
    } else {
        return gatherItemsByPage(cache, `?${params}`, get_bittorrent);
    }
}

function getTagItems(cache, tag, page, get_bittorrent = false) {
    return gatherItemsByPage(cache, `tag/${tag}/${page}`, get_bittorrent);
}

module.exports = { getFavoritesItems, getSearchItems, getTagItems };


<|start_filename|>lib/routes/verfghbw/press.js<|end_filename|>
const got = require('@/utils/got');
const cheerio = require('cheerio');

module.exports = async (ctx) => {
    const keyword = ctx.params.keyword;

    let request = {
        url: 'https://verfgh.baden-wuerttemberg.de/de/presse-und-service/pressemitteilungen/',
        headers: {
            Referer: 'https://verfgh.baden-wuerttemberg.de/de/presse-und-service/pressemitteilungen/',
        },
    };

    if (keyword) {
        request = {
            method: 'post',
            form: {
                'tx_bwlistheader_list[search][keywords]': keyword,
            },
            ...request,
        };
    } else {
        request = {
            method: 'get',
            ...request,
        };
    }

    const response = await got(request);

    const data = response.data;
    const $ = cheerio.load(data);

    const list = $('.pressListItem')
        .map((_, item) => {
            item = $(item);

            return {
                title: item.find('.pressListItemTeaser > h3').text().trim(),
                link: new URL(item.find('.link-download').attr('href'), 'https://verfgh.baden-wuerttemberg.de/'),
                pubDate: new Date(item.find('.pressListItemDate > span').text()).toUTCString(),
            };
        })
        .get();

    ctx.state.data = {
        title: 'Verfassungsgerichtshof Baden-Württemberg - Pressemitteilungen',
        link: request.url,
        description: 'Pressemitteilungen des Verfassungsgerichtshof für das Land Baden-Württemberg',
        item: list,
    };
};


<|start_filename|>lib/routes/hackernews/story.js<|end_filename|>
const got = require('@/utils/got');
const cheerio = require('cheerio');

const host = 'https://news.ycombinator.com';
const paths = {
    index: '/',
    new: '/newest',
    past: '/front',
    ask: '/ask',
    show: '/show',
    jobs: '/jobs',
    best: '/best',
};

module.exports = async (ctx) => {
    const section = ctx.params.section;
    const showStory = ctx.params.type !== 'comments';
    const path = paths[section];
    const url = `${host}${path}`;
    const response = await got.get(url);
    const $ = cheerio.load(response.data);

    const items = $('a.storylink')
        .map(function () {
            return {
                title: $(this).text(),
                link: $(this).attr('href'),
                author: 'Hacker News',
                description: '',
            };
        })
        .get();

    $('a.hnuser').each(function (i) {
        items[i].author = $(this).text();
    });

    $('tr.athing').each(function (i) {
        const commURL = `${host}/item?id=` + $(this).attr('id');
        const item = items[i];

        if (showStory) {
            item.description = `Comments: <a href="${commURL}"> ${commURL} </a>`;
        } else {
            item.description = `Link: <a href="${item.link}"> ${item.title} </a>`;
            item.link = commURL;
        }
    });

    let title = `Hacker News: ${section}`;
    if (!showStory) {
        title += '/comments';
    }

    ctx.state.data = {
        title,
        link: url,
        item: items,
    };
};


<|start_filename|>lib/v2/qq/radar.js<|end_filename|>
module.exports = {
    'qq.com': {
        _name: '腾讯网',
        live: [
            {
                title: '企鹅直播',
                docs: 'https://docs.rsshub.app/live.html#qi-e-zhi-bo-zhi-bo-jian-ti-xing',
                source: ['/:id', '/'],
                target: '/qq/live/:id',
            },
        ],
    },
};


<|start_filename|>lib/routes/douban/group.js<|end_filename|>
const got = require('../../utils/got');
const cheerio = require('cheerio');

module.exports = async (ctx) => {
    const groupid = ctx.params.groupid;
    const type = ctx.params.type;

    const url = `https://www.douban.com/group/${groupid}/${type ? `?type=${type}` : ''}`;
    const response = await got({
        method: 'get',
        url,
    });

    const $ = cheerio.load(response.data);
    const list = $('.olt tr:not(.th)').slice(0, 30).get();

    const items = await Promise.all(
        list.map((item) => {
            const $1 = $(item);
            const result = {
                title: $1.find('.title a').attr('title'),
                author: $1.find('a').eq(1).text(),
                link: $1.find('.title a').attr('href'),
            };
            return ctx.cache.tryGet(result.link, async () => {
                try {
                    const detailResponse = await got({
                        method: 'get',
                        url: result.link,
                    });
                    const $ = cheerio.load(detailResponse.data);

                    result.pubDate = $('.create-time').text();
                    result.description = $('.rich-content').html();
                    return result;
                } catch (error) {
                    return result;
                }
            });
        })
    );

    ctx.state.data = {
        title: `豆瓣小组-${$('h1').text().trim()}`,
        link: url,
        item: items,
    };
};


<|start_filename|>lib/routes/zaobao/util.js<|end_filename|>
const got = require('@/utils/got');
const cheerio = require('cheerio');
const date = require('@/utils/date');
const baseUrl = 'https://www.zaobao.com';
const got_ins = got.extend({
    headers: {
        Referer: baseUrl,
    },
});

/**
 * 通用解析页面类似 https://www.zaobao.com/realtime/china 的网站
 *
 * @param {*} ctx RSSHub 的 ctx 参数，用来设置缓存
 * @param {string} sectionUrl 形如 /realtime/china 的字符串
 * @returns {Promise<{
 *  title: string;
 *  resultList: {
 *    title: string;
 *    description: string;
 *    pubDate: string;
 *    link: string;
 *  }[];}>} 新闻标题以及新闻列表
 */
const parseList = async (ctx, sectionUrl) => {
    const response = await got_ins.get(baseUrl + sectionUrl);
    const $ = cheerio.load(response.data);
    let data = $('.article-list').find('.article-type');
    if (data.length === 0) {
        data = $('.post-list').find('.row.list');
    }

    const title = $('#breadcrumbs > a')
        .toArray()
        .reduce((acc, cV, cI) => {
            if (cI > 0) {
                return acc + '-' + $(cV).text();
            }
            return '';
        }, '');
    const resultList = await Promise.all(
        data.toArray().map(async (item) => {
            const $item = $(item);
            let link = $item.find('a')[0].attribs.href;

            let resultItem = {};

            if (link[0] !== '/') {
                // https://www.zaobao.com/interactive-graphics
                const title = $item.find('a').text();
                let dateNodes = $item.find('.meta-published-date');
                if (dateNodes.length === 0) {
                    dateNodes = $item.find('.datestamp');
                }
                let dateString;
                let pubDate;
                if (dateNodes.length !== 0) {
                    dateString = dateNodes.text().trim();
                    const dateParts = dateString.split('/');
                    dateParts.reverse();
                    dateString = dateParts.join('-');
                    pubDate = date(dateString);
                    pubDate = pubDate === dateString ? new Date().toUTCString() : pubDate;
                } else {
                    pubDate = new Date().toUTCString();
                }

                resultItem = {
                    title,
                    description: title,
                    pubDate,
                    link,
                };
                return Promise.resolve(resultItem);
            } else {
                link = baseUrl + link;
            }

            const value = await ctx.cache.get(link);

            if (value) {
                resultItem = JSON.parse(value);
            } else {
                const article = await got_ins.get(link);
                const $1 = cheerio.load(article.data);

                const time = $1('.date-published', '.content').slice(0, 1).text().replace('年', '-').replace('月', '-').replace('日', '').replace('发布 /', '');

                $1('.overlay-microtransaction').remove();
                $1('#video-freemium-player').remove();
                $1('script').remove();

                let descriptionNode = $1('.article-content-rawhtml');
                if (descriptionNode.length === 0) {
                    descriptionNode = $1('.article-content-container');
                }

                let description = descriptionNode.html();
                if ($1('.inline-figure-img').length) {
                    description = $1('.inline-figure-img').html() + description;
                }
                if ($1('.body-content .loadme picture img').length) {
                    description =
                        `<figure><img src="${$1('.body-content .loadme picture source').attr('data-srcset').split('?')[0]}" /><figcaption>${$1('.body-content .loadme picture img').attr('title')}</figcaption></figure>` + description;
                }
                if ($1('.inline-figure-gallery').length) {
                    description = $1('.inline-figure-gallery').html() + description;
                }

                resultItem = {
                    title: $1('h1', '.content').text(),
                    description,
                    pubDate: new Date(time).toUTCString(),
                    link,
                };
                ctx.cache.set(link, JSON.stringify(resultItem));
            }

            return Promise.resolve(resultItem);
        })
    );
    return {
        title,
        resultList,
    };
};
module.exports = {
    parseList,
};


<|start_filename|>lib/routes/bookwalkertw/news.js<|end_filename|>
const got = require('@/utils/got');
const cheerio = require('cheerio');

module.exports = async (ctx) => {
    const response = await got({
        method: 'get',
        url: 'https://www.bookwalker.com.tw/search?order=sell_desc&series_display=1&d=1',
    });

    const data = response.data;

    const $ = cheerio.load(data);
    const list = $('div[class="bwbookitem"]');
    let bookid;
    ctx.state.data = {
        title: 'BOOKWALKERTW热门新书',
        link: 'https://www.bookwalker.com.tw/search?order=sell_desc&series_display=1&d=1',
        item:
            list &&
            list
                .map((index, item) => {
                    item = $(item);
                    bookid = item.find('img').attr('data-src').split('/')[5];
                    return {
                        title: item.find('.bookname').text(),
                        description: `${item.find('.booknamesub').text()}&nbsp;|&nbsp;${item.find('.bprice').text()}<br><img src="https://image.bookwalker.com.tw/upload/product/${bookid}/zoom_big_${bookid}.jpg">`,
                        link: item.find('a').attr('href'),
                    };
                })
                .get(),
    };
};


<|start_filename|>lib/routes/cool18/index.js<|end_filename|>
const got = require('@/utils/got');
const cheerio = require('cheerio');

module.exports = async (ctx) => {
    const id = ctx.params.id || 'bbs4';
    const type = ctx.params.type || '';
    const keyword = ctx.params.keyword || '';

    const rootUrl = 'https://www.cool18.com';
    const indexUrl = `${rootUrl}/${id}/index.php`;
    const currentUrl = `${indexUrl}${type === '' || keyword === '' ? '' : type === 'gold' ? '?app=forum&act=gold' : `?action=search&act=threadsearch&app=forum&${type}=${keyword}&submit=${type === 'type' ? '查询' : '栏目搜索'}`}`;

    const response = await got({
        method: 'get',
        url: currentUrl,
    });

    const $ = cheerio.load(response.data);

    const list = $('#d_list ul li, #thread_list li, .t_l .t_subject')
        .slice(0, 10)
        .map((_, item) => {
            item = $(item);
            const a = item.find('a').eq(0);
            return {
                link: `${rootUrl}/${id}/${a.attr('href')}`,
            };
        })
        .get();

    const items = await Promise.all(
        list.map((item) =>
            ctx.cache.tryGet(item.link, async () => {
                const detailResponse = await got({
                    method: 'get',
                    url: item.link,
                });
                const content = cheerio.load(detailResponse.data);

                item.title = content('title').text().replace(' - cool18.com', '');
                item.author = detailResponse.data.match(/送交者: .*>(.*)<.*\[/)[1];
                item.pubDate = new Date(detailResponse.data.match(/于 (.*) 已读/)[1]).toUTCString();
                item.description = content('pre')
                    .html()
                    .replace(/<font color="#E6E6DD">cool18.com<\/font>/g, '');

                return item;
            })
        )
    );

    ctx.state.data = {
        title: $('title').text(),
        link: currentUrl,
        item: items,
    };
};


<|start_filename|>lib/routes/javlibrary/users.js<|end_filename|>
const { template, getVideoComments } = require('./utils');

module.exports = async (ctx) => {
    const utype = ctx.params.utype;
    const uid = ctx.params.uid;

    if (utype === 'userposts') {
        const uid = ctx.params.uid;
        const link = `http://www.javlibrary.com/cn/userposts.php?u=${uid}`;
        const items = await getVideoComments(link);
        ctx.state.data = {
            title: `Javlibrary - ${uid} 发表的文章`,
            link,
            item: items,
        };
    } else {
        ctx.state.data = {
            link: `http://www.javlibrary.com/cn/${utype}.php?u=${uid}`,
        };
        await template(ctx);
    }
};


<|start_filename|>lib/routes/weibo/super_index.js<|end_filename|>
const got = require('@/utils/got');
const weiboUtils = require('./utils');
const timezone = require('@/utils/timezone');
const queryString = require('query-string');

module.exports = async (ctx) => {
    const id = ctx.params.id;

    const res = await got.get('https://m.weibo.cn/api/container/getIndex', {
        searchParams: queryString.stringify({
            containerid: `${id}_-_feed`,
            luicode: '10000011',
            lfid: `${id}_-_main`,
        }),
        headers: {
            Referer: `https://m.weibo.cn/p/index?containerid=${id}_-_soul&luicode=10000011&lfid=${id}_-_main`,
            'MWeibo-Pwa': '1',
            'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
            'X-Requested-With': 'XMLHttpRequest',
        },
    });

    const resultItems = [];

    for (const card of res.data.data.cards) {
        if (!('card_group' in card)) {
            continue;
        }
        for (const mblogCard of card.card_group) {
            if (mblogCard.card_type === '9' && 'mblog' in mblogCard) {
                const mblog = mblogCard.mblog;
                const formatExtended = weiboUtils.formatExtended(ctx, mblog);
                const title = formatExtended.title;
                const desc = formatExtended.description;
                resultItems.push({
                    title,
                    description: desc,
                    author: mblog.user.screen_name,
                    link: `https://weibo.com/${mblog.user.id}/${mblog.bid}`,
                    pubDate: timezone(mblog.created_at, +8),
                });
            }
        }
    }

    ctx.state.data = {
        title: `微博超话 - ${res.data.data.pageInfo.page_title}`,
        link: `https://weibo.com/p/${id}/super_index`,
        description: `#${res.data.data.pageInfo.page_title}# 的超话`,
        item: resultItems,
    };
};


<|start_filename|>lib/routes/gamer/hot.js<|end_filename|>
const got = require('@/utils/got');
const cheerio = require('cheerio');

module.exports = async (ctx) => {
    const rootUrl = `https://forum.gamer.com.tw/A.php?bsn=${ctx.params.bsn}`;
    const response = await got({
        method: 'get',
        url: rootUrl,
    });

    const $ = cheerio.load(response.data);
    const list = $('div.FM-abox2A a.FM-abox2B')
        .slice(0, 10)
        .map((_, item) => {
            item = $(item);
            return {
                link: `https:${item.attr('href')}`,
            };
        })
        .get();

    const items = await Promise.all(
        list.map((item) =>
            ctx.cache.tryGet(item.link, async () => {
                const detailResponse = await got({
                    method: 'get',
                    url: item.link,
                });
                const content = cheerio.load(detailResponse.data);

                content('div.c-post__body__buttonbar').remove();

                item.title = content('.c-post__header__title').text();
                item.description = content('div.c-post__body').html();
                item.author = `${content('a.username').eq(0).text()} (${content('a.userid').eq(0).text()})`;
                item.pubDate = new Date(content('a.edittime').eq(0).attr('data-mtime') + ' GMT+8').toUTCString();

                return item;
            })
        )
    );

    ctx.state.data = {
        title: $('title').text(),
        link: rootUrl,
        item: items,
    };
};


<|start_filename|>lib/routes/javlibrary/utils.js<|end_filename|>
const url = require('url');
const cheerio = require('cheerio');
const got = require('@/utils/got');
const dateUtil = require('@/utils/date');

// 通过传入不同的ctx.state.data.link 返回javlibrary 不同link的rss
exports.template = async function template(ctx) {
    const link = ctx.state.data.link;
    const response = await got.get(link);
    const $ = cheerio.load(response.body);
    const list = $('.videothumblist .video').get();

    const items = await Promise.all(
        list.map(async (item) => {
            item = $(item);
            const itemLink = url.resolve(link, item.find('a').attr('href'));
            const simple = {
                title: `[${item.find('.id').text()}]${item.find('.title').text()}`,
                link: itemLink,
            };

            const details = await ctx.cache.tryGet(itemLink, async () => {
                const response = await got.get(itemLink);
                const $ = cheerio.load(response.body);
                $('#video_info #video_review .icon').remove();
                return {
                    author: $('.star').text(),
                    description: `<img src="${$('#video_jacket_img').attr('src')}"><br>${$('#video_info').html()}`,
                };
            });
            return Promise.resolve(Object.assign({}, simple, details));
        })
    );

    ctx.state.data = {
        title: `Javlibrary - ${$('.boxtitle').text()}`,
        link,
        item: items,
    };
};

exports.getVideoComments = async function getVideoComments(link) {
    const response = await got.get(link);
    const $ = cheerio.load(response.body);
    const list = $('#video_comments > table');

    return (
        list &&
        list
            .map((index, item) => {
                item = $(item);
                // 文章内容只能抓取到 td.t textarea，若含有图片则替换[img]image-link[/img] => <img src="image-link" />
                let comments = item.find('textarea').text();
                comments = comments.replace(new RegExp('\\[img\\]', 'g'), '<img src="').replace(new RegExp('\\[\\/img\\]', 'g'), '" />');
                return {
                    title: item.find('tbody > tr:nth-child(1) > td:nth-child(2)').text(),
                    link: item.find('tbody > tr:nth-child(1) > td:nth-child(2) > a').attr('href'),
                    description: `
                            <table><tbody>
                                <tr><td>${item.find('tbody > tr:nth-child(1) > td:nth-child(2)').html()}</td></tr>
                                <tr><td>${comments}</td></tr>
                            </tbody></table>`,
                    pubDate: dateUtil(item.find('tbody > tr:nth-child(3) > td.date')),
                };
            })
            .get()
    );
};
