Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name Torn Stats
- // @namespace vinkuun.tornStats
- // @author Vinkuun [1791283]
- // @description Hit counter, armoury usage tracker, submission of battle stats to tornstats.com, integration of spies submitted to tornstats.com
- // @include *.torn.com/index.php
- // @include *.tornstats.com/apiHome.php
- // @include *.torn.com/profiles.php?XID=*
- // @include *.torn.com/factions.php?step=your
- // @include *.tornstats.com/apiChainEnter.php
- // @version 6.2.1
- // @grant GM_getResourceText
- // @require http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js
- // @require http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.2/moment.min.js
- // @require http://cdnjs.cloudflare.com/ajax/libs/q.js/1.0.1/q.min.js
- // @require https://raw.githubusercontent.com/xdan/datetimepicker/master/jquery.datetimepicker.js
- // @resource datetimepicker_css https://raw.githubusercontent.com/xdan/datetimepicker/master/jquery.datetimepicker.css
- // ==/UserScript==
- try {
- this.$ = this.jQuery = jQuery.noConflict(true);
- // only run if logged in or if in an iframe
- if (!(window === window.top && $('#player-stats').length === 0)) {
- (function() {
- 'use strict';
- var DATE_FORMAT = 'DD/MM/YY hh:mm:ss a';
- var IS_HTTPS = location.protocol === 'https:';
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Utilities
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- /**
- * Adds CSS to the HEAD of the document
- * @param {string} css
- */
- function addCss(css) {
- var head = document.head,
- style = document.createElement('style');
- style.type = 'text/css';
- style.appendChild(document.createTextNode(css));
- head.appendChild(style);
- }
- /**
- * Creates a button in Torn-style
- * @param {String} text The text of the button
- * @return {jQuery-Object}
- */
- function createButton(text) {
- return $('<div>', {'class': 'btn-wrap silver vinkuun-button'}).append($('<div>', {'class': 'btn', text: text}));
- }
- /**
- * Creates a horizontal delimiter
- * @return {jQuery-Object}
- */
- function createHR() {
- return $('<hr>', { class: 'delimiter-999 m-top10' });
- }
- /**
- * Creates an info ui in torn style
- * @param {string} msg
- * @return {jQuery-Object}
- */
- function createInfoBox(msg) {
- return $('<div class="info-msg-cont border-round m-top10"><div class="info-msg border-round">' +
- '<i class="info-icon"></i><div class="delimiter"><div class="msg right-round">' +
- msg +
- '</div></div>' +
- '</div></div>');
- }
- /**
- * Creates an active infinite progress indicator; call .trigger('activate') or .trigger('deactivate')
- * @return {jQuery-Object}
- */
- function createProgressIndicator() {
- return $('<div class="vinkuun-progressIndicator active"></div>').bind('enable', function() {
- $(this).addClass('active');
- }).bind('disable', function() {
- $(this).removeClass('active');
- });
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Battle Stats Tracker
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- var BattleStatsTracker = (function() {
- function addTracker() {
- var battleStatsBlockTitle = $('div.title:contains("Battle Stats")');
- var battleStatsBlockContent = battleStatsBlockTitle.next('div').find('div');
- // add hr after the stats
- battleStatsBlockContent.append($('<hr>', { style: 'border: 1px solid #ccc; margin: 5px 0' }));
- if (IS_HTTPS) {
- battleStatsBlockContent.append('Battle Stats cannot be submitted when using HTTPS.');
- } else {
- addCss(
- '#vinkuun-tornStats-BattleStats iframe { margin-top: 5px; width: 100%; height: 250px; border: 1px solid #ccc; border-radius: 5px }'
- );
- // add an ID to the battle stats block
- battleStatsBlockTitle.parent().attr('id', 'vinkuun-tornStats-BattleStats');
- var battleStatsIframe = $('<iframe>', {
- src: 'http://www.tornstats.com/apiHome.php',
- text: 'You do not have iframes enabled.'
- });
- var submitButton = createButton('Post your Battle Stats to Torn Stats').css('display', 'block').on('click', function(event) {
- var currentBattleStats = [];
- battleStatsBlockContent.find('ul span.desc').slice(0, 4).each(function() {
- currentBattleStats.push(this.textContent.trim());
- });
- submitButton.addClass('disable');
- // post message to iframe that you want to update stats
- battleStatsIframe[0].contentWindow.postMessage(currentBattleStats, '*');
- });
- // add the iframe and the status element after the battle stats
- battleStatsBlockContent
- .append(submitButton)
- .append(battleStatsIframe);
- // listen for a message from the battle stats iframe
- window.addEventListener('message', function(e) {
- var responseText = '';
- if (typeof e.data === 'string') {
- responseText = e.data;
- } else {
- // build response text
- if (e.data.deltaStrength) {
- responseText += e.data.deltaStrength + ' Strength, ';
- }
- if (e.data.deltaDefense) {
- responseText += e.data.deltaDefense + ' Defense, ';
- }
- if (e.data.deltaDexterity) {
- responseText += e.data.deltaDexterity + ' Dexterity, ';
- }
- if (e.data.deltaSpeed) {
- responseText += e.data.deltaSpeed + ' Speed, ';
- }
- // battle stats changed if the responseText isn't empty
- if (responseText.length !== 0) {
- responseText = 'You gained <b>' + responseText.slice(0, responseText.length - 2) + '</b>';
- } else {
- responseText = 'You <b>gained no stats</b>';
- }
- responseText += ' since your last update ' + e.data.age + '.';
- }
- submitButton.replaceWith($('<span>', {html: responseText}));
- });
- }
- }
- function addSubmissionListener() {
- window.addEventListener('message', function(e) {
- var stats = e.data;
- $.ajax({
- type: 'POST',
- url: 'http://www.tornstats.com/apiUpdate.php',
- dataType: 'json',
- data: {
- strength: stats[0],
- defense: stats[1],
- speed: stats[2],
- dexterity: stats[3]
- },
- success: function(response) {
- window.parent.postMessage(response, '*');
- window.location.reload();
- },
- error: function (xhr, ajaxOptions, thrownError) {
- window.parent.postMessage('An error occured.', '*');
- }
- });
- });
- }
- return {
- addTracker: addTracker,
- addSubmissionListener: addSubmissionListener
- };
- }());
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Spies
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- var Spies = (function() {
- function addLatestInformation() {
- addCss('.profile-container-description { height: auto !important }');
- var id = window.location.href.match(/XID=(\d+)/)[1];
- $('head').append($('<script>', {src: 'http://www.tornstats.com/apiSpy.php?userid=' + id}));
- }
- return {
- addLatestInformation: addLatestInformation
- };
- }());
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Faction log collector
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- var LogCollector = (function() {
- function getEntries(type, offset) {
- return Q($.ajax({
- type: 'post',
- url: 'factions.php',
- data: {
- step: 'mainnews',
- type: type,
- start: offset
- }
- })).then(function(data) {
- return JSON.parse(data).list;
- });
- }
- /**
- * Collects logs for "Main News", "Attacking", "Donations" and "Armoury"
- * @param {Integer} type type of the log (between 1 and 4)
- * @param {Integer} offset skip x logs (x % 10 === 0)
- * @param {moment-Object} startDate
- * @param {moment-Object} endDate
- * @param {function} progressHandler
- * @return {Array} Found logs
- */
- function collect(type, offset, startDate, endDate, progressHandler) {
- return _collect(type, offset, startDate, endDate, progressHandler, [], 0);
- }
- function _collect(type, offset, startDate, endDate, progressHandler, logs, numOfRequests) {
- return Q.Promise(function(resolve) {
- if (numOfRequests === 30) { // wait 10 seconds after 30 requests have been made
- numOfRequests = 0;
- progressHandler('Zzzz ... the script is sleeping now for 10 seconds to avoid a temporary block caused by too many requests per minute.');
- setTimeout(resolve, 10 * 1000);
- } else {
- resolve();
- }
- }).then(function () {
- return getEntries(type, offset).then(function(entries) {
- progressHandler('(current date ' + entries[0].date + ' ' + entries[0].time + ')');
- var stopCollecting = entries.some(function(entry) {
- var date = moment(entry.date + ' ' + entry.time, DATE_FORMAT);
- if (!date.isAfter(startDate)) {
- // stop collecting as the current event happened before the time frame
- return true;
- } else {
- if (!date.isAfter(endDate)) {
- logs.push(entry);
- }
- return false;
- }
- });
- if (!stopCollecting) {
- return _collect(type, offset + 10, startDate, endDate, progressHandler, logs, ++numOfRequests);
- }
- return logs;
- });
- });
- }
- return {
- collect: collect
- };
- }());
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Attack log analysis
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- var AttackLog = (function(LogCollector) {
- function collectData(start, end, statusLabel) {
- return LogCollector.collect(2, 0, moment(start, DATE_FORMAT), moment(end, DATE_FORMAT), function(text) {
- statusLabel.text(text);
- });
- }
- function createUI() {
- var ui = $('<div>', { class: 'm-top10 vinkuun-attacklog' }).appendTo($('#factions'));
- ui.append(createInfoBox('Submit the attack log (displayed in the "Attacking"-tab above) to <a href="http://tornstats.com">Torn Stats</a> to find out what happened during a specific time frame; i.e. this will count the hits of all faction members. The result can be analysed in your faction\'s <a href="http://tornstats.com/chainbase.php?action=view">Chain Base</a>.'));
- if (IS_HTTPS) {
- ui.append(createInfoBox('This feature is not available when using HTTPS.'));
- } else {
- var iframe = $('<iframe>', {src:'http://tornstats.com/apiChainEnter.php', text: 'Please enable iframes to use this feature.'}).hide().appendTo(ui);
- var header = $('<div>', { class: 'title-black top-round m-top10', text: 'Attack log submission' }).appendTo(ui);
- var content = $('<div>', { class: 'cont-gray10 bottom-round' }).appendTo(ui);
- var storedStartDate = localStorage['tornstats.startDate'] || '';
- var storedEndDate = localStorage['tornstats.endDate'] || '';
- var startDateField = $('<input>', {type: 'text', value: storedStartDate});
- var endDateField = $('<input>', {type: 'text', value: storedEndDate});
- startDateField.datetimepicker({format: 'd/m/y h:m:s A'});
- endDateField.datetimepicker({format: 'd/m/y h:m:s A'});
- var statusWrapper = $('<p>');
- var progressIndicator = createProgressIndicator().appendTo(statusWrapper);
- var stepLabel = $('<span>').appendTo(statusWrapper);
- var statusLabel = $('<span>').appendTo(statusWrapper);
- var analyseButton = createButton('Submit to Torn Stats').on('click', function() {
- var $this = $(this);
- if (!$this.hasClass('disable') && isInputValid(startDateField, endDateField)) {
- localStorage['tornstats.startDate'] = startDateField.val();
- localStorage['tornstats.endDate'] = endDateField.val();
- $(this).addClass('disable');
- statusWrapper.show();
- progressIndicator.trigger('enable');
- stepLabel.text('Collecting data...');
- collectData(startDateField.val(), endDateField.val(), statusLabel).then(function(logs) {
- statusLabel.text('');
- stepLabel.text('Transforming collected data...');
- return transformLogs(logs);
- }).then(function(logs) {
- stepLabel.text('Submitting logs to Torn Stats...');
- submitLogs(logs, iframe);
- });
- }
- }).addClass('disable'); // button will be enabled by a message from the iframe
- stepLabel.text('Initializing: loading data from Torn Stats');
- content
- .append($('<label>', {'text': 'Start date'}).append(startDateField))
- .append($('<label>', {'text': 'End date'}).append(endDateField))
- .append(analyseButton)
- .append(statusWrapper);
- // event listener for messages sent from the iframe
- window.addEventListener('message', function(e) {
- progressIndicator.trigger('disable');
- if (e.data.type === 'dataSubmitted' || e.data.type === 'iframeReady') {
- analyseButton.removeClass('disable');
- if (e.data.type === 'dataSubmitted') {
- stepLabel.text(e.data.message);
- } else if (e.data.type === 'iframeReady') {
- iframe.hide();
- stepLabel.text('Ready.');
- }
- } else if (e.data.type === 'loginRequired' || e.data.type === 'noPermission') {
- analyseButton.addClass('disable');
- if (e.data.type === 'loginRequired') {
- iframe.show();
- iframe.css('height', '300px');
- stepLabel.text('You need to log in before you can use this feature.');
- } else {
- stepLabel.text(e.data.message);
- }
- }
- }, false);
- }
- return ui;
- }
- function isInputValid(startDateField, endDateField) {
- var startDate = moment(startDateField.val(), DATE_FORMAT);
- if (!startDate.isValid()) {
- startDateField.addClass('has-error');
- return false;
- } else {
- startDateField.removeClass('has-error');
- }
- var endDate = moment(endDateField.val(), DATE_FORMAT);
- if (!endDate.isValid()) {
- endDateField.addClass('has-error');
- return false;
- } else {
- endDateField.removeClass('has-error');
- }
- if (!startDate.isBefore(endDate)) {
- endDateField.addClass('has-error');
- startDateField.addClass('has-error');
- return false;
- } else {
- startDateField.removeClass('has-error');
- endDateField.removeClass('has-error');
- }
- return true;
- }
- /**
- * Transforms logs into a tornstats-readable format
- * @param {array} logs raw attack logs
- * @return {array} logs in tornstats-format
- */
- function transformLogs(logs) {
- return Q.Promise(function(resolve, reject) {
- var tornstatsLogs = [];
- for (var i = 0; i < logs.length; i++) {
- tornstatsLogs.push(transformLogEntry(logs[i]));
- }
- resolve(tornstatsLogs);
- });
- }
- var ACTION = {
- hospitalized: 0,
- mugged: 1,
- attacked: 2,
- other: 3
- };
- var ACTION_EXTRA = {
- nothing: 0,
- loss: 1,
- stalemate: 2,
- retal: 3
- };
- /**
- * Transforms a log entry from torn-format into a tornstats-format
- * @param {object} entry log entry in torn-format
- * @return {object} log entry in tornstats-format
- */
- function transformLogEntry(entry) {
- var rawEventText = $('<div>').html(entry.info).text().trim();
- var match,
- attacker,
- defender = '',
- action,
- extra = ACTION_EXTRA.nothing,
- bonus = 0;
- if ((match = rawEventText.match(/^Chain #(\d+) achieved thanks to (\S+)!/)) !== null) {
- attacker = match[2];
- bonus = parseInt(match[1], 10);
- action = ACTION.other;
- } else if ((match = rawEventText.match(/^(\S+) (\S+) (\S+)(.*)$/)) !== null) {
- attacker = match[1];
- action = ACTION[match[2]] !== undefined ? ACTION[match[2]] : ACTION.other;
- defender = match[3];
- if (match[4].indexOf('lost') !== -1) {
- extra = ACTION_EXTRA.loss;
- } else if (match[4].indexOf('stalemated') !== -1) {
- extra = ACTION_EXTRA.stalemate;
- } else if (match[4].indexOf('Retaliation') !== -1) {
- extra = ACTION_EXTRA.retal;
- }
- }
- return {
- news: rawEventText,
- date: entry.date,
- time: entry.time,
- timestamp: moment(entry.date + ' ' + entry.time, DATE_FORMAT).unix(),
- attacker: attacker,
- defender: defender,
- action: action,
- extra: extra,
- bonus: bonus
- };
- }
- function submitLogs(logs, iframe) {
- iframe[0].contentWindow.postMessage(logs, '*');
- }
- function prepareIFrame() {
- if ($('legend:contains("Login")').length !== 0) { // iframe required login
- window.parent.postMessage({type: 'loginRequired'}, '*');
- } else if($('div.container:contains("You do not have the proper rank")').length !== 0) {
- window.parent.postMessage({type: 'noPermission', message: $('div.container').text()}, '*');
- } else {
- if ($('p').length == 1) { // form has been submitted => notify embedding window about the status
- window.parent.postMessage({type: 'dataSubmitted', message: $('p').text()}, '*');
- } else { // iframe was loaded
- window.parent.postMessage({type: 'iframeReady'}, '*');
- }
- // submit attack logs if a message was sent to the iframe
- window.addEventListener('message', function(e) {
- var requests = [];
- var result = {
- entered: 0,
- duplicate: 0,
- error: 0
- };
- // split data in chunks of 500 logs
- for (var i = 0, chunkSize = 500; i < e.data.length; i += chunkSize) {
- var chunk = JSON.stringify(e.data.slice(i, i+chunkSize));
- requests.push(Q($.ajax({
- type: 'POST',
- url: '/apiChainEnter2.php',
- contentType: "application/json",
- dataType: 'json',
- processData: false,
- data: chunk
- })).then(function(response) {
- result.entered += response.entered;
- result.duplicate += response.duplicate;
- result.error += response.error;
- }));
- }
- Q.all(requests).done(function() {
- var message = result.entered + ' logs have been entered into the database and ' + result.duplicate + ' logs were already entered! There was an error with ' + result.error + ' logs.';
- window.parent.postMessage({type: 'dataSubmitted', message: message}, '*');
- }, function(error) {
- var message = 'An unexpected error occured while submitting the logs to Torn Stats: ' + JSON.stringify(error);
- window.parent.postMessage({type: 'dataSubmitted', message: message}, '*');
- });
- }, false);
- }
- }
- return {
- createUI: createUI,
- prepareIFrame: prepareIFrame
- }
- }(LogCollector));
- /**
- * Executes the provided watchers after the faction page has been loaded
- * @param {array} watchers
- */
- function watchFactionPage(watchers) {
- var page = $("#faction-main");
- new MutationObserver(function(mutations) {
- mutations.forEach(function(mutation) {
- if (mutation.addedNodes.length === 19) {
- watchers.forEach(function(watcher) {
- watcher(page);
- });
- }
- });
- }).observe(page[0], { childList: true });
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Main
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- var currentPage = window.location.href;
- if (currentPage.indexOf('torn.com/') !== -1) {
- addCss(
- '.vinkuun-button { cursor: pointer }' +
- '.vinkuun-button div.btn { padding: 0 10px 0 7px !important }' +
- '.vinkuun-progressIndicator { border: 4px solid #178FE5; border-color: rgba(127, 129, 131, 0.4) rgba(127, 129, 131, 0.6) rgba(127, 129, 131, 0.8) rgba(127, 129, 131, 1); border-radius: 150px; display: inline-block; width: 8px; height: 8px; margin: 2px; vertical-align: middle }' +
- '.vinkuun-progressIndicator.active { animation: spin 1s infinite linear; border-color: rgba(66, 129, 192, 0.4) rgba(66, 129, 192, 0.6) rgba(66, 129, 192, 0.8) rgba(66, 129, 192, 1) }' +
- '@keyframes spin { 0% { transform: rotate(0deg) } 100% { transform: rotate(360deg) } }' +
- 'input.has-error { border: 1px solid rgba(191, 56, 56, 1) !important; background-color: rgba(255, 210, 210, 1) !important }'
- );
- if (currentPage.indexOf('torn.com/index.php') !== -1) {
- BattleStatsTracker.addTracker();
- } else if (currentPage.indexOf('torn.com/profiles.php') !== -1) {
- Spies.addLatestInformation();
- } else if(currentPage.indexOf('torn.com/factions.php?step=your') !== -1) {
- addCss(
- '.vinkuun-attacklog .hint { border: 1px solid #aaa; border-left: none; border-right: none; padding: 10px; margin-bottom: 5px }' +
- '.vinkuun-attacklog input[type="text"] { padding: 4px 6px; margin: -2px 4px 0; line-height: 14px; border: 1px solid #CCC; border-radius: 5px; vertical-align: middle }' +
- '.vinkuun-attacklog label { margin: 0 5px 0 0; font-weight: bold }' +
- '.vinkuun-attacklog iframe { width: 100%; margin-top: 10px }'
- );
- addCss(GM_getResourceText('datetimepicker_css'));
- watchFactionPage([function(page) {
- page.append(createHR());
- page.append(AttackLog.createUI());
- }]);
- }
- } else {
- if (currentPage.indexOf('tornstats.com/apiHome.php') !== -1) {
- BattleStatsTracker.addSubmissionListener();
- } else if (currentPage.indexOf('tornstats.com/apiChainEnter.php') !== -1) {
- AttackLog.prepareIFrame();
- }
- }
- }());
- } // iframe/logged in
- } catch (err) {
- alert('Torn Stats Userscript encountered an error: ' + err);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement