Last active
March 17, 2021 21:28
-
-
Save Allen-B1/b5b9440306848d4349b287103cc18b98 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name generals.io mini-tournaments | |
// @namespace http://generals.io/profiles/person2597 | |
// @version 0.1.1 | |
// @description minitournament helper for generals.io | |
// @author person2597 | |
// @match https://brackethq.com/b/* | |
// @icon https://www.google.com/s2/favicons?domain=brackethq.com | |
// @grant GM.xmlHttpRequest | |
// ==/UserScript== | |
// Return completed rounds, from oldest to newest. | |
function getCompletedRounds() { | |
let gamesElems = document.querySelectorAll(".game.game-completed"); | |
let games = []; | |
for (let gameElem of gamesElems) { | |
let number = gameElem.querySelector(".game-number").childNodes[0].data.trim() | 0; | |
let slots = gameElem.querySelectorAll(".slot"); | |
let players = []; | |
let scores = []; | |
for (let slot of slots) { | |
players.push(slot.querySelector(".slot-name").innerText); | |
scores.push(slot.querySelector(".slot-score").innerText.trim() | 0); | |
} | |
games.push({id: number, players: players, scores: scores}); | |
} | |
games.sort(function(a,b) { | |
return a.id - b.id; | |
}); | |
return games; | |
} | |
function getPlayers(completedRounds) { | |
let players = new Set(); | |
for (let game of completedRounds) { | |
players.add(game.players[0]); | |
players.add(game.players[1]); | |
} | |
return players; | |
} | |
function getReplaysRange(username, offset) { | |
return new Promise(function(resolve) { | |
GM.xmlHttpRequest({ | |
url: "http://generals.io/api/replaysForUsername?u=" + encodeURIComponent(username) + "&offset=" + offset + "&count=200", | |
method: "GET", | |
onload: function(resp) { | |
let json = JSON.parse(resp.responseText); | |
resolve(json); | |
} | |
}); | |
}); | |
} | |
function getReplaysTo(username, date) { | |
return new Promise(function(resolve) { | |
let allData = []; | |
let f = function(offset) { | |
getReplaysRange(username, offset).then(function(data) { | |
allData.push.apply(allData, data); | |
if (data[0] && data[0].started > date) { | |
f(offset+200); | |
} else { | |
allData = allData.filter(r => r.started > date); | |
resolve(allData); | |
} | |
}); | |
}; | |
f(0); | |
}); | |
} | |
async function matchReplays(completedRounds, date) { | |
let players = getPlayers(completedRounds); | |
let allReplays = {}; | |
for (let player of players) { | |
let replays = await getReplaysTo(player, date); | |
for (let replay of replays) { | |
if (replay.type == "custom") { | |
allReplays[replay.id] = replay; | |
} | |
} | |
} | |
let allReplaysOrder = Object.keys(allReplays); | |
allReplaysOrder.sort(function (a, b) { | |
return allReplays[a].started - allReplays[b].started; | |
}); | |
let usedReplays = new Set(); | |
let matches = {}; | |
for (let round of completedRounds) { | |
var playerScores = {}; | |
for (let i = 0; i < round.scores.length; i++) playerScores[round.players[i]] = round.scores[i]; | |
let replays = []; | |
for (let player in playerScores) { | |
let otherPlayer = round.players.filter(x => x != player)[0]; | |
for (let j = 0; j < playerScores[player]; j++) { | |
// Find replay corresponding to player win | |
for (let replayID of allReplaysOrder) { | |
if (usedReplays.has(replayID)) continue; | |
let replay = allReplays[replayID]; | |
if (replay.ranking.length == 2 && replay.ranking[0].name == player && replay.ranking[1].name == otherPlayer) { | |
replays.push(replay.id); | |
usedReplays.add(replay.id); | |
break; | |
} | |
} | |
} | |
} | |
matches[round.id] = replays; | |
} | |
return matches; | |
} | |
let matches = {}; | |
setTimeout(async function() { | |
if (document.getElementById("field-sport").value == "generals.io") { | |
let date = new Date(document.title.match(/\(.*\)/)[0].slice(1, -1)); | |
let updateMatches = async function() { | |
let rounds = getCompletedRounds(); | |
matches = await matchReplays(rounds, date); | |
console.log("tournaments: update matches"); | |
}; | |
setInterval(updateMatches, 10000); | |
await updateMatches(); | |
let observer = new MutationObserver(updateMatches); | |
observer.observe(document.querySelector(".preview"), {childList:true, subtree:true}); | |
const style = document.createElement('style'); | |
style.textContent = ` | |
.game-number {color:teal !important; } | |
.game-number:hover {text-decoration: underline;} | |
`; | |
document.head.append(style); | |
let pathparts = location.pathname.split("/").filter(Boolean); | |
let tournID = pathparts[pathparts.length-1]; | |
let numbers = document.querySelectorAll(".game .game-number"); | |
for (let numberElem of numbers) { | |
if (numberElem.childNodes.length == 0) continue; | |
numberElem.addEventListener("click", function(e) { | |
if (this.childNodes[0].data.trim() in matches) { | |
let replays = matches[this.childNodes[0].data.trim()]; | |
for (let replay of replays) { | |
window.open("https://generals.io/replays/" + replay, "_blank"); | |
} | |
} else { | |
let num = this.childNodes[0].data.trim(); | |
window.open("https://generals.io/games/" + tournID + "-" + num); | |
} | |
e.stopPropagation(); | |
}); | |
} | |
} | |
}, 500); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment