昨天完成了L站rss订阅to飞书~
今天有友友联系我说:想订阅其他的RSS能不能实现?
YES! 是可以的,怎么实现呢?
怎么搞?直接AI改写!一分钟搞定!
以下提问AI模板(V2EX为例),直接发给GPT即可:
(测试用的GPT-4o,直接成功)
====Linuxdo站rss订阅TO飞书代码开始====
const RSS_URL = 'https://linux.do/latest.rss';
const FEISHU_WEBHOOK_URL = 'https://open.feishu.cn/open-apis/bot/v2/hook/xxxxx';//飞书群机器人Webhook 地址
const COOKIE = '_ga=xxxx; _t=xxxx; _forum_session=xxxx; _ga_xxx=xxxx';//你的linuxdo_cookie~
export default {
async scheduled(event, env, ctx) {
try {
const db = env.linuxdo_whfs;
const rssResponse = await handleRequest(new Request(RSS_URL));
const rssText = await rssResponse.text();
const items = parseRSS(rssText);
for (let item of items) {
const alreadySent = await checkIfSent(db, item.Link.url);
if (!alreadySent) {
await postToFeishu(item);
await recordSentUrl(db, item.Link.url);
}
}
} catch (error) {
console.error('Error in handleScheduledEvent:', error);
}
},
};
function parseRSS(rssText) {
const itemRegex = /<item>(.*?)<\/item>/gs;
let match, posts = [];
while ((match = itemRegex.exec(rssText)) && posts.length < 30) {
const itemContent = match[1];
const title = itemContent.match(/<title>(.*?)<\/title>/s)[1];
const creator = itemContent.match(/<dc:creator><!\[CDATA\[(.*?)\]\]><\/dc:creator>/s)[1];
const link = itemContent.match(/<link>(.*?)<\/link>/s)[1];
const description = itemContent.match(/<description><!\[CDATA\[(.*?)\]\]><\/description>/s)[1]
.replace(/ {2,}/g, ' ')
.replace(/<p><small>.*?">阅读完整话题<\/a><\/p>/s, '')
.replace(/<[^>]*>/g, '')
.replace(/^\s*[\r\n]/gm, '')
.split('\n')
.slice(0, 10)
.join('\n');
const category = itemContent.match(/<category>(.*?)<\/category>/s)[1];
const pubDate = itemContent.match(/<pubDate>(.*?)<\/pubDate>/s)[1];
posts.push({
Title: title,
Author: creator,
Description: description,
Category: category,
Time: new Date(pubDate).toUTCString(),
Link: { pc_url: "", android_url: "", ios_url: "", url: link }
});
}
return posts;
}
//其中的template是我做的飞书卡片模板,如果不喜欢可以自己制作一个~替换掉template_id和version即可~
async function postToFeishu(post) {
await fetch(FEISHU_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
msg_type: "interactive",
card: { type: "template", data: { template_id: "AAq3OTs48BODR", template_version_name: "1.0.2", template_variable: post } }
})
});
}
async function handleRequest(request) {
const modifiedRequest = new Request(RSS_URL, {
headers: { ...request.headers, 'Cookie': COOKIE },
method: request.method,
body: request.body,
redirect: 'follow'
});
const response = await fetch(modifiedRequest);
return new Response(response.body, { ...response, headers: { ...response.headers, 'Access-Control-Allow-Origin': '*' } });
}
async function checkIfSent(db, url) {
const query = 'SELECT 1 FROM topics WHERE topic = ?';
const result = await db.prepare(query).bind(url).first();
return result ? true : false;
}
async function recordSentUrl(db, url) {
const query = 'INSERT INTO topics (topic) VALUES (?)';
await db.prepare(query).bind(url).run();
}
export { handleRequest };
====Linuxdo站rss订阅TO飞书代码结束====
修改需求如下:
1. RSS_URL 改为:https://v2ex.com/index.xml;
2. handleRequest 直接请求,无需cookie;
3. db env使用 v2ex_rss;
4. parseRSS改为适配以下示例格式模板:
=====订阅模板示例内容开始=====
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>V2EX</title>
<subtitle>way to explore</subtitle>
<link rel="alternate" type="text/html" href="https://www.v2ex.com/" />
<link rel="self" type="application/atom+xml" href="https://www.v2ex.com/index.xml" />
<id>https://www.v2ex.com/</id>
<updated>2024-05-19T09:33:56Z</updated>
<rights>Copyright © 2010-2018, V2EX</rights>
<entry>
<title>[MacBook] 关于 Mac Air 和 Mac Pro 的性能</title>
<link rel="alternate" type="text/html" href="https://www.v2ex.com/t/1042046#reply0" />
<id>tag:www.v2ex.com,2024-05-19:/t/1042046</id>
<published>2024-05-19T09:36:56Z</published>
<updated>2024-05-19T09:33:56Z</updated>
<author>
<name>seekseat</name>
<uri>https://www.v2ex.com/member/seekseat</uri>
</author>
<content type="html" xml:base="https://www.v2ex.com/" xml:lang="en"><![CDATA[
<p>今天去体验店发现新的 air 非常经验,不再是缓坡式设计,而且非常薄。</p>
<p>有几个问题: 为什么 pro 比 air 厚那么多,厚出来的部分主要是什么元器件导致的。</p>
<p>M2 芯片的 Air ,性能会比 2021 款的 M1 芯片的 Pro 要好吗?</p>
]]></content>
</entry><entry>
<title>[路由器] 家庭宽带 ip 固定</title>
<link rel="alternate" type="text/html" href="https://www.v2ex.com/t/1042045#reply0" />
<id>tag:www.v2ex.com,2024-05-19:/t/1042045</id>
<published>2024-05-19T09:32:58Z</published>
<updated>2024-05-19T09:29:58Z</updated>
<author>
<name>GopherRustaceans</name>
<uri>https://www.v2ex.com/member/GopherRustaceans</uri>
</author>
<content type="html" xml:base="https://www.v2ex.com/" xml:lang="en"><![CDATA[
<p>家里使用电信的宽带,访问公司某个资源时,每次需要同事帮忙加白名单。但是过一段时间 ip 就会变更,需要重新联系同事。</p>
<p>于是有两个疑惑:</p>
<ol>
<li>
<p>有没有什么办法把 IP 固定住?</p>
</li>
<li>
<p>一般多久会更换 ip ?什么操作会触发 ip 的变更?比如我把路由器电源拔掉重新连,会使出口 ip 发生变化吗?</p>
</li>
</ol>
]]></content>
</entry><entry>
<title>[问与答] 不懂就问,某音的搬游戏主播</title>
<link rel="alternate" type="text/html" href="https://www.v2ex.com/t/1042044#reply0" />
<id>tag:www.v2ex.com,2024-05-19:/t/1042044</id>
<published>2024-05-19T09:23:13Z</published>
<updated>2024-05-19T09:23:13Z</updated>
<author>
<name>QingquanBaby</name>
<uri>https://www.v2ex.com/member/QingquanBaby</uri>
</author>
<content type="html" xml:base="https://www.v2ex.com/" xml:lang="en"><![CDATA[
搬游戏什么的,什么操作,是割韭菜吗
]]></content>
</entry><entry>
<title>[iPhone] 是现在买 iPhone15Pro 还是等 16 出来?</title>
<link rel="alternate" type="text/html" href="https://www.v2ex.com/t/1042043#reply0" />
<id>tag:www.v2ex.com,2024-05-19:/t/1042043</id>
<published>2024-05-19T09:13:50Z</published>
<updated>2024-05-19T09:11:50Z</updated>
<author>
<name>t202201</name>
<uri>https://www.v2ex.com/member/t202201</uri>
</author>
<content type="html" xml:base="https://www.v2ex.com/" xml:lang="en"><![CDATA[
最近京东打折,iPhone15Pro 256G 7000 ,iPhone15 256G 5649 ,查价格似乎都是目前最低价了。但是不知道 16 会不会有很大的更新,有点点期待一手 iPhone 对 ai 的接入。<br /><br />之前的 xr 用了 5 年多了,前段时间升级系统后,实在是太慢了,所以想换个。<br /><br />如果是各位大佬,是等等还是现在就买?
]]></content>
</entry><entry>
<title>[NAS] 22t 西部数据 elements,含税包邮¥2960 划算吗?</title>
<link rel="alternate" type="text/html" href="https://www.v2ex.com/t/1042042#reply3" />
<id>tag:www.v2ex.com,2024-05-19:/t/1042042</id>
<published>2024-05-19T09:10:13Z</published>
<updated>2024-05-19T09:28:37Z</updated>
<author>
<name>kuanos</name>
<uri>https://www.v2ex.com/member/kuanos</uri>
</author>
<content type="html" xml:base="https://www.v2ex.com/" xml:lang="en"><![CDATA[
<p>大家感觉呢,最近苦于没有空间了</p>
]]></content>
</entry><entry>
<title>[程序员] 谷歌搜索结果展示是不是又回到了原来的分页形式?</title>
<link rel="alternate" type="text/html" href="https://www.v2ex.com/t/1042040#reply1" />
<id>tag:www.v2ex.com,2024-05-19:/t/1042040</id>
=====订阅模板示例内容结束=====
主要内容解释:
- RSS_URL 改为:<rss订阅链接>;
- handleRequest <有cookie不修改 / 直接请求,无需cookie>;
- db env使用 <D1数据库名称>;
- parseRSS改为适配以下示例格式模板:<rss订阅从第一行开始复制,需包含大部分段落>
AI回复效果测试通过
下附AI生成后的代码(V2EX v2ex.com/index.xml 订阅):
数据库名称为: v2ex_rss,
其他与Linuxdo订阅(Linuxdo新话题推送到tg?不!推送飞书!! - 软件分享 - LINUX DO)操作一致
const RSS_URL = 'https://v2ex.com/index.xml';
const FEISHU_WEBHOOK_URL = 'https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx'; // 飞书群机器人Webhook 地址
export default {
async scheduled(event, env, ctx) {
try {
const db = env.v2ex_rss;
const rssResponse = await handleRequest(new Request(RSS_URL));
const rssText = await rssResponse.text();
const items = parseRSS(rssText);
for (let item of items) {
const alreadySent = await checkIfSent(db, item.Link.url);
if (!alreadySent) {
await postToFeishu(item);
await recordSentUrl(db, item.Link.url);
}
}
} catch (error) {
console.error('Error in handleScheduledEvent:', error);
}
},
};
function parseRSS(rssText) {
const entryRegex = /<entry>(.*?)<\/entry>/gs;
let match, posts = [];
while ((match = entryRegex.exec(rssText)) && posts.length < 30) {
const entryContent = match[1];
const title = entryContent.match(/<title>(.*?)<\/title>/s)[1];
const link = entryContent.match(/<link[^>]* href="(.*?)"/s)[1];
const published = entryContent.match(/<published>(.*?)<\/published>/s)[1];
const author = entryContent.match(/<author>.*?<name>(.*?)<\/name>.*?<\/author>/s)[1];
const content = entryContent.match(/<content[^>]*><!\[CDATA\[(.*?)\]\]><\/content>/s)[1]
.replace(/<[^>]*>/g, '')
.replace(/^\s*[\r\n]/gm, '')
.split('\n')
.slice(0, 10)
.join('\n');
posts.push({
Title: title,
Author: author,
Description: content,
Time: new Date(published).toUTCString(),
Link: { pc_url: "", android_url: "", ios_url: "", url: link }
});
}
return posts;
}
async function postToFeishu(post) {
await fetch(FEISHU_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
msg_type: "interactive",
card: { type: "template", data: { template_id: "AAq3OTs48BODR", template_version_name: "1.0.2", template_variable: post } }
})
});
}
async function handleRequest(request) {
const response = await fetch(RSS_URL, {
headers: { ...request.headers },
method: request.method,
body: request.body,
redirect: 'follow'
});
return new Response(response.body, { ...response, headers: { ...response.headers, 'Access-Control-Allow-Origin': '*' } });
}
async function checkIfSent(db, url) {
const query = 'SELECT 1 FROM topics WHERE topic = ?';
const result = await db.prepare(query).bind(url).first();
return result ? true : false;
}
async function recordSentUrl(db, url) {
const query = 'INSERT INTO topics (topic) VALUES (?)';
await db.prepare(query).bind(url).run();
}
export { handleRequest };
