Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] master from diygod:master #1015

Merged
merged 3 commits into from
Jul 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/anime.md
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,12 @@ Sources

<Route author="TonyRL" example="/qoo-app/notes/user/23266114" path="/qoo-app/notes/:lang?/user/:uid" :paramsDesc="['語言,見上表,留空為中文', '用户 ID,可在 URL 找到']" radar="1"/>

## Rawkuma

### Manga

<Route author="nczitzk" example="/rawkuma/manga/tensei-shitara-dai-nana-ouji-dattanode-kimamani-majutsu-o-kiwamemasu" path="/rawkuma/manga/:id" :paramsDesc="['Manga ID,可在 URL 找到']" radar="1"/>

## say 花火

### 文章
Expand Down Expand Up @@ -662,6 +668,12 @@ Sources

## 禁漫天堂

::: tip 提示

禁漫天堂有多个备用域名,本路由默认使用域名 <https://jmcomic.me>,若该域名无法访问,可以通过在路由最后加上 `?domain=<域名>` 指定路由访问的域名。如指定备用域名为 <https://jmcomic1.me>,则在所有禁漫天堂路由最后加上 `?domain=jmcomic1.me` 即可,此时路由为 [`/18comic?domain=jmcomic1.me`](https://rsshub.app/18comic?domain=jmcomic1.me)

:::

### 成人 A 漫

<Route author="nczitzk" example="/18comic" path="/18comic/:category?/:time?/:order?/:keyword?" :paramsDesc="['分类,见下表,默认为 `all` 即全部', '时间范围,见下表,默认为 `a` 即全部', '排列顺序,见下表,默认为 `mr` 即最新', '关键字,见下表,默认为空']">
Expand Down
6 changes: 6 additions & 0 deletions docs/en/anime.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,12 @@ The RSS routing has not been rigorously tested and the information provided cann

<RouteEn author="TonyRL" example="/qoo-app/notes/en/user/35399143" path="/qoo-app/notes/:lang?/user/:uid" :paramsDesc="['Language, see the table above, empty means `中文`', 'User ID, can be found in URL']" radar="1"/>

## Rawkuma

### Manga

<RouteEn author="nczitzk" example="/rawkuma/manga/tensei-shitara-dai-nana-ouji-dattanode-kimamani-majutsu-o-kiwamemasu" path="/rawkuma/manga/:id" :paramsDesc="['Manga ID, can be found in URL']" radar="1"/>

## THBWiki

### Calendar
Expand Down
4 changes: 3 additions & 1 deletion lib/v2/18comic/album.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ const got = require('@/utils/got');
const cheerio = require('cheerio');
const { parseDate } = require('@/utils/parse-date');

const { rootUrl } = require('./utils');
const { defaultDomain, getRootUrl } = require('./utils');

