Ahab / Npc times

// ==UserScript==
// @name         Npc times
// @namespace    http://tampermonkey.net/
// @version      1.7.3
// @description  try to take over the world!
// @author       Ahab [1735214]
// @include      *www.torn.com/*
// @require      https://raw.githubusercontent.com/lostandconfused/test/master/count.js
// @require      https://gist.githubusercontent.com/BrockA/2625891/raw/9c97aa67ff9c5d56be34a55ad6c18a314e5eb548/waitForKeyElements.js
// @require      https://raw.githubusercontent.com/craftpip/jquery-confirm/master/dist/jquery-confirm.min.js
// @grant        GM_addStyle
// @grant        GM.xmlHttpRequest
// @updateURL    https://openuserjs.org/meta/Ahab/Npc_times.meta.js
// @license      MIT
// ==/UserScript==

var data = {}
var settings = {}

if(localStorage.lootTimes === undefined){
    localStorage.lootTimes = JSON.stringify({})
}
if(localStorage.lootSettings === undefined){
    localStorage.lootSettings = JSON.stringify({'toLoot':['Duke','Leslie','Jimmy','Fernando','Tiny','Scrooge','Easter Bunny'],'soundAlert':['Duke','Leslie','Jimmy','Fernando','Tiny','Scrooge','Easter Bunny'],'min':2,'sec':40,'lootLevel':["4","5"],'showAll':0,'allLevels':0,'audioAlert':1,'timesAttack':1,'audio':'https://www.torn.com/casino/keno/audio/numberwin1.ogg','update':0,'lootState':1,'apiKey':"Add key"})
    settings = JSON.parse(localStorage.lootSettings)
    data = {}
}
else{
    data = JSON.parse(localStorage.lootTimes);
    settings = JSON.parse(localStorage.lootSettings)
}

var loots = ['loot_1','loot_2','loot_3','loot_4','loot_5']
var toLootback = []
var sub = 0
var audioPlayer = document.createElement('audio');
audioPlayer.src = JSON.parse(localStorage.lootSettings)['audio']
audioPlayer.preload = 'auto';

