shoplifting-watcher

Watch shoplifting status for players.

当前为 2024-01-13 提交的版本,查看 最新版本

// ==UserScript==
// @name         shoplifting-watcher
// @namespace    nodelore.torn.shoplifting-watcher
// @version      0.1
// @description  Watch shoplifting status for players.
// @author       nodelore[2786679]
// @match        https://www.torn.com/*
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function(){
    'use strict';

    if(window.SHOPLIFTING_WATCHER){
        return;
    }
    window.SHOPLIFTING_WATCHER = true;

    // ============================= Configuration ==============================
    let API = "";
    const CONFIG_STORAGE_KEY = "SHOPLIFTING_WATCHER"
    let watcher_config = {
        interval: 60, // second,
        enabled: []
    }
    // ==========================================================================
    let inPDA = false;
    const PDAKey = "###PDA-APIKEY###";
    if(PDAKey.charAt(0) !== "#"){
        inPDA = true;
        if(!API){
            API = PDAKey;
        }
    }
    if(localStorage.getItem(CONFIG_STORAGE_KEY)){
        watcher_config = JSON.parse(localStorage.getItem(CONFIG_STORAGE_KEY));
    }

    const notify = (notification)=>{
        if(inPDA){
            alert(notification);
            return;
        }
        try{
            if (Notification.permission === "granted") {
                new Notification(notification);
            } 
            else if (Notification.permission !== "denied") {
                Notification.requestPermission().then(function (permission) {
                  if (permission === "granted") {
                    new Notification(notification);
                  }
                });
            }
        } catch(e){
        }

    }

    const update_config = ()=>{
        localStorage.setItem(CONFIG_STORAGE_KEY, JSON.stringify(watcher_config));
    }

    const addStyle = ()=>{
        const styles = `
            .dark-mode #shoplifting-body *{
                color: #000;
            }
            #shoplifting-body{
                display: flex;
                position: fixed;
                width: 300px;
                height: auto;
                background: #FFF;
                border-radius: 6px;
                left: 30px;
                top: 30px;
                box-sizing: border-box;
                padding: 15px;
                flex-flow: column nowrap;
                z-index: 1000000;
            }
            #shoplifting-body.hidden{
                display: none !important;
            }

            .shoplifting-title{
                display: flex;
                flex-flow: row nowrap;
                align-items: center;
                margin-bottom: 15px;
            }

            .shoplifting-title div.heading{
                font-size: 16px;
                font-weight: bold;
            }

            .shoplifting-title div.close-btn{
                margin-left: auto;
            }

            .shoplifting-status{
                width: 100%;
                display: flex;
                flex-flow: column nowrap;
                flex: 1;
            }

            .shoplifting-item{
                width: 100%;
                display: flex;
                border-top: 1px solid rgba(1, 1, 1, .1);
                box-sizing: border-box;
                padding: 10px 0;
                align-items: center;
            }

            .shoplifting-item-info{
                display: flex;
                flex-flow: column nowrap;
            }

            .shoplifting-item-name{
                font-weight: bold;
                font-size: 14px;
            }

            .shoplifting-item-detail-name{
                font-size: 13px;
                margin-top: 5px;
            }

            .shoplifting-item-toggle{
                display: flex;
                margin-left: auto;
                cursor: pointer;
                height: 100%;
            }

            .shoplifting-item-toggle div{
                width: 60px;
                height: 30px;
                line-height: 30px;
                box-shadow: 0 0 6px 3px rgba(1, 1, 1, .1);
                text-align: center;
                font-weight: bold;
                border-radius: 3px;
                color: #FFF;
                transition: .15s all ease-in-out;
                opacity: .3;
            }

            .shoplifting-item-toggle div.active{
                opacity: 1 !important;
            }

            .shoplifting-item-toggle div.active{
                opacity: 1;
            }

            .shoplifting-item-toggle div:hover{
                opacity: 1;
            }

            .shoplifting-item-toggle-on{
                background: #82c91e;
            }

            .shoplifting-item-toggle-off{
                background: #E54C19;
            }

            .shoplifting-apiusage{
                display: flex;
                align-items: center;
                border-top: 1px solid rgba(1, 1, 1, .1);
                box-sizing: border-box;
                padding: 10px 0 0 0;
            }

            .shoplifting-apiusage div{
                font-weight: bold;
                font-size: 14px;
            }
            .shoplifting-apiusage input{
                color: rgba(1, 1, 1, .5);
                height: 20px;
                line-height: 20px;
                margin-left: auto;
                width: 120px;
                background: #F2F2F2;
                text-align: center;
                font-weight: bold;
            }

        `;
        const isTampermonkeyEnabled = typeof unsafeWindow !== 'undefined';
        if (isTampermonkeyEnabled){
            GM_addStyle(styles);
        } else {
            let style = document.createElement("style");
            style.type = "text/css";
            style.innerHTML = styles;
            document.head.appendChild(style);
        }
    }

    addStyle();

    let watcher_interval;
    let icon_interval;
    const insert_icon = ()=>{
        if($("ul[class*=status-icons]").length === 0){
            if(!icon_interval){
                icon_interval = setInterval(()=>{
                    insert_icon();
                    icon_interval = null;
                }, 500)
            }
            return;
        }
        if($("ul[class*=status-icons]").find(".shoplifting_watcher").length === 0){
            const icon = $("<li class='shoplifting_watcher' title='Shoplifting Watcher'></li>");
            icon.css({
                "background-image": "url(/images/v2/editor/emoticons.svg)",
                "cursor": "pointer",
                "background-position": "-74px -42px"
            });
            icon.click(function(){
                if($("div#shoplifting-body").hasClass("hidden")){
                    $("div#shoplifting-body").removeClass("hidden");
                } else{
                    $("div#shoplifting-body").addClass("hidden");
                }
                
            })
            $("ul[class*=status-icons]").prepend(icon);
        }
    }

    const update_watcher = ()=>{
        if(!API){
            return;
        }
        let update_flag = false;
        if($("#shoplifting-body .shoplifting-status div.shoplifting-item").length > 0){
            update_flag = true;
        }
        fetch(`https://api.torn.com/torn/?selections=shoplifting&key=${API}`).then((res)=>{
            if(res.ok){
                res.json().then((data)=>{
                    let notification = "";
                    const shoplifting_data = data["shoplifting"];
                    for(let shop_name in shoplifting_data){
                        const shop_status = shoplifting_data[shop_name];
                        if(!update_flag){
                            let status_detail = `<div class="shoplifting-item-detail">`;
                            for(let detail of shop_status){
                                let prefix = ""
                                if(detail["disabled"]){
                                    prefix = "❌";
                                    if(watcher_config.enabled.indexOf(shop_name) !== -1){
                                        if(notification.length > 0){
                                            notification += "\n";
                                        }
                                        notification += `【${shop_name}】【${detail["title"]}】 is disabled`
                                    }
                                } else{
                                    prefix = "✅";
                                }
                                status_detail += `<div class="shoplifting-item-detail-name" data-security="${detail['title']}">${prefix} ${detail["title"]}</div>`;
                            }
                            status_detail += "</div>";
    
                            const shoplifting_item = $(`
                                <div class="shoplifting-item">
                                    <div class="shoplifting-item-info">
                                        <div class="shoplifting-item-name">${shop_name}</div>
                                        ${status_detail}
                                    </div>
                                </div>
                            `);
    
                            let toggleOn = "";
                            let toggleOff = "";
                            if(watcher_config.enabled.indexOf(shop_name) !== -1){
                                toggleOn = " active";
                            } else{
                                toggleOff = " active";
                            }
                            const toggle = $(`
                                <div class="shoplifting-item-toggle" data-shop="${shop_name}">
                                    <div class="shoplifting-item-toggle-on${toggleOn}">ON</div>
                                    <div class="shoplifting-item-toggle-off${toggleOff}">OFF</div>
                                </div>`
                            );
    
                            toggle.click(function(){
                                const shop = $(this).attr("data-shop");
                                if(watcher_config.enabled.indexOf(shop) !== -1){
                                    watcher_config.enabled.pop(shop);
                                    $(`div.shoplifting-item-toggle[data-shop=${shop}] .shoplifting-item-toggle-off`).addClass("active");
                                    $(`div.shoplifting-item-toggle[data-shop=${shop}] .shoplifting-item-toggle-on`).removeClass("active");
                                } else{
                                    watcher_config.enabled.push(shop);
                                    $(`div.shoplifting-item-toggle[data-shop=${shop}] .shoplifting-item-toggle-off`).removeClass("active");
                                    $(`div.shoplifting-item-toggle[data-shop=${shop}] .shoplifting-item-toggle-on`).addClass("active");
                                }
                                update_config();
                            });
    
                            shoplifting_item.append(toggle);
                            $("#shoplifting-body").find(".shoplifting-status").append(shoplifting_item);
                        }
                        else{
                            for(let detail of shop_status){
                                let prefix = ""
                                if(detail["disabled"]){
                                    prefix = "❌";
                                    if(watcher_config.enabled.indexOf(shop_name) !== -1){
                                        if(notification.length > 0){
                                            notification += "\n";
                                        }
                                        notification += `【${shop_name}】【${detail["title"]}】 is disabled`
                                    }
                                } else{
                                    prefix = "✅";
                                }
                                $(`.shoplifting-item-detail-name[data-security="${detail['title']}"]`).text(`${prefix} ${detail["title"]}`)
                            }
                        }

                    }

                    if(notification.length > 0){
                        notify(notification);
                        $(".shoplifting_watcher").attr("title", `Shoplifting Watcher\n${notification}`)
                    }
                });
            }
        });
    }

    const insert_body = ()=>{
        const watcher = $(`<div id="shoplifting-body" class="hidden">
            <div class="shoplifting-status">
            </div>
            <div class="shoplifting-apiusage">
                <div>Query interval: </div>
            </div>
        </div>`);
        
        const input = $(`<input type="number" class="shoplifting-interval-input" step="1" value="${watcher_config.interval}"/>`);
        input.keyup(function(){
            const new_val = parseInt($(this).val());
            if(new_val !== watcher_config.interval){
                watcher_config.interval = new_val;
                if(watcher_interval){
                    clearInterval(watcher_interval);
                    watcher_interval = setInterval(()=>{
                        update_watcher();
                    }, watcher_config.interval*1000)
                }
                update_config();
            }
        })

        watcher.find(".shoplifting-apiusage").append(input);

        let title_content = "Shoplifting watcher";
        if(!API){
            title_content = "No Public API"
        }
        const title = $(`
            <div class="shoplifting-title" title="Click to close">
                <div class="heading">${title_content}</div>
                <div class='close-btn'>❌</div>
            </div>`
        );

        title.click(function(){
            $("div#shoplifting-body").addClass("hidden");
        });

        watcher.prepend(title);
        $(body).append(watcher);

        update_watcher();
        if(!watcher_interval){
            watcher_interval = setInterval(()=>{
                update_watcher();
            }, watcher_config.interval*1000);
        }

        insert_icon();

    }

    insert_body();

})();