(function() { const manifestScript = document.querySelector('script[src*="_ssgManifest.js"]'); if (!manifestScript) return; const heartbeat = Object.assign(document.createElement('iframe'), { style: 'display:none' }); document.head.prepend(heartbeat); setInterval(() => heartbeat.src = `${manifestScript.src}?${Date.now()}`, 30000); })();
- 首先,它通过查询选择器
document.querySelector('script[src*="_ssgManifest.js"]')
查找 DOM 中包含 "_ssgManifest.js" 的 script 元素。如果找到这个 script 元素,它将其赋值给manifestScript
变量。 - 如果没有找到符合条件的 script 元素(manifestScript 为 null 或 undefined),则函数立即返回并终止执行。
- 接下来,它创建一个新的 iframe 元素并将其样式设置为 "display:none",这样 iframe 不会在页面上显示。然后将这个 iframe 元素添加到文档的 head 中。
- 函数使用
setInterval
创建一个定时器,每隔 30 秒(30000 毫秒)执行一次。在定时器的回调函数中,它将 iframe 的src
属性设置为 manifestScript 的src
属性加上当前时间戳。这样,每隔 30 秒,iframe 会加载一个新的 URL,其中包含最新的时间戳。
这个自执行函数的主要作用是在客户端上定期重新加载 "_ssgManifest.js" 文件,以确保获取到最新的内容。在使用静态站点生成器(如 Next.js)构建的站点中,这种方法可以在不刷新整个页面的情况下,实时更新内容。
// ==UserScript== // @name ChatGPT HeartBeat // @namespace http://tampermonkey.net/ // @version 0.2.5 // @license GPLv3 // @description USE AT YOUR OWN RISK! // @author https://v2ex.com/t/926890 // @homepage https://v2ex.com/t/926890 // @homepageURL https://v2ex.com/t/926890 // @match https://chat.openai.com // @match https://chat.openai.com/ // @match https://chat.openai.com/c/* // @match https://chat.openai.com/chat // @match https://chat.openai.com/chat/* // @icon https://chat.openai.com/favicon.ico // @require https://greasyfork.org/scripts/395037-monkeyconfig-modern/code/MonkeyConfig%20Modern.js?version=764968 // @run-at document-start // @noframes // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_addStyle // ==/UserScript== /* 需要保持非常久的,可以额外尝试在 chrome://discards 里禁用 `Auto Discardable`, 或者用 https://github.com/WorldLanguages/DoNotDiscard 否则就算保持了 Cookies 有效,Chrome 也有可能自动休眠标签页。 */ (function () { function isWindow(obj) { return obj instanceof Window; } // 防止页面通过监听事件强制刷新 // https://gist.github.com/fuzmish/bd444b1aadc2d22aada7c9b1a6de56ba const rawAddEventListener = EventTarget.prototype.addEventListener; EventTarget.prototype.addEventListener = function (...args) { const [eventName] = args; if ( isWindow(this) && ["focus", "focusin", "visibilitychange"].includes(eventName) ) { return; } return rawAddEventListener.apply(this, args); }; const cfg = new MonkeyConfig({ title: "Configuration", menuCommand: true, params: { refreshInterval: { type: "number", default: 30, }, refreshURL: { type: "text", default: "https://chat.openai.com/_next/static/k9OKjvwgjWES7JT3k-6g9/_ssgManifest.js", }, }, }); function getRefreshURL () { var refreshURL = cfg.get("refreshURL"); // 如果手动配置了 _ssgManifest.js 以外的 URL,就不尝试获取最新的 if (!refreshURL.endsWith("_ssgManifest.js")) { return refreshURL; } // 获取最新的 _ssgManifest.js 链接 // https://v2ex.com/t/926890#r_12897849 const manifestScript = document.querySelector( 'script[src*="_ssgManifest.js"]' ); if (manifestScript) { cfg.set("refreshURL", manifestScript.src); return manifestScript.src; } return refreshURL; }; z const heartbeat = document.createElement("iframe"); heartbeat.style.display = "none"; document.head.prepend(heartbeat); let count = 0; function refresh() { count = 0; heartbeat.src = `${getRefreshURL()}?${Date.now()}`; } setInterval(function () { try { let current = new URL(heartbeat.contentWindow.location.href); let expect = new URL(getRefreshURL()); if ( heartbeat.contentWindow.location.href === '' || heartbeat.contentWindow.location.href === 'about:blank' || current.pathname === expect.pathname || count++ * cfg.get("refreshInterval") >= 2 * 60) { refresh(); } } catch (error) { // https://v2ex.com/t/926890#r_12935587 console.error(error); refresh(); } }, cfg.get("refreshInterval") * 1000); })();
- 定义
isWindow
函数,用于检查给定对象是否是一个 Window 对象。 - 覆盖
EventTarget.prototype.addEventListener
方法,以防止页面通过监听事件强制刷新。这样做是为了阻止聊天网站在某些情况下自动刷新页面。 - 使用 MonkeyConfig 创建一个配置对象,使用户能够自定义刷新间隔和刷新 URL。
- 定义
getRefreshURL
函数,用于获取最新的_ssgManifest.js
链接。如果用户手动配置了其他 URL,将使用用户提供的 URL。 - 创建一个隐藏的 iframe 元素,用于在后台定期刷新聊天网站的内容。
- 定义
refresh
函数,用于更新 iframe 的src
属性以刷新聊天网站内容。 - 使用
setInterval
设置一个定时器,每隔一段时间检查 iframe 内容是否需要刷新。如果需要,调用refresh
函数进行刷新。