Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name SteamGifts
- // @namespace SteamGifts
- // @description Custom Functions
- // @include *.steamgifts.com/
- // @include *.steamgifts.com/giveaway/*
- // @include *.steamgifts.com/giveaways/search?*
- // @include *.steamgifts.com/open
- // @include *.steamgifts.com/new*
- // @include *.steamgifts.com/offline*
- // @include *.steamgifts.com/suspensions
- // @include *store.steampowered.com/agecheck/*
- // @include *store.steampowered.com/*/agecheck
- // @version 5.0
- // @modified 10/07/2016
- // @grant GM_xmlhttpRequest
- // ==/UserScript==
- //Mini script for doing steam age checks automatically (used when scraping steam sites)
- if (/agecheck/i.test (location.pathname) ) {
- console.log('Age Check Required.');
- var ageForm = document.querySelector ("#agegate_box form");
- if (ageForm != null)
- {
- ageForm.querySelector ("[name='ageDay']").value = 18;
- ageForm.querySelector ("[name='ageMonth']").value = 'August';
- ageForm.querySelector ("[name='ageYear']").value = 1987;
- var newFrame = document.createElement('frame');
- newFrame.name = "PostResult"
- document.body.appendChild(newFrame);
- ageForm.target = newFrame.name;
- ageForm.submit();
- }
- else
- {
- var btnContinue = document.querySelector ("#app_agegate .btn_medium:nth-of-type(1)");
- btnContinue.innerHTML = "<span>Continuing...</span>";
- btnContinue.click();
- }
- setTimeout( function(){ window.close(); }, 30000);
- console.log('Performed Age Check.');
- }
- // If loading a giveaway page, add a listener for messages from our auto-register script.
- else if(/\/giveaway\//i.test (location.pathname)) {
- window.addEventListener("message", receiveMessage, false);
- console.log('Waiting for any giveaway entry messages...');
- function receiveMessage(event)
- {
- console.log('Giveaway Message Recieved...');
- if(event.data == "EnterGiveaway"){
- console.log('Revieved EnterGiveaway Message!');
- if( !document.getElementsByClassName("sidebar__entry-insert")[0].classList.contains('is-hidden'))
- {
- console.log('Entered giveaway at ' + location.pathname);
- registerGiveawayPage(window.document);
- setTimeout(function () {
- if(document.getElementsByClassName("sidebar__entry-insert")[0].classList.contains('is-hidden'))
- window.close();
- }, 100);
- }
- }
- }
- }
- else //Otherwise, we're on SteamGifts!
- {
- //////////////////////////////////// CONFIGURATION ////////////////////////////////
- var AUTOREGISTER = true;
- var LEAVEOPEN = false;
- var TimeOut = 60000; //1 Minute
- var RefreshTime = 60000; //1 Minute
- var RegistrationTime = 30000; //30 secs
- var pollingInterval = 1000; // 1 second update speet
- var pollingAttempts = 10;
- //Decision Making Constants for modified probability tolerance before auto-registering based on current points.
- var BaseThreshold = 4.64876241265283 / 100.0;
- var MinThreshold = 0.00544389516791109 / 100.0;
- var LogFactor = 4.10546665422788;
- var MaxP = 300;
- var MaxPenalty = 1E5; //Max probability penalty applied (anything with the max penalty should not be registered)
- // Game 'Quality' Modifiers (Used for favouring "Good" games and penalizing "Bad" games.)
- // These are generally additive with each other.
- // Overall Quality Modifier Power - how much these other modifiers matter
- // Set to zero to only enter games based on win probability. This will maximize games won.
- // Increase the value to increase the relative weight given to entering good games.
- var overallQualityModifierPower = 1.0;
- var onWishListModifier = 50.0; // Trumps any other 'quality' modifiers (e.g. not additive)
- //// Achievements (Bonus differs depending on number of achievements.)
- //// More achievements often indicates a more thorough game, but too many reeks of spam.
- var achieveTiers = [1,10,30,60,120]
- var achieveBonus = [1,2,3,2,1]
- //// Tags
- var unwantedTags = ["Match 3","Dating Sim","RPGMaker","Visual Novel","Sexual Content","Romance","Nudity"];
- var unwantedTagPenalty = -2; // Fitness multiplied by this value divided by number of unwanted tags
- var wantedTags = ["Co-op","Open World","Rhythm","Clicker","First-Person","FPS"];
- var wantedTagBonus = 2; // Fitness is multiplied by this value times the number of wanted tags
- //// Other
- var hasCardsBonus = 2; // If the game has steam trading cards
- var reviewScale = [15,10,5,1,0,-1,-2,-5,-10]; // For the 9 review categories from most positive to most negative.
- var bundleBonus = 2; // If there appears to be multiple games in the giveaway (but we don't know how many)
- var packGameValue = 1; // Bonus for each additional game in a steam bundle.
- var dlcPenalty = -3; // If you wanted the DLC, it would be on your wishlist right?
- var soundtrackPenalty = -10; // If the giveaway is being detected as just a soundtrack dlc
- // Giveaway 'Quality' Modifiers (used to favour giveaways with an above-average opportunity to win - which is already inherent in its number of entries, but this scales it further.)
- var shortGiveawayModifier = 0.5
- var shortGiveawayTime = (1.5*60*60) // 1.5 hours in seconds
- var groupGiveawayModifier = 1
- var maxContributorModifier = 2;
- var contributorLevelCap = 10;
- //////////////////////////////////// END CONFIGURATION ////////////////////////////////
- var polling;
- var arr_Giveaways = [];
- var arr_CantEnter = [];
- var asyncRequests = 0
- var asyncRequestsCompleted = 0;
- var asyncRequestsCancelled = 0;
- var steamRequestsCompleted = 0;
- var ageChecked = 0;
- var registrations = 0;
- try
- {
- console.log('Steamgits script loaded for ' + window.location);
- appendCSStoHead();
- var headerInsertionPoint = document.createElement('div');
- headerInsertionPoint.style.cssText =
- 'text-align: right; padding: 1em; position:fixed; z-index: 60; right:0; top:30px; max-height:500px;' +
- 'margin:1em; background: none repeat scroll 0 0 rgba(132, 193, 10, 0.8); ' +
- 'border-color: rgba(132, 193, 10, 0.5); border-radius: 3em 0.5em 0.5em 3em; ' +
- 'border-style: solid none solid solid; border-width: medium; box-shadow: 0 1px 3px black; color: white; ';
- document.body.appendChild(headerInsertionPoint);
- // Play some sound to prevent firefox from unloading scripts if while this tab is inactive.
- window.AudioContext = window.AudioContext || window.webkitAudioContext;
- context = new AudioContext();
- var o = context.createOscillator();
- var volume = context.createGain();
- o.connect(volume);
- volume.connect(context.destination);
- o.type = 'sine';
- o.frequency.value = 100;
- volume.gain.value = 0.0001;
- o.start(0);
- //Since many of the same games appear in giveaways, we can cache the steam info for that which has already been requested.
- var SteamRequestCache = {};
- // Restore session-persisted cache
- if (sessionStorage.getItem("steam_cache")) {
- //console.log(sessionStorage.getItem("steam_cache"));
- SteamRequestCache = JSON.parse(sessionStorage.getItem("steam_cache"));
- //console.log(SteamRequestCache);
- console.log('Restored cache of steam information (' + Object.keys(SteamRequestCache).length + ' games)');
- }
- if(window.self !== window.top)
- console.log('In a frame. Skipping script.');
- else if (/offline/i.test (location.pathname) ||
- /Database connection failed/.test(document.body.innerHTML) ||
- document.getElementsByClassName('nav__points').length == 0 )
- {
- console.log('Site doesn\'t look right. Scheduling a refresh.');
- polling = setInterval(refreshWhenReady, pollingInterval);
- }
- else
- {
- /****** Begin Magic. *****/
- var pointsElem = document.getElementsByClassName('nav__points')[0];
- var myP = parseInt(pointsElem.innerHTML);
- var thresholdForP = FitnessThreshold(myP);
- // Set the Async task to pull giveaway info
- polling = setInterval(scrapeGiveaways, pollingInterval);
- headerInsertionPoint.appendChild(document.createTextNode(
- 'Threshold for ' + myP + ' P = ' + (thresholdForP*100).toFixed(2) + '%'));
- headerInsertionPoint.appendChild(document.createElement('br'));
- /****** This is the end of the initial code execution. The rest will happen in 'setInterval' callbacks. *****/
- }
- }
- catch(err) //Unforseen things keep happening so...
- {
- console.log("Couldn't setup steamgifts script: " + err.message);
- reset();
- }
- }
- /////////////////////////////// FUNCTIONS /////////////////////////////
- function appendCSStoHead() {
- var css = '.giveaway__columns * { white-space: nowrap; line-height: 20px; } ' +
- '.giveaway__columns > * { padding: 0 5px; margin-right: 2px !important; } ' +
- '.giveaway__row-outer-wrap { padding: 0; } ' +
- '.giveaway__summary { padding: 0 8px; } ' +
- '.sidebar__mpu { display: none !important; } ' +
- '.sidebar__search-input { min-width: 0px; } ' +
- '.widget-container > div:not(:first-child) { padding-left: 10px; margin-left:10px; overflow: hidden; } ' +
- '.page__outer-wrap { padding: 10px } ' +
- '.pinned-giveaways__inner-wrap { padding: 0px 5px } ' +
- '.pinned-giveaways__inner-wrap--minimized { padding-bottom: 0px } ' +
- '.featured__outer-wrap { padding: 5px 0 } ' +
- '.featured__inner-wrap { padding: 0 220px 0 130px } ' +
- '.global__image-outer-wrap img { max-height: 100px; } ' +
- 'small { font-size: 0.8em; } ' +
- 'span, a { white-space: nowrap; } ' +
- 'span, a { font-size: 10px; } ' +
- '.sidebar { min-width: 115px; max-width: 120px; } ' +
- '.pagination + div, .pagination + div *, .pagination + div + div { height: 0px !important; padding: 0px !important; } ' +
- '.pagination + div + div + div, .pagination + div + div + div * { margin-top: 1px !important; padding-top: 0px !important; margin-bottom: 0px !important; padding-bottom: 0px !important; }' +
- '.inlinedEntry { height: 240px; overflow:scroll; margin-bottom: 4px; }' +
- '.inlinedEntry * { overflow: hidden; white-space: nowrap; width: auto; }';
- head = document.head || document.getElementsByTagName('head')[0],
- style = document.createElement('style');
- style.type = 'text/css';
- if (style.styleSheet){
- style.styleSheet.cssText = css;
- } else {
- style.appendChild(document.createTextNode(css));
- }
- head.appendChild(style);
- }
- function FitnessThreshold(p) {
- return (BaseThreshold-MinThreshold) * Math.pow(((MaxP - p) / MaxP), LogFactor) + MinThreshold;
- }
- function scrapeGiveaways()
- {
- var minimumMet = false;
- var giveaways = document.querySelectorAll(".giveaway__summary");
- console.log("Found " + giveaways.length + " giveaways.");
- try
- {
- var last_time_remaining = giveaways[giveaways.length-1].getElementsByClassName('fa-clock-o')[0]
- .parentNode.getElementsByTagName('span')[0].innerHTML;
- //We're going to wait at least for as long as 'TimeOut' to get all the gifts that meet these conditions: (less than 6 hours remaining)
- if( last_time_remaining.indexOf('day') > -1 || last_time_remaining.indexOf('week') > -1 ||
- (last_time_remaining.indexOf('hours') > -1 && parseInt(last_time_remaining.match(/(\d+) hours/)[1]) > 6) )
- minimumMet = true;
- }
- catch(err)
- { //Something's buggered up in getting the time of the last loaded giveaway, this gives us a chance to move on.
- console.log("Error scraping giveaways. Proceeding as success. " + err.message);
- minimumMet = true;
- }
- try
- {
- var scraperProgress = document.getElementById('gafLoading');
- //If we've loaded every giveaway (or the giveaway add-on isn't installed), move on to the next phase.
- if( scraperProgress != null )
- {
- //Show how long we've been waiting
- getOrCreateCustomElement('pollingRequests', scraperProgress).innerHTML =
- 'Scraping: ' + pollingAttempts * (pollingInterval / 1000) + ' of ' +
- (TimeOut / 1000) + ' Seconds Waited';
- if( scraperProgress.innerHTML.indexOf('the end') > -1 )
- doneScraping();
- else
- {
- scraperProgress.scrollIntoView();
- window.scrollBy(0, -100);
- }
- }
- //We've waited long enough, maybe the server is responding slowly? Anyways, move on to the loaded routine.
- /*
- if( (pollingAttempts >= TimeOut / pollingInterval) ||
- (pollingAttempts > 5 && minimumMet) )
- doneScraping();
- else
- pollingAttempts += 1; //Disabled scraping multiple pages to cut back on requests.
- */
- doneScraping();
- updateAsyncRequestsStatus();
- if( scraperProgress != null )
- setTimeout(AddNewGiveaways, pollingInterval/2);
- }
- catch(err) //Unforseen things keep happening so...
- {
- console.log("Error scraping giveaways. Resetting the page. " + err.message);
- reset();
- }
- }
- function doneScraping()
- {
- window.clearInterval(polling);
- window.scrollTo(0, 0);
- AddNewGiveaways();
- pollingAttempts = 0;
- polling = setInterval(waitOnAsyncRequests, pollingInterval );
- console.log("Done Scraping");
- }
- function reset()
- {
- window.clearInterval(polling);
- polling = setInterval(refreshWhenReady, pollingInterval);
- }
- function AddNewGiveaways()
- {
- try
- {
- var giveaways = collectionToArray(document.querySelectorAll(".giveaway__summary"));
- //If there is a block of developer giveaways at the top, we ignore them because they appear as duplicates in the normal list
- if( /(Developer\:|Featured\:)/.test(giveaways[0].innerHTML) )
- giveaways.splice(0,1);
- //giveaways = collectionToArray(giveaways.splice(0,1)[0].querySelectorAll(".post>div")).concat(giveaways);
- for( var I = 0; I < giveaways.length; I++ )
- {
- var elem = giveaways[I];
- var timeRemainingElem = elem.getElementsByClassName('fa-clock-o')[0]
- .parentNode.getElementsByTagName('span')[0].innerHTML;
- var entriesElem = elem.getElementsByClassName('giveaway__links')[0]
- .getElementsByTagName('span')[0].innerHTML;
- if( elem.clientWidth > 0 &&
- !/hackeried/.test(elem.className) &&
- !/\(Ended /.test(timeRemainingElem) &&
- !/Coming Soon/.test(entriesElem) )
- {
- elem.className += ' hackeried';
- var title = elem.getElementsByClassName('giveaway__heading__name')[0].innerHTML;
- var costElemIndex = 0;
- var copies = 1;
- var titleElements = elem.getElementsByClassName('giveaway__heading__thin');
- if(titleElements.length > 1) {
- costElemIndex = 1;
- var copies = parseInt(titleElements[0].innerHTML
- .match(/([\d,]+) Copies/)[1].replace(/\,/g,''));
- }
- var cost = parseInt(titleElements[costElemIndex].innerHTML.match(/\((\d+)P\)/)[1]);
- var time_remaining = parseTimeString(timeRemainingElem);
- var created = parseTimeString(elem.getElementsByClassName('giveaway__column--width-fill')[0]
- .childNodes[0].innerHTML);
- var entries = parseInt(entriesElem.match(/([\d,]+) entr(ies|y)/)[1].replace(/\,/g,''));
- var giveawayURL = elem.getElementsByClassName('giveaway__heading__name')[0].href;
- var isGroup = false; //elem.getElementsByClassName('group_only').length > 0;
- var steamURL = elem.getElementsByClassName('giveaway__icon');
- if(steamURL != null) steamURL = steamURL[0].href;
- var contribAmount = elem.getElementsByClassName('giveaway__column--contributor-level');
- contribAmount = contribAmount.length == 0 ? null : contribAmount[0].innerHTML.match(/Level (\d+)\+/);
- if( contribAmount == null )
- contribAmount = 0;
- else
- contribAmount = parseInt(contribAmount[1]);
- var giveaway = {
- elem: elem,
- title: title,
- cost: cost,
- copies: copies,
- remaining: time_remaining,
- created: created,
- entries: entries,
- isGroup: isGroup,
- contribAmount: contribAmount,
- url: giveawayURL,
- canEnter: true,
- SteamURL: steamURL
- }
- giveaway.prob = Math.min(1, 1/entries * copies);
- giveaway.prob_per_P = giveaway.prob/cost;
- giveaway.timePenalty = calcTimePenalty(giveaway);
- var requestDetails = {
- method: 'GET',
- url: giveawayURL,
- timeout: TimeOut,
- ontimeout: SGRequestFailure(giveaway),
- onerror: SGRequestFailure(giveaway)
- }
- requestDetails.onload =
- (function(giveaway) {
- return function(req) {
- try
- {
- giveaway.canEnter = /sidebar__entry-insert"/i.test (req.responseText);
- if(giveaway.canEnter)
- {
- arr_Giveaways.push(giveaway);
- //Default values in case we can't connect to Steam
- giveaway.achievements = null;
- giveaway.hasCards = null;
- giveaway.isDLC = null;
- giveaway.isWishlist = null
- giveaway.metascore = null;
- giveaway.review = null;
- giveaway.tags = [];
- calcModifierAndFitness(giveaway);
- if(giveaway.SteamURL != null)
- requestSteamInformation(giveaway);
- else
- appendSteamInfoToGiveaway(giveaway);
- }
- else
- {
- cannotEnterGiveaway(giveaway);
- }
- asyncRequests --;
- asyncRequestsCompleted ++;
- }
- catch(err)
- {
- console.log("Error retrieving request details." + err.message + "\nLine: " + err.lineNumber);
- throw err;
- }
- }
- }(giveaway));
- asyncRequests ++;
- GM_xmlhttpRequest(requestDetails);
- appendProbabilityInfoToGiveaway(giveaway);
- }
- }
- }
- catch(err)
- {
- console.log("Error in AddNewGiveaways. " + err.message + "\nLine: " + err.lineNumber);
- reset();
- }
- }
- function SGRequestFailure(giveaway)
- {
- return (function(giveaway) {
- return function(req) {
- console.log("SGRequestFailure. " + giveaway.title);
- asyncRequests--;
- asyncRequestsCancelled++;
- arr_Giveaways.push(giveaway);
- //Default steam values
- giveaway.achievements = null;
- giveaway.hasCards = null;
- giveaway.isDLC = null;
- giveaway.isWishlist = null;
- giveaway.review = null;
- giveaway.metascore = null;
- calcModifierAndFitness(giveaway);
- appendSteamInfoToGiveaway(giveaway);
- }
- }(giveaway));
- }
- function cannotEnterGiveaway(giveaway)
- {
- console.log("CannotEnterGiveaway. " + giveaway.title + ". AlreadyOwned=" + giveaway.alreadyOwned);
- arr_CantEnter.push(giveaway);
- //Strike-out things that can't be entered
- giveaway.elem.getElementsByClassName('giveaway__heading__name')[0].style.textDecoration = 'line-through';
- // Uncomment to hide from view games that are already owned (rather than just striking them out.)
- //if(!giveaway.alreadyOwned) {
- // giveaway.elem.className += ' fade';
- //}
- }
- // If steam reports that a game is already owned, mark it as ignored, in case we go offline some time.
- // TODO: re-implement. This doesn't work.
- function markIgnored(giveaway)
- {
- var children = giveaway.elem.getElementsByClassName('entries')[0].getElementsByTagName('span');
- for(var i = 0; i < children.length; i++)
- {
- if(children[i].getElementsByTagName('a')[0])
- if(children[i].getElementsByTagName('a')[0].name == 'gafbutton' )
- children[i].getElementsByTagName('a')[0].onclick();
- }
- }
- //Check the steam store page for more information
- function requestSteamInformation(giveaway)
- {
- //Check the cache first
- if( giveaway.SteamURL in SteamRequestCache )
- {
- if( SteamRequestCache[giveaway.SteamURL].ready )
- {
- // If the cached attempt failed, delete the cache entry.
- var lastRequestSucceeded = SteamRequestCache[giveaway.SteamURL];
- if(!lastRequestSucceeded)
- {
- delete SteamRequestCache[giveaway.SteamURL];
- console.log("Deleting cache entry, steamRequestSucceeded not set to true. (" + giveaway.title + " - " + (lastRequestSucceeded || "unset") + ")");
- }
- else
- copySteamInfo(SteamRequestCache[giveaway.SteamURL].first, giveaway);
- }
- else
- SteamRequestCache[giveaway.SteamURL].waiting.push(giveaway);
- }
- if( !(giveaway.SteamURL in SteamRequestCache) )
- {
- //Create a new request cache entry
- SteamRequestCache[giveaway.SteamURL] = {
- ready: false,
- first: giveaway,
- waiting: []
- }
- //Fire up a request to the steam website
- var steamRequest = {
- method: 'GET',
- url: giveaway.RealSteamURL || giveaway.SteamURL,
- timeout: TimeOut,
- ontimeout: SteamRequestFailure(giveaway),
- onerror: SteamRequestFailure(giveaway)
- }
- steamRequest.onload =
- (function(giveaway) {
- return function(req) {
- var deferredRequest = false;
- try
- {
- giveaway.steamRequestSucceeded = false;
- //If we're taken to an age check screen, we must pass through it
- if( req.finalUrl.indexOf('agecheck') > -1 && ageChecked == 0)
- {
- ageChecked = 1; //Prevent dozens of threads each doing the age check.
- //There'll be a form to submit, so we'll have to actually open this url in a new window. This same
- //greasemonkey script will submit the age check, and then subsequent requests from mature steam pages
- //be able to scrape info.
- var newWindow = window.open(req.finalUrl);
- // This attempt to load the game in the background has failed, don't cache the attempt.
- delete SteamRequestCache[giveaway.SteamURL];
- return;
- }
- if(req.finalUrl.indexOf((giveaway.RealSteamURL||giveaway.SteamURL)) == -1)
- throw ("Got Redirected to: " + req.finalUrl);
- // Detect if this giveaway is actually a package:
- if( req.finalUrl.indexOf('sub') > -1)
- {
- var steamURLsRegex = /href="(http:\/\/store.steampowered.com\/app\/[^\/]*\/[^\/]*\/)"/g;
- var urlMatches;
- giveaway.packGames = [];
- giveaway.RealSteamURL = null;
- var lowestGameId = null;
- while( (urlMatches = steamURLsRegex.exec(req.responseText)) != null) {
- giveaway.packGames.push(urlMatches[1]);
- var gameId = parseInt(urlMatches[1].match(/http:\/\/store.steampowered.com\/app\/([^\/]*)\/[^\/]*\//)[1]);
- if(lowestGameId == null || lowestGameId > gameId)
- {
- lowestGameId = gameId;
- giveaway.RealSteamURL = urlMatches[1];
- }
- }
- if(giveaway.RealSteamURL == null)
- throw ("Game is a steam pack, but can't find the real game: " + req.finalUrl);
- console.log(giveaway.title + " is a pack. Earliest game in pack is: " + giveaway.RealSteamURL );
- delete SteamRequestCache[giveaway.SteamURL];
- requestSteamInformation(giveaway);
- deferredRequest = true;
- return;
- }
- var dlcSection = req.responseText.match(/game_area_dlc_bubble/);
- giveaway.isDLC = dlcSection == null ? false : true;
- var achievements = req.responseText.match(/Includes (\d+) Steam Achievements/);
- giveaway.achievements = achievements == null || giveaway.isDLC ? 0 : parseInt(achievements[1]);
- var metascore = req.responseText.match(/(\d+).*reviewRating/);
- giveaway.metascore = metascore == null ? null : parseInt(metascore[1]);
- var review = req.responseText.match(/game_review_summary[^<>]*>([^<>]*)<\/span>/);
- giveaway.review = review == null ? null : review[1];
- var cardsSection = req.responseText.match(/Steam Trading Cards/);
- giveaway.hasCards = cardsSection == null || giveaway.isDLC ? false : true;
- if( giveaway.isDLC )
- { //We can collect information about the root game this DLC is for
- var dlcInfo = req.responseText.match(/requires the base game <a href="(.*)">(.*)<\/a> on Steam in order to play./);
- if( dlcInfo != null )
- {
- giveaway.requiredURL = dlcInfo[1];
- giveaway.requiredTitle = dlcInfo[2];
- }
- }
- var alreadyOwned = req.responseText.match(/game_area_already_owned/);
- // Only prevent entering the giveaway if we already own the game and this isn't just the main game in a bundle.
- // TODO: But still prevent entering if we own all the games in the bundle
- giveaway.alreadyOwned = alreadyOwned != null && (giveaway.RealSteamURL == null);
- //console.log(giveaway.title + " owned? " + givewaway.alreadyOwned)
- giveaway.canEnter = giveaway.alreadyOwned ? false : giveaway.canEnter;
- var wishListSection = req.responseText.match(/This product is already on your wishlist./);
- var wishListRedHerringSection = req.responseText.match(/add_to_wishlist_area_success/);
- giveaway.isWishlist = wishListSection != null && wishListRedHerringSection == null;
- var tagsRegex = /class="app_tag"[^<>]*>[\s]*([^<>\s]*)[\s]*<\/a>/g;
- var matches;
- while( (matches = tagsRegex.exec(req.responseText)) != null && giveaway.tags.length < 6) {
- giveaway.tags.push(matches[1]);
- }
- //console.log(giveaway.title + " done.")
- giveaway.steamRequestSucceeded = true;
- }
- catch(err)
- {
- //Something has gone wrong. Maybe this steam page no longer exists? Can't do much, but hack the dlc thing to note the error
- console.log("Error in giveaway: " + giveaway.title);
- giveaway.error = err;
- console.log(err);
- delete SteamRequestCache[giveaway.SteamURL];
- }
- finally {
- asyncRequests --;
- if(!deferredRequest) {
- updateGiveawayAndWaitingCache(giveaway);
- steamRequestsCompleted ++;
- }
- }
- }
- }(giveaway));
- asyncRequests ++;
- GM_xmlhttpRequest(steamRequest);
- }
- }
- function SteamRequestFailure(giveaway)
- {
- return (function(giveaway) {
- return function(req) {
- updateGiveawayAndWaitingCache(giveaway);
- asyncRequests--;
- asyncRequestsCancelled++;
- }
- }(giveaway));
- }
- function updateGiveawayAndWaitingCache(giveaway)
- {
- // Rarely, we own a game but steamgifts can't pick up on it.
- // TODO: not working. Just keeps ignoring already ignored titles.
- //if( giveaway.alreadyOwned && (' ' + giveaway.elem.className + ' ').indexOf(' fade ') == -1 )
- // markIgnored(giveaway); // Click the ignore button if this giveaway hasn't already been ignored.
- if( !giveaway.canEnter )
- {
- removeA(arr_CantEnter, giveaway);
- cannotEnterGiveaway(giveaway);
- }
- calcModifierAndFitness(giveaway);
- //Fill in every giveaway waiting for this request to complete
- var cacheEntry = SteamRequestCache[giveaway.SteamURL];
- if( cacheEntry != null )
- {
- cacheEntry.ready = true;
- while(cacheEntry.waiting.length > 0)
- copySteamInfo(giveaway, cacheEntry.waiting.pop());
- }
- appendSteamInfoToGiveaway(giveaway);
- }
- function copySteamInfo(src, dst)
- {
- dst.steamRequestSucceeded = src.steamRequestSucceeded;
- if(src.RealSteamURL != null)
- {
- dst.RealSteamURL = src.RealSteamURL;
- dst.packGames = src.packGames;
- }
- dst.achievements = src.achievements;
- dst.hasCards = src.hasCards;
- dst.isDLC = src.isDLC;
- if(dst.isDLC)
- {
- dst.requiredTitle = src.requiredTitle;
- dst.requiredURL = src.requiredURL;
- }
- dst.isWishlist = src.isWishlist;
- dst.metascore = src.metascore;
- dst.review = src.review;
- dst.tags = src.tags;
- dst.canEnter = src.canEnter
- if( !dst.canEnter )
- {
- removeA(arr_CantEnter, dst);
- cannotEnterGiveaway(dst);
- }
- dst.error = src.error;
- calcModifierAndFitness(dst);
- appendSteamInfoToGiveaway(dst);
- }
- function waitOnAsyncRequests()
- {
- updateAsyncRequestsStatus();
- if(asyncRequests == 0 && asyncRequestsCompleted > 0)
- {
- window.clearInterval(polling);
- sortAndDisplay();
- }
- else
- {
- //Show how long we've been waiting
- getOrCreateCustomElement('SteamPollingTime', headerInsertionPoint).innerHTML =
- pollingAttempts * (pollingInterval / 1000) + ' of ' + (TimeOut / 1000) + ' Seconds Waited';
- //Been trying for too long, maybe something is stuck? Just continue with what information we have
- if( pollingAttempts >= TimeOut / pollingInterval )
- {
- window.clearInterval(polling);
- sortAndDisplay();
- }
- else
- pollingAttempts += 1;
- }
- }
- function updateAsyncRequestsStatus()
- {
- //Async Requests Pending
- if(asyncRequests + asyncRequestsCompleted + asyncRequestsCancelled > 0)
- getOrCreateCustomElement('asyncRequests', headerInsertionPoint).innerHTML =
- asyncRequests + ' Requests Remaining';
- //Async Requests Completed
- if(asyncRequestsCompleted > 0)
- getOrCreateCustomElement('requestsCompleted', headerInsertionPoint).innerHTML =
- asyncRequestsCompleted + ' Requests Complete (+' + steamRequestsCompleted + ' Steam)';
- //Timed Out Requests
- if(asyncRequestsCancelled > 0)
- getOrCreateCustomElement('requestsCancelled', headerInsertionPoint).innerHTML =
- asyncRequestsCancelled + ' Requests Timed Out';
- }
- function sortAndDisplay()
- {
- try
- {
- //Order elements by time remaining, then modified probability (for data dump).
- arr_Giveaways.sort(function(a,b){
- if(a.timePenalty < b.timePenalty) return -1;
- if(a.timePenalty > b.timePenalty) return 1;
- if(a.fitness < b.fitness) return 1;
- if(a.fitness > b.fitness) return -1;
- if(a.remaining < b.remaining) return -1;
- if(a.remaining > b.remaining) return 1;
- return 0;
- });
- var result = 'Title\tCopies\tFitness\tModified Prob\tModifier\tProb per P\tProb\tCost ('
- + myP + 'P)\tEntries\tTime Left\tCreated\tTimePenalty\tAchievements\tMetaScore'
- + '\tReview\tGroup?\tContribAmt\tCan Enter'
- + '\tisDLC\tBase Game\tSteam URL\tBase Game URL\tGiveaway URL';
- for( var I = 0; I < arr_Giveaways.length; I++ )
- {
- result = result + '\n' + giveawayToString(arr_Giveaways[I])
- }
- var textarea = document.createElement('textarea');
- textarea.value = result;
- textarea.style = 'width: 100%; height: 100px; max-height: 100px';
- headerInsertionPoint.appendChild(textarea);
- textarea.select();
- //Auto-registers for deals based on rules
- if(AUTOREGISTER) doAutoregister();
- pollingAttempts = 0;
- // Persist the cache of information we've retrieved from steam
- sessionStorage.setItem("steam_cache", JSON.stringify(SteamRequestCache));
- //console.log(SteamRequestCache);
- }
- catch(ex){}
- // Start the countdown until the page refreshes
- polling = setInterval( function(){refreshWhenReady()}, pollingInterval);
- }
- function giveawayToString(giveaway)
- {
- return giveaway.title + '\t' + giveaway.copies + '\t' +
- giveaway.fitness + '\t' +
- giveaway.modifiedProb + '\t' +
- giveaway.modifier + '\t' +
- giveaway.prob_per_P + '\t' +
- giveaway.prob + '\t' +
- giveaway.cost + '\t' +
- giveaway.entries + '\t' +
- giveaway.remaining + '\t' +
- giveaway.created + '\t' +
- giveaway.timePenalty + '\t' +
- giveaway.achievements + '\t' +
- giveaway.metascore + '\t' +
- giveaway.review + '\t' +
- giveaway.isGroup + '\t' +
- giveaway.contribAmount + '\t' +
- giveaway.canEnter + '\t' +
- giveaway.isDLC + '\t' +
- (giveaway.isDLC ? giveaway.requiredTitle : 'N/A') + '\t' +
- giveaway.SteamURL + '\t' +
- (giveaway.isDLC ? giveaway.requiredURL : 'N/A') + '\t' +
- giveaway.url + '\t' +
- giveaway.tags.join();
- }
- function getOrCreateCustomElement(elemName, insertionPoint)
- {
- var myDiv = document.getElementById(elemName);
- if( myDiv == null )
- {
- myDiv = document.createElement('div');
- myDiv.setAttribute('id', elemName);
- insertionPoint.appendChild(myDiv);
- }
- return myDiv;
- }
- function appendProbabilityInfoToGiveaway(giveaway)
- {
- var giveawayInfo = giveaway.elem.getElementsByClassName('giveaway__columns')[0];
- var widthFill = giveaway.elem.getElementsByClassName('giveaway__column--width-fill')[0];
- giveawayInfo.children[0].innerHTML =
- giveawayInfo.children[0].innerHTML.replace("remaining", "left").replace("minute","min").replace("second","sec");
- giveawayInfo.children[0].style.fontSize = '10px';
- if(giveaway.timePenalty > 1.01)
- {
- var spanTimePenalty = document.createElement('span');
- spanTimePenalty.style.fontSize = '9px'
- spanTimePenalty.appendChild(document.createTextNode(' (Penalty: ' + giveaway.timePenalty.toFixed(0) + ')'));
- //giveawayInfo.insertBefore(spanTimePenalty, widthFill);
- // Shorten some words that are taking up space.
- giveawayInfo.children[0].appendChild(spanTimePenalty);
- }
- var spanProbability = document.createElement('span');
- spanProbability.appendChild(document.createTextNode('Prob: ' + (giveaway.prob * 100).toFixed(2) +
- '% (' + (giveaway.prob_per_P * 100).toFixed(2) + '% per P)'));
- giveawayInfo.insertBefore(spanProbability, giveawayInfo.childNodes[0]);
- }
- function appendSteamInfoToGiveaway(giveaway)
- {
- try{
- var heading = giveaway.elem.getElementsByClassName('giveaway__heading')[0];
- var giveawayLinks = giveaway.elem.getElementsByClassName('giveaway__links')[0];
- var span = document.createElement('span');
- span.innerHTML = ' <font size="0.5em"> '
- //+ (giveaway.isWishlist ? 'Wishlist! ' : '')
- //+ (giveaway.hasCards ? 'Cards ' : '')
- //+ (giveaway.achievements > 0 ? 'Achievements: ' + giveaway.achievements + ' ' : '')
- //+ (giveaway.review != null ? 'Reviews: ' + giveaway.review + ' ' : '')
- + (giveaway.isDLC ? ('DLC for ' + giveaway.requiredTitle + ' ') : '')
- + (giveaway.metascore != null ? 'Metascore: ' + giveaway.metascore + ' ' : '')
- + (giveaway.tags.length > 0 ? ('Tags: [' + giveaway.tags.join(', ') + '] ') : '')
- + (giveaway.error ? ('Error: ' + giveaway.error + ' ') : '')
- + '</font>';
- //console.log(giveaway.title + ": " + span.innerHTML);
- heading.appendChild(span);
- var textNode = document.createElement('span');
- textNode.style.fontSize = "10px";
- textNode.style.borderBottom = "none";
- textNode.style.boxShadow = "none";
- textNode.style.color = "inherit";
- if(typeof(giveaway.modifierLog) !== 'undefined')
- textNode.title = giveaway.modifierLog;
- textNode.innerHTML = 'Modifier: ' + Math.round(giveaway.modifier * 100) / 100 +
- ', ModifiedProb: ' + (giveaway.modifiedProb * 100).toFixed(2) +
- '%, Fitness: ' + (giveaway.modifiedProb / giveaway.timePenalty * 100).toFixed(3) + '%';
- giveawayLinks.appendChild(textNode);
- // Place some additional key features in the second row.
- var giveawayInfo = giveaway.elem.getElementsByClassName('giveaway__columns')[0];
- var widthFill = giveaway.elem.getElementsByClassName('giveaway__column--width-fill')[0];
- if(giveaway.isWishlist === true)
- {
- var spanWishlist = document.createElement('span');
- spanWishlist.style.color = "DarkGreen";
- spanWishlist.style.fontWeight = "bold";
- spanWishlist.appendChild(document.createTextNode('Wishlist!'));
- giveawayInfo.insertBefore(spanWishlist, widthFill);
- }
- if(giveaway.hasCards === true)
- {
- var spanCards = document.createElement('span');
- spanCards.appendChild(document.createTextNode('Cards'));
- giveawayInfo.insertBefore(spanCards, widthFill);
- }
- if(giveaway.achievements != null && giveaway.achievements > 0)
- {
- var spanAchievements = document.createElement('span');
- spanAchievements.appendChild(document.createTextNode(giveaway.achievements + ' Achievements'));
- giveawayInfo.insertBefore(spanAchievements, widthFill);
- }
- if(giveaway.review != null)
- {
- var spanReview = document.createElement('span');
- spanReview.appendChild(document.createTextNode('Rated ' + giveaway.review));
- switch(giveaway.review)
- {
- case "Overwhelmingly Positive":
- spanReview.style.fontWeight = "bold";
- case "Very Positive":
- spanReview.style.color = "DarkGreen";
- break;
- case "Positive": spanReview.style.color = "Green"; break;
- case "Mostly Positive": spanReview.style.color = "#6f9c6f"; break;
- case "Mixed": spanReview.style.color = "Grey"; break;
- case "Mostly Negative":
- case "Negative":
- spanReview.style.color = "Maroon";
- break;
- case "Very Negative":
- case "Overwhelmingly Negative":
- spanReview.style.fontWeight = "bold";
- spanReview.style.color = "Red";
- break;
- default: spanReview = null;
- }
- if(spanReview != null)
- giveawayInfo.insertBefore(spanReview, widthFill);
- }
- //Change the entry background colour based on how good/bad the modified probability is.
- if(giveaway.modifiedProb < thresholdForP)
- giveaway.elem.style.background = ColorLuminance("#FF8080", Math.max(0, Math.pow(giveaway.modifiedProb/thresholdForP, 0.2) - 0.2) );
- else
- giveaway.elem.style.background = ColorLuminance("#66FF66", Math.max(0, 1.0 - giveaway.modifiedProb/(thresholdForP*50)) );
- }
- catch(err)
- {
- console.log("Error appending giveaway info: " + err.message);
- reset();
- }
- }
- function refreshWhenReady()
- {
- modifiedRefreshTime = RefreshTime + (registrations > 0 ? RegistrationTime : 0);
- updateAsyncRequestsStatus();
- //Async Requests Pending
- if(registrations > 0)
- getOrCreateCustomElement('Registering', headerInsertionPoint).innerHTML =
- registrations + ' New Registration(s).';
- //Show how long we've been waiting
- getOrCreateCustomElement('RefreshTime', headerInsertionPoint).innerHTML =
- 'Refreshing in T-' + ((modifiedRefreshTime - pollingAttempts * pollingInterval ) / 1000) + ' Seconds.';
- //Been trying for too long, maybe something is stuck? Just continue with what information we have
- if( pollingAttempts >= modifiedRefreshTime / pollingInterval )
- {
- window.clearInterval(polling);
- window.location = 'http://www.steamgifts.com';
- //In case we have trouble connecting, retry every 15 seconds:
- pollingAttempts = 0;
- polling = setInterval( function(){keeptryingToRefresh()}, pollingInterval);
- }
- else
- pollingAttempts += 1;
- }
- function keeptryingToRefresh()
- {
- updateAsyncRequestsStatus();
- pollingAttempts += 1;
- if(pollingAttempts % (RefreshTime / pollingInterval) == 0)
- {
- // try to kick off a fresh request.
- window.location = 'http://www.steamgifts.com';
- }
- //Show how long we've been trying to refresh
- getOrCreateCustomElement('RefreshTime', headerInsertionPoint).innerHTML =
- 'Reloading for ' + ((pollingAttempts * pollingInterval) / 1000) + ' seconds.'
- }
- function doAutoregister()
- {
- try
- {
- console.log("Auto Registration Process Beginning.");
- AUTOREGISTER = false; //Avoid re-entry if some threading thing happens.
- //Get myP again in case it's changed.
- var pointsElem = document.getElementsByClassName('nav__points')[0];
- myP = parseInt(pointsElem.innerHTML);
- //Order elements by descending fitness
- arr_Giveaways.sort(function(a,b){
- if(a.fitness < b.fitness) return 1;
- if(a.fitness > b.fitness) return -1;
- return 0;
- });
- //register(arr_Giveaways[arr_Giveaways.length-1]);
- //First rule, get in on good deals that are about to expire
- //Register for anything within the fitness tolerance based on our current points
- console.log("Looking at " + arr_Giveaways.length + " giveaways.");
- for( var I = 0; I < arr_Giveaways.length; I++ )
- {
- //console.log("Considering " + arr_Giveaways[I].title +
- // ". Fitness is " + parseInt(arr_Giveaways[I].fitness * 100000) / 1000 +
- // "% (threshold is " + parseInt(FitnessThreshold(myP) * 100000) / 1000 + "%)");
- if( arr_Giveaways[I].fitness >= FitnessThreshold(myP) ) {
- if( meetsBareMinimumRequirementsForRegistration(arr_Giveaways[I]) )
- {
- console.log(arr_Giveaways[I].title + " fitness and minimum requirements met.");
- register(arr_Giveaways[I]);
- myP -= arr_Giveaways[I].cost;
- }
- else {
- break;
- }
- }
- else {
- console.log("Fitness requirements not met. All other giveaways have a lower fitness.");
- break;
- }
- }
- //Cap is at 300 points, so might as well spend them or lose them:
- if( myP >= (MaxP - 20) )
- {
- console.log("Points are almost at maximum, looking at giveaways to enter regardless of time penalty.");
- //Second rule:
- //Reduce the timepenalty by one for certain giveaways that we predict to do well,
- //in case nothing actually below the max time penalty is fit enough to register and we're near our cap.
- for( var I = 0; I < arr_Giveaways.length; I++ )
- {
- //Group giveaways and high contrib amount giveaways usually end up having good odds,
- //so if we have points to blow and nothing good is ending soon, we can allow long term hunches.
- if( arr_Giveaways[I].timePenalty == MaxPenalty &&
- ( arr_Giveaways[I].isGroup ||
- arr_Giveaways[I].ContribAmount >= 10 ) )
- arr_Giveaways[I].timePenalty --;
- }
- //Sort by some random metric of modified prob and time penalty
- arr_Giveaways.sort(function(a,b){
- if((a.modifiedProb / (a.timePenalty/10000)) < (b.modifiedProb / (b.timePenalty/10000))) return -1;
- return 1;
- //if(a.timePenalty > b.timePenalty) return 1;
- //if(a.fitness < b.fitness) return 1;
- //if(a.fitness > b.fitness) return -1;
- //return 0;
- });
- //Register for the first item in this new ordered list that meets min requirements and whose modified prob exceeds our threshold.
- for( var I = 0; I < arr_Giveaways.length; I++ )
- {
- if( arr_Giveaways[I].modifiedProb >= FitnessThreshold(myP) &&
- meetsBareMinimumRequirementsForRegistration(arr_Giveaways[I]) )
- {
- register(arr_Giveaways[I]);
- return;
- }
- }
- //Third rule:
- if( myP >= (MaxP - 5) )
- {
- console.log("Points at maximum, entering the fittest giveaway.");
- //If all else fails, register for the one with the lowest cost and highest fitness.
- arr_Giveaways.sort(function(a,b){
- if(a.cost < b.cost) return -1;
- if(a.cost > b.cost) return 1;
- if(a.fitness < b.fitness) return 1;
- if(a.fitness > b.fitness) return -1;
- return 0;
- });
- register(arr_Giveaways[0]);
- }
- }
- }
- catch(err)
- {
- console.log("Error during auto-register." + err.message + "\nLine: " + err.lineNumber);
- throw err;
- }
- }
- //We penalize the probability of games that have a long time left, since there's always a flood of entries in the last legs of a race.
- //Expect "MaxPenalty" times more entries if the entry has more than "LifeLimit" time left.
- function calcTimePenalty(g)
- {
- //Lower Limit, Upper Limit, and Exponential Factors for Interpolation of Penalties
- var TLeftLower = 6*60, TLeftUpper = 6*60*60, TLeftExponent = 1.9; //For penalizing having a lot of time left
- var AliveLower = 20*60, AliveUpper = 1*60*60, AliveExponent = 8.0; //For penalizing not having been alive for long
- var ElapsLower = 0.05, ElapsUpper = 0.75, ElapsExponent = 3.0; //Longevaty penalty alternative, using percentage of lifetime elapsed
- if(g.remaining <= TLeftLower) return 1;
- var lifeElapsed = g.remaining / (g.remaining + g.created);
- var TLeftPenalty = Math.pow(Math.min(1, Math.max(0, g.remaining - TLeftLower) / (TLeftUpper-TLeftLower) ), TLeftExponent);
- var AlivePenalty = Math.pow(Math.max(0, 1 - Math.max(0, g.created - AliveLower) / (AliveUpper-AliveLower) ), AliveExponent);
- var ElapsPenalty = Math.pow(Math.min(1, Math.max(0, lifeElapsed - ElapsLower) / (ElapsUpper-ElapsLower) ), ElapsExponent);
- var CumulPenalty = 1 + (MaxPenalty-1) * Math.min(AlivePenalty + ElapsPenalty + TLeftPenalty, 1);
- return CumulPenalty;
- }
- function meetsBareMinimumRequirementsForRegistration(giveaway)
- {
- return giveaway.canEnter &&
- giveaway.timePenalty < MaxPenalty && //The timing for entry isn't so bad as having incurred the max penalty
- giveaway.remaining < (12 * 60 * 60) && //The giveaway has less than 12 hours remaining.
- giveaway.created > (10 * 60) && //The giveaway has been alive for more than 10 minutes.
- !(giveaway.entries <= 5 && giveaway.remaining > 15*60 ); //If the giveaway has less than 5 entries it has less than 15 minutes remaining.
- // && giveaway.elem.clientWidth > 0; //The giveaway hasn't been hidden (filtered) on the main page. This may prevent race conditions with the filtering script.
- }
- function calcModifierAndFitness(giveaway)
- {
- var baseChance = 100*giveaway.copies/giveaway.entries;
- giveaway.modifierLog = "Pure win chance is " + giveaway.copies + " / " + giveaway.entries + " (copies / current entries) = " + (baseChance).toPrecision(3)/1 + "%\n";
- var chancePerP = baseChance / giveaway.cost;
- giveaway.modifierLog += "Divided by entry cost (" + giveaway.cost + "P) = " + chancePerP.toPrecision(3)/1 + "% chance per unit cost.\n\n";
- giveaway.modifier = favourabilityModifier(giveaway);
- giveaway.modifiedProb = giveaway.prob_per_P * giveaway.modifier;
- giveaway.modifierLog += "\nFitness = " + (100*giveaway.prob_per_P).toPrecision(3)/1 + "% * " + giveaway.modifier.toPrecision(3)/1 + " = " + (100*giveaway.modifiedProb).toPrecision(3)/1 + "%";
- giveaway.fitness = giveaway.modifiedProb / giveaway.timePenalty;
- if(giveaway.timePenalty > 1)
- giveaway.modifierLog += "\n\nPenalty of " + giveaway.timePenalty.toFixed(0)/1 + " applied since there's time for odds to change before giveaway closes." +
- "\nSo current session fitness is: " + (100*giveaway.fitness).toPrecision(3)/1 + "%";
- var currentThreshold = FitnessThreshold(myP);
- giveaway.modifierLog += "\n\nThreshold for entry at current balance of " + myP + "P is " + (currentThreshold*100).toPrecision(3)/1 + "%.\n";
- if(currentThreshold <= giveaway.fitness)
- giveaway.modifierLog += "Entering Giveaway!";
- else if(currentThreshold <= giveaway.modifiedProb)
- giveaway.modifierLog += "Not entering giveaway now, but likely entering it before it closes.";
- else
- giveaway.modifierLog += "Not entering giveaway now, probably not ever.";
- }
- //Detects preferred items (such as game bundles or those with steam achievements) and lowers the threshold for preferred items
- function favourabilityModifier(giveaway)
- {
- var modifier = 1.0;
- // Note using .toPrecision(3)/1 helps display non-integer values as a string without crazy precision, like 0.2 becoming 0.19999999999999
- giveaway.modifierLog += "Base multiplier is " + modifier.toPrecision(3)/1 + "\n";
- // Giveaway 'Quality' Modifiers used to favour giveaways with an above-average opportunity to win -
- // which is already inherent in its number of entries, but this scales it further.)
- //Reward games where the giveaway period is small, since less people have had
- //time to enter, it's probably a relatively good deal for that particular game.
- if( giveaway.created + giveaway.remaining <= shortGiveawayTime ){
- modifier += shortGiveawayModifier;
- giveaway.modifierLog += "+" + shortGiveawayModifier + " (Short giveaway - less competition) = " + modifier.toPrecision(3)/1 + "\n";
- }
- //Same for games that are group giveaways
- if( giveaway.isGroup ){
- modifier += groupGiveawayModifier;
- giveaway.modifierLog += "+" + groupGiveawayModifier + " (Group giveaway - less competition) = " + modifier.toPrecision(3)/1 + "\n";
- }
- //Same for games that have a high contributor value
- if( giveaway.contribAmount > 0 ){
- var contribBonus = giveaway.contribAmount >= contributorLevelCap ? maxContributorModifier :
- maxContributorModifier * giveaway.contribAmount / contributorLevelCap;
- modifier += contribBonus;
- giveaway.modifierLog += "+" + contribBonus + " (Contributor level " + giveaway.contribAmount + " required - less competition) = " + modifier.toPrecision(3)/1 + "\n";
- }
- // Game 'Quality' Modifiers (Used for favouring "Good" games and penalizing "Bad" games.)
- // Games on my wish-list are the best! Big bonus
- if(giveaway.isWishlist === true) {
- modifier = onWishListModifier;
- giveaway.modifierLog += "+" + onWishListModifier + " (Wishlisted) = " + modifier.toPrecision(3)/1 + "\n";
- }
- // Otherwise, try to guess how much I'd want the game based on these things:
- else
- {
- // Reward the game having a certain number of achievements
- if(giveaway.achievements != null) {
- for(var i = achieveTiers.length; i > 0; i--) {
- if(giveaway.achievements >= achieveTiers[i]) {
- modifier += achieveBonus[i];
- giveaway.modifierLog += "+" + achieveBonus[i] + " (More than " + achieveTiers[i] + " achievements) = " + modifier.toPrecision(3)/1 + "\n";
- break;
- }
- }
- }
- // Games that produce cards can have the cards sold for profit!
- if(giveaway.hasCards === true) {
- modifier += hasCardsBonus;
- giveaway.modifierLog += "+" + hasCardsBonus + " (Has Cards) = " + modifier.toPrecision(3)/1 + "\n";
- }
- // Reward good metascore, penalize bad score (cumulative)
- // Note: Probably obsolete, steam pages don't tend to list metascores anymore
- if(giveaway.metascore != null) {
- modifier += (giveaway.metascore - 70) / 50;
- giveaway.modifierLog += "+" + ((giveaway.metascore - 70) / 50) + " (Metascore) = " + modifier.toPrecision(3)/1 + "\n";
- }
- if(giveaway.review != null) {
- var reviewScore = 0;
- switch(giveaway.review)
- {
- case "Overwhelmingly Positive": reviewScore += reviewScale[0]; break;
- case "Very Positive": reviewScore += reviewScale[1]; break;
- case "Positive": reviewScore += reviewScale[2]; break;
- case "Mostly Positive": reviewScore += reviewScale[3]; break;
- case "Mixed": reviewScore += reviewScale[4]; break;
- case "Mostly Negative": reviewScore += reviewScale[5]; break;
- case "Negative": reviewScore += reviewScale[6]; break;
- case "Very Negative": reviewScore += reviewScale[7]; break;
- case "Overwhelmingly Negative": reviewScore += reviewScale[8]; break;
- }
- if(reviewScore != 0){
- modifier += reviewScore;
- giveaway.modifierLog += "+" + reviewScore + " (Reviews are " + giveaway.review + ") = " + modifier.toPrecision(3)/1 + "\n";
- }
- }
- // Reward Game Bundles (multiple games)
- if(giveaway.title.indexOf('Gala') > -1 || giveaway.title.indexOf('Collection') > -1 ||
- giveaway.title.indexOf('Pack') > -1 || giveaway.title.indexOf('Bundle') > -1) {
- modifier += bundleBonus;
- giveaway.modifierLog += "+" + bundleBonus + " (Includes multiple games) = " + modifier.toPrecision(3)/1 + "\n";
- }
- // Steam Game Packs get a bonus for each game in the pack.
- if(giveaway.packGames != null) {
- var numPackGames = giveaway.packGames.length -1;
- modifier += numPackGames * packGameValue;
- giveaway.modifierLog += "+" + (numPackGames * packGameValue) + " (Bundle includes " + numPackGames + " additional games/dlc) = " + modifier.toPrecision(3)/1 + "\n";
- }
- // If DLC isn't on my wish list I probably don't want it.
- // This used to be cut down far more when there was no detection for when you didn't own the base game for the DLC.
- if(giveaway.isDLC){
- modifier += dlcPenalty;
- giveaway.modifierLog += "+" + dlcPenalty + " (Is DLC) = " + modifier.toPrecision(3)/1 + "\n";
- }
- // For every unwanted tag, apply a penalty.
- var unwantedTagCount = 0;
- for(tag of unwantedTags) {
- if(giveaway.tags.includes(tag))
- unwantedTagCount++;
- }
- if(unwantedTagCount > 0){
- modifier += unwantedTagCount * unwantedTagPenalty;
- giveaway.modifierLog += "+" + (unwantedTagCount * unwantedTagPenalty) + " (Has " + unwantedTagCount + " unwanted tags) = " + modifier.toPrecision(3)/1 + "\n";
- }
- // For every desireable tag, apply a bonus.
- var wantedTagCount = 0;
- for(tag of wantedTags) {
- if(giveaway.tags.includes(tag))
- wantedTagCount++;
- }
- if(wantedTagCount > 0){
- modifier += wantedTagCount * wantedTagBonus;
- giveaway.modifierLog += "+" + (wantedTagCount * wantedTagBonus) + " (Has " + wantedTagCount + " wanted tags) = " + modifier.toPrecision(3)/1 + "\n";
- }
- // Don't want Soundtracks (but game and soundtrack bundle is ok)
- if( !/and Soundtrack/i.test(giveaway.title) && !/\+ Soundtrack/i.test(giveaway.title) )
- if( / Soundtrack/i.test(giveaway.title) || / OST/.test(giveaway.title) ){
- modifier += soundtrackPenalty;
- giveaway.modifierLog += "+" + soundtrackPenalty + " (Is a Soundtrack) = " + modifier.toPrecision(3)/1 + "\n";
- }
- // If after all the modifiers are compiled, the net result is negative, convert it into
- // a number between 0 and 1 such that the returned multiplier will reduce the fitness but remain positive.
- if( modifier < 1 ){
- var punitiveMultiplier = 1 / Math.pow(10, 1-modifier);
- giveaway.modifierLog += "Negative modifier of " + modifier.toPrecision(3)/1 + " converted into a punative multiplier of " + punitiveMultiplier.toPrecision(3)/1 + "\n";
- modifier = punitiveMultiplier;
- }
- }
- var qualityModifierAdjusted = 1 + overallQualityModifierPower * (modifier-1);
- if(overallQualityModifierPower != 1) {
- giveaway.modifierLog += "Quality modifier scaling factor of " + overallQualityModifierPower + " is applied.";
- }
- giveaway.modifierLog += "Final multiplier is: " + qualityModifierAdjusted.toPrecision(3)/1 + "\n";
- return qualityModifierAdjusted;
- }
- //Returns the time (in seconds) based on the formatted time string.
- function parseTimeString(timeString)
- {
- var toSecondsMult = 1;
- var match = timeString.match(/(\d+) second/);
- if( match == null ) {
- toSecondsMult *= 60;
- match = timeString.match(/(\d+) minute/);
- } if( match == null ) {
- toSecondsMult *= 60;
- match = timeString.match(/(\d+) hour/);
- } if( match == null ) {
- toSecondsMult *= 24;
- match = timeString.match(/(\d+) day/);
- } if( match == null ) {
- toSecondsMult *= 7;
- match = timeString.match(/(\d+) week/);
- }
- return match == null ? null : parseInt(match[1]) * toSecondsMult;
- }
- function registerGiveawayPage(document)
- {
- try
- {
- //Check to see this giveaway is still enterable (in case of a multiple instances conflict)
- var enterButton = document.querySelector(".sidebar__entry-insert");
- if( !enterButton.classList.contains('is-hidden') )
- {
- // try to submit a comment after registering
- /* Apparently people don't like comments anymore.
- setTimeout( function()
- {
- var commentForm = w.document.querySelector (".comment__description form");
- var responses = ["Thanks!", "ty", "thanks", "Thanks", "thanks!", "cheers", "ty :)",
- "Awesome, thanks!", "ty~", ":D", "thanks <3"];
- commentForm.querySelector ("textarea").value = responses[Math.floor(Math.random()*responses.length)];
- commentForm.querySelector (".comment__submit-button").click();
- console.log('Commented on ' + giveaway.title);
- w.scrollTo(0, 0);
- }, RegistrationTime / 2.0 );
- */
- enterButton.click();
- }
- else
- {
- console.log('Was already registered for ' + document.location);
- }
- }
- catch(err)
- {
- console.log('Registration error: ' + err.message + "\nLine: " + err.lineNumber);
- }
- }
- function register(giveaway)
- {
- asyncRequests ++;
- var success = false;
- var toRunOnLoad = function(e){
- console.log('Registration frame loaded for ' + giveaway.title);
- registerGiveawayPage(e);
- asyncRequests --;
- registrations ++;
- console.log('Registered for ' + giveaway.title);
- };
- console.log('Registering for ' + giveaway.title + '...');
- try {
- ////////// NEW WINDOW METHOD /////////////////////
- var popup = window.open(giveaway.url, "_blank");
- var autoEnterInterval = setInterval( function(){
- try {
- console.log('Attempting to send retister message for ' + giveaway.title);
- popup.postMessage("EnterGiveaway", "*");
- }
- catch(ex) {
- console.log("Window closed. Assuming we've registered for " + giveaway.title);
- clearInterval(autoEnterInterval);
- asyncRequests --;
- registrations ++;
- }
- }, pollingInterval);
- /////////// IFRAME METHOD ////////////////////////
- /*
- var myIFrame = document.createElement('iframe');
- myIFrame.setAttribute('id', 'Entry' + asyncRequests);
- document.body.insertBefore(myIFrame, document.getElementsByClassName('page__outer-wrap')[0]);
- myIFrame.width = '100%'
- myIFrame.src = giveaway.url;
- myIFrame.contentWindow.onload = function() { toRunOnLoad(myIFrame.contentWindow.document) };
- */
- /////////// EMBEDDED DIV METHOD ////////////////
- /*
- var entryDiv = document.createElement('div');
- entryDiv.className = "inlinedEntry";
- giveaway.elem.appendChild(entryDiv);
- // Requires JQUERY selector
- //entryDiv.load(giveaway.url, function() { toRunOnLoad(entryDiv); });
- var request = new XMLHttpRequest();
- request.open('GET', giveaway.url, true);
- request.onload = function() {
- if (request.status >= 200 && request.status < 400) {
- var resp = request.responseText;
- resp = resp.replace(/style/g, "style-disabled");
- entryDiv.innerHTML = resp;
- toRunOnLoad(entryDiv);
- }
- };
- request.send();
- */
- console.log('Waiting for load...');
- }
- catch(err)
- {
- console.log('Registration error: ' + err.message + "\nLine: " + err.lineNumber);
- }
- }
- function collectionToArray(collection)
- {
- var ary = [];
- for(var i=0, len = collection.length; i < len; i++)
- {
- ary.push(collection[i]);
- }
- return ary;
- }
- function ColorLuminance(hex, lum) {
- // validate hex string
- hex = String(hex).replace(/[^0-9a-f]/gi, '');
- if (hex.length < 6) {
- hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
- }
- lum = lum || 0;
- // convert to decimal and change luminosity
- var rgb = "#", c, i;
- for (i = 0; i < 3; i++) {
- c = parseInt(hex.substr(i*2,2), 16);
- c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
- rgb += ("00"+c).substr(c.length);
- }
- return rgb;
- }
- //Remove an element from an array by value
- function removeA(arr) {
- var what, a = arguments, L = a.length, ax;
- while (L > 1 && arr.length) {
- what = a[--L];
- while ((ax= arr.indexOf(what)) !== -1) {
- arr.splice(ax, 1);
- }
- }
- return arr;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement