今天看到了 段先生 的文章,他用Freshrss的API在个人网站上面,实现了“朋友圈”。
我也想弄一个,说干就干,我和ChatGPT4在他的基础上新增了一些功能:
加载更多
按钮:默认显示 10 篇最新的文章,每次点击“加载更多”按钮时,都会通过 AJAX 请求加载更多文章,并在当前文章列表后追加新加载的文章。同时,如果还有更多文章可加载,新的“加载更多”按钮也会被添加到页面上。<a>
标签)都被添加了 rel="nofollow"
属性。这样,当搜索引擎蜘蛛爬取这些页面时,知道不跟随这些链接,这有助于管理网站的SEO链接权重分配。效果展示:https://pipuwong.com/friends
甚至,我们还做了 2 个版本:
版本一:手动加载,手动点击加载更多
按钮
版本 2:自动加载,滑到底部加载更多
最新文章,就像跟刷朋友圈一样地爽
大家可选择不同的版本进行使用,Enjoy !
通过 FreshRSS 的 API 获取订阅源的文章,并在 WordPress 中展示这些文章。
安装Freshrss —— 设置Freshrss API访问权限和密码 —— 添加rss.php —— 编辑主题funtions.php文件 —— 添加自定义CSS —— 添加自动加载按钮 —— 订阅好友的 RSS
新建docker compose.yml文件,并且输入以下代码
因为新版的docker已经去掉了version: “3”,所以在此也去掉了
# ~/freshrss/docker-compose.yml
services:
freshrss-db:
image: postgres:latest
container_name: freshrss-db
hostname: freshrss-db
restart: unless-stopped
volumes:
- ./freshrss-db:/var/lib/postgresql/data
environment:
POSTGRES_USER: freshrss
POSTGRES_PASSWORD: df6Qi5SnPQUdScsagzB
POSTGRES_DB: freshrss
freshrss-app:
image: freshrss/freshrss:latest
container_name: freshrss-app
hostname: freshrss-app
restart: unless-stopped
ports:
- 34211:80
depends_on:
- freshrss-db
volumes:
- ./data:/var/www/FreshRSS/data
- ./extensions:/var/www/FreshRSS/extensions
environment:
CRON_MIN: "*/45"
TZ: Asia/Shanghai
volumes:
freshrss-db: null
cd 到 yml 文件目录,输入安装命令docker compose up -d
即可安装成功
允许api访问
按需设置清理策略、自动刷新间隔(可选,你也可保持默认)
设置 api密码
访问api地址
api 地址如下
一般常用的,都是用htps://xxx/api/greader.php
这个api地址,记住它!
到此,Freshrss的安装和设置相关工作已经结束了!
在网站根目录新建rss.php文件,并且填入以下代码,按需修改成自己的域名。
*标记红色的都是需要你自己改
<?php
/**
* 获取最新订阅文章并生成JSON文件
*/
function getAllSubscribedArticlesAndSaveToJson($user, $password)
{
$apiUrl = 'https://你部署FreshRSS的域名/p/api/greader.php';
$loginUrl = $apiUrl . '/accounts/ClientLogin?Email=' . urlencode($user) . '&Passwd=' . urlencode($password);
$loginResponse = curlRequest($loginUrl);
if (strpos($loginResponse, 'Auth=') !== false) {
$authToken = substr($loginResponse, strpos($loginResponse, 'Auth=') + 5);
$articlesUrl = $apiUrl . '/reader/api/0/stream/contents/reading-list?&n=1000';
$articlesResponse = curlRequest($articlesUrl, $authToken);
$articles = json_decode($articlesResponse, true);
if (isset($articles['items'])) {
usort($articles['items'], function ($a, $b) {
return $b['published'] - $a['published'];
});
$subscriptionsUrl = $apiUrl . '/reader/api/0/subscription/list?output=json';
$subscriptionsResponse = curlRequest($subscriptionsUrl, $authToken);
$subscriptions = json_decode($subscriptionsResponse, true);
if (isset($subscriptions['subscriptions'])) {
$subscriptionMap = array();
foreach ($subscriptions['subscriptions'] as $subscription) {
$subscriptionMap[$subscription['id']] = $subscription;
}
$formattedArticles = array();
foreach ($articles['items'] as $article) {
$desc_length = mb_strlen(strip_tags(html_entity_decode($article['summary']['content'], ENT_QUOTES, 'UTF-8')), 'UTF-8');
if ($desc_length > 20) {
$short_desc = mb_substr(strip_tags(html_entity_decode($article['summary']['content'], ENT_QUOTES, 'UTF-8')), 0, 99, 'UTF-8') . '...';
} else {
$short_desc = strip_tags(html_entity_decode($article['summary']['content'], ENT_QUOTES, 'UTF-8'));
}
$formattedArticle = array(
'site_name' => $article['origin']['title'],
'title' => $article['title'],
'link' => $article['alternate'][0]['href'],
'time' => date('Y-m-d H:i', $article['published']),
'description' => $short_desc,
);
$subscriptionId = $article['origin']['streamId'];
if (isset($subscriptionMap[$subscriptionId])) {
$subscription = $subscriptionMap[$subscriptionId];
$iconUrl = $subscription['iconUrl'];
$filename = 'https://你部署FreshRSS的域名/p/'.substr($iconUrl, strrpos($iconUrl, '/') + 1);
$formattedArticle['icon'] = $filename;
}
$formattedArticles[] = $formattedArticle;
}
saveToJsonFile($formattedArticles);
return $formattedArticles;
} else {
echo 'Error retrieving articles.';
}
} else {
echo 'Error retrieving articles.';
}
} else {
echo 'Login failed.';
}
return null;
}
function curlRequest($url, $authToken = null)
{
$ch = curl_init($url);
if ($authToken) {
$headers = array(
'Authorization: GoogleLogin auth=' . $authToken,
);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
/**
* 将数据保存到JSON文件中
*/
function saveToJsonFile($data)
{
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
file_put_contents('output.json', $json);
echo '数据已保存到JSON文件中';
}
// 调用函数并提供用户名和密码
getAllSubscribedArticlesAndSaveToJson('这里输入FreshRSS的用户名', '这里输入Freshrss的api密码');
请先检测是否成功:地址栏输入 https://你的域名/rss.php
,假如看到“数据已保存到JSON文件中”,就意味着成功,可进行下一步。
在子主题的funtions.php里添加以下代码
(建议用子主题,因为当主题更新时,不怕丢失funtions.php里面的自定义代码)
(这里的代码无修修改,找funtions的自定义代码区域,粘贴进去)
function display_articles_shortcode($atts) {
$attributes = shortcode_atts(array('offset' => 0), $atts);
$offset = intval($attributes['offset']);
$jsonFilePath = ABSPATH . 'output.json';
$jsonData = file_get_contents($jsonFilePath);
if ($jsonData === false) {
return '<div class="error">Failed to load data from ' . htmlspecialchars($jsonFilePath) . '</div>';
}
$articles = json_decode($jsonData, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return '<div class="error">Error decoding JSON data: ' . htmlspecialchars(json_last_error_msg()) . '</div>';
}
if (empty($articles)) {
return '<div class="error">No articles to display.</div>';
}
usort($articles, function ($a, $b) {
return strtotime($b['time']) - strtotime($a['time']);
});
$itemsPerPage = 10;
$articlesToShow = array_slice($articles, $offset, $itemsPerPage);
ob_start();
foreach ($articlesToShow as $article) {
?>
<div class="article">
<h3>
<img src="<?php echo htmlspecialchars($article['icon']); ?>" alt="Icon" class="icon">
<a href="<?php echo htmlspecialchars($article['link']); ?>" rel="nofollow" target="_blank"><?php echo htmlspecialchars($article['title']); ?></a>
</h3>
<p>作者:<?php echo htmlspecialchars($article['site_name']); ?></p>
<p><?php echo htmlspecialchars($article['description']); ?></p>
<time><?php echo htmlspecialchars($article['time']); ?></time>
</div>
<?php
}
// Check if there are more articles to load
if ($offset + $itemsPerPage < count($articles)) {
echo '<button id="load-more" data-offset="' . ($offset + $itemsPerPage) . '">加载更多</button>';
} else {
echo '<div id="all-loaded">加载完毕</div>';
}
return ob_get_clean();
}
add_shortcode('display_articles', 'display_articles_shortcode');
function enqueue_custom_scripts() {
wp_enqueue_script('custom-js', get_template_directory_uri() . '/js/custom.js', array('jquery'), null, true);
wp_localize_script('custom-js', 'ajax_params', array('ajax_url' => admin_url('admin-ajax.php')));
}
add_action('wp_enqueue_scripts', 'enqueue_custom_scripts');
function load_more_articles() {
$offset = isset($_POST['offset']) ? intval($_POST['offset']) : 0;
echo display_articles_shortcode(array('offset' => $offset));
wp_die();
}
add_action('wp_ajax_load_more_articles', 'load_more_articles');
add_action('wp_ajax_nopriv_load_more_articles', 'load_more_articles');
新建js文件
在主主题,是主主题,不是子主题,在主主题的根目录新建 js 文件夹,在 js 里面新增 custom.js,并且把下面代码粘贴进去
(也就是:/wp-content/themes/主主题/js/custom.js)
custom.js
jQuery(document).ready(function($) {
$(document).on('click', '#load-more', function() {
var button = $(this);
var offset = button.data('offset');
$.ajax({
url: ajax_params.ajax_url,
type: 'POST',
data: {
'action': 'load_more_articles',
'offset': offset
},
success: function(response) {
button.replaceWith(response);
}
});
});
});
custom.js
jQuery(document).ready(function($) {
// 函数用于检查是否滚动到页面底部
function isScrolledToBottom() {
return $(window).scrollTop() + $(window).height() >= $(document).height() - 100; // 100px提前量
}
// 函数用于加载更多文章
function loadMoreArticles() {
var button = $('#load-more');
if (button.length) {
var offset = button.data('offset');
$.ajax({
url: ajax_params.ajax_url,
type: 'POST',
data: {
'action': 'load_more_articles',
'offset': offset
},
beforeSend: function() {
button.text('加载中...'); // 可选:在加载时更改按钮文本
},
success: function(response) {
button.replaceWith(response);
}
});
}
}
// 绑定滚动事件
$(window).scroll(function() {
if (isScrolledToBottom()) {
loadMoreArticles(); // 当滚动到底部时,调用加载更多文章的函数
}
});
// 也保留点击加载更多的功能
$(document).on('click', '#load-more', function() {
loadMoreArticles();
});
});
/* Article container */
.article {
border: 1px solid #ccc;
border-radius: 5px;
padding: 15px;
margin-bottom: 20px;
}
/* Article title */
.article h3 {
margin-top: 0;
}
/* Article icon */
.icon {
width: 50px;
height: 50px;
margin-right: 10px;
border-radius: 50%;
}
/* Article metadata */
.article p, .article time {
margin: 5px 0;
}
/* Article time */
.article time {
font-style: italic;
}
/* Hover effect on article */
.article:hover {
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.3s ease;
}
/* Article icon */
.icon {
width: 1.5em; /* 使用 em 单位可以根据标题字体大小调整图标大小 */
height: auto; /* 自动调整高度以保持宽高比 */
margin-right: 10px;
vertical-align: middle; /* 垂直居中对齐 */
border-radius: 50%;
}
在需要的地方输入 简码 即可
[display_articles]
定时访问刚开始创建的php文件,以更新订阅数据
以1panel为例,宝塔面板同理
用Freshrss订阅好友非常方便
See You~
参考资料:
若志:用FreshRSS 实现友圈rss订阅
段先生:跟风利用FreshRSS实现朋友圈