HWM_Def_Notifier

Система уведомлений о защитах

Verze ze dne 29. 09. 2015. Zobrazit nejnovější verzi.

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

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

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

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

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name        HWM_Def_Notifier
// @namespace   Рианти
// @description Система уведомлений о защитах
// @include     http://www.heroeswm.ru/*
// @version     1
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_deleteValue
// @grant       GM_xmlhttpRequest
// @grant       GM_openInTab
// @grant       GM_addStyle
// ==/UserScript==

// <b><center><font color="red">Вы уже в заявке, ожидайте начала битвы!</font></center></b>
try {
    var _notificationSound = 'http://hwm.mcdir.ru/sounds/and-a-happy-new-year-sms.mp3';

    var _updateInterval = 1000 * 60 * 1;
    var _defPage = 'http://www.heroeswm.ru/mapwars.php';
    var _defaultSettingRows = 2;
    var _defaultSettingRows2 = 4;
    var _defaultSettings = '{"dn_timeUntilDef0":"15","dn_playersEnrolled0":"0","dn_totalRows0":"0","dn_percLost0":"0","dn_uncloosed0":"0","dn_timeUntilDef1":"5","dn_playersEnrolled1":"0","dn_totalRows1":"0","dn_percLost1":"0","dn_uncloosed1":"0","dn_notify_interval":"600000","dn_rule_modifier":"false","dn_sound_alert":"true"}';
    var _defaultSettings2 = '{"dn_timeUntilDef0":"5","dn_playersEnrolled0":"6","dn_totalRows0":"0","dn_percLost0":"0","dn_uncloosed0":"0","dn_timeUntilDef1":"2","dn_playersEnrolled1":"0","dn_totalRows1":"0","dn_percLost1":"0","dn_uncloosed1":"1","dn_timeUntilDef2":"10","dn_playersEnrolled2":"12","dn_totalRows2":"7","dn_percLost2":"0","dn_uncloosed2":"0","dn_timeUntilDef3":"15","dn_playersEnrolled3":"0","dn_totalRows3":"0","dn_percLost3":"45","dn_uncloosed3":"0","dn_notify_interval":"600000","dn_rule_modifier":"true","dn_sound_alert":"true"}';
    var _tabId = Math.random().toString();
    var _curCharID = document.querySelector('param[name="FlashVars"]').value.split('|')[3]; // will throw error if run on wrong page, thats ok.
    var _gmVars = {
        settingRows: 'dn_settingRows',
        timeSinceLastNotify: 'dn_timeSinceLastNotify',
        timeSinceLastUpdate: 'dn_timeSinceLastUpdate',
        lastActiveTab: 'dn_lastActiveTab',
        notifyCommand: 'dn_notifyCommand',
        bkSign: 'dn_bkSign_' + _curCharID
    }

    var _bkSign = GM_getValue(_gmVars.bkSign, 'null');
    if (_bkSign == 'null') _bkSign = prompt('Вас приветствует мастер настройки скрипта уведомления о защитах!\n\nВведите номер и название своего клана. Сначала номер, через пробел название.\nБудьте внимательны, если введете неправильно, скрипт придется переустанавливать.\n\nПравильный ввод выглядит так:', '823 Ginger Tail');
    if (_bkSign) GM_setValue(_gmVars.bkSign, _bkSign);
    else {
        alert ('Настройка не завершена :(');
        throw {message: 'HWM_Def_notifier: setup failed'};
    }

    // Последующий сегмент кода отвечает за то, чтоб уведомления исходили только из последней активной вкладки, а не случайной.
    if (document.visibilityState == "visible") updateAsActiveTab();

    document.addEventListener("visibilitychange", function(){
        if (document.visibilityState == "visible") updateAsActiveTab();
    });
    
    checkForNotifyCommand();
    
} catch (e) { console.log (e) }

function updateAsActiveTab(){
    GM_setValue(_gmVars.lastActiveTab, _tabId);
}