GM_addStyle(`
#npcs{
  border-radius: 0 0 5px 5px;
  line-height: 16px;
  margin-bottom: 10px;
}
#npcs a{
  color: var(--default-blue-color);
}
#npcs td{
  text-align: center;
  line-height: 12px;
  flex: 1;
  padding: 0 9px;
  color: #444;
  color: var(--activity-log-table-text-color);
  font-size: 12px;
  height: 25px;
  vertical-align: middle;
  border-bottom: 0px !important;
}
#npcs tr{
  border-bottom: 0px !important;
}
.svg-icon{
  height: 16px;
  float: right;
  margin: 7px;
  padding-right: 8px;
}
.svg-icon path,
.svg-icon polygon,
.svg-icon rect{
  fill: #ffffff;
}
#error{
  border-radius: 0 0 5px 5px;
  line-height: 16px;
  margin-bottom: 10px;
  padding: 9px;
  text-align: center;
}
.vInput::placeholder {
    padding: 5px;
    min-width: 15ch;
    max-width: 55ch;
}
.vInput[type="text"] {
    box-sizing: border-box;
    padding-left: 5px;
    min-width: 2ch;
    max-width: 55ch;
}
.vCheckbox{
  margin-left:5px;
}
.vLabel{
  vertical-align:text-top;
}
.vInput{
  width:25%;
  margin-left:5px;
  border-radius:5px;
  height:22px;
}
.vButton{
  margin:5px;
  background: transparent linear-gradient(180deg,#CCCCCC 0%,#999999 60%,#666666 100%) 0 0 no-repeat;
  box-sizing: border-box;
  border-radius: 5px;
  font-weight: 700;
  color: #333;
  text-shadow: 0 1px 0 #ffffff66;
  padding: 4px 8px;
  font-size: 14px;
  cursor: pointer;
}
.vButton:hover{
  background: transparent linear-gradient(180deg,#E5E5E5 0%,#BBBBBB 60%,#999999 100%) 0 0 no-repeat;
  color: #333;
}
.vCon{
  padding: 5px;
}
.vSet{
  background-color: #f2f2f2;
  border-radius: 0 0 5px 5px;
}
body[class*=jconfirm-no-scroll-] {
    overflow: hidden !important;
}
.jconfirm {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 99999999;
    font-family: inherit;
    overflow: hidden;
}
.jconfirm .jconfirm-bg {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    -webkit-transition: opacity .4s;
    transition: opacity .4s;
}
.jconfirm .jconfirm-bg {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    -webkit-transition: opacity .4s;
    transition: opacity .4s;
}
.jconfirm .jconfirm-scrollpane {
    -webkit-perspective: 500px;
    perspective: 500px;
    -webkit-perspective-origin: center;
    perspective-origin: center;
    width: 100%;
    height: 100%;
}
.jconfirm .jconfirm-row {
    width: 100%;
}
.jconfirm .jconfirm-cell {
    display: flex;
    justify-content: space-around;
    vertical-align: middle;
}
.jconfirm .jconfirm-holder {
    max-height: 100%;
    padding: 50px 0;
}
.jconfirm .jconfirm-box-container {
    -webkit-transition: -webkit-transform;
    transition: -webkit-transform;
    transition: transform;
    transition: transform, -webkit-transform;
}
.jconfirm .jconfirm-box {
    background: white;
    border-radius: 4px;
    position: relative;
    outline: none;
    padding: 15px 15px 0;
    overflow: hidden;
    margin-left: auto;
    margin-right: auto;
}
.jconfirm .jconfirm-box .jconfirm-buttons button {
    display: inline-block;
    padding: 6px 12px;
    font-size: 14px;
    font-weight: 400;
    line-height: 1.42857143;
    text-align: center;
    white-space: nowrap;
    vertical-align: middle;
    -ms-touch-action: manipulation;
    touch-action: manipulation;
    cursor: pointer;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    border-radius: 4px;
    min-height: 1em;
    -webkit-transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, background 0.1s ease, -webkit-box-shadow 0.1s ease;
    transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, background 0.1s ease, -webkit-box-shadow 0.1s ease;
    transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, background 0.1s ease;
    transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, background 0.1s ease, -webkit-box-shadow 0.1s ease;
    -webkit-tap-highlight-color: transparent;
    border: none;
    background-image: none;
}
.jconfirm .jconfirm-box .jconfirm-buttons {
    padding-bottom: 11px;
    text-align: center;
}
.jconfirm .jconfirm-box .jconfirm-buttons > button {
    margin-bottom: 4px;
    margin-left: 2px;
    margin-right: 2px;
}
.jconfirm .jconfirm-box .jconfirm-buttons button.btn-default {
    background-color: #ecf0f1;
    color: #000;
    text-shadow: none;
    -webkit-transition: background .2s;
    transition: background .2s;
}
.jconfirm .jconfirm-box .jconfirm-buttons button.btn-default:hover {
    background-color: #bdc3c7;
    color: #000;
}
.jconfirm-content {
    text-align: center;
    font-size: 15px;
    color: #777;
    padding-bottom: 10px;
}
.jc-bs3-container{
    width: max-content !important;
}
.jconfirm-bg {
    background-color: #444;
    opacity: .2;
}
`)

