博客就像是我们精心打造的个人空间,我们在这里分享想法、记录生活、展示才华。而友情链接(友链)就像是这些个人空间之间的桥梁,它将我们与志同道合的朋友们连接起来,形成了一个互相支持、共同成长的网络社区。今天,我要和大家分享的是一个特别的“友链朋友圈部署方案”,它将帮助我们的博客不仅成为信息的孤岛,而是变成一个互联互通的生态圈。让我们一起来看看如何通过一些简单而实用的步骤,让我们的博客在“朋友圈”中绽放光彩吧!
1.编写该方案的缘由
在逛友友的博客或者网站时,突然发现有这么好玩而又实用的项目"友链朋友圈",不用"出门"就能在自家网站 查看/浏览 别人写的文章,实在是妙极了。于是,我就开始研究、折腾……此处省略N字:友链朋友圈折腾记录。
当然了,好用的东西肯定是要和大家一起分享的,于是便有了本文。
本文提供的以下2种方案,刘郎亲测均真实有效(但需要注意的是:不同平台、不同系统、不同主题可能有所偏差,自己动手微调即可)。
2."友链朋友圈"项目
2.1.项目部署参考:hexo-circle-of-friends-doc
2.2.项目效果展示
以上两种效果展示图后端部署方法大致相同,不同的是前端效果不同(这取决于你使用的平台/主题和你对CSS的调整)。
如果你觉得官网提供的方法太复杂,看不懂,没关系,我这里还有几种部署方法,傻瓜式操作(复制粘贴)就可以完成!
3.部署方法参考
以下方法根据你自己使用的 系统/平台/主题 对号入座即可。
3.1.思路
安装Freshrss,设置Freshrss API访问权限和密码,添加rss.php,编辑主题funtions.php文件,添加自定义CSS,添加自动加载按钮 ,订阅好友的 RSS
3.2.Freshrss安装
a.在宝塔面板(其他面板也可以)添加一个8.1版本的PHP
b.准备一个单独的域名(二级域名也可以,但必须提前获得SSL证书" https:// "备用)
c.将 Freshrss 程序源码下载并上传到该域名根目录,然后解压(注意:把解压出来的所有文件剪切到域名根目录)
Freshrss程序源码:立即下载
d.访问域名,如果出现提示配置安装Freshrss页面。则代表Freshrss安装成功
e.根据页面提示进行操作(这一步不难,如果有不符合的项目,记得去宝塔面板修改即可)
f.配置安装完成后,立即登陆Freshrss 备用
3.3. Freshrss 设置
a.点击"管理","认证","允许Api访问"打勾
b.点击"配置","归档",按需设置清理策略、自动刷新间隔(可选,你也可保持默认)
c.点击"账户","API管理",设置 api密码(后面要用到)
d.接着在"账户","API管理",设置 api密码的下面,复制api地址,并访问该地址
e.在出现的页面中找到:https://你的域名/api/greader.php 这个api地址,记住它!
到此,Freshrss的安装和设置相关工作已经结束了!
3.4.配置WordPress文件
a.在网站根目录新建 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密码');
注意:将以上代码中的3处信息改成你自己的:
a1.顶部位置:
$apiUrl = 'https://你部署FreshRSS的域名/p/api/greader.php';
a2.中间位置:
$filename = 'https://你部署FreshRSS的域名/p/'.substr($iconUrl,
a3.尾部位置:
// 调用函数并提供用户名和密码
getAllSubscribedArticlesAndSaveToJson('这里输入FreshRSS的用户名', '这里输入Freshrss的api密码');
b.检测改代码是否成功:地址栏输入 https://你的域名/rss.php,假如看到“数据已保存到JSON文件中”,就意味着成功,可进行下一步。
c.在你的WordPress主题目录下的funtions.php文件中添加以下代码(位置参考:/wp-content/themes/你的主题/funtions.php):
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');
d. 在你的WordPress主题目录下的 js 文件中新添加一个 custom.js 文件并复制以下代码到其中(位置参考:/wp-content/themes/你的主题/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);
}
});
});
});
e.登录你的WordPress后台管理,添加以下CSS样式代码(当然你也可以自定义修改):
/* 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%;
}
f.在后台管理中心发布页面或者文章并输入以下简码
即可:
[display_articles]
到这里,你已经可以通过你发布的文章或者页面链接看到你的友链朋友圈效果了。但是,现在有一个问题,那就是如果有人发布文章,你需要到 Freshrss 和 第3.4.a步骤中的rss.php 文件链接刷新一下,才能看到实时更新的效果。
怎么解决这个问题呢?
g.我们可以到宝塔面板添加一个"计划任务"即可实现:
打开宝塔面板(或者其他面板),点击"计划任务","创建新的计划任务",任务类型选择"访问URL",任务名称自定义,执行周期自定义(就是你需要多久让他刷新一次),"URL地址"填写你在第3.4.a步骤中创建的rss.php 文件链接地址。
3.5.用Freshrss订阅好友动态
用Freshrss订阅好友动态后,系统会根据你之前在Freshrss、WordPress和宝塔面板上的配置信息,自动同步并更新。
4.写在最后
本文提供的这个方法其实主要还是以WordPress平台为主的,但Typecho稍微改动一下也是可以使用的😂。至于怎么改… iframe也行,直接改前面的Js代码适配也可以。这个看你的发挥了!
5.补充
这是我部署后的样子(虽然优化不咋的,但最终达到的效果还是可以滴😀)
在完成第3.4步骤之前的操作后,后面的操作可直接替换为该方法:
3.4.显示订阅数据
在网站的主题目录中添加以下代码,以显示订阅数据内容:
<?php
// 获取JSON数据
$jsonData = file_get_contents('./output.json');
// 将JSON数据解析为PHP数组
$articles = json_decode($jsonData, true);
// 对文章按时间排序(最新的排在前面)
usort($articles, function ($a, $b) {
return strtotime($b['time']) - strtotime($a['time']);
});
// 设置每页显示的文章数量
$itemsPerPage = 30;
// 生成文章列表
foreach (array_slice($articles, 0, $itemsPerPage) as $article) {
$articles_list ='
图标:' . htmlspecialchars($article['icon']) . '
站点标题:' . htmlspecialchars($article['site_name']) . '
文章标题:' . htmlspecialchars($article['title']) . '
文章内容摘要:' . htmlspecialchars($article['description']) . '
文章链接:' . htmlspecialchars($article['link']) . '
文章发布时间:' . htmlspecialchars($article['time']) . '
';
echo $articles_list;
}
?>
3.5.添加计划任务
在宝塔中添加一个计划任务,定时访问执行第3.4步创建的 rss.php 文件,以更新订阅数据
3.6.添加RSS订阅源
最后就是去你部署好的FreshRSS管理站点添加管理你想要订阅的站点了,访问第4步创建的php文件更新数据。
完!
要是能有一个简单、适合小白折腾的wordpress友链朋友圈方案就好了😵💫
这就是基于wordpress部署的(注意看文章的第4点)
苦于没有宝塔面板,唉
不用宝塔也可以的
效果很不错啊,我目前是用的feedbro浏览器插件,有时间研究下你这个
这个真心不错,尝试着按你的教程我去部署一下试一试。
不行 搞不定 在3.4步的时候报错 不知道哪里的问题
提示什么没有
我恢复了,昨晚也没截屏,应该就是主题哪里不太对。不会搞呀!
拓展性和可玩性挺不错的,去试试吧😎
咱是同一主题哈,发现你写了不少typecho教程,学习起来
欢迎😄
跟蜡客的朋友圈果园一样
换汤不换药😂
不错,支持
😄
不错。鼓掌。加油。
😀还在学习中…
这个php版本的请求貌似有些问题。昨天有个博主出问题了,帮着看了半天 curl 92错误,但是我用postman 命令行 跟python都没问题,所以,就很奇怪
嗯哼,会不会是PHP的CURL请求出现问题?可能是因为CURL版本、请求设置或服务器配置引起的?检查PHP代码、配置和服务器日志以解决问题……🧐
不知道,token获取正常,列表拉取错误。哈哈哈
不是所有的接口都有问题
😂😂
我就说为什么动态博客的朋友圈部署放了一个静态hexo的友链朋友圈部署截图呢,原来是用freshrss🫠😂😂
所谓的“效果展示”哈哈