function checkForNotifyCommand(){
    var temp;
    if ((temp = GM_getValue(_gmVars.notifyCommand, '0')) != '0'){
        if (GM_getValue(_gmVars.lastActiveTab, _tabId) == _tabId){
            GM_setValue(_gmVars.notifyCommand, '0');
            notify(temp);
        } else {
            setTimeout(function(){
                var temp;
                if ((temp = GM_getValue(_gmVars.notifyCommand, '0')) != '0'){
                    GM_setValue(_gmVars.notifyCommand, '0');
                    notify(temp);
                }
            }, 2000);
        }
    }

    setTimeout(checkForNotifyCommand, 1000);
}

function sendNotifyCommand(message){
    GM_setValue(_gmVars.notifyCommand, message);
}
// Конец сегмента. Отслылка уведомлений происходит только посредством вызова функции sendNotifyCommand.

function findActiveDefs(doc){
    var regexp = /<font color=(?:"red"|red)><b>([\d:]*)<\/b><\/font>(.*?)<br><\/td><\/tr>/g;
    var regexp2 = /\d\)/g;
    var page = doc.querySelector('body').innerHTML;
    var match, activeDefs = [];
    var time, playersEnrolled;
    var res, count, unclosed;
    while (match = regexp.exec(page)){
        if (match[2].indexOf(_bkSign) == -1) continue;
        time = match[1];
        playersEnrolled = match[2].split('pl_info').length - 1;
        activeDefs.push({time : time, playersEnrolled : playersEnrolled});
    }
    for(var i = 0; i < activeDefs.length; i++){
        count = 0;
        while(res = regexp2.exec(doc.querySelectorAll('td.wb:nth-child(3)')[i].innerHTML)) count++;
        activeDefs[i].totalRows = count;
        try {
            activeDefs[i].curPercLost = parseInt(document.querySelectorAll('td.wb:nth-child(2)')[i].innerHTML.match(/Захвачен врагом на (\d+)/)[1]);
        } catch (e) {
            activeDefs[i].curPercLost = 0;
        }
        unclosed = 0;
        if ( activeDefs[i].playersEnrolled % 3 > 0 || Math.ceil((activeDefs[i].totalRows * 3 - activeDefs[i].playersEnrolled) / 3) < document.querySelectorAll('td.wb:nth-child(2)')[i].innerHTML.split('[Вступить]').length - 1) unclosed = 1;
        activeDefs[i].unclosed = unclosed;
    }

    var temp = activeDefs.reduce(function(o, v, i) { o[i] = v; return o; }, {});

    return activeDefs;
}

function getCurServerTime(doc){
    var regexp = /([\d:]+), \d+ online&nbsp;/;
    var page = doc.querySelector('body').innerHTML;
    var time = page.match(regexp)[1];
    return time;
}

function minutesDifference(time1, time2){
    var t1 = time1.split(':'), t2 = time2.split(':');
    if(parseInt(t1[0]) == 0) t1[0] = '24'; if(parseInt(t2[0]) == 0) t2[0] = '24';
    var difMin = 60 * (parseInt(t2[0]) - parseInt(t1[0])) + parseInt(t2[1]) - parseInt(t1[1]);
    //console.log('minutesDifference', difMin);
    return difMin;
}

function requestPage (url, onloadHandler){
    console.log('loading: ', url);
    try{
        GM_xmlhttpRequest({
            overrideMimeType: 'text/plain; charset=windows-1251',
            synchronous: false,
            url: url,
            method: "GET",
            onload: function(response){
                onloadHandler(new DOMParser().parseFromString(response.responseText, 'text/html').documentElement);
            },
            onerror: function(){ requestPage (url, onloadHandler) },
            ontimeout: function(){ requestPage (url, onloadHandler) },
            timeout: 5000
        });
    } catch (e) {
        console.log(e);
    }
}

