您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Display the time stared at for GitHub repositories on the repository page.
// ==UserScript== // @name GitHub Repo Star Time // @namespace http://github.com/qbosen/tm-gh-star-time // @version 0.3 // @description Display the time stared at for GitHub repositories on the repository page. // @author qbosen // @match https://github.com/*/* // @connect api.github.com // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_xmlhttpRequest // @license MIT // ==/UserScript== (function () { 'use strict'; let githubToken = GM_getValue('githubToken', ''); // 注册菜单命令,用于设置 Token GM_registerMenuCommand("Setup GitHub Token", function () { const token = prompt("Input GitHub Personal Access Token\n(Permission: 'Metadata' repository permissions (read) and 'Starring' user permissions (read)):", githubToken); if (token !== null) { GM_setValue('githubToken', token); githubToken = token; alert("GitHub Token Saved"); } }); // 清理 缓存 命令 GM_registerMenuCommand("Clean Cache", function () { GM_setValue('starredRepos', '{"repos": [], "timestamp": 0}'); alert("Cache Cleaned"); }); if (!githubToken) { console.warn("请先设置 GitHub Token。"); return; // 没有 Token,不执行后续操作 } // 检查是否 star 过 function checkStarred(owner, repo, callback) { // https://docs.github.com/en/rest/activity/starring?apiVersion=2022-11-28#check-if-a-repository-is-starred-by-the-authenticated-user GM_xmlhttpRequest({ method: "GET", url: `https://api.github.com/user/starred/${owner}/${repo}`, headers: { "Authorization": `token ${githubToken}`, "Accept": "application/vnd.github.v3+json", "X-GitHub-Api-Version": "2022-11-28" }, onload: function (response) { if (response.status === 204) { callback(true); } else if (response.status === 404) { callback(false); } else { console.error(`检查 star 状态失败: ${response.status} ${response.statusText}`); callback(false); } }, onerror: function (error) { console.error("检查 star 状态错误:", error); callback(false); } }); } // 获取用户所有star过的仓库 function getAllStarredRepos(username, callback) { let allRepos = []; let page = 1; const perPage = 100; // 每页 100 个 function fetchPage(page) { GM_xmlhttpRequest({ method: "GET", url: `https://api.github.com/users/${username}/starred?per_page=${perPage}&page=${page}`, headers: { "Authorization": `token ${githubToken}`, "Accept": "application/vnd.github.star+json", "X-GitHub-Api-Version": "2022-11-28", }, onload: function (response) { if (response.status >= 200 && response.status < 300) { try { const repos = JSON.parse(response.responseText); // 只提取需要的字段 const simplifiedRepos = repos.map(repo => ({ full_name: repo.repo.full_name, starred_at: repo.starred_at })); allRepos = allRepos.concat(simplifiedRepos); if (repos.length === perPage) { fetchPage(page + 1); } else { callback(allRepos); } } catch (error) { console.error("解析 JSON 失败:", error); callback(null); } } else { console.error(`获取 star 列表失败: ${response.status} ${response.statusText}`); callback(null); } }, onerror: function (error) { console.error("获取 star 列表错误:", error); callback(null); } }); } fetchPage(page); } const pathParts = window.location.pathname.split('/'); if (pathParts.length < 3) return; const owner = pathParts[1]; const repo = pathParts[2]; const username = document.querySelector('meta[name="user-login"]').content; checkStarred(owner, repo, function (isStarred) { if (isStarred) { let cachedStarredRepos = JSON.parse(GM_getValue('starredRepos', '{"repos": [], "timestamp": 0}')); const now = Date.now(); const oneDay = 24 * 60 * 60 * 1000; if (now - cachedStarredRepos.timestamp > oneDay || cachedStarredRepos.repos.length === 0) { // 修改判断条件 getAllStarredRepos(username, function (repos) { if (repos) { GM_setValue('starredRepos', JSON.stringify({ repos: repos, timestamp: now })); cachedStarredRepos = { repos: repos, timestamp: now }; displayStarTime(cachedStarredRepos, owner, repo); } }); } else { displayStarTime(cachedStarredRepos, owner, repo); } } }); function displayStarTime(cachedStarredRepos, owner, repo) { const fullName = `${owner}/${repo}`; const starredRepo = cachedStarredRepos.repos.find(r => r.full_name === fullName); if (starredRepo) { const starredAt = new Date(starredRepo.starred_at); const timeString = starredAt.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false // 使用 24 小时制 }) const detailsElement = document.querySelector('.BorderGrid .BorderGrid-cell'); if (detailsElement) { const timeElement = document.createElement('div'); timeElement.textContent = `Starred at ${timeString}`; detailsElement.appendChild(timeElement); } else { console.log(`star time: ${timeString}`); } } } })();