Final Earth Notifier

Notify when various Final Earth Timers are over and for troop movements.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Final Earth Notifier
// @namespace    http://tampermonkey.net/
// @version      1.1.2
// @description  Notify when various Final Earth Timers are over and for troop movements.
// @author       Natty_Boh
// @include      https://www.finalearth.com/*
// @include      https://finalearth.com/*
// @grant        GM_notification
// @grant        GM.xmlHttpRequest
// @grant        GM.getValue
// @grant        GM.setValue
// @connect      finalearth.com
// ==/UserScript==

'use strict';
setTimeout(getApi, 600);
setTimeout(addButton, 500)

//check for and insert script settings link on HQ page menu bar
function addButton() {
    if (!document.getElementById('script-settings-button') && document.querySelector(".hq_new")) {
        let elem = document.querySelector ( '.hq_new > #banner > .menu_cont' )
        let button = `<span class="hover-span" id="script-settings-button">Script Settings</span>`
        if(elem) {
            elem.insertAdjacentHTML('afterbegin', button);
            const settingsButton = document.getElementById('script-settings-button');
            settingsButton.addEventListener('click', function () {
                showScriptSettings()
            });
        }
    }
    setTimeout(addButton, 500)
}

//insert script settings form over existing content
function showScriptSettings() {
    let elem = document.querySelector ( '.hq_new' )
    elem.innerHTML = "";
    elem.insertAdjacentHTML('beforebegin', html);

    populateExistingSettings()

    const settingsButton = document.getElementById('saveSettingsButton');
        settingsButton.addEventListener('click', function () {
            setSettings()
        });
}

//save settings and redirect to HQ page so normal content will show again
async function setSettings() {
    await GM.setValue("apiKey", document.getElementById('settingKey').value)
    await GM.setValue("warTimerNotif", document.getElementById('warCheckbox').checked)
    await GM.setValue("queueNotif", document.getElementById('trainCheckbox').checked)
    await GM.setValue("reimbNotif", document.getElementById('reimbCheckbox').checked)
    await GM.setValue("eventNotif", document.getElementById('eventCheckbox').checked)
    await GM.setValue("mailNotif", document.getElementById('mailCheckbox').checked)
    await GM.setValue("countryNotif", buildCountryNotifSettings())
    await GM.setValue("userTeam", userTeam);
    await GM.setValue("currLocationNotif", document.getElementById('myLocation').checked)
    await GM.setValue("currLocationCD", document.getElementById('locationCD').value)
    window.location.href = '/HQ';
}

//prefill form with the existing settings
async function populateExistingSettings() {
    document.getElementById('settingKey').value = await GM.getValue("apiKey", "");
    document.getElementById('warCheckbox').checked = await GM.getValue("warTimerNotif", false);
    document.getElementById('trainCheckbox').checked = await GM.getValue("queueNotif", false);
    document.getElementById('reimbCheckbox').checked = await GM.getValue("reimbNotif", false);
    document.getElementById('eventCheckbox').checked = await GM.getValue("eventNotif", false);
    document.getElementById('mailCheckbox').checked = await GM.getValue("mailNotif", false);
    document.getElementById('myLocation').checked = await GM.getValue("currLocationNotif", false);
    document.getElementById('locationCD').value = await GM.getValue("currLocationCD", 5);
    populateCountryNotifSettings()

}

function buildCountryNotifSettings() {
    var setting = {
        countryId: document.getElementById('countrySelector').value,
        axis: document.getElementById('axisCheck').checked,
        allies: document.getElementById('alliesCheck').checked,
        cooldown: document.getElementById('notifCD').value
    };
    return setting;
}

async function populateCountryNotifSettings() {
    var countryNotif = await GM.getValue("countryNotif", {
        countryId: 0,
        axis: false,
        allies: false,
        cooldown: 5
    });
    document.getElementById('countrySelector').value = countryNotif.countryId
    document.getElementById('axisCheck').checked = countryNotif.axis
    document.getElementById('alliesCheck').checked = countryNotif.allies
    document.getElementById('notifCD').value = countryNotif.cooldown
}


