Skip to content

Instantly share code, notes, and snippets.

@ifthenelse
Last active December 13, 2025 14:13
Show Gist options
  • Select an option

  • Save ifthenelse/c57a69c50657e867e4733f99fd9d36a6 to your computer and use it in GitHub Desktop.

Select an option

Save ifthenelse/c57a69c50657e867e4733f99fd9d36a6 to your computer and use it in GitHub Desktop.
Twitch bookmarklet to auto-claim points
javascript: (() => {
const KEY = "__twitchAutoClaimer";
if (window[KEY]?.stop) {
window[KEY].stop(false);
return;
}
const state = {
start: new Date(),
checks: 0,
collected: 0,
running: true,
intervalMs: 30000,
timer: null,
stopped: false,
};
const pad = (n) => String(n).padStart(2, "0");
const fmt = (d) =>
`${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
/* ---------- UI ---------- */
const cssBtn =
"cursor:pointer;background:rgba(255,255,255,.12);color:#fff;border:1px solid rgba(255,255,255,.18);border-radius:8px;padding:2px 8px;font-size:12px;";
const overlay = document.createElement("div");
overlay.style.cssText = [
"position:fixed",
"left:16px",
"top:16px",
"z-index:2147483647",
"background:rgba(0,0,0,.82)",
"color:#fff",
"font:12px/1.35 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif",
"border:1px solid rgba(255,255,255,.18)",
"border-radius:10px",
"box-shadow:0 10px 30px rgba(0,0,0,.35)",
"padding:10px 12px",
"min-width:260px",
"user-select:none",
].join(";");
overlay.innerHTML = `
<div id="tac-header" style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:6px;cursor:move;">
<div style="font-weight:700;">Twitch Auto-Claimer</div>
<div style="display:flex;gap:6px;">
<button id="tac-pause" style="${cssBtn}">Pause</button>
<button id="tac-stop" style="${cssBtn}background:rgba(255,80,80,.25);">Stop & Close</button>
</div>
</div>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">
<span id="tac-dot" style="width:10px;height:10px;border-radius:999px;display:inline-block;"></span>
<span id="tac-state" style="font-weight:700;"></span>
</div>
<div style="display:grid;grid-template-columns:auto 1fr;gap:2px 10px;">
<div style="opacity:.75;">Started</div><div id="tac-start">-</div>
<div style="opacity:.75;">Checks</div><div id="tac-checks">-</div>
<div style="opacity:.75;">Movement</div><div id="tac-movement">-</div>
<div style="opacity:.75;">Collected</div><div id="tac-collected">-</div>
<div style="opacity:.75;">Last</div><div id="tac-last">-</div>
</div>
`;
document.body.appendChild(overlay);
const ui = {
pause: overlay.querySelector("#tac-pause"),
stop: overlay.querySelector("#tac-stop"),
dot: overlay.querySelector("#tac-dot"),
state: overlay.querySelector("#tac-state"),
start: overlay.querySelector("#tac-start"),
checks: overlay.querySelector("#tac-checks"),
movement: overlay.querySelector("#tac-movement"),
collected: overlay.querySelector("#tac-collected"),
last: overlay.querySelector("#tac-last"),
};
const setStateUI = () => {
if (state.running) {
ui.dot.style.background = "rgba(80,255,120,.95)";
ui.state.textContent = "RUNNING";
ui.state.style.color = "rgba(80,255,120,.95)";
ui.pause.textContent = "Pause";
} else {
ui.dot.style.background = "rgba(255,80,80,.95)";
ui.state.textContent = "PAUSED";
ui.state.style.color = "rgba(255,80,80,.95)";
ui.pause.textContent = "Continue";
}
};
const updateUI = ({ msg_mov, msg_claim }) => {
ui.start.textContent = `${state.start.toLocaleString()} (${fmt(
state.start
)})`;
if (msg_mov !== undefined) {
ui.movement.textContent = msg_mov;
}
if (msg_claim !== undefined) {
ui.checks.textContent = String(state.checks);
ui.collected.textContent = String(state.collected);
ui.last.textContent = msg_claim;
}
setStateUI();
};
/* ---------- Core logic ---------- */
const simulateHumanMovement = () => {
let msg = `Movement ${fmt(new Date())}`;
try {
const target = document.querySelector(
'[aria-label="Chat messages"] .chat-scrollable-area__message-container'
);
const rect = target.getBoundingClientRect();
let t = 0;
const move = () => {
const x = rect.left + 150 + Math.sin(t / 20) * 100;
const y = rect.top + 150 + Math.cos(t / 20) * 100;
const event = new MouseEvent("mousemove", {
clientX: x,
clientY: y,
bubbles: true,
});
target.dispatchEvent(event);
t += 1;
if (t < 100) setTimeout(move, 20);
}
move();
msg = `✅ Movement simulated ${fmt(new Date())}`;
console.log("[Twitch Auto-Claimer] ✅ Movement simulated");
}
catch (e) {
msg = `⚠️ Error on movement simulation: ${fmt(new Date())}`;
console.error("[Twitch Auto-Claimer] ⚠️ Error on movement simulation:", e);
}
updateUI({ msg_mov: msg });
}
const tick = () => {
if (!state.running || state.stopped) return;
state.checks++;
let msg = `Checked ${fmt(new Date())}`;
simulateHumanMovement();
try {
const btn = document.querySelector('[aria-label="Claim Bonus"]');
if (btn) {
btn.click();
state.collected++;
msg = `✅ Collected ${fmt(new Date())}`;
console.log("[Twitch Auto-Claimer] ✅ Collected bonus");
}
} catch (e) {
msg = `⚠️ Error on bonus claim: ${fmt(new Date())}`;
console.error("[Twitch Auto-Claimer] ⚠️ Error on bonus claim:", e);
}
updateUI({ msg_claim: msg });
};
const startTimer = () => {
clearInterval(state.timer);
state.timer = setInterval(tick, state.intervalMs);
};
const stopTimer = () => {
clearInterval(state.timer);
state.timer = null;
};
const stopAll = (ask = true) => {
if (ask && !confirm("Stop Twitch Auto-Claimer and remove overlay?")) return;
state.stopped = true;
stopTimer();
overlay.remove();
delete window[KEY];
console.log("[Twitch Auto-Claimer] ⏹️ stopped");
};
/* ---------- Controls ---------- */
ui.pause.onclick = () => {
state.running = !state.running;
if (state.running) {
startTimer();
updateUI(`Resumed ${fmt(new Date())}`);
console.log("[Twitch Auto-Claimer] ▶️ resumed");
} else {
stopTimer();
updateUI(`Paused ${fmt(new Date())}`);
console.log("[Twitch Auto-Claimer] ⏸️ paused");
}
};
ui.stop.onclick = () => stopAll(true);
/* ---------- Drag ---------- */
const drag = { on: false, dx: 0, dy: 0 };
overlay.querySelector("#tac-header").onmousedown = (e) => {
drag.on = true;
const r = overlay.getBoundingClientRect();
drag.dx = e.clientX - r.left;
drag.dy = e.clientY - r.top;
};
window.addEventListener("mousemove", (e) => {
if (!drag.on) return;
const r = overlay.getBoundingClientRect();
overlay.style.left =
clamp(e.clientX - drag.dx, 0, innerWidth - r.width) + "px";
overlay.style.top =
clamp(e.clientY - drag.dy, 0, innerHeight - r.height) + "px";
});
window.addEventListener("mouseup", () => (drag.on = false));
/* ---------- Boot ---------- */
updateUI("Ready");
tick();
startTimer();
window[KEY] = { stop: stopAll, state };
console.log("[Twitch Auto-Claimer] ▶️ running");
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment