Skip to content

Instantly share code, notes, and snippets.

@xbb
Last active July 31, 2022 21:19
Show Gist options
  • Save xbb/1568b8219d94dca7ae9a630545208bcd to your computer and use it in GitHub Desktop.
Save xbb/1568b8219d94dca7ae9a630545208bcd to your computer and use it in GitHub Desktop.
itch.io claim all button
// ==UserScript==
// @name itch.io claim all button
// @version 0.2
// @description claims all the items automatically in the download bundle pages, make sure to start from page 1
// @author xbb
// @match https://itch.io/bundle/download/*
// @grant none
// ==/UserScript==
(() => {
const findBackward = (node, callback) => {
while (node.parentNode) {
if (callback?.(node.parentNode)) {
return node.parentNode;
}
node = node.parentNode;
}
return null;
};
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const buildClaimList = () => Array.from(document.querySelectorAll('button[value=claim]')).reduce((list, btn) => {
const ct = findBackward(btn, (n) => n?.classList?.contains('game_row_data'));
const form = findBackward(btn, (n) => n?.tagName?.toLowerCase() === 'form');
const title = ct.querySelector('.game_title')?.textContent;
if (form) {
const data = new FormData(form);
data.append('action', 'claim');
list.push({
title,
data
});
}
return list;
}, []);
const searchEl = document.querySelector('.filter_options .form');
if (!searchEl) {
console.error('could not find pager container, fix me!');
return;
}
const claimBtn = (() => {
const btn = document.createElement('button');
btn.className = "button";
btn.textContent = "Claim all";
btn.style.marginRight = "10px";
searchEl.parentNode.insertBefore(btn, searchEl);
return btn;
})();
const pathName = (new URL(window.location.href)).pathname;
const claimKey = `claiming_${pathName}`;
const storage = window.sessionStorage;
let abort;
let claiming = storage.getItem(claimKey);
let claimList = buildClaimList();
const claimAll = async () => {
claimBtn.textContent = 'Claiming…';
claimList = claimList.filter(item => !item.claimed);
const length = claimList.length;
await wait(1000);
let n = 1;
for (const item of claimList) {
if (abort) return;
claimBtn.textContent = `Claiming ${n++} of ${length}`;
console.log(`Claiming ${item.title}`);
await fetch(window.location.href, {
mode: 'no-cors',
method: 'POST',
body: item.data,
}).catch(err => console.error(err));
item.claimed = true;
await wait(1000);
}
// Next page
const nextBtn = document.querySelector('.next_page.button');
if (nextBtn) {
nextBtn.click();
} else {
// all done
storage.removeItem(claimKey);
claimBtn.textContent = 'All claimed!';
claimBtn.setAttribute('disabled','disabled');
}
};
const beginClaiming = () => {
claimAll().catch(err => {
console.error(err);
claiming = false;
storage.removeItem(claimKey);
});
};
claimBtn.addEventListener('click', () => {
abort = !!claiming;
claiming = !claiming;
if (claiming) {
storage.setItem(claimKey, true);
beginClaiming();
} else {
storage.removeItem(claimKey);
claimBtn.textContent = 'Claim all';
}
});
// next page auto claiming, a press of the button will stop claiming
if (claiming) {
beginClaiming();
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment