You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
890 lines
36 KiB
890 lines
36 KiB
// ==UserScript==
|
|
// @name AlwaysWin-ntsmhf
|
|
// @namespace https://git.qhrei.com/steven/alwayswin-ntsmhf/
|
|
// @version 0.1
|
|
// @description Make sure you are positioned where you want to be in the bidding for each county.
|
|
// @author Steven Allen
|
|
// @match https://leads.needtosellmyhousefast.com/*
|
|
// @require https://code.jquery.com/jquery-3.7.1.min.js
|
|
// @require http://crypto.stanford.edu/sjcl/sjcl.js
|
|
// @icon https://www.google.com/s2/favicons?sz=64&domain=needtosellmyhousefast.com
|
|
// @grant none
|
|
// ==/UserScript==
|
|
|
|
'use strict';
|
|
|
|
//Load our settings
|
|
//localStorage.removeItem('alwayswin_settings');
|
|
|
|
console.log("Loading settings");
|
|
var alwaysWinSettingsString = localStorage.getItem('alwayswin_settings');
|
|
//console.log(alwaysWinSettingsString);
|
|
|
|
if(alwaysWinSettingsString == null || alwaysWinSettingsString == undefined) {
|
|
const defaultSettings = {"isReloadEnabled":true, "isAutoLoginEnabled":false, "minSecondsBetweenReloads": 60, "maxSecondsBetweenReloads":300, "isGeneralCapEnabled":false, "generalCap":500, "oversight":[]};
|
|
const defaultSettingsString = JSON.stringify(defaultSettings);
|
|
localStorage.setItem('alwayswin_settings',defaultSettingsString);
|
|
alwaysWinSettingsString = defaultSettingsString;
|
|
}
|
|
|
|
var alwaysWinSettings = JSON.parse(alwaysWinSettingsString);
|
|
//console.log(alwaysWinSettings);
|
|
|
|
////////////
|
|
// Div injection and auto reload functions
|
|
////////////
|
|
function getRandomNumberBetween(min, max) {
|
|
min = Number(min);
|
|
max = Number(max);
|
|
return Math.floor(Math.random() * (max - min) + min);
|
|
}
|
|
|
|
function getCheckedValue(fieldName) {
|
|
switch (fieldName) {
|
|
case "isReloadEnabled":
|
|
return reloadIsEnabled() ? "checked": "";
|
|
case "isAutoLoginEnabled":
|
|
return autoLoginIsEnabled() ? "checked": "";
|
|
case "isGeneralCapEnabled":
|
|
return generalCapIsEnabled() ? "checked" :"";
|
|
default:
|
|
return "";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function addAlwaysWinStyle(css) {
|
|
var head, style;
|
|
head = document.getElementsByTagName('head')[0];
|
|
if (!head) { return; }
|
|
style = document.createElement('style');
|
|
style.type = 'text/css';
|
|
style.innerHTML = css;
|
|
head.appendChild(style);
|
|
}
|
|
|
|
function handleSaveSettings() {
|
|
saveSettings();
|
|
location.reload();
|
|
}
|
|
|
|
function saveSettings() {
|
|
let isReloadEnabledValue = document.querySelector("[name='isReloadEnabled']").checked;
|
|
let isAutoLoginEnabledValue = document.querySelector("[name='isAutoLoginEnabled']").checked;
|
|
let minSecondsBetweenReloadsValue = document.querySelector("[name='minSecondsBetweenReloads']").value;
|
|
let maxSecondsBetweenReloadsValue = document.querySelector("[name='maxSecondsBetweenReloads']").value;
|
|
let isGeneralCapEnabledValue = document.querySelector("[name='isGeneralCapEnabled']").checked;
|
|
let generalCap = document.querySelector("[name='generalCap']").value;
|
|
let capPresidence = document.querySelector("[name='capPresidence']").value;
|
|
|
|
|
|
let oversight = [];
|
|
|
|
var latestSettings = alwaysWinSettings;
|
|
latestSettings.isReloadEnabled = isReloadEnabledValue;
|
|
latestSettings.isAutoLoginEnabled = isAutoLoginEnabledValue;
|
|
latestSettings.minSecondsBetweenReloads = minSecondsBetweenReloadsValue;
|
|
latestSettings.maxSecondsBetweenReloads = maxSecondsBetweenReloadsValue;
|
|
latestSettings.isGeneralCapEnabled = isGeneralCapEnabledValue;
|
|
latestSettings.generalCap = generalCap;
|
|
latestSettings.capPresidence = capPresidence;
|
|
if(latestSettings.oversight == null || latestSettings == undefined) {
|
|
latestSettings.oversight = oversight;
|
|
}
|
|
|
|
const latestSettingsString = JSON.stringify(latestSettings);
|
|
localStorage.setItem('alwayswin_settings',latestSettingsString);
|
|
|
|
alwaysWinSettingsString = latestSettings;
|
|
|
|
return latestSettings;
|
|
}
|
|
|
|
function saveCountyInfoStats(newStats) {
|
|
let existingStats = [];
|
|
//get existing stats if they exist
|
|
let existingStatsString = localStorage.getItem('alwayswin_stats');
|
|
if(existingStatsString != null && existingStatsString !== undefined) {
|
|
existingStats = JSON.parse(existingStatsString);
|
|
}
|
|
const combinedStats = existingStats.concat(newStats)
|
|
const updatedStatsString = JSON.stringify(combinedStats);
|
|
localStorage.setItem('alwayswin_stats',updatedStatsString);
|
|
//console.log(combinedStats);
|
|
}
|
|
|
|
function reload() {
|
|
console.log("Saving settings...");
|
|
let alwaysWinSettings = saveSettings();
|
|
|
|
awLog("Its time to perform a reload.");
|
|
awLog("isEnabled: "+ alwaysWinSettings.isReloadEnabled);
|
|
awLog("secondsBetweenReloads:"+ alwaysWinSettings.minSecondsBetweenReloads +" - "+ alwaysWinSettings.maxSecondsBetweenReloads);
|
|
if(!reloadIsEnabled() ) {
|
|
awLog("Reload is not enabled");
|
|
return;
|
|
}
|
|
awLog("Reload is enabled. Reloading...");
|
|
location.reload();
|
|
}
|
|
|
|
function reloadIsEnabled(){
|
|
var checkedValue = alwaysWinSettings.isReloadEnabled ?? false;// document.querySelector("[name='isReloadEnabled']").checked;
|
|
if(checkedValue == true) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function autoLoginIsEnabled(){
|
|
var checkedValue = alwaysWinSettings.isAutoLoginEnabled ?? false;
|
|
var retVal = (checkedValue == true);
|
|
return retVal;
|
|
}
|
|
|
|
function generalCapIsEnabled () {
|
|
var checkedValue = alwaysWinSettings.isGeneralCapEnabled ?? false;
|
|
var retVal = (checkedValue == true);
|
|
return retVal;
|
|
}
|
|
|
|
function getBidCapForCountyId(countyId) {
|
|
//Get the default bidcap from settings
|
|
var bidCap = alwaysWinSettings.generalCap;
|
|
const countyOversight = alwaysWinSettings.oversight.find(({ id }) => id === countyId);
|
|
if(countyFound(countyOversight)) {
|
|
bidCap = countyOversight.bidCap;
|
|
}
|
|
return Number(bidCap);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
// Always win code to find and take the highest/tie bid
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
function getCountyInfo(countyId){
|
|
var county = document.querySelector('[data-subscription_id="'+ countyId +'"].subscription-status');
|
|
if(county === null) {
|
|
console.log("county "+ countyId +" row not found");
|
|
return;
|
|
}
|
|
let countyRow = county.closest("tr");
|
|
let bidController = {};
|
|
bidController.countyRow = countyRow;
|
|
|
|
const bidIncriment = 25; //This is hard coded into site but could change later.
|
|
let num_bid = Number(countyRow.querySelector('.bid-controller--bid').value);
|
|
let num_tieBid = Number(countyRow.querySelector('.bid-controller').dataset.top_bid);
|
|
let num_floorBid = num_tieBid - bidIncriment;
|
|
let num_winningBid = num_tieBid + bidIncriment;
|
|
let num_clicksToMinWin = 1;
|
|
let winGap = (num_tieBid - num_bid);
|
|
if(winGap < 1) {
|
|
num_clicksToMinWin = (winGap / bidIncriment) + 1;
|
|
} else if(winGap > 0) {
|
|
num_clicksToMinWin = (winGap + bidIncriment) / bidIncriment;
|
|
}
|
|
|
|
let num_bidCap = getBidCapForCountyId(countyId);
|
|
let num_clicksToBidCap = num_clicksToMinWin;
|
|
|
|
bidController.readTimestamp = new Date();
|
|
bidController.isEnabled = county.checked;
|
|
bidController.id = county.dataset.subscription_id;
|
|
bidController.name = county.closest("div").getElementsByTagName('a')[0].innerText;
|
|
bidController.bid = num_bid;
|
|
bidController.tieBid = num_tieBid;
|
|
bidController.floorBid = num_floorBid;
|
|
bidController.winningBid = num_winningBid;
|
|
bidController.status = countyRow.querySelector(".bid-status").innerText;//.trim(); //countyRow.querySelector(".bid-status").dataset.bid_status;
|
|
bidController.isWinning = (num_bid > num_tieBid);
|
|
bidController.isWinningByTooMuch = (num_bid > num_winningBid);
|
|
bidController.clicksToMinWin = num_clicksToMinWin;
|
|
bidController.bidCap = num_bidCap;
|
|
bidController.clicksToBidCap = num_clicksToBidCap;
|
|
bidController.btnSave = countyRow.querySelector('.bid-controller-btn--save');
|
|
bidController.btnBidUp = countyRow.querySelector('.bid-controller-btn--increment');
|
|
bidController.btnBidDown = countyRow.querySelector('.bid-controller-btn--decrement');
|
|
return bidController;
|
|
}
|
|
|
|
function winCounty(countyJson, saveChanges) {
|
|
if(countyJson?.id == undefined) {
|
|
awLog("Unable to find county. So no way to win it.");
|
|
return;
|
|
}
|
|
let countyInfo = countyJson.id + " "+ countyJson.name;
|
|
//console.log(countyJson.isWinning +" = "+ countyJson.tieBid +" > "+ countyJson.bid);
|
|
//console.log(countyJson.isWinningByTooMuch +" = "+ countyJson.bid +" > "+ countyJson.winningBid);
|
|
//console.log("clicksToMinWin: "+ countyJson.clicksToMinWin);
|
|
//console.log(countyJson);
|
|
if(countyJson.clicksToMinWin == 0) {
|
|
//if we made it here no changes were needed to win by just the right amount
|
|
//Nothing else to do to win optimally
|
|
//console.log(countyInfo +" is already winning by just the right amount");
|
|
awLog(countyInfo +" is already winning by just the right amount");
|
|
} else if(countyJson.clicksToMinWin > 0) {
|
|
//console.log(countyInfo +" is currently loosing. Increasing bid now");
|
|
awLog(countyInfo +" is currently loosing by "+countyJson.clicksToMinWin+" Increasing bid now");
|
|
for(var i = 0; i < countyJson.clicksToMinWin; i++) {
|
|
//console.log("calling btnBidUp.click() for "+ countyInfo);
|
|
countyJson.btnBidUp.click();
|
|
}
|
|
if(saveChanges) {
|
|
//console.log("calling btnSave.click() for "+ countyInfo);
|
|
countyJson.btnSave.click();
|
|
}
|
|
} else if (countyJson.clicksToMinWin < 0) {
|
|
//console.log(countyInfo +" is winning by too much. Decreasing bid now");
|
|
awLog(countyInfo +" is winning by too much. Decreasing bid now");
|
|
let clicksRemaining = Math.abs(countyJson.clicksToMinWin);
|
|
while(clicksRemaining > 0) {
|
|
//console.log("calling btnBidDown.click() for "+ countyInfo);
|
|
countyJson.btnBidDown.click();
|
|
clicksRemaining--;
|
|
}
|
|
if(saveChanges) {
|
|
//console.log("calling btnSave.click() for "+ countyInfo);
|
|
countyJson.btnSave.click();
|
|
}
|
|
}
|
|
}
|
|
|
|
function tieCounty(countyJson, saveChanges) {
|
|
//TODO: implemnt me
|
|
let countyInfo = countyJson.id + " "+ countyJson.name;
|
|
awLog(countyInfo +" tieCounty code not yet implemented");
|
|
}
|
|
|
|
function watchCounty(countyJson, saveChanges) {
|
|
//TODO: implemnt me
|
|
let countyInfo = countyJson.id + " "+ countyJson.name;
|
|
awLog(countyInfo +" watchCounty code not yet implemented");
|
|
}
|
|
|
|
function ignoreCounty(countyJson, saveChanges) {
|
|
//TODO: implemnt me
|
|
let countyInfo = countyJson.id + " "+ countyJson.name;
|
|
awLog(countyInfo +" ignoreCounty code not yet implemented");
|
|
}
|
|
|
|
function awLog(message) {
|
|
console.log(message);
|
|
const currentContent = document.getElementById("awLog").innerHTML;
|
|
document.getElementById("awLog").innerHTML = currentContent +""+ message +"<br/>";
|
|
}
|
|
|
|
function encryptAndStore(encryptionKey, clearText) {
|
|
return JSON.stringify(sjcl.encrypt(encryptionKey, clearText));
|
|
}
|
|
|
|
function unStoreAndDecrypt (encryptionKey, jsonObj) {
|
|
return sjcl.decrypt(encryptionKey, JSON.parse(jsonObj));
|
|
}
|
|
|
|
function loginIfNeeded(){
|
|
var div_signin = document.querySelector('.signin-box');
|
|
var loginIsNeeded = false;
|
|
if(div_signin != undefined && div_signin != null) {
|
|
loginIsNeeded = true;
|
|
|
|
if(!alwaysWinSettings.isAutoLoginEnabled) {
|
|
console.log("AutoLogin is turned off.");
|
|
awLog("AutoLogin is turned off.");
|
|
return loginIsNeeded;
|
|
}
|
|
console.log("A signin dialog exists");
|
|
var txt_email = div_signin.querySelector("[id='email']");
|
|
var txt_password = div_signin.querySelector("[id='inputPassword']");
|
|
var btn_signin = div_signin.querySelector('.btn-signin'); //btn btn-primary btn-block btn-signin
|
|
|
|
var encKey = alwaysWinSettings.encKey ?? "";
|
|
var ntsmhfUser = alwaysWinSettings.ntsmhfUser ?? "";
|
|
var ntsmhfPass = alwaysWinSettings.ntsmhfPass ?? "";
|
|
if (!encKey) {
|
|
encKey = prompt('Script key not set for ' + location.hostname + '. Please enter a random string:','');
|
|
alwaysWinSettings.encKey = encKey;
|
|
ntsmhfUser = "";
|
|
ntsmhfPass = "";
|
|
}
|
|
|
|
if(ntsmhfUser) {
|
|
ntsmhfUser = unStoreAndDecrypt(encKey, ntsmhfUser);
|
|
} else {
|
|
ntsmhfUser = prompt('UserName is not set for '+ location.hostname +'. Enter it now:','');
|
|
alwaysWinSettings.ntsmhfUser = encryptAndStore(encKey, ntsmhfUser);
|
|
}
|
|
|
|
if(ntsmhfPass) {
|
|
ntsmhfPass = unStoreAndDecrypt(encKey, ntsmhfPass);
|
|
} else {
|
|
ntsmhfPass = prompt('Password is not set for '+ location.hostname +'. Enter it now:','');
|
|
alwaysWinSettings.ntsmhfPass = encryptAndStore(encKey, ntsmhfPass);
|
|
}
|
|
|
|
const latestSettingsString = JSON.stringify(alwaysWinSettings);
|
|
localStorage.setItem('alwayswin_settings',latestSettingsString);
|
|
//////
|
|
|
|
txt_email.value = ntsmhfUser;
|
|
txt_password.value = ntsmhfPass;
|
|
btn_signin.click();
|
|
}
|
|
return loginIsNeeded;
|
|
}
|
|
|
|
function renderOption(value, selectedValue, displayValue) {
|
|
if(displayValue == null || displayValue===undefined) {
|
|
displayValue = value;
|
|
}
|
|
var optionHtml = '<option value="'+ value +'"';
|
|
if(value == selectedValue) {
|
|
optionHtml += ' selected';
|
|
}
|
|
optionHtml += '>'+ displayValue +'</option>';
|
|
return optionHtml;
|
|
}
|
|
|
|
|
|
const Presidence = Object.freeze({
|
|
General: "general",
|
|
Row: "row",
|
|
Highest: "highest",
|
|
Lowest: "lowest"
|
|
})
|
|
|
|
const AppliesTo = Object.freeze({
|
|
All: "all",
|
|
None: "none",
|
|
Capped: "capped",
|
|
Uncapped: "uncapped"
|
|
})
|
|
|
|
const Oversight = Object.freeze({
|
|
Win: "win",
|
|
Tie: "tie",
|
|
Watch: "watch",
|
|
Ignore: "ignore"
|
|
})
|
|
|
|
const Capsource = Object.freeze({
|
|
Row: "row",
|
|
General: "general"
|
|
})
|
|
|
|
const CapTrigger = Object.freeze({
|
|
None: "None",
|
|
General: "General",
|
|
Row: "Row"
|
|
})
|
|
|
|
function getTrueCap(isGeneralCapEnabled, isRowCapIsEnabled, presidenceValue, generalCapValue, rowCapValue){
|
|
|
|
//Our response object
|
|
function trueCap(trigger, value) {
|
|
let response = {};
|
|
response.trigger = trigger;
|
|
response.value = value;
|
|
return response;
|
|
}
|
|
|
|
trueCap.trigger = CapTrigger.None; //Used to identify which cap the trueCap value was derived from.
|
|
trueCap.value = null; //The actual calculated true cap
|
|
|
|
//Do some error handling to start off
|
|
if(isGeneralCapEnabled == true && generalCapValue == null) {
|
|
if(verboseLevel >= 1) {
|
|
console.warn("GeneralCap is enabled but the GeneralCapValue is not set - Acting as if GeneralCap was not enabled.");
|
|
}
|
|
isGeneralCapEnabled = false;
|
|
}
|
|
|
|
if(isRowCapIsEnabled == true && rowCapValue == null) {
|
|
if(verboseLevel >= 1) {
|
|
console.warn("RowCap is enabled but the RowCapValue is not set - Acting as if RowCap was not enabled.");
|
|
}
|
|
isRowCapIsEnabled = false;
|
|
}
|
|
|
|
//Now our actual logic begins
|
|
if(isGeneralCapEnabled == false && isRowCapIsEnabled == false) {
|
|
return trueCap(CapTrigger.None,null); //No cap available to return
|
|
}
|
|
|
|
|
|
if(isRowCapIsEnabled == true && rowCapValue == null) {
|
|
//console.warn("RowCap is enabled but the RowCapValue is not set");
|
|
return trueCap(CapTrigger.None,null); //No cap available to return
|
|
}
|
|
|
|
if(isGeneralCapEnabled == true) {
|
|
switch (presidenceValue) {
|
|
case Presidence.General:
|
|
//General wins
|
|
return trueCap(CapTrigger.General, generalCapValue);
|
|
|
|
|
|
case Presidence.Row:
|
|
if(isRowCapIsEnabled == true) {
|
|
//Row wins
|
|
return trueCap(CapTrigger.Row, rowCapValue);
|
|
}
|
|
return trueCap(CapTrigger.General, generalCapValue);
|
|
|
|
case Presidence.Highest:
|
|
if(isRowCapIsEnabled == true) {
|
|
//Figure out which is higher
|
|
if(rowCapValue > generalCapValue) {
|
|
//Row wins
|
|
return trueCap(CapTrigger.Row, rowCapValue);
|
|
}
|
|
}
|
|
return trueCap(CapTrigger.General, generalCapValue);;
|
|
|
|
case Presidence.Lowest:
|
|
if(isRowCapIsEnabled == true) {
|
|
if(rowCapValue < generalCapValue) {
|
|
//Row wins
|
|
return trueCap(CapTrigger.Row, rowCapValue);
|
|
}
|
|
}
|
|
return trueCap(CapTrigger.General, generalCapValue);;
|
|
|
|
default:
|
|
return trueCap(CapTrigger.General, generalCapValue);;
|
|
}
|
|
|
|
return trueCap(CapTrigger.General, generalCapValue);;
|
|
}
|
|
|
|
//If we made it here then generalCap is not enabled but the row cap may still be enabled
|
|
if(isRowCapIsEnabled == true) {
|
|
return trueCap(CapTrigger.Row, rowCapValue);
|
|
}
|
|
|
|
//We should never get here
|
|
return trueCap(CapTrigger.None, null);
|
|
}
|
|
|
|
|
|
|
|
function getCapPresidenceOptions(selectedOption) {
|
|
var optionsHtml = "";
|
|
optionsHtml += renderOption(Presidence.Row, selectedOption, "Row Cap");
|
|
optionsHtml += renderOption(Presidence.General, selectedOption, "General Cap");
|
|
optionsHtml += renderOption(Presidence.Highest, selectedOption, "Highest Cap");
|
|
optionsHtml += renderOption(Presidence.Lowest, selectedOption, "Lowest Cap");
|
|
return optionsHtml;
|
|
}
|
|
|
|
function getOversightOptions(countyId, selectedOption) {
|
|
var optionsHtml = "";
|
|
optionsHtml += renderOption(Oversight.Win, selectedOption);
|
|
optionsHtml += renderOption(Oversight.Tie, selectedOption);
|
|
optionsHtml += renderOption(Oversight.Watch, selectedOption);
|
|
optionsHtml += renderOption(Oversight.Ignore, selectedOption);
|
|
return optionsHtml;
|
|
}
|
|
|
|
function countyFound(countyValue) {
|
|
return countyValue !== undefined;
|
|
}
|
|
|
|
function upsertOversight(countyOversightJson) {
|
|
//console.log(countyOversightJson);
|
|
const countyId = countyOversightJson.id;
|
|
var countyFoundAt = alwaysWinSettings.oversight.findIndex(({ id }) => id === countyId);
|
|
if(countyFoundAt == -1) {
|
|
alwaysWinSettings.oversight.push(countyOversightJson);
|
|
countyFoundAt = alwaysWinSettings.oversight.length;
|
|
}
|
|
}
|
|
|
|
function convertToOversightJson(countyInfoJson, action, isBidCapEnabled){
|
|
const countyInfo = {
|
|
"id": countyInfoJson.id,
|
|
"bidCap": countyInfoJson.bidCap,
|
|
"isBidCapEnabled": isBidCapEnabled,
|
|
"action": action
|
|
};
|
|
return countyInfo;
|
|
}
|
|
|
|
function getBidCapReasonIcon(reason) {
|
|
switch(reason) {
|
|
case 1:
|
|
return "🔀";
|
|
case 2:
|
|
return "⏹";
|
|
case 3:
|
|
return "🔼";
|
|
case 4:
|
|
return "🔽";
|
|
default:
|
|
return "";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function getBidCapReason(tieBid, myBid, oversightAction, rowCapIsEnabled, rowCap, generalCapIsEnabled, generalCap, capPresidence, applyPresidenceTo) {
|
|
let tieBidNum = Number(tieBid);
|
|
let winBidNum = Number(tieBid) + 25;
|
|
let generalCapNum = Number(generalCap);
|
|
let rowCapNum = Number(rowCap);
|
|
let myBidNum = Number(myBid);
|
|
|
|
//Build our response object
|
|
let results = {};
|
|
results.shouldApplyCap = false; //Flag to indicate if the cap should be applied
|
|
results.reason = 0; //No Cap to start of with
|
|
results.trueCap = null; //Default to no true cap
|
|
results.capSource = null; //No caps applied
|
|
results.myBid = myBidNum;
|
|
|
|
//WIP - LOTS of rules here - these are just the beginning.
|
|
if(generalCapIsEnabled) {
|
|
if(capPresidence == Presidence.General) {
|
|
if(applyPresidenceTo == AppliesTo.All) {
|
|
|
|
results.shouldApplyCap = false;
|
|
results.capSource = Capsource.General;
|
|
results.trueCap = generalCapNum;
|
|
|
|
switch(oversightAction) {
|
|
case Oversight.Win:
|
|
if(winBidNum > generalCapNum) {
|
|
results.shouldApplyCap = true;
|
|
results.reason = 1;
|
|
results.myBid = generalCapNum;
|
|
return results;
|
|
}
|
|
//winBidNum <= generalCapNum
|
|
results.reason = 2;
|
|
results.myBid = myBidNum;
|
|
break;
|
|
|
|
case Oversight.Tie:
|
|
if(tieBidNum > generalCapNum) {
|
|
results.shouldApplyCap = true;
|
|
results.reason = 3;
|
|
results.myBid = generalCapNum;
|
|
return results;
|
|
}
|
|
//tieBidNum <= generalCapNum
|
|
results.reason = 4;
|
|
results.myBid = myBidNum;
|
|
break;
|
|
|
|
case Oversight.Watch:
|
|
results.reason = 5;
|
|
break;
|
|
|
|
case Oversight.Ignore:
|
|
results.reason = 6;
|
|
break;
|
|
|
|
default:
|
|
//We should never get here because all oversight actions have been covered.
|
|
}
|
|
return results;
|
|
}
|
|
else if(applyPresidenceTo == AppliesTo.None){
|
|
|
|
}
|
|
else if(applyPresidenceTo == AppliesTo.Capped){
|
|
|
|
}
|
|
else if(applyPresidenceTo == AppliesTo.Uncapped){
|
|
|
|
}
|
|
return results;
|
|
}
|
|
else if(capPresidence == Presidence.Row) {
|
|
return results;
|
|
}
|
|
else if(capPresidence == Presidence.Highest){
|
|
return results;
|
|
}
|
|
else if(capPresidence == Presidence.Lowest){
|
|
return results;
|
|
}
|
|
}
|
|
//If we made it here, generalCapIsEnabled is FALSE so no conflicts should exist
|
|
//This means rowCaps are the winners if they are enabled.
|
|
if(rowCapIsEnabled) {
|
|
switch(oversightAction) {
|
|
case Oversight.Win:
|
|
if(winBidNum > rowCapNum) {
|
|
results.shouldApplyCap = true;
|
|
results.reason = 101;
|
|
results.trueCap = rowCapNum;
|
|
results.capSource = Capsource.Row;
|
|
results.myBid = rowCapNum;
|
|
return results;
|
|
}
|
|
//winBidNum <= rowCapNum
|
|
results.shouldApplyCap = false;
|
|
results.reason = 102;
|
|
results.trueCap = rowCapNum;
|
|
results.capSource = Capsource.Row;
|
|
results.myBid = myBidNum;
|
|
break;
|
|
|
|
case Oversight.Tie:
|
|
if(tieBidNum > rowCapNum) {
|
|
results.shouldApplyCap = true;
|
|
results.reason = 103;
|
|
results.trueCap = rowCapNum;
|
|
results.capSource = Capsource.Row;
|
|
results.myBid = rowCapNum;
|
|
return results;
|
|
}
|
|
//tieBidNum <= generalCapNum
|
|
results.shouldApplyCap = false;
|
|
results.reason = 104;
|
|
results.trueCap = rowCapNum;
|
|
results.capSource = Capsource.Row;
|
|
results.myBid = myBidNum;
|
|
break;
|
|
|
|
case Oversight.Watch:
|
|
results.shouldApplyCap = false;
|
|
results.reason = 105;
|
|
results.trueCap = null;
|
|
results.capSource = Capsource.Row;
|
|
results.myBid = myBidNum;
|
|
break;
|
|
|
|
case Oversight.Ignore:
|
|
results.shouldApplyCap = false;
|
|
results.reason = 106;
|
|
results.trueCap = null;
|
|
results.capSource = Capsource.Row;
|
|
results.myBid = myBidNum;
|
|
break;
|
|
default:
|
|
//We should never get here because all oversight actions have been covered.
|
|
}
|
|
return results;
|
|
}
|
|
//Caps are not enabled for this row.
|
|
|
|
return results;
|
|
}
|
|
|
|
function injectOversight(countyId, action) {
|
|
countyId += ""; //cast into string incase number was passed in
|
|
|
|
//console.log("injecting oversight into county "+countyId);
|
|
var countyJson = getCountyInfo(countyId);
|
|
if(countyJson === null || countyJson === undefined) {
|
|
console.log(" could not find the county row "+countyId);
|
|
return;
|
|
}
|
|
|
|
//// BEGIN TempCode TODO: figure out if upsert Code should live in injectOversight
|
|
const isBidCapEnabled = false;
|
|
const countyOversightJson = convertToOversightJson(countyJson, action, isBidCapEnabled);
|
|
upsertOversight(countyOversightJson);
|
|
//// END TempCode
|
|
|
|
let countyRow = countyJson.countyRow;
|
|
|
|
var oversightCountyDiv = document.createElement('div');
|
|
oversightCountyDiv.id = "oversight-county-"+countyId;
|
|
oversightCountyDiv.style = "border: 1px dotted gray; padding: 0; margin: 0; font-size: 12px; overflow-x: visible";
|
|
|
|
var countyDivHtml ='';
|
|
countyDivHtml += ''+ countyJson.id +'';
|
|
countyDivHtml += ' | <label>tie:'+ countyJson.tieBid +'</label>';
|
|
countyDivHtml += ' | <label>margin:'+ countyJson.clicksToMinWin +'</label>';
|
|
|
|
oversightCountyDiv.innerHTML = countyDivHtml;
|
|
var targetContainerCounty = countyRow.firstElementChild;
|
|
targetContainerCounty.appendChild(oversightCountyDiv);
|
|
|
|
var oversightPriceDiv = document.createElement('div');
|
|
oversightPriceDiv.id = "oversight-price-"+countyId;
|
|
oversightPriceDiv.style = "border: 1px dotted gray; padding: 0; margin: 0; font-size: 12px; overflow-x: visible";
|
|
|
|
//Begin TODO: move this code block elsewhere IT IS WIP
|
|
const settingsCapPresidence = alwaysWinSettings.capPresidence;
|
|
const settings_Presidence_RowCap = (settingsCapPresidence == Presidence.Row);
|
|
const settings_Presidence_GeneralCap = (settingsCapPresidence == Presidence.General);
|
|
const settings_Presidence_HighestCap = (settingsCapPresidence == Presidence.Highest);
|
|
const settings_Presidence_LowestCap = (settingsCapPresidence == Presidence.Lowest);
|
|
|
|
const settingsGeneralCapAppliesTo = Presidence.General
|
|
const settings_GeneralCapAppliesTo_None = (settingsGeneralCapAppliesTo == AppliesTo.None)//None of the
|
|
const settings_GeneralCapAppliesTo_All = (settingsGeneralCapAppliesTo == AppliesTo.All)//All
|
|
const settings_GeneralCapAppliesTo_Capped = (settingsGeneralCapAppliesTo == AppliesTo.Capped)//Capped Only
|
|
const settings_GeneralCapAppliesTo_Uncapped = (settingsGeneralCapAppliesTo == AppliesTo_Uncapped)//UnCapped Only
|
|
|
|
const rowCapControlsShouldBeVisible = (action == Oversight.Win || action == Oversight.Tie); //TODO write logic to determine if the enable Bidcap checkbox and words should be visible
|
|
const rowCapCheckboxShouldBeVisible = (action == Oversight.Win || action == Oversight.Tie); //TODO: write logic
|
|
const rowCapCheckboxShouldBeDisabled = false; //TODO: write logic
|
|
const rowCapCheckboxShouldBeChecked = false; //TODO: write logic
|
|
|
|
const rowCapInputShouldBeVisible = (action == "win"); //TODO: run thru rules to determine showCapInfo value
|
|
const rowCapInputIsBeingOverridden = settings_Presidence_GeneralCap; //TODO: write logic
|
|
const rowCapInputShouldBeDisabled = (rowCapInputIsBeingOverridden || !rowCapCheckboxShouldBeChecked); //TODO: write logic
|
|
|
|
const trueCap = alwaysWinSettings.generalCap; //countyJson.bidCap; //TODO actually calcuate instead of reading from settings
|
|
//const trueCap = getTrueCap(isGeneralCapEnabled,isRowCapIsEnabled,presidenceValue,generalCapNum,rowCapNum); //TODO: Uncomment this line once I get the params right.
|
|
const capIsCurrentlyBeingAppliedToBid = countyJson.bid > Number(trueCap);
|
|
//End TODO
|
|
|
|
|
|
var priceDivHtml ='';
|
|
//priceDivHtml += ''+ countyJson.id +'';
|
|
priceDivHtml += '<label for="oversightAction-'+countyId+'"><select name="oversightAction-'+ countyId +'" id="oversightAction-'+ countyId +'">'+ getOversightOptions(countyId, action) +'</select></label>';
|
|
if(rowCapControlsShouldBeVisible) {
|
|
priceDivHtml += ' | <label><input ';
|
|
if(rowCapCheckboxShouldBeDisabled) {
|
|
priceDivHtml += 'disabled ';
|
|
}
|
|
priceDivHtml += 'type="checkbox" style="transform:scale(.5);" name="enableBidCap-'+countyId+'"';
|
|
if(rowCapCheckboxShouldBeChecked) {
|
|
priceDivHtml += ' checked';
|
|
}
|
|
priceDivHtml += '/>cap';
|
|
|
|
if(rowCapInputShouldBeVisible) {
|
|
priceDivHtml += ' at <input ';
|
|
if(rowCapInputShouldBeDisabled) {
|
|
priceDivHtml += 'disabled ';
|
|
}
|
|
priceDivHtml += 'type="text" name="bidCap-'+ countyId +'" id="bidCap-'+ countyId +'" style="font-size:10px; width:40px; height:12px;" value="'+ countyJson.bidCap +'" /></label>';
|
|
}
|
|
if(rowCapInputIsBeingOverridden) {
|
|
priceDivHtml += '<strike>'+ countyJson.bidCap +'</strike> ->'+ alwaysWinSettings.bidCap;//TODO render override html
|
|
}
|
|
}
|
|
|
|
if(capIsCurrentlyBeingAppliedToBid) {
|
|
priceDivHtml += getBidCapReasonIcon(1) + ' bid is capped at '+ trueCap;
|
|
}
|
|
|
|
|
|
//priceDivHtml += '<button disabled id="oversightSave-'+countyId+'" style="font-size:9px;background-color:#aaaaaa;color:#FFFFFF;padding:1px;border-radius:2px;-moz-border-radius:2px;-webkit-border-radius:2px;margin:0px;">Apply</button>';
|
|
oversightPriceDiv.innerHTML = priceDivHtml;
|
|
|
|
var targetContainer = countyRow.lastElementChild;
|
|
targetContainer.appendChild(oversightPriceDiv);
|
|
}
|
|
|
|
|
|
const sleep = ms => new Promise(res => setTimeout(res, ms))
|
|
|
|
jQuery(window).on('load',function() {
|
|
console.log("Page loaded at: "+ new Date());
|
|
let minSecondsBetweenReloads = alwaysWinSettings.minSecondsBetweenReloads ?? 30;
|
|
let maxSecondsBetweenReloads = alwaysWinSettings.maxSecondsBetweenReloads ?? 180;
|
|
let isReloadEnabled = alwaysWinSettings.isReloadEnabled ?? false;
|
|
let isAutoLoginEnabled = alwaysWinSettings.isAutoLoginEnabled ?? false;
|
|
let isGeneralCapEnabled = alwaysWinSettings.isGeneralCapEnabled ?? false;
|
|
let capPresidence = alwaysWinSettings.capPresidence ?? "row";
|
|
let generalCap = alwaysWinSettings.generalCap ?? 500;
|
|
let secondsBetweenReloads = getRandomNumberBetween(minSecondsBetweenReloads, maxSecondsBetweenReloads);
|
|
//console.log("Next reload should occur in "+ secondsBetweenReloads +" seconds");
|
|
|
|
//Inject our controls
|
|
var newDiv = document.createElement ('div');
|
|
var newHtml = '<div class="alwaysWin-ntsmhf" id="alwaysWin-ntsmhf">';
|
|
newHtml += '<input type="checkbox" name="isAutoLoginEnabled" '+ getCheckedValue("isAutoLoginEnabled") +'/><label for="isAutoLoginEnabled">Auto Login</label> | ';
|
|
newHtml += '<button class="alwaysWinButton" id="alwaysWin-ntsmhf-save" type="button">Save</button><br />';
|
|
newHtml += '<input type="checkbox" name="isReloadEnabled" '+ getCheckedValue("isReloadEnabled") +'/><label for="isReloadEnabled">Auto Refresh</label> ';
|
|
newHtml += '<label for="minSecondsBetweenReloads">Every</label>:<input id="minSecondsBetweenReloads" type="text" size="3" name="minSecondsBetweenReloads" value="'+minSecondsBetweenReloads+'" /> to <input id="maxSecondsBetweenReloads" type="text" size="3" name="maxSecondsBetweenReloads" value="'+maxSecondsBetweenReloads+'" /> seconds <br/>';
|
|
|
|
newHtml += '<label for="isGeneralCapEnabled"><input type="checkbox" name="isGeneralCapEnabled" '+ getCheckedValue("isGeneralCapEnabled") +'/>Don't bid more than</label> ';
|
|
newHtml += '<label for="generalCap">:</label><input id="generalCap" type="text" size="5" name="generalCap" value="'+ generalCap+'"/> per county<br />';
|
|
newHtml += '<label for="capPresidence" title="If a row and general cap are enabled"><select id="capPresidence" name="capPresidence" />'+ getCapPresidenceOptions(capPresidence) +'</select> takes presidence</label><br/>';
|
|
|
|
newHtml += '<div id="reloaderData"><div id="countdown"></div><progress value="0" max="100" id="progressBar" style="width: 100%;"></progress></div><div id="awLog" class="alwaysWinLog"></div>';
|
|
newHtml += '</div>';
|
|
|
|
newDiv.innerHTML = newHtml;
|
|
|
|
addAlwaysWinStyle('.alwaysWin-ntsmhf {position: fixed; top: 0px; left: 0px; background-color: #DDDDDD; border-radius: 5px; padding:2px; box-shadow: 5px 5px 3px #777777;}');
|
|
addAlwaysWinStyle('.alwaysWin-ntsmhf .alwaysWinLog {float:left; width:100%; overflow-y: auto; height: 100px; font-size: 0.75em; border-style:ridge; background-color:#FEFEFE;}');
|
|
addAlwaysWinStyle('.alwaysWin-ntsmhf .alwaysWinButton { border: none; color: #333333; padding: 1px 2px; text-align: center; text-decoration: none; display: inline-block; font-size: 8x; margin: 1px 1px; cursor: pointer;}');
|
|
addAlwaysWinStyle('.alwaysWin-ntsmhf .alwaysWinButton:hover {box-shadow: 0 2px 2px 0}');
|
|
addAlwaysWinStyle('.alwaysWin-ntsmhf .alwaysWinButton:active {position: relative; top: 1px;}');
|
|
document.body.appendChild (newDiv);
|
|
|
|
const alwaysWinSave = document.getElementById("alwaysWin-ntsmhf-save");
|
|
alwaysWinSave.addEventListener("click", handleSaveSettings);
|
|
|
|
//Only run our code on certain pages
|
|
//console.log(window.location.pathname);
|
|
switch (window.location.pathname) {
|
|
case "/app":
|
|
case "/app/subscriptions":
|
|
break;
|
|
case "/signin":
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
//Are we logged in?
|
|
var logWasNeeded = loginIfNeeded();
|
|
if (logWasNeeded) {
|
|
return; //don't execute any more code until we are logged in.
|
|
}
|
|
|
|
//Inject our bits into the page for oversight
|
|
//TODO: Get rid of these calls and only read injections from settings (possible when apply button is coded)
|
|
// For now they will be commented out unless it's a first run of the script
|
|
//injectOversight("84035","win"); //washington, OR
|
|
//injectOversight("87833","win"); //marion, OR
|
|
//injectOversight("84034","tie"); //cascade, MT
|
|
//injectOversight("84037","win"); //clackamas, OR
|
|
//injectOversight("84038","watch"); //benton, OR
|
|
//injectOversight("87835","watch"); //multnomah, OR
|
|
//injectOversight("91976","win"); //CLARK, WA
|
|
|
|
//// Now do the work
|
|
let newStats = [];
|
|
let oversightCount = alwaysWinSettings.oversight.length;
|
|
awLog("Checking "+ oversightCount +" counties");
|
|
for(let i = 0; i < oversightCount; i++) {
|
|
let currentEntity = alwaysWinSettings.oversight[i];
|
|
injectOversight(currentEntity.id, currentEntity.action);
|
|
const countyInfoJson = getCountyInfo(currentEntity.id);
|
|
//console.log(currentEntity);
|
|
switch (currentEntity.action) {
|
|
case Oversight.Win:
|
|
winCounty(countyInfoJson,true);
|
|
break;
|
|
case Oversight.Tie:
|
|
tieCounty(countyInfoJson,true);
|
|
break;
|
|
case Oversight.Watch:
|
|
watchCounty(countyInfoJson,false);
|
|
break;
|
|
case Oversight.Ignore:
|
|
ignoreCounty(countyInfoJson, false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
newStats.push(countyInfoJson);
|
|
}
|
|
|
|
//console.log("Saving county info stats");
|
|
const countyInfoStats = saveCountyInfoStats(newStats);
|
|
|
|
//Start our ui feedback for the enduser
|
|
var reloadTimeleft = secondsBetweenReloads;
|
|
var reloadTimer = setInterval(function() {
|
|
document.getElementById("countdown").innerHTML = "("+secondsBetweenReloads+") Next reload will occur in "+ reloadTimeleft +" seconds.";
|
|
var progressBarValue = 100 - Math.floor(100 * (reloadTimeleft / secondsBetweenReloads)); //Calculate percent complete
|
|
document.getElementById("progressBar").value = progressBarValue;
|
|
if(reloadTimeleft <= 0){
|
|
clearInterval(reloadTimer);
|
|
document.getElementById("countdown").innerHTML = secondsBetweenReloads +" second reload timer exhausted";
|
|
reload();
|
|
}
|
|
reloadTimeleft -= 1;
|
|
}, 1000);
|
|
});
|