module.exports = async (ctx) => {
const id = ctx.params.id;
const { domain = defaultDomain } = ctx.query;
const rootUrl = getRootUrl(domain, ctx);

const currentUrl = `${rootUrl}/album/${id}`;

Expand Down
4 changes: 3 additions & 1 deletion lib/v2/18comic/blogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ const got = require('@/utils/got');
const cheerio = require('cheerio');
const { parseDate } = require('@/utils/parse-date');

const { rootUrl } = require('./utils');
const { defaultDomain, getRootUrl } = require('./utils');

module.exports = async (ctx) => {
const category = ctx.params.category ?? '';
const { domain = defaultDomain } = ctx.query;
const rootUrl = getRootUrl(domain, ctx);

const currentUrl = `${rootUrl}/blogs${category ? `/${category}` : ''}`;

Expand Down
6 changes: 4 additions & 2 deletions lib/v2/18comic/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
const { rootUrl, ProcessItems } = require('./utils');
const { defaultDomain, getRootUrl, ProcessItems } = require('./utils');

module.exports = async (ctx) => {
const category = ctx.params.category ?? 'all';
const keyword = ctx.params.keyword ?? '';
const time = ctx.params.time ?? 'a';
const order = ctx.params.order ?? 'mr';
const { domain = defaultDomain } = ctx.query;
const rootUrl = getRootUrl(domain, ctx);

const currentUrl = `${rootUrl}/albums${category !== 'all' ? `/${category}` : ''}${keyword ? `?screen=${keyword}` : '?'}${time !== 'a' ? `&t=${time}` : ''}${order !== 'mr' ? `&o=${order}` : ''}`;

ctx.state.data = await ProcessItems(ctx, currentUrl);
ctx.state.data = await ProcessItems(ctx, currentUrl, rootUrl);
};
6 changes: 4 additions & 2 deletions lib/v2/18comic/search.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
const { rootUrl, ProcessItems } = require('./utils');
const { defaultDomain, getRootUrl, ProcessItems } = require('./utils');

module.exports = async (ctx) => {
const option = ctx.params.option ?? 'photos';
const category = ctx.params.category ?? 'all';
const keyword = ctx.params.keyword ?? '';
const time = ctx.params.time ?? 'a';
const order = ctx.params.order ?? 'mr';
const { domain = defaultDomain } = ctx.query;
const rootUrl = getRootUrl(domain, ctx);

const currentUrl = `${rootUrl}/search/${option}${category !== 'all' ? `/${category}` : ''}${keyword ? `?search_query=${keyword}` : '?'}${time !== 'a' ? `&t=${time}` : ''}${order !== 'mr' ? `&o=${order}` : ''}`;

ctx.state.data = await ProcessItems(ctx, currentUrl);
ctx.state.data = await ProcessItems(ctx, currentUrl, rootUrl);
};
19 changes: 15 additions & 4 deletions lib/v2/18comic/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,24 @@ const cheerio = require('cheerio');
const { parseDate } = require('@/utils/parse-date');
const { art } = require('@/utils/render');
const path = require('path');
const config = require('@/config').value;

const rootUrl = 'https://jmcomic.me';
// list of address: https://jmcomic1.bet
const defaultDomain = 'jmcomic1.me';
// list of address: https://jmcomic2.bet
const allowDomain = ['18comic.vip', '18comic.org', 'jmcomic.me', 'jmcomic1.me', 'jm-comic3.art', 'jm-comic.club', 'jm-comic2.ark'];

const getRootUrl = (domain, ctx) => {
if (!config.feature.allow_user_supply_unsafe_domain && !allowDomain.includes(domain)) {
ctx.throw(403, `This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}

return `https://${domain}`;
};

module.exports = {
rootUrl,
ProcessItems: async (ctx, currentUrl) => {
defaultDomain,
getRootUrl,
ProcessItems: async (ctx, currentUrl, rootUrl) => {
currentUrl = currentUrl.replace(/\?$/, '');

const response = await got(currentUrl);
Expand Down
3 changes: 3 additions & 0 deletions lib/v2/rawkuma/maintainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
'/manga/:id': ['nczitzk'],
};
81 changes: 81 additions & 0 deletions lib/v2/rawkuma/manga.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const { parseDate } = require('@/utils/parse-date');
const { art } = require('@/utils/render');
const path = require('path');

module.exports = async (ctx) => {
const { id } = ctx.params;
const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 50;

const rootUrl = 'https://rawkuma.com';
const currentUrl = new URL(`/manga/${id}`, rootUrl).href;

const { data: response } = await got(currentUrl);

const $ = cheerio.load(response);

const author = $('div.fmed span').eq(1).text().trim();
const category = $('div.wd-full span.mgen a[rel="tag"]')
.toArray()
.map((c) => $(c).text());

let items = $('div.eph-num')
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item);

return {
title: item.find('span.chapternum').text(),
link: item.find('a').prop('href'),
author,
category,
pubDate: parseDate(item.find('span.chapterdate').text(), 'MMMM DD'),
enclosure_url: item.next().find('a.dload').prop('href'),
enclosure_type: 'application/zip',
};
});

items = await Promise.all(
items.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const { data: detailResponse } = await got(item.link);

const content = cheerio.load(detailResponse);

const imageMatches = detailResponse.match(/"images":(\[.*?\])\}\],"lazyload"/);

const images = imageMatches ? JSON.parse(imageMatches[1]) : [];

item.title = content('div.chpnw').text().trim();
item.description = art(path.join(__dirname, 'templates/description.art'), {
images,
});
item.author = author;
item.category = category;
item.pubDate = parseDate(content('time.entry-date').prop('datetime').replace(/WIB/, 'T'));
item.enclosure_url = content('span.dlx a').prop('href');
item.enclosure_type = 'application/zip';

return item;
})
)
);

