Grundo's Cafe Book Helper

Show unread books on your pet's book page

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Grundo's Cafe Book Helper
// @namespace    github.com/windupbird144/
// @version      0.9
// @description  Show unread books on your pet's book page
// @author       You
// @match        https://www.grundos.cafe/books_read/?pet_name=*
// @match        https://grundos.cafe/books_read/?pet_name=*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=grundos.cafe
// @grant        none
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js
// ==/UserScript==
/* globals $ */

// Add on-click toggle for booklist
function toggleBooks() {
    $("#books-to-read").toggle();
}

const booksUrl = `https://raw.githubusercontent.com/rowanberryyyy/gc-book-helper/main/books.json`;

/**
 * Analyse the document to find out which books your pet has read
 *
 * @returns string[] title of book your pets has read
 */
const getReadBooks = () =>
    Array.from(document.querySelectorAll(".center > table > tbody > tr:nth-of-type(n+1)")).map(e => e.childNodes[3].childNodes[0].textContent.trim().replace(" (","").toUpperCase())

/**
 * Return a list of books your pet has not read
 * TODO: Use a more efficient algorithm
 *
 * @param {[string, number][]} listOfBooks
 * @param {string[]} booksRead
 *
 * @returns {[string, number][]} the books your pet has not read
 */
const filterReadBooks = (listOfBooks, booksRead) => {
    return listOfBooks.filter(([name,_]) => !booksRead.includes(name.toUpperCase()));
}

// callback to sort books by rarity
const byRarity = ([_, rarity1], [__, rarity2]) => rarity1 - rarity2;

const html = (books) => `
<div class="bookbutton"><p><button id='viewbooks'>Your pet has ${books.length} books left to read!</button></p></div>
<div id="books-to-read" style="display:none">
    <div class="booklist">
    <ul>
            ${books.sort(byRarity).map(([name, rarity]) => `
                <li>
                    <span style="user-select:all">${name}</span> (<b>r${rarity})</b><br>
                    <a href="/market/wizard/?query=${name}" target="_bookSearch"><img width="15px" src="https://s3.us-west-2.amazonaws.com/cdn.grundos.cafe/misc/wiz.png"></a>
                    <a href="/island/tradingpost/browse/?query=${name}" target="_bookSearch"><img width="15px" src="https://s3.us-west-2.amazonaws.com/cdn.grundos.cafe/misc/tp.png"></a>
                    <a href="/safetydeposit/?page=1&category=&type=&query=${name}" target="_bookSearch"><img width="15px" src="https://s3.us-west-2.amazonaws.com/cdn.grundos.cafe/misc/sdb.gif"></a>
                </li>
            `).join("")}
        </ul>
    </div>
</div>`;

// Append CSS to the page
let customCSS = `
.bookbutton {
    margin: auto;
}
.booklist {
    height: 800px;
    overflow: auto;
    line-height: 20px;
}
.booklist ul {
    column-count: 2;
}
.booklist li {
    width: 95%;
}
.booklist li:nth-of-type(2n) {
    background: var(--grid_select);
}
`;

$("<style>").prop("type", "text/css").html(customCSS).appendTo("head");

// Ty Riz for the SW/TP add-on!

async function main() {
    const listOfBooks = await fetch(booksUrl).then(res => res.json());
    const toRead = filterReadBooks(listOfBooks, getReadBooks());
    $(".center > table").before(html(toRead));

    // Attach the toggleBooks function to the button click event
    $("#viewbooks").on("click", toggleBooks);
}

main().then();