//make api call every 30 seconds and call functions to check timer statuses
async function getApi(){
    var api = await GM.getValue("apiKey", "")
    if(api != "") {
        let url = 'http://www.finalearth.com/api/notifications?key=' + api
        GM.xmlHttpRequest({
            method: 'GET',
            url: url,
            onload: function (response) {
                if (response.status == '200') {
                    var json = JSON.parse(response.responseText)
                    if (json.reason === "invalid-api-key") {
                        console.log("Invalid api key, please add correct api key to script")
                    } else {
                        checkSettingsAndCallChecks(json)
                    }
                } else {
                    console.log("Something went wrong")
                }
            },
            onerror: function (error) {
                console.log('Something went wrong, please let Natty_Boh know')
            }
        })
        setTimeout(getApi, 30000);
    } else {
        console.log("No api key provided, please add correct api key to script")
    }
}

//check setting flags and call the relevant checks
async function checkSettingsAndCallChecks(json) {
    if (await GM.getValue("warTimerNotif", false)) {
        await warCheck(json.data.timers.war)
    }
    if (await GM.getValue("queueNotif", false)) {
        await trainingCheck(json.data.timers.statistics, json.data.training.queued.length)
    }
    if (await GM.getValue("reimbNotif", false)) {
       await reimbCheck(json.data.timers.reimbursement)
    }
    if (await GM.getValue("eventNotif", false)) {
        await checkEvent(json.data.unreadEvents)
    }
    if (await GM.getValue("mailNotif", false)) {
        await checkMail(json.data.unreadMails)
    }
    var countryObj = await GM.getValue("countryNotif", {countryId: 0,axis: false,allies: false,cooldown: 5 });
    var monitorLocation = await GM.getValue("currLocationNotif", false)
    if(monitorLocation) {
        getCountry(currentCountry => getWorldAndCheckTroops(countryObj, currentCountry));
    } else if (countryObj) {
        getWorldAndCheckTroops(countryObj, undefined);
    }
  }


async function warCheck(war) {
    var warTimer = await GM.getValue("warTimer", 0);
    if (timerEndedRecently(war, Date.now()/1000) && warTimer != war) {
        GM_notification ( {title: 'War timer up!', text: 'Your Final Earth war timer is over.'} );
        console.log("Notifcation Sent!")
        await GM.setValue("warTimer", war);
    }
}

async function trainingCheck(trainEndTime, trainQueue) {
    var trainingTimer = await GM.getValue("trainingTimer", 0);
    if (timerEndedRecently(trainEndTime, Date.now()/1000) && trainingTimer != trainEndTime && trainQueue == 0) {
        GM_notification ( {title: 'Training queue empty!', text: 'Your Final Earth stat training queue is empty.'} );
        console.log("Notifcation Sent!")
        await GM.setValue("trainingTimer", trainEndTime);
    }
}

async function reimbCheck(reimb) {
    var reimbTimer = await GM.getValue("reimbTimer", 0);
    if (timerEndedRecently(reimb, Date.now()/1000) && reimbTimer != reimb) {
        GM_notification ( {title: 'Reimbursement ready!', text: 'Your Final Earth reimbursement is ready.'} );
        console.log("Notifcation Sent!")
        await GM.setValue("reimbTimer", reimb);
    }
}

async function checkEvent(unread) {
    var getNotified = await GM.getValue("eventNotified", false)
    if (unread != 0 && getNotified == false) {
        GM_notification ( {title: 'New event!', text: 'You have a new event in Final Earth'} );
        console.log("Notifcation Sent!")
        await GM.setValue("eventNotified", true);
    } else if (unread == 0) {
        await GM.setValue("eventNotified", false);
    }
}

