Greasy Fork is available in English.
打开网页时,查询常用的 RSS 后缀并验证是否可用,在网页右下角显示,可一键复制。除常见网站、博客, 添加 BiliBili、GitHub、Twitter
当前为
// ==UserScript==
// @name RSS 订阅链接查找器(扩展版)
// @namespace http://greasyfork.icu/users/1171320
// @version 1.08
// @description 打开网页时,查询常用的 RSS 后缀并验证是否可用,在网页右下角显示,可一键复制。除常见网站、博客, 添加 BiliBili、GitHub、Twitter
// @author yzcjd
// @author2 ChatGPT4 辅助
// @match *://*/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @license MIT
// ==/UserScript==
(function() {
'use strict';
if (window.self !== window.top) return; // 排除 iframe
const rssLinks = new Map();
const possibleRssPaths = [
'/rss', '/feed', '/atom.xml', '/rss.xml', '/feed.xml', '/index.xml',
'/feed.atom', '/rss.json', '/atom', '/index.atom', '/index.rss'
];
const xmlRegex = /<\?xml.*?<rss|<feed/;
function addStyle() {
GM_addStyle(`
#rss-finder-container {
position: fixed;
bottom: 20px;
right: 20px;
background: #fdfdfd;
border: 1px solid #ccc;
padding: 10px;
max-height: 300px;
overflow-y: auto;
z-index: 9999;
font-family: Arial, sans-serif;
font-size: 14px;
}
#rss-finder-container h4 {
margin: 0 0 5px 0;
font-size: 1em;
}
#rss-finder-list {
list-style: none;
padding: 0;
margin: 0;
}
#rss-finder-list li {
margin-bottom: 5px;
display: flex;
align-items: center;
font-size: 0.9em;
flex-wrap: wrap;
}
#rss-finder-list li a {
color: blue;
text-decoration: none;
margin-right: 10px;
max-width: 400px;
overflow-wrap: break-word;
word-break: break-word;
line-break: anywhere;
}
#rss-finder-list li button {
font-size: 0.8em;
padding: 2px 6px;
margin-left: 10px;
cursor: pointer;
color: #666;
transition: all 0.3s ease;
flex-shrink: 0;
}
`);
}
function findRssLinks() {
const linkTags = document.querySelectorAll('link[type="application/rss+xml"], link[type="application/atom+xml"]');
linkTags.forEach(link => {
if (link.href) addRssLink(link.href, '页面内RSS');
});
const metaTags = document.querySelectorAll('meta[type="application/rss+xml"], meta[type="application/atom+xml"]');
metaTags.forEach(meta => {
if (meta.content) addRssLink(meta.content, '页面内RSS');
});
possibleRssPaths.forEach(path => {
const guessedUrl = window.location.origin + path;
addRssLink(guessedUrl, '常规猜测');
});
if (window.location.hostname === 'github.com') {
const parts = window.location.pathname.split('/').filter(Boolean);
if (parts.length === 1) {
addRssLink(`https://github.com/${parts[0]}.atom`, 'GitHub 用户动态');
} else if (parts.length === 2) {
addRssLink(`https://github.com/${parts[0]}/${parts[1]}/commits.atom`, 'GitHub 提交记录');
addRssLink(`https://github.com/${parts[0]}/${parts[1]}/releases.atom`, 'GitHub 发布记录');
}
}
if (window.location.hostname === 'twitter.com') {
const username = window.location.pathname.slice(1);
if (username) {
addRssLink(`https://news.google.com/rss/search?q=site:twitter.com/${username}+when:7d`, 'Twitter (7天更新)');
}
}
if (window.location.hostname === 'space.bilibili.com') {
const uidMatch = window.location.pathname.match(/^\/(\d+)/);
if (uidMatch) {
const uid = uidMatch[1];
addRssLink(`https://rsshub.app/bilibili/user/article/${uid}`, 'Bilibili 专栏文章');
addRssLink(`https://rsshub.app/bilibili/user/video/${uid}`, 'Bilibili 投稿视频');
}
}
const rssSubscribeLinks = document.querySelectorAll('a.rss-subscribe');
rssSubscribeLinks.forEach(link => {
if (link.href) addRssLink(link.href, '订阅链接');
});
}
function addRssLink(url, label = '') {
if (rssLinks.has(url)) return;
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function(response) {
if (response.status === 200 && xmlRegex.test(response.responseText)) {
rssLinks.set(url, label);
updateRssList();
}
},
onerror: function(error) {
console.log(`加载失败: ${url}`, error);
}
});
}
function updateRssList() {
let container = document.getElementById('rss-finder-container');
if (!container) {
container = document.createElement('div');
container.id = 'rss-finder-container';
container.innerHTML = '<h4>可用 RSS 订阅:</h4><ul id="rss-finder-list"></ul>';
document.body.appendChild(container);
setTimeout(() => {
if (container) container.style.display = 'none';
}, 15000);
}
const list = container.querySelector('#rss-finder-list');
list.innerHTML = '';
rssLinks.forEach((label, url) => {
const li = document.createElement('li');
const a = document.createElement('a');
a.href = url;
a.target = '_blank';
a.rel = 'noopener noreferrer';
a.textContent = label ? `[${label}] ${url}` : url;
const copyButton = document.createElement('button');
copyButton.textContent = 'copy';
copyButton.style.transition = 'all 0.3s ease';
copyButton.style.fontSize = '0.8em';
copyButton.style.color = '#666';
copyButton.onclick = function() {
GM_setClipboard(url);
copyButton.textContent = 'success';
copyButton.style.color = 'green';
copyButton.style.fontSize = '1em';
copyButton.style.transform = 'scale(1.2)';
setTimeout(() => {
copyButton.textContent = 'copy';
copyButton.style.color = '#666';
copyButton.style.fontSize = '0.8em';
copyButton.style.transform = 'scale(1)';
}, 3000);
};
li.appendChild(a);
li.appendChild(copyButton);
list.appendChild(li);
});
}
addStyle();
findRssLinks();
})();