|
// 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(); |
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 theClicker
.This also likely updates some selectors that may have been old or too generalized if I didn't already do that.