async function checkMail(unread) {
    var getMailNotified = await GM.getValue("mailNotified", false)
    if (unread != 0 && getMailNotified == false) {
        GM_notification ( {title: 'New message!', text: 'You have a new message in Final Earth'} );
        console.log("Notifcation Sent!")
        await GM.setValue("mailNotified", true);
    } else if (unread == 0) {
        await GM.setValue("mailNotified", false);
    }
}


//only notify if the timer has ended in the last 60 seconds
function timerEndedRecently(timer, now) {
    if (timer < now && now - timer <= 60) {
        return true
    }
    return false
}

//make world api call and call function to check troops
async function getWorldAndCheckTroops(countrySettings, currentCountry) {
    var api = await GM.getValue("apiKey", "")
    if(countrySettings.countryId != 0) {
        let url = 'http://www.finalearth.com/api/world?key=' + api
        GM.xmlHttpRequest({
            method: 'GET',
            url: url,
            onload:  function (response) {
                if (response.status == '200') {
                    var json = JSON.parse(response.responseText)
                    getCountriesToCheck(json, countrySettings, currentCountry)
                }
            },
            onerror: function (error) {
                console.log('Something went wrong')
            }
        })
    } else {
         getCountriesToCheck(undefined, countrySettings, currentCountry)
    }

}

//check all countries
async function getCountriesToCheck(json, countrySettings, currentCountry) {
    if (json) {
        json.data.forEach(country => processCountry(country, currentCountry ? currentCountry.data.id : -1, countrySettings));
    } else {
        processCountry(currentCountry.data, currentCountry.data.id, countrySettings)
    }
}

//special handling for current country and monitored country
async function processCountry(country, currentCountry, countrySettings) {
    var countryId = countrySettings.countryId
    var cooldown = await GM.getValue("currLocationCD", 5)
    if (countryId != 0 && country.id == countryId) {
        checkTroops(country, countryId, countrySettings.axis, countrySettings.allies, countrySettings.cooldown * 60);
    }
    else if (country.id == currentCountry) {
        if (await GM.getValue("userTeam") == "Allies") {
            checkTroops(country, currentCountry, true, false, cooldown * 60 );
        } else {
          checkTroops(country, currentCountry, false, true, cooldown * 60 );
        }
    } else {
        checkTroops(country, country.id, false, false, countrySettings.cooldown ? countrySettings.cooldown *60 : cooldown * 60);
    }
}

//get old troops and save new value + notify (yes I know this is gross soz I'll fix eventually)
async function checkTroops(data, countryId, axisFlag, alliesFlag, cooldown) {
    var country = JSON.parse(await GM.getValue("country" + countryId, JSON.stringify(countryConstructor(0, 0, 0, 0))))
    var axisCount = data.units.axis;
    var alliesCount = data.units.allies;
    var now = Date.now()/1000

    if(axisFlag && axisCount != country.axisCount && parseInt(now) - country.notifiedTs >= cooldown) {
        var change = data.units.axis - country.axisCount;
        var countryName = data.name
        GM_notification ( {title: 'Unit change detected!', text: 'Axis troops in ' + countryName + ' have changed by ' + change + ' units'} );
        var newCountry = countryConstructor(countryId, axisCount, alliesCount, now)
        await GM.setValue("country" + countryId, JSON.stringify(newCountry))
    }
    if(alliesFlag && alliesCount != country.alliesCount && now - country.notifiedTs >= cooldown) {
        var change2 = data.units.allies - country.alliesCount;
        var countryName2 = data.name
        GM_notification ( {title: 'Unit change detected!', text: 'Allies troops in ' + countryName2 + ' have changed by ' + change2 + ' units'} );
        var newCountry2 = countryConstructor(countryId, axisCount, alliesCount, now)
        await GM.setValue("country" + countryId, JSON.stringify(newCountry2))
    }
    if (!alliesFlag && !axisFlag) {
        var newCountry3 = countryConstructor(countryId, axisCount, alliesCount, now-cooldown)
        await GM.setValue("country" + countryId, JSON.stringify(newCountry3))
    }
}

