Skip to content

Instantly share code, notes, and snippets.

@Allen-B1
Last active March 17, 2021 21:28
Show Gist options
  • Save Allen-B1/b5b9440306848d4349b287103cc18b98 to your computer and use it in GitHub Desktop.
Save Allen-B1/b5b9440306848d4349b287103cc18b98 to your computer and use it in GitHub Desktop.
// ==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