折腾博客 之 通过Freshrss API实现朋友文章
折腾博客 之 通过Freshrss API实现朋友文章
之前玩静态博客用过hexo-circle-of-friends
来获取友情链接中的好友文章. 如今这个项目我部署的过程中总是出现各种意外,不想继续折腾了,于是想起在大佬处看到 用FreshRSS 实现友圈rss订阅 于是开始今日的折腾
Docker部署Freshrss
使用docker run 命令
1
2
3
4
5
6
7
8
docker run -d --restart unless-stopped --log-opt max-size=10m \
-p 8282:80 \
-e TZ=Asia/Shanghai \
-e 'CRON_MIN=1,31' \
-v /root/rss/data:/var/www/FreshRSS/data \
-v /root/rss/extensions:/var/www/FreshRSS/extensions \
--name freshrss \
freshrss/freshrss
获取API密码
- 在
认证
中打开允许 API 访问 (用于手机应用)
选项 - 在
账户
-API管理
中输入API密码
CF Workers
这时候就要使用CF大法了.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
const siteurl = 'Freshssr地址';
async function handleRequest(request) {
// 直接使用固定的用户名与密码
const user = '用户名';
const password = 'API密码';
try {
const authToken = await login(user, password);
if (!authToken) {
return new Response('Login failed.', { status: 403 });
}
const articles = await fetchArticles(authToken);
const subscriptions = await fetchSubscriptions(authToken);
const formattedArticles = formatArticles(articles, subscriptions);
return new Response(JSON.stringify(formattedArticles, null, 2), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response('Error: ' + error.message, { status: 500 });
}
}
async function login(user, password) {
const loginUrl = `${siteurl}/api/greader.php/accounts/ClientLogin?Email=${encodeURIComponent(user)}&Passwd=${encodeURIComponent(password)}`;
const response = await fetch(loginUrl);
const text = await response.text();
const match = text.match(/Auth=(.*)/);
return match ? match[1] : null;
}
async function fetchArticles(authToken) {
const articlesUrl = `${siteurl}/api/greader.php/reader/api/0/stream/contents/reading-list?&n=1000`;
const response = await fetch(articlesUrl, {
headers: {
'Authorization': `GoogleLogin auth=${authToken}`
}
});
const data = await response.json();
return data.items || [];
}
async function fetchSubscriptions(authToken) {
const subscriptionsUrl = `${siteurl}/api/greader.php/reader/api/0/subscription/list?output=json`;
const response = await fetch(subscriptionsUrl, {
headers: {
'Authorization': `GoogleLogin auth=${authToken}`
}
});
const data = await response.json();
return data.subscriptions || [];
}
function formatArticles(articles, subscriptions) {
const subscriptionMap = new Map();
subscriptions.forEach(subscription => {
subscriptionMap.set(subscription.id, subscription);
});
return articles.map(article => {
const strippedContent = stripHtml(article.summary.content);
const desc_length = strippedContent.length;
const short_desc = desc_length > 20
? strippedContent.substring(0, 99) + '...'
: strippedContent;
const subscriptionId = article.origin.streamId;
const subscription = subscriptionMap.get(subscriptionId);
return {
site_name: article.origin.title,
title: article.title,
link: article.alternate[0].href,
time: new Date(article.published * 1000).toISOString(), // Convert timestamp to ISO string
description: short_desc,
icon: subscription ? `${siteurl}/${subscription.iconUrl.split('/').pop()}` : ''
};
});
}
function stripHtml(html) {
return html.replace(/<[^>]*>?/gm, ''); // 使用正则表达式去除HTML标签
}
访问看看,输出 https://rss.imsun.workers.dev/ 由于cf的加载速度不是很理想,当然也可以使用php或者bash脚本生成json文件,这样可以加速页面加载的速度,大佬已经有代码贴出来了我就不复制了. 如果是使用静态博客的话 则可以选择使用github工作流 生成静态文件并通过vercel加速访问
调用
我把朋友圈文章加在了友情链接页面
使用php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?php
// 获取JSON数据
$jsonData = file_get_contents('https://rss.imsun.workers.dev/');
// 检查是否成功获取数据
if ($jsonData === false) {
die('Error fetching JSON data.');
}
// 将JSON数据解析为PHP数组
$articles = json_decode($jsonData, true);
// 检查是否成功解析JSON
if ($articles === null) {
die('Error decoding JSON data.');
}
// 对文章按时间排序(最新的排在前面)
usort($articles, function ($a, $b) {
return strtotime($b['time']) - strtotime($a['time']);
});
// 设置每页显示的文章数量
$itemsPerPage = 30;
// 生成文章列表
echo '<header class="archive--header"><h1 class="post--single__title">朋友文章</h1><h2 class="post--single__subtitle">通过Freshrss获取 </h2>
</header><div class="articleList">';
foreach (array_slice($articles, 0, $itemsPerPage) as $article) {
// 格式化发布时间
$date = new DateTime($article['time']);
$date->setTimezone(new DateTimeZone('Asia/Shanghai')); // 设置为中国上海时区
$formattedDate = $date->format('Y年m月d日 H:i:s');
echo '<article class="post--item"><div class="content">';
echo '<a target=_blank href="' . htmlspecialchars($article['link']) . '"><h2 class="post--title">' . htmlspecialchars($article['title']) . '</h2></a>';
echo ' <div class="description">' . htmlspecialchars($article['description']) . '</div>';
echo '<div class="meta"><img src="' . htmlspecialchars($article['icon']) . '" class="icon" width="16" height="16" alt="Icon" />'. htmlspecialchars($article['site_name']) .'';
echo '<svg class="icon" viewBox="0 0 1024 1024" width="16" height="16">
<path
d="M512 97.52381c228.912762 0 414.47619 185.563429 414.47619 414.47619s-185.563429 414.47619-414.47619 414.47619S97.52381 740.912762 97.52381 512 283.087238 97.52381 512 97.52381z m0 73.142857C323.486476 170.666667 170.666667 323.486476 170.666667 512s152.81981 341.333333 341.333333 341.333333 341.333333-152.81981 341.333333-341.333333S700.513524 170.666667 512 170.666667z m36.571429 89.697523v229.86362h160.865523v73.142857H512a36.571429 36.571429 0 0 1-36.571429-36.571429V260.388571h73.142858z">
</path>
</svg><time> ' . htmlspecialchars($formattedDate) . '</time></div>
</div> ';
echo '</article>';
}
echo '</div>';
?>
大功告成
使用JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<div class="articleList" id="articleList"></div>
<button id="load-more">加载更多</button>
<script>
document.addEventListener('DOMContentLoaded', async () => {
const jsonData = await fetchJsonData('https://cdn.jkjoy.cn/rss/rss.json');
if (!jsonData) {
console.error('Failed to fetch JSON data.');
return;
}
const articles = jsonData;
// 对文章按时间排序(最新的排在前面)
articles.sort((a, b) => new Date(b.time) - new Date(a.time));
// 初始化分页变量
let currentPage = 1;
const itemsPerPage = 30;
// 加载第一页文章
loadArticles(currentPage, itemsPerPage, articles);
// 设置“加载更多”按钮点击事件
const loadMoreButton = document.getElementById('load-more');
loadMoreButton.addEventListener('click', () => {
currentPage++;
loadArticles(currentPage, itemsPerPage, articles);
});
});
async function fetchJsonData(url) {
try {
const response = await fetch(url);
if (response.ok) {
return await response.json();
} else {
console.error('HTTP error:', response.status, response.statusText);
}
} catch (error) {
console.error('Fetch error:', error);
}
return null;
}
function loadArticles(page, perPage, articles) {
const startIndex = (page - 1) * perPage;
const endIndex = page * perPage;
const articleListElem = document.getElementById('articleList');
articles.slice(startIndex, endIndex).forEach(article => {
const articleElem = createArticleElement(article);
articleListElem.appendChild(articleElem);
});
}
function createArticleElement(article) {
// 格式化发布时间
const date = new Date(article.time);
const formattedDate = new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).format(date);
const div = document.createElement('div');
div.className = 'post--item';
const contentDiv = document.createElement('div');
contentDiv.className = 'content';
const link = document.createElement('a');
link.href = article.link;
link.target = '_blank';
const title = document.createElement('h2');
title.className = 'post--title';
title.textContent = article.title;
link.appendChild(title);
const descriptionDiv = document.createElement('div');
descriptionDiv.className = 'description';
descriptionDiv.textContent = article.description;
const metaDiv = document.createElement('div');
metaDiv.className = 'meta';
const iconImg = document.createElement('img');
iconImg.src = article.icon;
iconImg.width = 16;
iconImg.height = 16;
iconImg.alt = 'Icon';
iconImg.className = 'icon';
const siteName = document.createElement('span');
siteName.textContent = article.site_name;
const timeIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
timeIcon.className = 'icon';
timeIcon.setAttribute('viewBox', '0 0 1024 1024');
timeIcon.setAttribute('width', '16');
timeIcon.setAttribute('height', '16');
timeIcon.innerHTML = `<path d="M512 97.52381c228.912762 0 414.47619 185.563429 414.47619 414.47619s-185.563429 414.47619-414.47619 414.47619S97.52381 740.912762 97.52381 512 283.087238 97.52381 512 97.52381z m0 73.142857C323.486476 170.666667 170.666667 323.486476 170.666667 512s152.81981 341.333333 341.333333 341.333333 341.333333-152.81981 341.333333-341.333333S700.513524 170.666667 512 170.666667z m36.571429 89.697523v229.86362h160.865523v73.142857H512a36.571429 36.571429 0 0 1-36.571429-36.571429V260.388571h73.142858z"></path>`;
const timeText = document.createElement('time');
timeText.textContent = formattedDate;
metaDiv.appendChild(iconImg);
metaDiv.appendChild(siteName);
metaDiv.appendChild(timeIcon);
metaDiv.appendChild(timeText);
contentDiv.appendChild(link);
contentDiv.appendChild(descriptionDiv);
contentDiv.appendChild(metaDiv);
div.appendChild(contentDiv);
return div;
}
</script>
本文由作者按照 CC BY 4.0 进行授权