//build country notification object
function countryConstructor(id, axis ,allies, ts) {
    var country = {
        countryId: id,
        axisCount: axis,
        alliesCount: allies,
        notifiedTs: ts
    }
    return country;
}

//make country api call and return country id
async function getCountry(callback) {
    var api = await GM.getValue("apiKey", "")
    let url = 'http://www.finalearth.com/api/country?key=' + api
        GM.xmlHttpRequest({
            method: 'GET',
            url: url,
            onload: function (response) {
                if (response.status == '200') {
                    var json = JSON.parse(response.responseText)
                    callback(json)
                }
            },
            onerror: function (error) {
                console.log('Something went wrong')
            }
        })
}

let html = `<div class="bigdiv" style="height:100%; overflow-y:auto;"><form id="frm1" style="padding-top: 25px">
      API key: <input type="text" id="settingKey"><br><br>
     <h2>Timer Notification Settings</h2>
      <input type="checkbox" id="warCheckbox"> War timer notifications <br>
      <input type="checkbox" id="trainCheckbox"> Empty training queue notifications <br>
      <input type="checkbox" id="mailCheckbox"> New Mail notifications <br>
      <input type="checkbox" id="eventCheckbox"> New Event notifications <br>
      <input type="checkbox" id="reimbCheckbox"> Reimbursement ready notifications <br>
 <br><br>
      </form>

<h2 style="  text-align: center">Current Location Monitoring</h2> <br>

      <input type="checkbox" id="myLocation"> Notify for enemy unit changes in my current location<br>
<br>  Max Notification Frequency &nbsp;<input type="text" id="locationCD">  &nbsp; Minutes<br>
<p>*Note: You will get notification immediately after a unit count change, this is a 'cooldown' to prevent notification spam if lots of changes occur quickly. </p><br>

<h2 style="  text-align: center">Unit Change Monitoring</h2> <br>

                            Specific country to monitor <select id="countrySelector" name="country">
                            <option value="0">No country monitoring</option>
                            <option value="1">Afghanistan</option>
                            <option value="2">Albania</option>
                            <option value="3">Algeria</option>
                            <option value="209">Andorra</option>
                            <option value="5">Angola</option>
                            <option value="6">Anguilla</option>
                            <option value="7">Argentina</option>
                            <option value="8">Armenia</option>
                            <option value="9">Aruba</option>
                            <option value="10">Australia</option>
                            <option value="11">Austria</option>
                            <option value="12">Azerbaijan</option>
                            <option value="13">Bahamas</option>
                            <option value="14">Bahrain</option>
                            <option value="15">Bangladesh</option>
                            <option value="16">Barbados</option>
                            <option value="17">Belarus</option>
                            <option value="18">Belgium</option>
                            <option value="19">Belize</option>
                            <option value="20">Benin</option>
                            <option value="21">Bermuda</option>
                            <option value="22">Bhutan</option>
                            <option value="23">Bolivia</option>
                            <option value="203">Bosnia</option>
                            <option value="24">Botswana</option>
                            <option value="25">Bouvet Island</option>
                            <option value="26">Brazil</option>
                            <option value="27">Brunei</option>
                            <option value="28">Bulgaria</option>
                            <option value="29">Burkina Faso</option>
                            <option value="30">Burundi</option>
                            <option value="31">Cambodia</option>
                            <option value="32">Cameroon</option>
                            <option value="33">Canada</option>
                            <option value="34">Cape Verde</option>
                            <option value="35">Cayman Islands</option>
                            <option value="204">Central African Republic</option>
                            <option value="36">Chad</option>
                            <option value="37">Chile</option>
                            <option value="38">China</option>
                            <option value="39">Colombia</option>
                            <option value="40">Comoros</option>
                            <option value="41">Costa Rica</option>
                            <option value="42">Croatia</option>
                            <option value="43">Cuba</option>
                            <option value="44">Cyprus</option>
                            <option value="45">Czech Republic</option>
                            <option value="205">Democratic Rep. of Congo</option>
                            <option value="46">Denmark</option>
                            <option value="47">Djibouti</option>
                            <option value="48">Dominica</option>
                            <option value="206">Dominican Republic</option>
                            <option value="207">East Timor</option>
                            <option value="49">Ecuador</option>
                            <option value="50">Egypt</option>
                            <option value="51">El Salvador</option>
                            <option value="52">Equatorial Guinea</option>
                            <option value="53">Eritrea</option>
                            <option value="54">Estonia</option>
                            <option value="55">Ethiopia</option>
                            <option value="56">Falkland Islands</option>
                            <option value="57">Faroe Islands</option>
                            <option value="58">Fiji</option>
                            <option value="59">Finland</option>
                            <option value="60">France</option>
                            <option value="61">Gabon</option>
                            <option value="62">Gambia</option>
                            <option value="63">Georgia</option>
                            <option value="64">Germany</option>
                            <option value="65">Ghana</option>
                            <option value="66">Gibraltar</option>
                            <option value="67">Greece</option>
                            <option value="68">Greenland</option>
                            <option value="69">Grenada</option>
                            <option value="70">Guam</option>
                            <option value="71">Guatemala</option>
                            <option value="72">Guinea</option>
                            <option value="73">Guinea Bissau</option>
                            <option value="74">Guyana</option>
                            <option value="75">Haiti</option>
                            <option value="76">Holy See</option>
                            <option value="77">Honduras</option>
                            <option value="78">Hong Kong</option>
                            <option value="79">Hungary</option>
                            <option value="80">Iceland</option>
                            <option value="81">India</option>
                            <option value="82">Indonesia</option>
                            <option value="83">Iran</option>
                            <option value="84">Iraq</option>
                            <option value="85">Ireland</option>
                            <option value="86">Israel</option>
                            <option value="87">Italy</option>
                            <option value="210">Ivory Coast</option>
                            <option value="88">Jamaica</option>
                            <option value="89">Japan</option>
                            <option value="90">Jordan</option>
                            <option value="221">Karabakh</option>
                            <option value="91">Kazakhstan</option>
                            <option value="92">Kenya</option>
                            <option value="93">Kiribati</option>
                            <option value="222">Kosovo </option>
                            <option value="94">Kuwait</option>
                            <option value="95">Kyrgyzstan</option>
                            <option value="96">Laos</option>
                            <option value="97">Latvia</option>
                            <option value="98">Lebanon</option>
                            <option value="99">Lesotho</option>
                            <option value="100">Liberia</option>
                            <option value="101">Libya</option>
                            <option value="102">Liechtenstein</option>
                            <option value="103">Lithuania</option>
                            <option value="104">Luxembourg</option>
                            <option value="105">Macau</option>
                            <option value="106">Macedonia</option>
                            <option value="107">Madagascar</option>
                            <option value="108">Malawi</option>
                            <option value="109">Malaysia</option>
                            <option value="110">Maldives</option>
                            <option value="111">Mali</option>
                            <option value="112">Malta</option>
                            <option value="113">Marshall Islands</option>
                            <option value="114">Mauritania</option>
                            <option value="115">Mauritius</option>
                            <option value="116">Mayotte</option>
                            <option value="117">Mexico</option>
                            <option value="118">Micronesia</option>
                            <option value="119">Moldova</option>
                            <option value="120">Monaco</option>
                            <option value="121">Mongolia</option>
                            <option value="208">Montenegro</option>
                            <option value="122">Montserrat</option>
                            <option value="123">Morocco</option>
                            <option value="124">Mozambique</option>
                            <option value="125">Myanmar</option>
                            <option value="126">Namibia</option>
                            <option value="127">Nauru</option>
                            <option value="128">Nepal</option>
                            <option value="129">Netherlands</option>
                            <option value="218">New Caledonia</option>
                            <option value="130">New Zealand</option>
                            <option value="131">Nicaragua</option>
                            <option value="132">Niger</option>
                            <option value="133">Nigeria</option>
                            <option value="134">Niue</option>
                            <option value="135">Norfolk Island</option>
                            <option value="136">North Korea</option>
                            <option value="225">Northern Cyprus</option>
                            <option value="137">Norway</option>
                            <option value="138">Oman</option>
                            <option value="139">Pakistan</option>
                            <option value="140">Palau</option>
                            <option value="220">Palestine</option>
                            <option value="141">Panama</option>
                            <option value="142">Papua New Guinea</option>
                            <option value="143">Paraguay</option>
                            <option value="144">Peru</option>
                            <option value="145">Philippines</option>
                            <option value="146">Pitcairn Island</option>
                            <option value="147">Poland</option>
                            <option value="148">Portugal</option>
                            <option value="149">Puerto Rico</option>
                            <option value="150">Qatar</option>
                            <option value="214">Rep. of Congo</option>
                            <option value="151">Reunion</option>
                            <option value="152">Romania</option>
                            <option value="153">Russia</option>
                            <option value="154">Rwanda</option>
                            <option value="155">Saint Helena</option>
                            <option value="156">Saint Lucia</option>
                            <option value="157">Samoa</option>
                            <option value="158">San Marino</option>
                            <option value="159">Saudi Arabia</option>
                            <option value="160">Senegal</option>
                            <option value="200">Serbia</option>
                            <option value="161">Seychelles</option>
                            <option value="162">Sierra Leone</option>
                            <option value="163">Singapore</option>
                            <option value="164">Slovakia</option>
                            <option value="165">Slovenia</option>
                            <option value="217">Solomon Islands</option>
                            <option value="166">Somalia</option>
                            <option value="224">Somaliland</option>
                            <option value="202">South Africa</option>
                            <option value="167">South Korea</option>
                            <option value="219">South Sudan</option>
                            <option value="168">Spain</option>
                            <option value="169">Sri Lanka</option>
                            <option value="170">Sudan</option>
                            <option value="171">Suriname</option>
                            <option value="172">Swaziland</option>
                            <option value="173">Sweden</option>
                            <option value="174">Switzerland</option>
                            <option value="175">Syria</option>
                            <option value="176">Taiwan</option>
                            <option value="177">Tajikistan</option>
                            <option value="178">Tanzania</option>
                            <option value="179">Thailand</option>
                            <option value="180">Togo</option>
                            <option value="181">Tokelau</option>
                            <option value="182">Tonga</option>
                            <option value="216">Trinidad and Tobago</option>
                            <option value="183">Tunisia</option>
                            <option value="184">Turkey</option>
                            <option value="185">Turkmenistan</option>
                            <option value="186">Tuvalu</option>
                            <option value="187">Uganda</option>
                            <option value="188">Ukraine</option>
                            <option value="201">United Arab Emirates</option>
                            <option value="189">United Kingdom</option>
                            <option value="190">United States</option>
                            <option value="191">Uruguay</option>
                            <option value="192">Uzbekistan</option>
                            <option value="193">Vanuatu</option>
                            <option value="194">Venezuela</option>
                            <option value="195">Vietnam</option>
                            <option value="196">Virgin Islands</option>
                            <option value="223">Western Sahara</option>
                            <option value="197">Yemen</option>
                            <option value="198">Zambia</option>
                            <option value="199">Zimbabwe</option>
                    </select><br><br>
      Team(s) unit changes to monitor <input type="checkbox" id="axisCheck">Axis &nbsp;&nbsp;&nbsp;
      <input type="checkbox" id="alliesCheck">Allies<br><br>
      Max Notification Frequency &nbsp;<input type="text" id="notifCD">  &nbsp; Minutes<br>
<p>*Note: You will get notification immediately after a unit count change, this is a 'cooldown' to prevent notification spam if lots of changes occur quickly. </p>

<br><a><button id="saveSettingsButton" class="btn_stl">Save and Close</button></a><br><br>
</div>
`