feat(route): 微博自定义分组(使用Cookie) (#12514)

* change route 'weibo/user' to fit api could be opened with Share(third-party android weibo clent)

* update weibo group route using cookies approach

* fix typo

* code optimization for /weibo/group; docs for /weibo/group

* try fix ESLint error

* fix typo and code optimization

* fix missing ','

* '/weibo/group' throw error when the cookie is not set
This commit is contained in:
Junk_Chen
2023-05-15 23:04:59 +08:00
committed by GitHub
parent 9bdba060c7
commit 2bd8e4842a
8 changed files with 129 additions and 1 deletions

2
.gitignore vendored
View File

@@ -7,6 +7,8 @@
.vercel
.vscode
.yarn
.yarnrc.yml
.pnp*
*.swp
*.iml

View File

@@ -943,6 +943,14 @@ RSSHub 支持使用访问密钥 / 码,白名单和黑名单三种方式进行
- `WEIBO_APP_SECRET`: 微博 App Secret
- `WEIBO_REDIRECT_URL`: 微博登录授权回调地址,默认为 `RSSHub 地址/weibo/timeline/0`,自定义回调地址请确保最后可以转跳到 `RSSHub 地址/weibo/timeline/0?code=xxx`
- 微博 自定义分组
- `WEIBO_COOKIES`: 用户访问网页微博时所使用的cookie, 获取方式:
1. 打开并登录 <https://m.weibo.cn> (确保打开页面为手机版, 如果强制跳转电脑端可尝试使用可更改UserAgent的浏览器插件)
2. 按下`F12`打开控制台,切换至`Network(网络)`面板
3. 在该网页切换至任意关注分组, 并在面板打开最先捕获到的请求(该情形下捕获到的请求路径应包含`/feed/group`)
4. 查看该请求的`Headers(请求头)`, 找到`Cookie`字段并复制内容
- 小宇宙:需要 App 登陆后抓包获取相应数据。
- `XIAOYUZHOU_ID`: 即数据包中的 `x-jike-device-id`。

View File

@@ -1574,6 +1574,22 @@ rule
</Route>
### 自定义分组
<Route author="monologconnor" example="/weibo/group/4541216424989965/微博分组/:routeParams?" path="/weibo/group/:gid/:gname?/:routeParams?" :paramsDesc="['分组id, 在网页版分组地址栏末尾`?gid=`处获取', '分组显示名称; 默认为: `微博分组`', '额外参数;请参阅上面的说明和表格']" anticrawler="1" selfhost="1">
::: warning 注意
由于微博官方未提供自定义分组相关api, 此方案必须使用用户`Cookie`进行抓取
因微博cookies的过期与更新方案未经验证, 部署一次Cookie的有效时长未知
微博用户Cookie的配置可参照部署文档
:::
</Route>
## 微博绿洲
### 用户

View File

@@ -277,6 +277,7 @@ const calculateValue = () => {
weibo: {
app_key: envs.WEIBO_APP_KEY,
app_secret: envs.WEIBO_APP_SECRET,
cookies: envs.WEIBO_COOKIES,
redirect_url: envs.WEIBO_REDIRECT_URL,
},
wenku8: {

99
lib/v2/weibo/group.js Normal file
View File

@@ -0,0 +1,99 @@
const querystring = require('querystring');
const got = require('@/utils/got');
const config = require('@/config').value;
const weiboUtils = require('./utils');
const { fallback, queryToBoolean } = require('@/utils/readable-social');
module.exports = async (ctx) => {
if (!config.weibo_cookies) {
throw 'Weibo Group Timeline is not available due to the absense of [Weibo Cookies]. Check <a href="https://docs.rsshub.app/install/#pei-zhi-bu-fen-rss-mo-kuai-pei-zhi">relevant config tutorial</a>';
}
const gid = ctx.params.gid;
const groupName = ctx.params.gname || '微博分组';
let displayVideo = '1';
let displayArticle = '0';
let displayComments = '0';
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';
displayArticle = fallback(undefined, queryToBoolean(routeParams.displayArticle), false) ? '1' : '0';
displayComments = fallback(undefined, queryToBoolean(routeParams.displayComments), false) ? '1' : '0';
}
}
const responseData = await ctx.cache.tryGet(
`weibo:group:index:${gid}`,
async() => {
const _r = await got({
method: 'get',
url: `https://m.weibo.cn/feed/group?gid=${gid}`,
headers: {
Referer: `https://m.weibo.cn/`,
'MWeibo-Pwa': 1,
'X-Requested-With': 'XMLHttpRequest',
'Cookie': config.weibo.cookies,
}
});
return _r.data.data;
},
config.cache.routeExpire,
false
);
const resultItems = await Promise.all(
responseData.statuses.map(async (item) => {
const retweet = item.retweeted_status;
if (retweet && retweet.isLongText) {
const retweetData = await ctx.cache.tryGet(`weibo:retweeted:${retweet.user.id}:${retweet.bid}`, () => weiboUtils.getShowData(retweet.user.id, retweet.bid));
if (retweetData !== undefined && retweetData.text) {
item.retweeted_status.text = retweetData.text;
}
}
const link = `https://m.weibo.cn/status/${item.bid}`;
const formatExtended = weiboUtils.formatExtended(ctx, item);
let description = formatExtended.description;
const title = formatExtended.title;
const pubDate = item.created_at;
if (displayVideo === '1') {
if (item.retweeted_status) {
description = weiboUtils.formatVideo(description, item.retweeted_status);
} else {
description = weiboUtils.formatVideo(description, item);
}
}
if (displayComments === '1') {
description = await weiboUtils.formatComments(ctx, description, item);
}
if (displayArticle === '1') {
if (item.retweeted_status) {
description = await weiboUtils.formatArticle(ctx, description, item.retweeted_status);
} else {
description = await weiboUtils.formatArticle(ctx, description, item);
}
}
return {
title,
description,
link,
pubDate,
author: item.user.screen_name
};
})
);
ctx.state.data = weiboUtils.sinaimgTvax({
title: groupName,
link: `https://weibo.com/mygroups?gid=${gid}`,
description: '微博自定义分组',
item: resultItems,
});
};

View File

@@ -1,4 +1,5 @@
module.exports = {
'/group/:gid/:gname?/:routeParams?': ['monologconnor'],
'/keyword/:keyword/:routeParams?': ['DIYgod'],
'/oasis/user/:userid': ['kt286'],
'/search/hot': ['xyqfer'],

View File

@@ -1,4 +1,5 @@
module.exports = (router) => {
router.get('/group/:gid/:gname?/:routeParams?', require('./group'));
router.get('/keyword/:keyword/:routeParams?', require('./keyword'));
router.get('/oasis/user/:userid', require('./oasis/user'));
router.get('/search/hot', require('./search/hot'));

View File

@@ -94,7 +94,7 @@ module.exports = async (ctx) => {
}
}
const link = `https://weibo.com/${uid}/${item.mblog.bid}`;
const link = `https://m.weibo.cn/status/${item.mblog.bid}`;
const formatExtended = weiboUtils.formatExtended(ctx, item.mblog);
let description = formatExtended.description;
const title = formatExtended.title;