Grundo's Cafe Book Helper

Show unread books on your pet's book page

// ==UserScript==
// @name         Grundo's Cafe Book Helper
// @namespace    github.com/windupbird144/
// @version      0.6
// @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.grundos.cafe/static/images/favicon.66a6c5f11278.ico
// @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+2)")).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="center"><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 = `
.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());
    $(".content").before(html(toRead));

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

main().then();