function notifyRules(timeUntil, playersEnrolled, totalRows, percLost, unclosed){
    //console.log('notifyRules', timeUntil, playersEnrolled, totalRows, percLost, unclosed);
    for (var i = 0; i < parseInt(GM_getValue(_gmVars.settingRows, _defaultSettingRows)); i++)
        if ( notifyCase(i, timeUntil, playersEnrolled, totalRows, percLost, unclosed) )
            return true;
    return false;
}

function notifyCase(id, timeUntil, playersEnrolled, totalRows, percLost, unclosed){
    var playersEnrolledReq = parseInt(_settings.getSetting('dn_playersEnrolled' + id));
    if (_settings.getSetting('dn_rule_modifier') == 'true') playersEnrolledReq = Math.max(0, playersEnrolledReq - 3 * Math.floor(percLost / 15));

    if(timeUntil > parseInt(_settings.getSetting('dn_timeUntilDef' + id))) return false;
    if(totalRows * 3 - playersEnrolled <= playersEnrolledReq) return false;
    if(parseInt(_settings.getSetting('dn_totalRows' + id)) != 0 && parseInt(_settings.getSetting('dn_totalRows' + id)) != totalRows) return false;
    if(parseInt(_settings.getSetting('dn_percLost' + id)) > percLost) return false;
    if(parseInt(_settings.getSetting('dn_uncloosed' + id)) == 1 && unclosed == 1) return false;
    return true;
}

function notify(message){
    //console.log(_settings.getSetting('dn_sound_alert'));
    if (_settings.getSetting('dn_sound_alert')) new Audio(_notificationSound).play();
    setTimeout(function(){
        alert(message);
        if(document.location.href.indexOf(_defPage) == -1) GM_openInTab(_defPage);
    }, 100);
}

function main(){
    if (parseInt(_settings.getSetting('dn_notify_interval')) == 0) return;
    if (parseInt(GM_getValue(_gmVars.timeSinceLastNotify, '0')) < new Date().getTime() - parseInt(_settings.getSetting('dn_notify_interval')) && parseInt(GM_getValue(_gmVars.timeSinceLastUpdate, '0')) < new Date().getTime() - _updateInterval){
        requestPage(_defPage, function(doc){
            try{
                //console.log(doc);
                var sTime = getCurServerTime(doc);
                //console.log('time:',sTime );
                var defs = findActiveDefs(doc);
                //console.log('defs:',defs );
                for (var i = 0; i < defs.length; i++){
                    if (notifyRules(minutesDifference(sTime, defs[i].time), defs[i].playersEnrolled, defs[i].totalRows, defs[i].curPercLost, defs[i].unclosed)){
                        var message = 'Уведомление: защита в ' + defs[i].time;
                        GM_setValue(_gmVars.timeSinceLastNotify, new Date().getTime());
                        sendNotifyCommand(message);
                        break;
                    }
                }
                GM_setValue(_gmVars.timeSinceLastUpdate, new Date().getTime());
            } catch (e) {
                console.log(e);
            }
        });
        GM_setValue(_gmVars.timeSinceLastUpdate, new Date().getTime());
    }
    setTimeout(main, Math.max(parseInt(GM_getValue(_gmVars.timeSinceLastUpdate, '0')) - new Date().getTime() + (1 + 0.1 * Math.random()) * _updateInterval, parseInt(GM_getValue(_gmVars.timeSinceLastNotify, '0')) - new Date().getTime() + (1 + 0.1 * Math.random()) * parseInt(_settings.getSetting('dn_notify_interval'))));
    //console.log('checking in: ', Math.max(parseInt(GM_getValue(_gmVars.timeSinceLastUpdate, '0')) - new Date().getTime() + (1 + 0.01 * Math.random()) * _updateInterval, parseInt(GM_getValue(_gmVars.timeSinceLastNotify, '0')) - new Date().getTime() + (1 + 0.01 * Math.random()) * parseInt(_settings.getSetting('dn_notify_interval'))));
}

