Skip to content

Instantly share code, notes, and snippets.

@ctsstc
Last active March 13, 2025 02:50
Show Gist options
  • Save ctsstc/73a74ae0f0c315262bf07cea9fdc7aa2 to your computer and use it in GitHub Desktop.
Save ctsstc/73a74ae0f0c315262bf07cea9fdc7aa2 to your computer and use it in GitHub Desktop.
Safeway Just for U Auto Clicker. Add all the discounts at once.
// Allow redefinition for copy/paste
let Clicker = class {
#document;
#selector;
#delay;
#randomWait;
#randomWaitMax;
constructor(
document,
selector,
{ delay = 1000, randomWait = true, randomWaitMax = 250 } = {}
) {
this.#document = document;
this.#selector = selector;
this.#delay = delay;
this.#randomWait = randomWait;
this.#randomWaitMax = randomWaitMax;
}
hasMore() {
return this.#findAll().length > 0;
}
clickAll() {
return new Promise((resolve) => {
const all = this.#findAll();
all.forEach((el, index) =>
setTimeout(() => {
const isLast = index == all.length - 1;
el.click();
if (isLast) {
resolve(null);
}
}, index * this.#delay + this.#getRandomWait())
);
});
}
#getRandomWait() {
return this.#randomWait
? Math.floor(Math.random() * this.#randomWaitMax)
: 0;
}
#findAll() {
return this.#document.querySelectorAll(this.#selector);
}
};
let coupons = new Clicker(window.document, "[id^=couponAddBtn]");
let loadButton = new Clicker(window.document, ".btn.load-more", {
delay: 2000,
randomWaitMax: 500,
});
// Anonymous singleton
new (class {
#coupons;
#loadButton;
constructor(coupons, loadButton) {
this.#coupons = coupons;
this.#loadButton = loadButton;
}
async run() {
// State 1
// Has coupon buttons -> Click all buttons
if (this.#coupons.hasMore()) {
// Note: console.log has been removed
console.warn("GOTTA CLICK'EM ALL!!!");
await this.#coupons.clickAll();
this.run();
}
// State 2
// Has load more -> Click load more
else if (this.#loadButton.hasMore()) {
console.warn("LOAD MOARRR!!!");
await this.#loadButton.clickAll();
this.run();
}
// State 3
// Has neither -> Finish
else {
console.warn("LE FINI! 🚀");
}
}
})(coupons, loadButton).run();
@ctsstc
Copy link
Author

ctsstc commented Feb 24, 2025

Updated to utilize blocking asynchronous code so that clicking doesn't accidentally stack up or require math to try and avoid overlapping. This removes the pollingLength from the SafewayClicker, but moves a delay to the Clicker.
This also likely updates some selectors that may have been old or too generalized if I didn't already do that.

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