AO3: Badge for Unread Inbox Messages

puts a little notification badge in the menu for unread messages in your AO3 inbox

目前為 2025-04-05 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         AO3: Badge for Unread Inbox Messages
// @namespace    https://greasyfork.org/en/users/906106-escctrl
// @version      2.0
// @description  puts a little notification badge in the menu for unread messages in your AO3 inbox
// @author       escctrl
// @match        https://*.archiveofourown.org/*
// @license      MIT
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js
// @grant        none
// ==/UserScript==
 
/****************** CONFIGURATION ******************/
 
// how often the script will check for unread messages (in hours)
const REFRESH_INTERVAL = 12;
 
// if the badge should show as an icon (true), or as text (false)
const BADGE_ICON = true;
 
// pick a background color for the badge to stand out more, or leave empty quotes ""
const HIGHLIGHT_COLOR = "gold";
 
// if the inbox link in the sidebar should automatically filter to unread messages only
const FILTER_INBOX = false;
 
 
// ****************** NOTE ON LOCAL STORAGE ******************
// For compatibility between userscript managers, this script uses local storage, which is visible from the Developer console.
// If you ever uninstall the script, unfortunately its data can't be automatically deleted.
// If you want to remove the data it sets, (1) visit archiveofourown.org, (2) go into the Developer console,
// (3) look for the Local Storage (4) and delete the entries for "unread_inbox_count" and "unread_inbox_date".
// The script also removes its data if you ever visit AO3 while logged out.
 
 
(function($) {
    'use strict';
 
    // first question: is the user logged in? if not, don't bother with any of this
    const linkDash = $("#greeting p.icon a").attr('href') || "";
    if (linkDash === "") {
        localStorage.removeItem('unread_inbox_count');
        localStorage.removeItem('unread_inbox_date');
        return;
    }
 
    var highlight_css = (HIGHLIGHT_COLOR !== "") ? `#greeting #inboxbadge { background-color: ${HIGHLIGHT_COLOR}; border-radius: .25em; }` : "";
 
    $("head").append(`<style type="text/css"> a#inboxbadge .iconify { width: 1em; height: 1em; display: inline-block; vertical-align: -0.125em; }
        a#inboxbadge { display: block; padding: .25em .75em !important; text-align: center; float: left; margin: 0 1em; line-height: 1.286; height: 1.286em; }
        p.icon a { float: right; } ${highlight_css}</style>`)
        .prepend(`<script src="https://use.fontawesome.com/ed555db3cc.js" />`);
 
    // build a new inbox link (filtered to unread)
    const linkInbox = linkDash + "/inbox?filters[read]=false&filters[replied_to]=all&filters[date]=desc&commit=Filter";
 
    // the fun begins: on a page where we're seeing the unread msgs, we simply set the value
    var page_url = window.location.pathname;
    if (page_url.includes(linkDash)) {
 
        // grab unread msgs # from the sidebar
        var badge = (page_url.includes("/inbox")) ? $("div#dashboard li span.current").html() : $("div#dashboard a[href$='inbox']").html();
        badge = badge.match(/\d+/);
 
        // store the currently seen value with the current date, on every page visit, no questions asked
        localStorage.setItem('unread_inbox_count', badge);
        localStorage.setItem('unread_inbox_date', new Date());
 
        // change sidebar inbox link as well to filtered
        if (FILTER_INBOX) $("div#dashboard a[href$='inbox']").attr('href', linkInbox);
 
        printBadge();
    }
    // on other pages, we check if the stored value is recent enough, otherwise we load it again
    else {
 
        var timeStored = new Date(localStorage.getItem("unread_inbox_date") || '1970'); // the date when the storage was last refreshed
        var timeNow = createDate(0, 0, REFRESH_INTERVAL*-1, 0, 0, 0); // hours before that's max allowed
 
        // if recent enough, simply create the badge
        if (timeStored > timeNow) printBadge();
 
        // if not, we have to start a background load
        else {
            $.get(linkDash, function(response) {
            }).done(function(response) {
 
                // grab the number from within the response
                if ($(response).find("div#dashboard a[href$='inbox']").length > 0) {
                    var badge = $(response).find("div#dashboard a[href$='inbox']").html();
                    badge = badge.match(/\d+/);
 
                    // update the stored data with what we just received
                    localStorage.setItem('unread_inbox_count', badge);
                    localStorage.setItem('unread_inbox_date', new Date());
 
                    printBadge();
                }
                // the response has hit a different page e.g. a CF prompt
                else
                    console.log("[script] Badge for Unread Inbox Messages: ajax error", response);
            }).fail(function(data, textStatus, xhr) {
                //This shows status code eg. 429
                console.log("[script] Badge for Unread Inbox Messages: ajax error", data.status);
            });
        }
    }
 
    // add a little round badge to the user icon in the menu (if there are unread emails)
    // this is called as a function as it needs to run only when the async ajax page load has completed
    function printBadge() {
        const badge = localStorage.getItem('unread_inbox_count');
        const displaytext = (BADGE_ICON) ? `<span class="iconify"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path fill="currentColor"a d="M48 64C21.5 64 0 85.5 0 112c0 15.1 7.1 29.3 19.2 38.4L236.8 313.6c11.4 8.5 27 8.5 38.4 0L492.8 150.4c12.1-9.1 19.2-23.3 19.2-38.4c0-26.5-21.5-48-48-48L48 64zM0 176L0 384c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-208L294.4 339.2c-22.8 17.1-54 17.1-76.8 0L0 176z"/></svg></span>&nbsp;&nbsp;${badge}`
                                         : `Inbox (${badge})`;
        if (badge != "0") $("#greeting p.icon").prepend(`<a id="inboxbadge" href="${linkInbox}" title="You have unread messages in you inbox">${displaytext}</a>`);
    }
 
})(jQuery);
 
// convenience function to be able to pass minus values into a Date, so JS will automatically shift correctly over month/year boundaries
// thanks to Phil on Stackoverflow for the code snippet https://stackoverflow.com/a/37003268
function createDate(secs, mins, hours, days, months, years) {
    var date = new Date();
    date.setFullYear(date.getFullYear() + years);
    date.setMonth(date.getMonth() + months);
    date.setDate(date.getDate() + days);
    date.setHours(date.getHours() + hours);
    date.setMinutes(date.getMinutes() + mins);
    date.setSeconds(date.getSeconds() + secs);
    return date;
}