RSS 订阅链接查找器(扩展版)

打开网页时,查询常用的 RSS 后缀并验证是否可用,在网页右下角显示,可一键复制。添加了一些自用的规则, 如 GitHub commits 和 Twitter (Google News)

// ==UserScript==
// @name         RSS 订阅链接查找器(扩展版)
// @namespace    https://greasyfork.org/users/1171320
// @version      1.06
// @description  打开网页时,查询常用的 RSS 后缀并验证是否可用,在网页右下角显示,可一键复制。添加了一些自用的规则, 如 GitHub commits 和 Twitter (Google News)
// @author       Lama AI 辅助
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @license MIT

// ==/UserScript==

(function() {
    'use strict';
    if (window.self !== window.top) return; // 排除 iframe

    const rssLinks = new Set();
    const possibleRssPaths = [
        '/rss',
        '/feed',
        '/atom.xml',
        '/rss.xml',
        '/feed.xml',
        '/index.xml',
        '/feed.atom',
        '/rss.json',
       '/atom',
       '/index.atom',
       '/index.rss'
        // 可添加其他常见的 RSS 路径
    ];
    const xmlRegex = /<\?xml.*?<rss|<feed/; // 用于检测返回内容是否为 XML/RSS/Atom

     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;
        }
        #rss-finder-container h4{
           margin:0px;
           margin-bottom:5px;
        }
        #rss-finder-list {
            list-style: none;
            padding: 0;
        }
        #rss-finder-list li {
            margin-bottom: 5px;
             display: flex;
            align-items: center;
             font-size: 0.8em;
        }
         #rss-finder-list li a {
            color: blue;
            text-decoration: none;
            margin-right: 5px;
        }
         #rss-finder-list li button {
            font-size: 0.6em;
            padding: 2px 5px;
             margin-left: auto;
            cursor: pointer;
        }
       `);
    }

    function findRssLinks() {
        // 查找 link 标签
        const linkTags = document.querySelectorAll('link[type="application/rss+xml"], link[type="application/atom+xml"]');
        linkTags.forEach(link => {
            if (link.href) {
                addRssLink(link.href);
            }
        });

        // 查找页面 meta 标签
       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);
        });

          // GitHub Actions 仓库
        if (window.location.hostname === 'github.com' && window.location.pathname.split('/').length === 3) {
            const [user, repo] = window.location.pathname.slice(1).split('/');
            addRssLink(`https://github.com/${user}.atom`);
            addRssLink(`https://github.com/${user}/${repo}/commits.atom`);
            addRssLink(`https://github.com/${user}/${repo}/releases.atom`);
        }

        // 尝试从Google News搜索Twitter
        if (window.location.hostname === "twitter.com" && window.location.pathname.startsWith('/')){
            const userName = window.location.pathname.slice(1)
            if(userName)
            addRssLink(`https://news.google.com/rss/search?q=site:twitter.com/${userName}+when:7d`)
         }

        // 查找特定类名的 RSS 链接
        const rssSubscribeLinks = document.querySelectorAll('a.rss-subscribe');
        rssSubscribeLinks.forEach(link => {
             if(link.href){
               addRssLink(link.href);
             }
         })
    }


      function addRssLink(url) {
        if(rssLinks.has(url)) return;

        GM_xmlhttpRequest({
             method: 'GET',
            url: url,
            onload: function(response) {
                if (response.status === 200 && xmlRegex.test(response.responseText)) {
                        rssLinks.add(url);
                        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(url => {
            const li = document.createElement('li');
            const a = document.createElement('a');
            a.href = url;
            a.target = '_blank';
            a.rel = 'noopener noreferrer';
            a.textContent = url;


             const copyButton = document.createElement('button');
             copyButton.textContent = 'copy';
            copyButton.onclick = function() {
                GM_setClipboard(url);
                 alert('success'); // 可以添加复制成功的提示
            };

            li.appendChild(a);
           li.appendChild(copyButton);
            list.appendChild(li);
        });
    }

    addStyle();
     window.onload = findRssLinks;
})();