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