function getLoot(){
    if(sub == 0){
        GM.xmlHttpRequest({
            method: "GET",
            headers: {'Cache-Control': 'no-cache'},
            url: "https://www.tornstats.com/api/v1/"+JSON.parse(localStorage.lootSettings)['apiKey']+"/loot",
            onload: function(response){
                if(JSON.parse(response.responseText).status == false){
                    $($('div[class^="content-wrapper"]')[0]).prepend('<div id="timesCont" style="width:'+$('div[class^="content-wrapper"]').css( "width" )+'; margin: 0 auto;"><div id="timesTitle" class="title-black top-round">NPC Loot Times<span id="state"><svg xmlns="http://www.w3.org/2000/svg" class="svg-icon" viewBox="0 0 20 32"><path d="M16 10l-6 6-6-6-4 4 10 10 10-10-4-4z"></path></svg></span></div><div id="error" class="cont-gray">'+JSON.parse(response.responseText).message+'</div><hr id="addedd" class="page-head-delimiter m-top10 m-bottom10"></div>')
                }else{
                    settings['update'] = Date.now()
                    localStorage.lootSettings = JSON.stringify(settings)
                    data = JSON.parse(response.responseText);
                    $.each(data, function(npc){
                        if(!isNaN(npc)){
                            delete Object.assign(data[npc], {['loot_1']: data[npc]['hosp_out'] })['hosp_out'];
                        }
                    })
                    localStorage.lootTimes = JSON.stringify(data)
                    $('div[id^="timesCont"]').remove()
                    cd()
                }
            }
        })
    }
}

function disp(toDisp){
    $.each(toDisp, function(index, npc){
        var id = 0
        var cdt = ""
        $('table[id^="npcs"]')[0].firstChild.insertAdjacentHTML("beforeend",'<tr id="'+data[npc]['name']+'" class="cont-gray"><td class="cont-gray"><a href="https://www.torn.com/profiles.php?XID='+data[npc]['torn_id']+'">'+data[npc]['name']+'</a></td>')
        $.each(loots, function(index, lootLvl){
            if(JSON.parse(localStorage.lootSettings)['allLevels'] == 0){
                if(JSON.parse(localStorage.lootSettings)['lootLevel'].includes(lootLvl.split('_')[1])){
                    id = lootLvl.split('_')[1]
                    cdt = new Date(data[npc][lootLvl]*1000).toLocaleString(Intl.DateTimeFormat().resolvedOptions().locale, {timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone})
                    $('tr[id^="'+data[npc]['name']+'"]')[0].insertAdjacentHTML("beforeend",'<td data-level="'+id+'">Loot '+lootLvl.split('_')[1]+': <div data-countdown="'+cdt+'" style="display: inline;"></div></td>    ')
                }
            }
            else if(JSON.parse(localStorage.lootSettings)['allLevels'] == 1){
                id = lootLvl.split('_')[1]
                cdt = new Date(data[npc][lootLvl]*1000).toLocaleString(Intl.DateTimeFormat().resolvedOptions().locale, {timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone})
                $('tr[id^="'+data[npc]['name']+'"]')[0].insertAdjacentHTML("beforeend",'<td data-level="'+id+'">Loot '+lootLvl.split('_')[1]+': <div data-countdown="'+cdt+'" style="display: inline;"></div></td>    ')
            }
        })
    })
}

