shoplifting-watcher

Watch shoplifting status for players.

目前為 2024-01-13 提交的版本,檢視 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==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();

})();