const icon = $('link[rel="apple-touch-icon"]')
.prop('href')
.replace(/-\d+x\d+/, '');

ctx.state.data = {
item: items,
title: $('title').text(),
link: currentUrl,
description: $('div[itemprop="description"]').text(),
image: $('meta[property="og:image"]').prop('content'),
icon,
logo: icon,
subtitle: $('div.wd-full span').text(),
author,
};
};
13 changes: 13 additions & 0 deletions lib/v2/rawkuma/radar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module.exports = {
'rawkuma.com': {
_name: 'Rawkuma',
'.': [
{
title: 'Manga',
docs: 'https://docs.rsshub.app/anime.html#rawkuma-manga',
source: ['/manga/:id', '/'],
target: '/rawkuma/manga/:id',
},
],
},
};
3 changes: 3 additions & 0 deletions lib/v2/rawkuma/router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = function (router) {
router.get('/manga/:id', require('./manga'));
};
3 changes: 3 additions & 0 deletions lib/v2/rawkuma/templates/description.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{{ each images image }}
<img src="{{ image }}">
{{ /each }}
30 changes: 14 additions & 16 deletions lib/v2/twitter/web-api/twitter-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,26 +134,21 @@ const tweetDetail = (userId, params) =>
['threaded_conversation_with_injections']
);

function gatherLegacyFromData(entries, filter = 'tweet-') {
function gatherLegacyFromData(entries, filterNested = undefined, userId = undefined) {
const tweets = [];
const filte_entries = [];
const filteredEntries = [];
entries.forEach((entry) => {
const entryId = entry.entryId;
if (entryId) {
if (filter === 'none') {
if (entryId.startsWith('tweet-')) {
filte_entries.push(entry);
} else if (entryId.startsWith('homeConversation-') || entryId.startsWith('conversationthread-')) {
filte_entries.push(...entry.content.items);
}
} else {
if (entryId.startsWith(filter)) {
filte_entries.push(entry);
}
if (entryId.startsWith('tweet-')) {
filteredEntries.push(entry);
}
if (filterNested && filterNested.some((f) => entryId.startsWith(f))) {
filteredEntries.push(...entry.content.items);
}
}
});
filte_entries.forEach((entry) => {
filteredEntries.forEach((entry) => {
if (entry.entryId) {
const content = entry.content || entry.item;
let tweet = content?.itemContent?.tweet_results?.result;
Expand All @@ -178,7 +173,9 @@ function gatherLegacyFromData(entries, filter = 'tweet-') {
if (retweet) {
legacy.retweeted_status = retweet.legacy;
}
tweets.push(legacy);
if (userId === undefined || legacy.user_id_str === userId + '') {
tweets.push(legacy);
}
}
}
}
Expand All @@ -187,10 +184,11 @@ function gatherLegacyFromData(entries, filter = 'tweet-') {
}

const getUserTweetsByID = async (id, params = {}) => gatherLegacyFromData(await timelineTweets(id, params));
const getUserTweetsAndRepliesByID = async (id, params = {}) => gatherLegacyFromData(await timelineTweetsAndReplies(id, params));
// TODO: show the whole conversation instead of just the reply tweet
const getUserTweetsAndRepliesByID = async (id, params = {}) => gatherLegacyFromData(await timelineTweetsAndReplies(id, params), ['profile-conversation-'], id);
const getUserMediaByID = async (id, params = {}) => gatherLegacyFromData(await timelineMedia(id, params));
const getUserLikesByID = async (id, params = {}) => gatherLegacyFromData(await timelineLikes(id, params));
const getUserTweetByStatus = async (id, params = {}) => gatherLegacyFromData(await tweetDetail(id, params), 'none');
const getUserTweetByStatus = async (id, params = {}) => gatherLegacyFromData(await tweetDetail(id, params), ['homeConversation-', 'conversationthread-']);

const excludeRetweet = function (tweets) {
const excluded = [];
Expand Down