function addInterface(){
    GM_addStyle('#settingsInner tr{position: relative; left: 6px;} .copyright td{text-align: right;font: 12px "Times New Roman",Times,serif;color: #000;}');
    if (navigator.userAgent.toLowerCase().indexOf('chrome') == -1) GM_addStyle('#settingsInner {border-collapse: collapse;}');
    else GM_addStyle('#settingsInner td {top: 21px!important; right: 0px!important;}');
    var table = document.querySelector('td[align="center"][width="50%"]').parentElement.parentElement;
    var tr = document.createElement('tr');
    tr.innerHTML = '<td></td><td align="middle"><span id="dn_settingBoxControl" style="cursor:pointer; color:blue;">Настройки уведомлений</span></td><td></td>';
    table.appendChild(tr);
    document.getElementById('dn_settingBoxControl').onclick = function(){
        var els = document.getElementsByClassName('dn_settingBox');
        for(var i = 0; i < els.length; i++){
            if(els[i].style.display == 'none') els[i].style.display = '';
            else els[i].style.display = 'none';
        }
        if(els[0].style.display != 'none'){ // Вкл/выкл таймер обновления страницы (взаимодействие со скриптом на странице)
            setTimeout("clearTimeout(Timer)", 0);
        } else {
            setTimeout("Timer=setTimeout('Refresh()', 1000);", 0);
        }
    }
    drawSettingsBox();
}

function drawSettingsBox(drawAgain){
    var table = document.querySelector('td[align="center"][width="50%"]').parentElement.parentElement;
    var t;
    if(t = document.getElementsByClassName('dn_settingBox')[0]) table.removeChild(t);

    var tr = document.createElement('tr');
    tr.className = 'dn_settingBox';
    if (!drawAgain) tr.style.display = 'none';
    var row = settingsRow();
    var inputRows = '<tr align="center"><td colspan="9"><i>Уведомлять если</i></td></tr>' + row;
    for (var i = 1; i < parseInt(GM_getValue(_gmVars.settingRows, _defaultSettingRows)); i++) inputRows += '<tr align="center"><td colspan="9"><i>или</i></td></tr>' + row;
    tr.innerHTML = '<td colspan="3"><form id="dn_settingsForm"><table id="settingsInner" align="center" style="border-width: 1px 1px 1px;border-style: solid solid solid;border-color: #000;width: 85%;">' + inputRows + '<tr style="padding-bottom: 7px;"><td colspan="6" align="left"><input type="button" id="dn_addRule" value="Добавить условие"> <input type="button" id="dn_reduceRule" value="Убрать условие"> <select class="dn_defaults" align="center"><option value="0">Готовые настройки</option><option value="1">Для активного защитника</option><option value="2">Для страхующего дефы</option></select></td><td colspan="3" align="right">Частота уведомлений: <select id="dn_notify_interval"></select></td></tr><tr><td colspan="7" align="left"><input type="checkbox" id="dn_rule_modifier" name="dn_rule_modifier"> <label for="dn_rule_modifier">Уменьшать требования к заполненности на 3 места за каждые 15% потерянного контроля</label></td><td colspan="2" align="right"><input type="checkbox" id="dn_sound_alert" name="dn_sound_alert" checked> <label for="dn_sound_alert">Звук уведомления</label></td></tr><tr class="copyright" style="border-top: 1px solid rgb(0, 0, 0); border-right: medium hidden; border-left: medium hidden; border-bottom: medium hidden;"><td style="position: relative; top: 1px; right: 6px; border-bottom: 0px none;" colspan="10" align="right">2015, © <a href="http://www.heroeswm.ru/pl_info.php?id=712329" target="_blank">Рианти</a></td></tr></table></form></td>';
    table.appendChild(tr);
    fetchValuesToSettings();
    document.getElementById('dn_addRule').onclick = addRow;
    document.getElementById('dn_reduceRule').onclick = reduceRow;
    document.querySelector('select[class="dn_defaults"]').onchange = setDefaults;
}

