您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Notifies the user whenever they have new unread notifications on GitHub.
// ==UserScript== // @name GitHub Notifier // @namespace https://rafaelgssa.gitlab.io/monkey-scripts // @version 5.0.0 // @author rafaelgssa // @description Notifies the user whenever they have new unread notifications on GitHub. // @match https://github.com/* // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js // @require https://gf.zukizuki.org/scripts/405813-monkey-utils/code/Monkey%20Utils.js?version=821710 // @require https://gf.zukizuki.org/scripts/405802-monkey-dom/code/Monkey%20DOM.js?version=821769 // @require https://gf.zukizuki.org/scripts/405935-iwc/code/IWC.js?version=819599 // @run-at document-end // @grant GM.info // @grant GM_info // @noframes // ==/UserScript== /* global DOM, SJ */ (() => { 'use strict'; const scriptId = 'ghn'; const scriptName = GM.info.script.name; const unreadIndicator = '(New) '; let doShowBrowserNotifications = false; let hasChecked = false; let hasUnread = false; /** * Loads the script. */ const load = () => { const indicatorEl = document.querySelector('.mail-status'); if (!indicatorEl) { // User is not logged in. return; } SJ.lock(`${scriptId}_lock_browserNotifications`, onBrowserNotificationsLock); // Locks browser notifications so that only one tab shows them. check(indicatorEl); DOM.observeNode(indicatorEl, { attributes: true }, /** @type {NodeCallback} */ (check)); }; /** * Triggered when browser notifications are locked. */ const onBrowserNotificationsLock = () => { console.log(`[${scriptName}] Locked browser notifications!`); doShowBrowserNotifications = true; }; /** * Checks for unread notifications and notifies the user. * @param {Element} indicatorEl The element that indicates the notification status. */ const check = (indicatorEl) => { const newValue = indicatorEl.classList.contains('unread'); if (hasUnread !== newValue) { hasUnread = newValue; notifyUser(); } hasChecked = true; }; /** * Notifies the user. */ const notifyUser = () => { if (hasUnread) { if (!document.title.startsWith(unreadIndicator)) { document.title = `${unreadIndicator}${document.title}`; } } else if (document.title.startsWith(unreadIndicator)) { document.title = document.title.slice(unreadIndicator.length); } if (doShowBrowserNotifications && hasChecked && hasUnread && document.hidden) { // Only show a browser notification for subsequent unread notifications if the user is away from the tab. showBrowserNotification('You have new unread notifications.'); } }; /** * Shows a browser notification. * @param {string} body The message to show. * @return {Promise<void>} */ const showBrowserNotification = async (body) => { if (Notification.permission !== 'granted') { await Notification.requestPermission(); } if (Notification.permission === 'granted') { new Notification(scriptName, { body }); } }; try { load(); } catch (err) { console.log(`Failed to load ${scriptName}: `, err); } })();