function cd(){
    $($('div[class^="content-wrapper"]')[0]).prepend('<div id="timesCont" style="width:'+$('div[class^="content-wrapper"]').css( "width" )+'; margin: 0 auto;"><div id="timesTitle" class="title-black top-round">NPC Loot Times<span id="state"><svg xmlns="http://www.w3.org/2000/svg" class="svg-icon" viewBox="0 0 20 32"><path d="M16 10l-6 6-6-6-4 4 10 10 10-10-4-4z"></path></svg></span></div><table id="npcs" class="cont-gray" style="width:100%"><tbody></tbody></table><hr id="addedd" class="page-head-delimiter m-top10 m-bottom10"></div>')
    var toDisp = []
    if(window.location.href.split("/")[4] == 'mymaps'){
        $('div[id*="timesCont"]').css({'float': 'right'})
    }
    if(window.location.href.split("/")[4] == 'mapeditor'){
        $('div[id*="timesCont"]').css({'float': ''})
    }
    $.each(data, function(npc){
        if(JSON.parse(localStorage.lootSettings)['showAll'] == 0){
            if(!isNaN(npc) && JSON.parse(localStorage.lootSettings)['toLoot'].includes(data[npc]['name'])){
                toDisp.push(npc)
            }
        }
        else if(!isNaN(npc)){
            toDisp.push(npc)
        }
    })
    disp(toDisp)
    $('[data-countdown]').each(function() {
        var $this = $(this), finalDate = $(this).data('countdown');
        $this.countd(finalDate, function(event) {
            $this.html(event.strftime('%H:%M:%S'));
        }).on('finish.countd', function() {
            $(this)[0].parentElement.remove()
            $($('table[id^="npcs"]')[0].children[0].childNodes).each(function() {
                if($(this)[0].children.length == 1){
                    $('tr[id^="'+$(this)[0].id+'"]')[0].insertAdjacentHTML("beforeend",'<td>Loot 5 Ready</td>')
                }
            })
        }).on('update.countd', function(event) {
            if(event.offset.seconds == JSON.parse(localStorage.lootSettings)['sec'] && event.offset.minutes == JSON.parse(localStorage.lootSettings)['min'] && event.offset.hours == 0 && JSON.parse(localStorage.lootSettings)['soundAlert'].includes($(this)[0].parentElement.parentElement.id) && JSON.parse(localStorage.lootSettings)['lootLevel'].includes($(this)[0].parentNode.attributes[0].value) && $('div[class*="travel-agency-travelling"]').length == 0 && $('div[class*="travel-agency-market"]').length == 0 && sub == 0){
                var name = $(this)[0].parentElement.parentElement.id
                var ll = $(this)[0].parentNode.attributes[0].value
                var y = $($(this)[0].parentElement.parentElement).find('a')[0].href.split('=')[1]
                if(JSON.parse(localStorage.lootSettings)['audioAlert'] == 1){
                    audioPlayer.play();
                    //audioPlayer.onended = function() {if(confirm(name+' Ready to attack at level '+ll)) document.location = 'https://www.torn.com/loader.php?sid=attack&user2ID='+y;}
                    audioPlayer.onended = function() {$.confirm({
                        title: false,
                        autoClose: 'Ignore|20000',
                        content: name+' ready to attack at level '+ll,
                        buttons: {
                            Attack: {
                                keys: ['enter'],
                                action: function () {window.open('https://www.torn.com/loader.php?sid=attack&user2ID='+y) }
                            },
                            Ignore: function () {
                            },
                        }
                    });
                  }
                }
                else{
                    //if(confirm(name+' Ready to attack at level '+ll)) document.location = 'https://www.torn.com/loader.php?sid=attack&user2ID='+y;
                    $.confirm({
                        title: false,
                        autoClose: 'Ignore|20000',
                        content: name+' ready to attack at level '+ll,
                        buttons: {
                            Attack: {
                                keys: ['enter'],
                                action: function () {window.open('https://www.torn.com/loader.php?sid=attack&user2ID='+y) }
                            },
                            Ignore: function () {
                            },
                        }
                    });
                }
            }
        });
    });
    $('tr[id^="Duke"]').find('a').attr('title','Rheinmetall MG 3 (Primary)<br>Homemade Pocket Shotgun (Secondary)<br>Madball (Melee)<br>Nail Bomb (Temporary)<br>Classic Fedora<br>Pinstripe Suit Trousers<br>Duster');
    $('tr[id^="Leslie"]').find('a').attr('title','Nock Gun (Primary)<br>Beretta Pico (Secondary)<br>Riding Crop (Melee)<br>Sand (Temporary)<br>Black Oxfords<br>Sweatpants<br>String Vest');
    $('tr[id^="Jimmy"]').find('a').attr('title','Bread Knife (Melee)<br>Concussion Grenade (Temporary)<br>Ripped Jeans<br>Lawyer Business Card<br>Bag of Sherbert<br>Jawbreaker<br>Pixie Sticks');
    $('tr[id^="Fernando"]').find('a').attr('title','Poison Umbrella (Melee)<br>Bandit Mask<br>Feathery Hotel Coupon');
    $('tr[id^="Tiny"]').find('a').attr('title','Sledgehammer (Melee)<br>Energy Cans (Munster, Red Cow, Taurine)<br>Six Pack of Energy Drink<br>Dumbbells<br>Boxing Gloves');
    $('tr[id^="Scrooge"]').find('a').attr('title',"Snow Cannon (Primary)<br>Diamond Icicle (Melee)<br>Snowball (Temporary)<br>Scrooge's Boots<br>Scrooge's Gloves<br>Scrooge's Hat<br>Scrooge's Topcoat<br>Scrooge's Trousers");
    $('tr[id^="Easter Bunny"]').find('a').attr('title','Egg-Propelled Launcher (Primary)<br>Frying Pan (Melee)<br>Bunny Suit<br>Ski Mask<br>Bunny Nose<br>Goodie Bag<br>Rabbit Foot');
    $('div[id^="timesTitle"]').on('click', function(event) {
        icon()
    })
    $('table[id^="npcs"]').find('tr').on('click', function(event) {
        if(JSON.parse(localStorage.lootSettings)['timesAttack'] == 1){
            document.location = 'https://www.torn.com/loader.php?sid=attack&user2ID='+$(this).find('a')[0].href.split('=')[1]
        }else{
            document.location = $(this).find('a')[0].href
        }
    })
    if(JSON.parse(localStorage.lootSettings)['lootState'] == 0){
        icon()
    }
}