function settingsRow(){
    function Setting(header, inputType, inputClass){ this.header = header; this.inputType = inputType; this.inputClass = inputClass; };
    var settings = [
        new Setting('Времени до боя меньше', 'select', 'dn_timeUntilDef'),
        new Setting('Свободных мест больше', 'select', 'dn_playersEnrolled'),
        new Setting('Всего дорожек', 'select', 'dn_totalRows'),
        new Setting('Слито процентов', 'select', 'dn_percLost'),
        new Setting('Незакрытые заявки', 'select', 'dn_uncloosed')
    ]
    var output = '<tr>';
    for (var i = 0; i < settings.length; i++){
        output += '<td align="center">' + settings[i].header + '</td><td style="width: 2%;"> </td>';
    }
    output += '</tr><tr>';
    for (i = 0; i < settings.length; i++){
        if (settings[i].inputType != 'select') output += '<td align="center"><input type="' + settings[i].inputType + '" class="' +  settings[i].inputClass + '"></td>';
        else output += '<td align="center"><select class="' +  settings[i].inputClass + '"></td>';
        if (i != settings.length - 1) output += '<td style="width: 2%;"> </td>';
    }
    return output + '</tr><tr><td colspan="9"><hr></td></tr>';
}

function reduceRow(){
    var curRows = parseInt(GM_getValue(_gmVars.settingRows, _defaultSettingRows));
    if (curRows > 1) curRows--;
    GM_setValue(_gmVars.settingRows, curRows);
    drawSettingsBox(1);
    _settings.importAgain();
}

function addRow(){
    var curRows = parseInt(GM_getValue(_gmVars.settingRows, _defaultSettingRows));
    if (curRows < 10) curRows++;
    GM_setValue(_gmVars.settingRows, curRows);
    drawSettingsBox(1);
    _settings.importAgain();
}

function setDefaults(e){
    var selectedDefauls = e.target.value;
    var rows, settings;
    if (selectedDefauls == '0') return;
    else if (selectedDefauls == '1'){
        rows = _defaultSettingRows;
        settings = _defaultSettings;
    } else {
        rows = _defaultSettingRows2;
        settings = _defaultSettings2;
    }
    GM_setValue(_gmVars.settingRows, rows);
    GM_setValue('dn_settings', settings);
    drawSettingsBox(1);
    _settings = new ScriptSettings('dn_settings', 'dn_settingsForm');
    setTimeout( function (){ GM_setValue('dn_settings', settings) }, 300); // Затычка бага, найти баг в будущем.
}

function fetchValuesToSettings(){
    for(var i = 0; i < document.getElementsByClassName('dn_timeUntilDef').length; i++){
        var el = document.getElementsByClassName('dn_timeUntilDef')[i];
        el.id = el.className + i;
        for (var j = 1; j <= 15; j++){
            var newEl = document.createElement('option');
            newEl.value = j;
            newEl.innerHTML = j + ' мин.';
            el.appendChild(newEl);
        }
        var el = document.getElementsByClassName('dn_playersEnrolled')[i];
        el.id = el.className + i;
        for (var j = 0; j <= 20; j++){
            var newEl = document.createElement('option');
            newEl.value = j;
            newEl.innerHTML = j;
            el.appendChild(newEl);
        }
        var el = document.getElementsByClassName('dn_totalRows')[i];
        el.id = el.className + i;
        var newEl = document.createElement('option');
        newEl.value = 0;
        newEl.innerHTML = 'Не важно';
        el.appendChild(newEl);
        for (var j = 3; j <= 6; j++){
            if (j == 6) j++
            var newEl = document.createElement('option');
            newEl.value = j;
            newEl.innerHTML = j;
            el.appendChild(newEl);
        }
        var el = document.getElementsByClassName('dn_percLost')[i];
        el.id = el.className + i;
        for (var j = 0; j <= 50; j += 15){
            var newEl = document.createElement('option');
            newEl.value = j;
            if(j == 0) newEl.innerHTML = 'Не важно';
            else newEl.innerHTML = 'больше ' + j + '%';
            el.appendChild(newEl);
        }
        var el = document.getElementsByClassName('dn_uncloosed')[i];
        el.id = el.className + i;
        var newEl = document.createElement('option');
        newEl.value = 0; newEl.innerHTML = 'Не важно';
        el.appendChild(newEl);
        var newEl = document.createElement('option');
        newEl.value = 1; newEl.innerHTML = 'Да';
        el.appendChild(newEl);
    }
    var el = document.getElementById('dn_notify_interval');
    for (var j = 0; j <= 15; j++){
        var newEl = document.createElement('option');
        newEl.value = j * 1000 * 60;
        if(j == 0) newEl.innerHTML = 'Не уведомлять';
        else newEl.innerHTML = j + ' мин';
        el.appendChild(newEl);
    }
}

