feat(route)(wechat/tgchannel): efb-patch-middleware format support; using search to filter mpName (#9183)
Signed-off-by: Rongrong <15956627+Rongronggg9@users.noreply.github.com>
This commit is contained in:
parent
f1c53d15e2
commit
7bffa64e48
|
|
@ -3206,11 +3206,24 @@ column 为 third 时可选的 category:
|
|||
|
||||
### 公众号(Telegram 频道来源)
|
||||
|
||||
<Route author="LogicJake" example="/wechat/tgchannel/lifeweek" path="/wechat/tgchannel/:id/:mpName?" :paramsDesc="['公众号绑定频道 id', '欲筛选的公众号全名(精确匹配),在频道订阅了多个公众号时可选用']">
|
||||
<Route author="LogicJake Rongronggg9" example="/wechat/tgchannel/lifeweek" path="/wechat/tgchannel/:id/:mpName?/:searchQueryType?" :paramsDesc="['公众号绑定频道 id', '欲筛选的公众号全名(URL-encoded, 精确匹配),在频道订阅了多个公众号时可选用', '搜索查询类型,见下表']">
|
||||
|
||||
| 搜索查询类型 | 将使用的搜索关键字 | 适用于 |
|
||||
|:------:|:---------:|:------------------------:|
|
||||
| `0` | (禁用搜索) | 所有情况 (默认) |
|
||||
| `1` | 公众号全名 | 未启用 efb-patch-middleware |
|
||||
| `2` | #公众号全名 | 已启用 efb-patch-middleware |
|
||||
|
||||
::: tip 提示
|
||||
|
||||
启用搜索有助于在订阅了过多公众号的频道里有效筛选,不易因为大量公众号同时推送导致一些公众号消息被遗漏,但必须正确选择搜索查询类型,否则会搜索失败。
|
||||
|
||||
:::
|
||||
|
||||
::: warning 注意
|
||||
|
||||
该方法需要通过 efb 进行频道绑定,具体操作见<https://github.com/DIYgod/RSSHub/issues/2172>
|
||||
|
||||
:::
|
||||
|
||||
</Route>
|
||||
|
|
|
|||
|
|
@ -466,7 +466,7 @@ router.get('/wechat/csm/:id', lazyloadRouteHandler('./routes/tencent/wechat/csm'
|
|||
router.get('/wechat/ce/:id', lazyloadRouteHandler('./routes/tencent/wechat/ce'));
|
||||
router.get('/wechat/announce', lazyloadRouteHandler('./routes/tencent/wechat/announce'));
|
||||
router.get('/wechat/miniprogram/plugins', lazyloadRouteHandler('./routes/tencent/wechat/miniprogram/plugins'));
|
||||
router.get('/wechat/tgchannel/:id/:mpName?', lazyloadRouteHandler('./routes/tencent/wechat/tgchannel'));
|
||||
router.get('/wechat/tgchannel/:id/:mpName?/:searchQueryType?', lazyloadRouteHandler('./routes/tencent/wechat/tgchannel'));
|
||||
router.get('/wechat/uread/:userid', lazyloadRouteHandler('./routes/tencent/wechat/uread'));
|
||||
router.get('/wechat/ershicimi/:id', lazyloadRouteHandler('./routes/tencent/wechat/ershcimi'));
|
||||
router.get('/wechat/wjdn/:id', lazyloadRouteHandler('./routes/tencent/wechat/wjdn'));
|
||||
|
|
|
|||
|
|
@ -4,8 +4,15 @@ const cheerio = require('cheerio');
|
|||
module.exports = async (ctx) => {
|
||||
const id = ctx.params.id;
|
||||
const mpName = ctx.params.mpName ?? '';
|
||||
let searchQueryType = ctx.params.searchQueryType ?? '0';
|
||||
if (searchQueryType !== '0' && searchQueryType !== '1' && searchQueryType !== '2') {
|
||||
searchQueryType = '0';
|
||||
}
|
||||
searchQueryType = +searchQueryType; // 将字符串转换为数字
|
||||
|
||||
const { data } = await got.get(`https://t.me/s/${id}`);
|
||||
const channelUrl = `https://t.me/s/${id}`;
|
||||
const searchQuery = mpName && searchQueryType ? (searchQueryType === 2 ? `?q=%23${mpName}` : `?q=${mpName}`) : '';
|
||||
const { data } = await got.get(`${channelUrl}${searchQuery}`);
|
||||
const $ = cheerio.load(data);
|
||||
const list = $('.tgme_widget_message_wrap').slice(-20);
|
||||
|
||||
|
|
@ -14,49 +21,97 @@ module.exports = async (ctx) => {
|
|||
.map(async (index, item) => {
|
||||
item = $(item);
|
||||
|
||||
if (searchQuery) {
|
||||
// 删除关键字高亮 <mark class="highlight">
|
||||
const highlightMarks = item.find('mark.highlight').get();
|
||||
highlightMarks.forEach((mark) => {
|
||||
mark = $(mark);
|
||||
const markInnerHtml = mark.html();
|
||||
mark.replaceWith(markInnerHtml);
|
||||
});
|
||||
if (highlightMarks) {
|
||||
item = $(item.html()); // 删除关键字高亮后,相邻的裸文本节点不会被自动合并,重新生成 cheerio 对象以确保后续流程正常运行
|
||||
}
|
||||
}
|
||||
|
||||
// [ div.tgme_widget_message_text 格式简略说明 ]
|
||||
// 若频道只订阅一个公众号:
|
||||
// 第 1 个元素: <a href="${用于 link priview 的预览图 url}"><i><b>${emoji(链接)}</b></i></a>
|
||||
// 第 1 个元素: <a href="${用于 link priview 的预览图 url}"><i><b>🔗</b></i></a>
|
||||
// 第 2 个元素: <a href="${文章 url}">${文章标题}</a>
|
||||
// (余下是文章简介,一般是裸文本,这里用不到)
|
||||
//
|
||||
// 若频道订阅多于一个公众号:
|
||||
// 第 1 个元素: <i><b>${emoji(标注消息来源于什么 slave,这里是表示微信的对话气泡)}</b></i>
|
||||
// 第 2 个元素: <i><b>${emoji(标注对话类型,这里是表示私聊的半身人像)</b></i>
|
||||
// 第 1 个元素: <i><b>${emoji(标注消息来源于什么 slave,这里是表示微信的💬)}</b></i>
|
||||
// 第 2 个元素: <i><b>${emoji(标注对话类型,这里是表示私聊的👤)</b></i>
|
||||
// 裸文本: (半角空格)${公众号名}(半角冒号)
|
||||
// 第 3 个元素: <br />
|
||||
// 第 4 个元素: <a href="${用于 link priview 的预览图 url}"><i><b>${emoji(链接)}</b></i></a>
|
||||
// 第 4 个元素: <a href="${用于 link priview 的预览图 url}"><i><b>🔗</b></i></a>
|
||||
// 第 5 个元素: <a href="${文章 url}">${文章标题}</a>
|
||||
// (余下是文章简介,一般是裸文本,这里用不到)
|
||||
//
|
||||
// 若启用 efb-patch-middleware 且频道订阅多于一个公众号:
|
||||
// 第 1 个元素: <i><b>${emoji(标注消息来源于什么 slave,这里是表示微信的💬)}</b></i>
|
||||
// 第 2 个元素: <i><b>${emoji(标注对话类型,这里是表示私聊的👤)</b></i>
|
||||
// 第 3 个元素: <a href="${?q=%23url-encoded公众号名}">#${公众号名}</a>
|
||||
// 裸文本: ${公众号名余下部分 (若 hashtag 不合法 (遇到空格、标点) 导致被截断才会有)}(半角冒号)
|
||||
// 第 4 个元素: <br />
|
||||
// 第 5 个元素: <a href="${用于 link priview 的预览图 url}"><i><b>🔗</b></i></a>
|
||||
// 第 6 个元素: <a href="${文章 url}">${文章标题}</a>
|
||||
// (余下是文章简介,一般是裸文本,这里用不到)
|
||||
|
||||
const title_elem = item.find('.tgme_widget_message_text > a:nth-of-type(2)'); // 第二个 a 元素会是文章链接
|
||||
let author = '';
|
||||
let titleElemIs3thA = false;
|
||||
|
||||
if (title_elem.length === 0) {
|
||||
// 获取不到第二个 a 元素,这可能是公众号发的服务消息,丢弃它
|
||||
const brNode = item.find('.tgme_widget_message_text > br:nth-of-type(1)').get(0); // 获取第一个换行
|
||||
const authorNode = brNode && brNode.prev; // brNode 不为 undefined 时获取它的前一个节点
|
||||
const authorNodePrev = authorNode && authorNode.prev; // authorNode 不为 undefined 时获取它的前一个节点
|
||||
if (authorNode && authorNode.type === 'text') {
|
||||
// 只有这个节点是一个裸文本时它才可能是公众号名,开始找寻公众号名
|
||||
if (authorNodePrev && authorNodePrev.type === 'tag' && authorNodePrev.name === 'a' && authorNodePrev.attribs.href && authorNodePrev.attribs.href.startsWith('?q=%23')) {
|
||||
// authorNode 前一个节点是链接, 且是个 hashtag,表示启用了 efb-patch-middleware,这个节点是公众号名
|
||||
// 有两种可能:
|
||||
// 带 # 的完整公众号名 (efb-patch-middleware 启用,且 hashtag 完全合法)
|
||||
// 被截断的公众号名前半部分 (efb-patch-middleware 启用,但 hashtag 被空格或标点截断)
|
||||
// (若 efb-patch-middleware 未启用,或 hashtag 完全不合法,不会进入此流程)
|
||||
titleElemIs3thA = true;
|
||||
author += $(authorNodePrev).text();
|
||||
}
|
||||
|
||||
const spaceIndex = authorNode.data.indexOf(' ');
|
||||
const colonIndex = authorNode.data.indexOf(':');
|
||||
if (authorNode.data.length > 1 && colonIndex !== -1 && (spaceIndex !== -1 || titleElemIs3thA)) {
|
||||
// 有三种可能:
|
||||
// 不带 # 的完整公众号名 (efb-patch-middleware 未启用)
|
||||
// 带 # 的完整公众号名 (efb-patch-middleware 启用,但 hashtag 完全不合法)
|
||||
// 被截断的公众号名后半部分 (efb-patch-middleware 启用,但 hashtag 被空格或标点截断,此时空格有意义)
|
||||
// (若 efb-patch-middleware 启用,且 hashtag 完全合法,不会进入此流程)
|
||||
const sliceStart = titleElemIs3thA ? 0 : spaceIndex + 1;
|
||||
author += authorNode.data.slice(sliceStart, colonIndex); // 提取作者
|
||||
}
|
||||
|
||||
if (author.startsWith('#')) {
|
||||
author = author.slice(1); // 去掉开头的 #
|
||||
}
|
||||
}
|
||||
|
||||
// 如果启用了 efb-patch-middleware 且 hashtag (部分)合法,第三个 a 元素会是文章链接,否则是第二个
|
||||
const titleElemNth = titleElemIs3thA ? 3 : 2;
|
||||
const titleElem = item.find(`.tgme_widget_message_text > a:nth-of-type(${titleElemNth})`);
|
||||
|
||||
if (titleElem.length === 0) {
|
||||
// 获取不到标题 a 元素,这可能是公众号发的服务消息,丢弃它
|
||||
return;
|
||||
}
|
||||
|
||||
let author;
|
||||
let title = title_elem.text();
|
||||
const link = title_elem.attr('href');
|
||||
let title = titleElem.text();
|
||||
const link = titleElem.attr('href');
|
||||
|
||||
const br_node = item.find('.tgme_widget_message_text > br:nth-of-type(1)').get(0); // 获取第一个换行
|
||||
const author_node = br_node && br_node.prev; // br_node 不为 undefined 时获取它的前一个节点
|
||||
if (author_node && author_node.type === 'text') {
|
||||
// 只有这个节点是一个裸文本时它才可能是公众号名
|
||||
const spaceIndex = author_node.data.indexOf(' ');
|
||||
const colonIndex = author_node.data.indexOf(':');
|
||||
if (spaceIndex !== -1 && colonIndex !== -1) {
|
||||
// 找到了公众号名,说明这个频道订阅了多个公众号
|
||||
author = author_node.data.slice(spaceIndex + 1, colonIndex); // 提取作者
|
||||
if (mpName && author !== mpName) {
|
||||
// 指定了要筛选的公众号名,且该文章不是该公众号发的
|
||||
return; // 丢弃
|
||||
} else if (!mpName) {
|
||||
// 没有指定要筛选的公众号名
|
||||
title = author + ': ' + title; // 给标题里加上获取到的作者
|
||||
}
|
||||
}
|
||||
if (mpName && author !== mpName) {
|
||||
// 指定了要筛选的公众号名,且该文章不是该公众号发的
|
||||
return; // 丢弃
|
||||
} else if (!mpName && author) {
|
||||
// 没有指定要筛选的公众号名,且匹配到了作者
|
||||
title = author + ': ' + title; // 给标题里加上获取到的作者
|
||||
}
|
||||
|
||||
const pubDate = new Date(item.find('.tgme_widget_message_date time').attr('datetime')).toUTCString();
|
||||
|
|
|
|||
Loading…
Reference in New Issue