function icon(){
    $('table[id*="npcs"]').toggle()
    if($('span[id*="state"]')[0].id == 'state'){
        $('span[id*="state"]')[0].innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" class="svg-icon" viewBox="0 0 14 32"><path d="M4 6l-4 4 6 6-6 6 4 4 10-10L4 6z"></path></svg>'
        $('span[id*="state"]')[0].id = 'state1'
        settings['lootState'] = 0
        localStorage.lootSettings = JSON.stringify(settings)
    }
    else{
        $('span[id*="state"]')[0].innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" class="svg-icon" viewBox="0 0 20 32"><path d="M16 10l-6 6-6-6-4 4 10 10 10-10-4-4z"></path></svg>'
        $('span[id*="state"]')[0].id = 'state'
        settings['lootState'] = 1
        localStorage.lootSettings = JSON.stringify(settings)
    }
}

function addSettings(){
    $('div[class^="preferences-container"]').append("<div class='title-black top-round' id='settingsHead' style='margin-top: 5px;'>NPC times Settings</div>").append("<div id='settingsCont' class='vSet cont-gray'></div>")
    $('#settingsCont').append(`
        <div class="vCon"><label for="apiKey" class="vLabel"> API key</label><input type="text" id="apiKey" class="vInput"/></input></div>
        <div class="vCon"><label for="toLoot" class="vLabel"> NPC/s to display</label><input type="text" id="toLoot" class="vInput"/></input></div>
        <div class="vCon"><label for="soundAlert" class="vLabel"> NPC/s to alert on</label><input type="text" id="soundAlert" class="vInput"/></input></div>
        <div class="vCon"><label for="lootLevel" class="vLabel"> Levels to alert on</label><input type="text" id="lootLevel" class="vInput"/></input></div>
        <div class="vCon"><label for="min" class="vLabel"> Minute to alert on</label><input type="text" id="min" class="vInput"/></input></div>
        <div class="vCon"><label for="sec" class="vLabel"> Second to alert on</label><input type="text" id="sec" class="vInput"/></input></div>
        <div class="vCon"><label for="audio" class="vLabel"> Audio for alert</label><input type="text" id="audio" class="vInput"/></input></div>
        <div class="vCon"><label for="showAll" class="vLabel"> Show all NPCs</label><input type="checkbox" id="showAll" class="vCheckbox"></input></div>
        <div class="vCon"><label for="allLevels" class="vLabel"> Show all loot levels</label><input type="checkbox" id="allLevels" class="vCheckbox"></input></div>
        <div class="vCon"><label for="audioAlert" class="vLabel"> Audio alert</label><input type="checkbox" id="audioAlert" class="vCheckbox"></input></div>
        <div class="vCon"><label for="timesAttack" class="vLabel"> NPC row links to attack page</label><input type="checkbox" id="timesAttack" class="vCheckbox"></input></div>
        <div class="vCon"><input id="sValue" name="sLink" type="submit" class="vButton" value="Submit"></input><input id="resetSettings" name="sLink" type="submit" class="vButton" value="Reset"></input></div>
                                     `)
    $.each($('input[class*="vInput"]'), function(){
        $(this).css('width',JSON.stringify(JSON.parse(localStorage.lootSettings)[$(this)[0].id]).replace(/\[/g, '').replace(/\]/g, '').replace(/"/g, '').length+2+'ch')
        $(this).val(JSON.stringify(JSON.parse(localStorage.lootSettings)[$(this)[0].id]).replace(/\[/g, '').replace(/\]/g, '').replace(/"/g, ''))
    })
    $('input[id="min"],input[id="sec"]').keypress(function (e) {
        if (String.fromCharCode(e.keyCode).match(/[^0-9]/g)) return false;
    })
    $('input[class*="vInput"]').on('input', function(){
        this.style.width=this.value.length+2+'ch'
        if(this.id == 'apiKey' || this.id == 'sec' || this.id == 'min' || this.id == 'audio'){
            if(this.id == 'apiKey' || this.id == 'audio'){
                settings[this.id] = this.value
                localStorage.lootSettings = JSON.stringify(settings)
            }
            else{
                settings[this.id] = parseInt(this.value)
                localStorage.lootSettings = JSON.stringify(settings)
            }
        }
        else if(this.id == 'toLoot' || this.id == 'soundAlert' || this.id == 'lootLevel'){
            settings[this.id] = this.value.split(',')
            localStorage.lootSettings = JSON.stringify(settings)
        }
    })
    $.each($('div[class*="vCon"] input[type*="checkbox"]'), function(){
        if(JSON.parse(localStorage.lootSettings)[$(this)[0].id] == 1){
            $(this).prop('checked', true)
        }
    })
    $('div[class*="vCon"] input[type*="checkbox"]').on('click', function(){
        if($("input[id="+$(this)[0].id+"]:checked").length == 0){
            settings[$(this)[0].id] = 0
            localStorage.lootSettings = JSON.stringify(settings)
        }else{
            settings[$(this)[0].id] = 1
            localStorage.lootSettings = JSON.stringify(settings)
        }
    })
    $('input[id*="sValue"]').on('click', function(){
        if(parseInt(JSON.parse(localStorage.lootSettings)['update'])+600000 < Date.now()){
            $('div[id^="timesCont"]').remove()
            getLoot()
            loop()
        }
        else{
            $('div[id^="timesCont"]').remove()
            cd()
            loop()
        }
    })
    $('input[id*="resetSettings"]').on('click', function(){
        localStorage.lootSettings = JSON.stringify({'toLoot':['Duke','Leslie','Jimmy','Fernando','Tiny','Scrooge','Easter Bunny'],'soundAlert':['Duke','Leslie','Jimmy','Fernando','Tiny','Scrooge','Easter Bunny'],'min':2,'sec':40,'lootLevel':["4","5"],'showAll':0,'allLevels':0,'audioAlert':1,'timesAttack':1,'audio':'https://www.torn.com/casino/keno/audio/numberwin1.ogg','update':0,'lootState':1,'apiKey':"Add key"})
    })
}

if(window.location.href.split('/')[3].split('.')[0] === 'preferences'){
    addSettings()
}

function loop(){
    setInterval(function(){
        getLoot()
    }, 600000);
}

function start(){
    if(parseInt(JSON.parse(localStorage.lootSettings)['update'])+600000 < Date.now()){
        getLoot()
        loop()
    }
    else{
        cd()
        loop()
    }
}

document.addEventListener('visibilitychange', function (event) {
    if (document.hidden) {
        sub = 1
    } else {
        sub = 0
    }
});

waitForKeyElements("#mainContainer", start);