function ScriptSettings(settingsKey, formId){
    function save(){
        GM_setValue(_settingsKey, JSON.stringify(_settings));
    }

    function load(){
        var stringSettings = GM_getValue(_settingsKey);
        if (!stringSettings) importFromDocument();
        else _settings = JSON.parse(stringSettings);
    }

    function apply(){
        try{
            var element;
            for(var setting in _settings){
                element = document.getElementById(setting);
                if(element.type == 'checkbox') element.checked = _settings[setting];
                else if (element.type == 'radio') element.selected = _settings[setting];
                else element.value = _settings[setting];
            }
            document.getElementById(_formId).onchange = function(){_self.changed()};
        } catch (e) {
            try{
                document.getElementById(_formId).onchange = function(){_self.changed()};
            } catch (ee){
                // Page without settings
            }
        }
    }

    function importFromDocument(){
        try{
            var element, value, formElements = document.getElementById(_formId).elements;
            for(var i = 0; i < formElements.length; i++){
                element = formElements[i];
                if(element.id != null && element.id != '' && element.type != 'button') {
                    if (element.type == 'checkbox') value = element.checked;
                    else if (element.type == 'radio') value = element.selected;
                    else value = element.value;
                    _settings[element.id] = value;
                }
            }
        } catch ( e ) {
            // not set on page
            _settings = JSON.parse(_defaultSettings);
        }
        save();
    }

    function enableGMvars() {
        if (!this.GM_getValue || typeof this.GM_getValue != 'function') {
            if (typeof(Storage) === "undefined") console.log( 'Local storage must be enabled to use scriptSettings.' );
            else {
                this.GM_getValue = function (key, def) { return localStorage[key] || def; }
                this.GM_setValue = function (key, value) { return localStorage[key] = value; }
                this.GM_deleteValue = function (key) { return delete localStorage[key]; }
            }
        }
    }

    this.changed = function(){
        var element, value;
        for(var setting in _settings){
            element = document.getElementById(setting);
            if(element.type == 'checkbox') value = element.checked;
            else if (element.type == 'radio') value = element.selected;
            else value = element.value;
            _settings[setting] = this.validateValue(element.type, element.id, value);
            if(element.type == 'checkbox') element.checked = _settings[setting];
            else if (element.type == 'radio') element.selected = _settings[setting];
            else element.value = _settings[setting];
        }
        save(); this.onchange();
    }

    this.importAgain = function(){
        apply(); this.forget(); _settings = {}; load();
    }

    this.validateValue = function(elementType, elementId, elementValue){return elementValue};

    this.onchange = function(){};

    this.getSetting = function(setting){
        if(_settings[setting] != null) return _settings[setting];
        throw {message: 'undefined setting requested: ' + setting};
    }

    this.forget = function(){
        GM_deleteValue(_settingsKey);
    }

    var _self = this;
    var _settings = {};
    var _settingsKey = settingsKey;
    var _formId = formId;

    enableGMvars();
    load(); apply();
}

try{
    if (document.location.href.indexOf('mapwars.php') != -1){
        addInterface();
    }
    var _settings = new ScriptSettings('dn_settings', 'dn_settingsForm');
    main();
} catch (e) {
    console.log(e);
}