Skip to content

Instantly share code, notes, and snippets.

@StachuDotNet
Last active April 16, 2026 19:19
Show Gist options
  • Select an option

  • Save StachuDotNet/4bbf43e93256d5a94bfb6e563b8eab93 to your computer and use it in GitHub Desktop.

Select an option

Save StachuDotNet/4bbf43e93256d5a94bfb6e563b8eab93 to your computer and use it in GitHub Desktop.
linkedin games daily rankings overview

I've gotten into the habit of playing LinkedIn games daily. at least, most of them. I wanted to compare my results against my "connections."

So I built this silly hacky bookmarklet to get the job done.

javascript:%28async%20%28%29%20%3D%3E%20%7B%0A%20%20const%20GAMES%20%3D%20%5B%0A%20%20%20%20%7B%20slug%3A%20%22patches%22%2C%20label%3A%20%22Patches%22%20%7D%2C%0A%20%20%20%20%7B%20slug%3A%20%22queens%22%2C%20label%3A%20%22Queens%22%20%7D%2C%0A%20%20%20%20%7B%20slug%3A%20%22zip%22%2C%20label%3A%20%22Zip%22%20%7D%2C%0A%20%20%20%20%7B%20slug%3A%20%22mini-sudoku%22%2C%20label%3A%20%22Sudoku%22%20%7D%2C%0A%20%20%20%20%7B%20slug%3A%20%22tango%22%2C%20label%3A%20%22Tango%22%20%7D%2C%0A%20%20%5D%3B%0A%20%20const%20MAX_WAIT%20%3D%2015000%2C%20SETTLE%20%3D%201200%3B%0A%20%20const%20scrape%20%3D%20async%20%28%7B%20slug%2C%20label%20%7D%29%20%3D%3E%20%7B%0A%20%20%20%20const%20iframe%20%3D%20document.createElement%28%22iframe%22%29%3B%0A%20%20%20%20iframe.style.cssText%20%3D%20%22position%3Afixed%3Bleft%3A-99999px%3Btop%3A0%3Bwidth%3A1200px%3Bheight%3A1600px%3Bborder%3A0%22%3B%0A%20%20%20%20iframe.src%20%3D%20%60%2Fgames%2F%24%7Bslug%7D%2Fresults%2Fleaderboard%2Fconnections%2F%60%3B%0A%20%20%20%20document.body.appendChild%28iframe%29%3B%0A%20%20%20%20const%20SEL_ROW%20%3D%20%27%5Bclass%2A%3D%22leaderboard-player__container%22%5D%27%3B%0A%20%20%20%20const%20SEL_NAME%20%3D%20%27%5Bclass%2A%3D%22leaderboard-player__name%22%5D%27%3B%0A%20%20%20%20const%20SEL_SCORE%20%3D%20%27%5Bclass%2A%3D%22leaderboard-player__score%22%5D%27%3B%0A%20%20%20%20const%20start%20%3D%20Date.now%28%29%3B%0A%20%20%20%20let%20lastCount%20%3D%20-1%2C%20stableAt%20%3D%200%3B%0A%20%20%20%20while%20%28Date.now%28%29%20-%20start%20%3C%20MAX_WAIT%29%20%7B%0A%20%20%20%20%20%20const%20doc%20%3D%20iframe.contentDocument%3B%0A%20%20%20%20%20%20const%20count%20%3D%20doc%0A%20%20%20%20%20%20%20%20%3F%20%5B...doc.querySelectorAll%28SEL_ROW%29%5D.filter%28c%20%3D%3E%20c.querySelector%28SEL_SCORE%29%29.length%0A%20%20%20%20%20%20%20%20%3A%200%3B%0A%20%20%20%20%20%20if%20%28count%20%3E%200%20%26%26%20count%20%3D%3D%3D%20lastCount%29%20%7B%0A%20%20%20%20%20%20%20%20if%20%28Date.now%28%29%20-%20stableAt%20%3E%20SETTLE%29%20break%3B%0A%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20stableAt%20%3D%20Date.now%28%29%3B%0A%20%20%20%20%20%20%20%20lastCount%20%3D%20count%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20await%20new%20Promise%28r%20%3D%3E%20setTimeout%28r%2C%20200%29%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20const%20rows%20%3D%20%5B%5D%3B%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20let%20rank%20%3D%200%3B%0A%20%20%20%20%20%20for%20%28const%20c%20of%20iframe.contentDocument.querySelectorAll%28SEL_ROW%29%29%20%7B%0A%20%20%20%20%20%20%20%20const%20name%20%3D%20c.querySelector%28SEL_NAME%29%3F.textContent.trim%28%29%3B%0A%20%20%20%20%20%20%20%20const%20score%20%3D%20c.querySelector%28SEL_SCORE%29%3F.textContent.trim%28%29%3B%0A%20%20%20%20%20%20%20%20if%20%28%21name%20%7C%7C%20%21score%29%20continue%3B%0A%20%20%20%20%20%20%20%20rows.push%28%7B%20rank%3A%20%2B%2Brank%2C%20name%2C%20score%20%7D%29%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%20catch%20%7B%7D%0A%20%20%20%20console.log%28%60%5Bli-rankings%5D%20%24%7Blabel%7D%3A%20%24%7Brows.length%7D%20rows%60%29%3B%0A%20%20%20%20iframe.remove%28%29%3B%0A%20%20%20%20return%20%7B%20label%2C%20rows%20%7D%3B%0A%20%20%7D%3B%0A%20%20const%20all%20%3D%20await%20Promise.all%28GAMES.map%28scrape%29%29%3B%0A%20%20const%20people%20%3D%20%7B%7D%2C%20maxRank%20%3D%20%7B%7D%3B%0A%20%20for%20%28const%20%7B%20label%2C%20rows%20%7D%20of%20all%29%20%7B%0A%20%20%20%20maxRank%5Blabel%5D%20%3D%20rows.length%3B%0A%20%20%20%20for%20%28const%20%7B%20rank%2C%20name%2C%20score%20%7D%20of%20rows%29%20%28people%5Bname%5D%20%7C%7C%3D%20%7B%7D%29%5Blabel%5D%20%3D%20%7B%20rank%2C%20score%20%7D%3B%0A%20%20%7D%0A%20%20const%20table%20%3D%20Object.entries%28people%29.map%28%28%5Bname%2C%20ranks%5D%29%20%3D%3E%20%7B%0A%20%20%20%20const%20perGame%20%3D%20GAMES.map%28g%20%3D%3E%20ranks%5Bg.label%5D%20%7C%7C%20null%29%3B%0A%20%20%20%20const%20sum%20%3D%20perGame.reduce%28%28a%2C%20rk%2C%20i%29%20%3D%3E%20a%20%2B%20%28rk%3F.rank%20%3F%3F%20maxRank%5BGAMES%5Bi%5D.label%5D%29%2C%200%29%3B%0A%20%20%20%20return%20%7B%20name%2C%20perGame%2C%20sum%20%7D%3B%0A%20%20%7D%29.sort%28%28a%2C%20b%29%20%3D%3E%20a.sum%20-%20b.sum%29%3B%0A%20%20document.getElementById%28%22__li_rankings%22%29%3F.remove%28%29%3B%0A%20%20document.getElementById%28%22__li_rankings_css%22%29%3F.remove%28%29%3B%0A%20%20const%20esc%20%3D%20s%20%3D%3E%20String%28s%29.replace%28%2F%5B%26%3C%3E%5D%2Fg%2C%20c%20%3D%3E%20%28%7B%20%22%26%22%3A%20%22%26amp%3B%22%2C%20%22%3C%22%3A%20%22%26lt%3B%22%2C%20%22%3E%22%3A%20%22%26gt%3B%22%20%7D%5Bc%5D%29%29%3B%0A%20%20const%20style%20%3D%20document.createElement%28%22style%22%29%3B%0A%20%20style.id%20%3D%20%22__li_rankings_css%22%3B%0A%20%20style.textContent%20%3D%20%60%0A%20%20%20%20%23__li_rankings%7Bposition%3Afixed%3Btop%3A50%25%3Bleft%3A50%25%3Btransform%3Atranslate%28-50%25%2C-50%25%29%3Bwidth%3A680px%3Bmax-width%3A95vw%3Bmax-height%3A90vh%3Boverflow%3Aauto%3Bbackground%3A%23fff%3Bcolor%3A%231d2226%3Bfont%3A14px%2F1.45%20-apple-system%2CBlinkMacSystemFont%2C%22Segoe%20UI%22%2CRoboto%2Csystem-ui%2Csans-serif%3Bbox-shadow%3A0%2020px%2060px%20rgba%280%2C0%2C0%2C.25%29%2C0%204px%2016px%20rgba%280%2C0%2C0%2C.08%29%3Bborder-radius%3A12px%3Bz-index%3A2147483647%3Bpadding%3A20px%2024px%2024px%7D%0A%20%20%20%20%23__li_rankings%20.h%7Bdisplay%3Aflex%3Bjustify-content%3Aspace-between%3Balign-items%3Abaseline%3Bmargin-bottom%3A16px%7D%0A%20%20%20%20%23__li_rankings%20h3%7Bmargin%3A0%3Bfont-size%3A16px%3Bfont-weight%3A600%7D%0A%20%20%20%20%23__li_rankings%20.ts%7Bfont-size%3A11px%3Bcolor%3A%23888%3Bfont-variant-numeric%3Atabular-nums%7D%0A%20%20%20%20%23__li_rankings%20.x%7Bbackground%3A0%3Bborder%3A0%3Bfont%3A20px%2F1%20system-ui%3Bcursor%3Apointer%3Bcolor%3A%23999%3Bpadding%3A0%204px%3Bmargin-left%3A12px%7D%0A%20%20%20%20%23__li_rankings%20.x%3Ahover%7Bcolor%3A%23000%7D%0A%20%20%20%20%23__li_rankings%20table%7Bborder-collapse%3Acollapse%3Bwidth%3A100%25%3Bfont-variant-numeric%3Atabular-nums%7D%0A%20%20%20%20%23__li_rankings%20th%7Bpadding%3A6px%208px%3Bfont-size%3A11px%3Bfont-weight%3A600%3Btext-transform%3Auppercase%3Bletter-spacing%3A.05em%3Bcolor%3A%23666%3Bborder-bottom%3A1px%20solid%20%23d9d9d9%3Btext-align%3Acenter%7D%0A%20%20%20%20%23__li_rankings%20th.L%2C%23__li_rankings%20td.L%7Btext-align%3Aleft%7D%0A%20%20%20%20%23__li_rankings%20th.R%2C%23__li_rankings%20td.R%7Btext-align%3Aright%7D%0A%20%20%20%20%23__li_rankings%20td%7Bpadding%3A8px%206px%3Bborder-bottom%3A1px%20solid%20%23f0f0f0%3Btext-align%3Acenter%7D%0A%20%20%20%20%23__li_rankings%20td%20small%7Bfont-size%3A11px%3Bcolor%3A%23999%3Bmargin-left%3A2px%3Bfont-weight%3A400%7D%0A%20%20%20%20%23__li_rankings%20td.n%7Bfont-weight%3A600%3Bcolor%3A%23888%3Bwidth%3A28px%7D%0A%20%20%20%20%23__li_rankings%20td.name%7Bfont-weight%3A500%7D%0A%20%20%20%20%23__li_rankings%20td.sum%7Bfont-weight%3A600%7D%0A%20%20%20%20%23__li_rankings%20td.miss%7Bcolor%3A%23c4c4c4%7D%0A%20%20%20%20%23__li_rankings%20tr.you%7Bbackground%3A%23eaf3ff%7D%0A%20%20%20%20%23__li_rankings%20tr.you%20td%7Bfont-weight%3A600%7D%0A%20%20%20%20%23__li_rankings%20tr%3Alast-child%20td%7Bborder-bottom%3A0%7D%0A%20%20%20%20%23__li_rankings%20td.m1%7Bcolor%3A%23c19400%7D%0A%20%20%20%20%23__li_rankings%20td.m2%7Bcolor%3A%238a8a8a%7D%0A%20%20%20%20%23__li_rankings%20td.m3%7Bcolor%3A%23a06836%7D%0A%20%20%60%3B%0A%20%20document.head.appendChild%28style%29%3B%0A%20%20const%20ts%20%3D%20new%20Date%28%29.toLocaleTimeString%28%5B%5D%2C%20%7B%20hour%3A%20%222-digit%22%2C%20minute%3A%20%222-digit%22%20%7D%29%3B%0A%20%20const%20header%20%3D%20%60%3Ctr%3E%3Cth%20class%3D%22L%22%3E%23%3C%2Fth%3E%3Cth%20class%3D%22L%22%3EName%3C%2Fth%3E%24%7BGAMES.map%28g%20%3D%3E%20%60%3Cth%3E%24%7Bg.label%7D%3C%2Fth%3E%60%29.join%28%22%22%29%7D%3Cth%20class%3D%22R%22%3ESum%3C%2Fth%3E%3C%2Ftr%3E%60%3B%0A%20%20const%20body%20%3D%20table.map%28%28r%2C%20i%29%20%3D%3E%20%7B%0A%20%20%20%20const%20medal%20%3D%20i%20%3C%203%20%3F%20%60%20m%24%7Bi%20%2B%201%7D%60%20%3A%20%22%22%3B%0A%20%20%20%20const%20cells%20%3D%20r.perGame.map%28rk%20%3D%3E%20rk%20%3D%3D%20null%0A%20%20%20%20%20%20%3F%20%60%3Ctd%20class%3D%22miss%22%3E%E2%80%94%3C%2Ftd%3E%60%0A%20%20%20%20%20%20%3A%20%60%3Ctd%3E%24%7Brk.rank%7D%3Csmall%3E%28%24%7Besc%28rk.score%29%7D%29%3C%2Fsmall%3E%3C%2Ftd%3E%60%29.join%28%22%22%29%3B%0A%20%20%20%20return%20%60%3Ctr%20class%3D%22%24%7Br.name%20%3D%3D%3D%20%22You%22%20%3F%20%22you%22%20%3A%20%22%22%7D%22%3E%3Ctd%20class%3D%22n%20L%24%7Bmedal%7D%22%3E%24%7Bi%20%2B%201%7D%3C%2Ftd%3E%3Ctd%20class%3D%22name%20L%22%3E%24%7Besc%28r.name%29%7D%3C%2Ftd%3E%24%7Bcells%7D%3Ctd%20class%3D%22sum%20R%22%3E%24%7Br.sum%7D%3C%2Ftd%3E%3C%2Ftr%3E%60%3B%0A%20%20%7D%29.join%28%22%22%29%3B%0A%20%20const%20panel%20%3D%20document.createElement%28%22div%22%29%3B%0A%20%20panel.id%20%3D%20%22__li_rankings%22%3B%0A%20%20panel.innerHTML%20%3D%20%60%3Cdiv%20class%3D%22h%22%3E%3Ch3%3ELinkedIn%20dailies%20%E2%80%94%20overall%3C%2Fh3%3E%3Cdiv%3E%3Cspan%20class%3D%22ts%22%3Eas%20of%20%24%7Bts%7D%3C%2Fspan%3E%3Cbutton%20class%3D%22x%22%3E%C3%97%3C%2Fbutton%3E%3C%2Fdiv%3E%3C%2Fdiv%3E%3Ctable%3E%3Cthead%3E%24%7Bheader%7D%3C%2Fthead%3E%3Ctbody%3E%24%7Bbody%7D%3C%2Ftbody%3E%3C%2Ftable%3E%60%3B%0A%20%20panel.querySelector%28%22.x%22%29.addEventListener%28%22click%22%2C%20%28%29%20%3D%3E%20%7B%20panel.remove%28%29%3B%20style.remove%28%29%3B%20%7D%29%3B%0A%20%20document.body.appendChild%28panel%29%3B%0A%7D%29%28%29%3B
// LinkedIn dailies — overall ranking across connections leaderboards.
// Rank = position among players with a score. Sum across games, lower wins.
// Didn't play a game = tie with last place in that game.
(async () => {
const GAMES = [
{ slug: "patches", label: "Patches" },
{ slug: "queens", label: "Queens" },
{ slug: "zip", label: "Zip" },
{ slug: "mini-sudoku", label: "Sudoku" },
{ slug: "tango", label: "Tango" },
];
const MAX_WAIT = 15000, SETTLE = 1200;
const scrape = async ({ slug, label }) => {
const iframe = document.createElement("iframe");
iframe.style.cssText = "position:fixed;left:-99999px;top:0;width:1200px;height:1600px;border:0";
iframe.src = `/games/${slug}/results/leaderboard/connections/`;
document.body.appendChild(iframe);
const SEL_ROW = '[class*="leaderboard-player__container"]';
const SEL_NAME = '[class*="leaderboard-player__name"]';
const SEL_SCORE = '[class*="leaderboard-player__score"]';
const start = Date.now();
let lastCount = -1, stableAt = 0;
while (Date.now() - start < MAX_WAIT) {
const doc = iframe.contentDocument;
const count = doc
? [...doc.querySelectorAll(SEL_ROW)].filter(c => c.querySelector(SEL_SCORE)).length
: 0;
if (count > 0 && count === lastCount) {
if (Date.now() - stableAt > SETTLE) break;
} else {
stableAt = Date.now();
lastCount = count;
}
await new Promise(r => setTimeout(r, 200));
}
const rows = [];
try {
let rank = 0;
for (const c of iframe.contentDocument.querySelectorAll(SEL_ROW)) {
const name = c.querySelector(SEL_NAME)?.textContent.trim();
const score = c.querySelector(SEL_SCORE)?.textContent.trim();
if (!name || !score) continue;
rows.push({ rank: ++rank, name, score });
}
} catch {}
console.log(`[li-rankings] ${label}: ${rows.length} rows`);
iframe.remove();
return { label, rows };
};
const all = await Promise.all(GAMES.map(scrape));
const people = {}, maxRank = {};
for (const { label, rows } of all) {
maxRank[label] = rows.length;
for (const { rank, name, score } of rows) (people[name] ||= {})[label] = { rank, score };
}
const table = Object.entries(people).map(([name, ranks]) => {
const perGame = GAMES.map(g => ranks[g.label] || null);
const sum = perGame.reduce((a, rk, i) => a + (rk?.rank ?? maxRank[GAMES[i].label]), 0);
return { name, perGame, sum };
}).sort((a, b) => a.sum - b.sum);
document.getElementById("__li_rankings")?.remove();
document.getElementById("__li_rankings_css")?.remove();
const esc = s => String(s).replace(/[&<>]/g, c => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" }[c]));
const style = document.createElement("style");
style.id = "__li_rankings_css";
style.textContent = `
#__li_rankings{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:680px;max-width:95vw;max-height:90vh;overflow:auto;background:#fff;color:#1d2226;font:14px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,system-ui,sans-serif;box-shadow:0 20px 60px rgba(0,0,0,.25),0 4px 16px rgba(0,0,0,.08);border-radius:12px;z-index:2147483647;padding:20px 24px 24px}
#__li_rankings .h{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:16px}
#__li_rankings h3{margin:0;font-size:16px;font-weight:600}
#__li_rankings .ts{font-size:11px;color:#888;font-variant-numeric:tabular-nums}
#__li_rankings .x{background:0;border:0;font:20px/1 system-ui;cursor:pointer;color:#999;padding:0 4px;margin-left:12px}
#__li_rankings .x:hover{color:#000}
#__li_rankings table{border-collapse:collapse;width:100%;font-variant-numeric:tabular-nums}
#__li_rankings th{padding:6px 8px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:#666;border-bottom:1px solid #d9d9d9;text-align:center}
#__li_rankings th.L,#__li_rankings td.L{text-align:left}
#__li_rankings th.R,#__li_rankings td.R{text-align:right}
#__li_rankings td{padding:8px 6px;border-bottom:1px solid #f0f0f0;text-align:center}
#__li_rankings td small{font-size:11px;color:#999;margin-left:2px;font-weight:400}
#__li_rankings td.n{font-weight:600;color:#888;width:28px}
#__li_rankings td.name{font-weight:500}
#__li_rankings td.sum{font-weight:600}
#__li_rankings td.miss{color:#c4c4c4}
#__li_rankings tr.you{background:#eaf3ff}
#__li_rankings tr.you td{font-weight:600}
#__li_rankings tr:last-child td{border-bottom:0}
#__li_rankings td.m1{color:#c19400}
#__li_rankings td.m2{color:#8a8a8a}
#__li_rankings td.m3{color:#a06836}
`;
document.head.appendChild(style);
const ts = new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
const header = `<tr><th class="L">#</th><th class="L">Name</th>${GAMES.map(g => `<th>${g.label}</th>`).join("")}<th class="R">Sum</th></tr>`;
const body = table.map((r, i) => {
const medal = i < 3 ? ` m${i + 1}` : "";
const cells = r.perGame.map(rk => rk == null
? `<td class="miss">—</td>`
: `<td>${rk.rank}<small>(${esc(rk.score)})</small></td>`).join("");
return `<tr class="${r.name === "You" ? "you" : ""}"><td class="n L${medal}">${i + 1}</td><td class="name L">${esc(r.name)}</td>${cells}<td class="sum R">${r.sum}</td></tr>`;
}).join("");
const panel = document.createElement("div");
panel.id = "__li_rankings";
panel.innerHTML = `<div class="h"><h3>LinkedIn dailies — overall</h3><div><span class="ts">as of ${ts}</span><button class="x">×</button></div></div><table><thead>${header}</thead><tbody>${body}</tbody></table>`;
panel.querySelector(".x").addEventListener("click", () => { panel.remove(); style.remove(); });
document.body.appendChild(panel);
})();
@StachuDotNet
Copy link
Copy Markdown
Author

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment