Last active
March 1, 2025 15:26
-
-
Save raxityo/c82abe49b77d5feaeb5cf6b69f0e0d9d to your computer and use it in GitHub Desktop.
Clip all available BJ's coupons
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name BJ's Coupon Clipper | |
// @namespace Violentmonkey Scripts | |
// @match https://www.bjs.com/ | |
// @grant none | |
// @version 1.0 | |
// @author @raxityo | |
// @description 3/24/2024, 11:01:30 PM | |
// ==/UserScript== | |
/** | |
* Clip all available BJ's coupons | |
* - Login to BJ's website | |
* - Open dev console and paste this function | |
* - Run it! Example `await clipAllOffers()` | |
* @return {Promise<T>} | |
*/ | |
(async function clipAllOffers() { | |
const membershipNumber = localStorage.getItem("x_MembershipNumber"); | |
const zipcode = JSON.parse( | |
localStorage.getItem("clubDetailsForClubId") | |
).postalCode; | |
await fetch( | |
"https://api.bjs.com/digital/live/api/v1.0/member/available/offers", | |
{ | |
method: "post", | |
credentials: "include", | |
body: JSON.stringify({ | |
membershipNumber, | |
zipcode, | |
category: "", | |
isPrev: false, | |
isNext: true, | |
pagesize: 500, | |
searchString: "", | |
indexForPagination: 0, | |
brand: "" | |
}) | |
} | |
) | |
.then((r) => r.json()) | |
.then(([{ availableOffers }]) => { | |
// Intentionally doing sequential requests to avoid hammering the backend | |
availableOffers.forEach(async ({ offerId, storeId }) => { | |
await fetch( | |
`https://api.bjs.com/digital/live/api/v1.0/store/${storeId}/coupons/activate?zip=07302&offerId=${offerId}`, | |
{ | |
credentials: "include" | |
} | |
); | |
}); | |
}); | |
})().then(); |
@TheMarf I have just converted this to a UserScript, so you can use it in FireFox or other browsers using plugins such as Tampermonkey or ViolentMonkey and installing the script as a UeserScript. Doing so would automatically run the script whenever you visit bjs.com.
@raxityo Great idea. Thanks for the tip and the code!
I've done some improvements to the script, including adding a button on the page. Thank you for the script:
// ==UserScript==
// @name BJ's Coupon Clipper with Live Count
// @namespace Violentmonkey Scripts
// @match *://*.bjs.com/*
// @grant none
// @version 1.5
// @author @raxityo
// @description Adds a button that shows a live count of coupons being clipped on any BJ's website subdomain.
// ==/UserScript==
(function() {
'use strict';
async function clipAllOffers() {
button.disabled = true;
button.style.backgroundColor = "#ccc";
button.style.cursor = "not-allowed";
try {
const membershipNumber = localStorage.getItem("x_MembershipNumber");
const clubDetails = localStorage.getItem("clubDetailsForClubId");
if (!membershipNumber || !clubDetails) {
console.error("Missing membership or club details in localStorage.");
button.textContent = "Missing login/club info";
return;
}
const zipcode = JSON.parse(clubDetails).postalCode;
const response = await fetch("https://api.bjs.com/digital/live/api/v1.0/member/available/offers", {
method: "POST",
credentials: "include",
body: JSON.stringify({
membershipNumber,
zipcode,
category: "",
isPrev: false,
isNext: true,
pagesize: 500,
searchString: "",
indexForPagination: 0,
brand: ""
})
});
const data = await response.json();
if (!data || !Array.isArray(data) || !data[0]?.availableOffers) {
console.error("Unexpected response structure.", data);
button.textContent = "Unexpected response";
return;
}
const offers = data[0].availableOffers;
const total = offers.length;
if (total === 0) {
button.textContent = "No coupons available";
return;
}
for (let i = 0; i < total; i++) {
button.textContent = `Clipping coupon ${i + 1} of ${total}`;
const { offerId, storeId } = offers[i];
await fetch(
`https://api.bjs.com/digital/live/api/v1.0/store/${storeId}/coupons/activate?zip=07302&offerId=${offerId}`,
{
credentials: "include"
}
);
}
button.textContent = "Coupons clipped successfully!";
} catch (error) {
console.error("Error clipping coupons:", error);
button.textContent = "Error clipping coupons";
}
}
const button = document.createElement("button");
button.textContent = "Clip All Coupons";
button.style.position = "fixed";
button.style.top = "10px";
button.style.right = "10px";
button.style.zIndex = "10000";
button.style.padding = "10px 15px";
button.style.backgroundColor = "#007bff";
button.style.color = "#fff";
button.style.border = "none";
button.style.borderRadius = "5px";
button.style.cursor = "pointer";
button.style.boxShadow = "0 2px 6px rgba(0,0,0,0.3)";
button.addEventListener("click", clipAllOffers);
function addButton() {
if (!document.body.contains(button)) {
document.body.appendChild(button);
}
}
const observer = new MutationObserver(addButton);
observer.observe(document.body, { childList: true, subtree: true });
addButton();
})();
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Love this code, was able to get it to work like a charm. Not very versed in js, is there any easy way to save this to a bookmark or something to easily run in firefox?
Edit: tried making a bookmark with the following...don't think it worked. Will try again in a couple days when coupons refresh.
javascript:(async function clipAllOffers() { const membershipNumber = localStorage.getItem('x_MembershipNumber') const zipcode = JSON.parse(localStorage.getItem('clubDetailsForClubId')).postalCode await fetch('https://api.bjs.com/digital/live/api/v1.0/member/available/offers', { method: 'post', credentials: 'include', body: JSON.stringify({ membershipNumber, zipcode, 'category': '', 'isPrev': false, 'isNext': true, 'pagesize': 500, 'searchString': '', 'indexForPagination': 0, 'brand': '' }) }) .then(r => r.json()) .then(([{ availableOffers }]) => { // Intentionally doing sequential requests to avoid hammering the backend availableOffers.forEach(async ({ offerId, storeId }) => { await fetch(
https://api.bjs.com/digital/live/api/v1.0/store/${storeId}/coupons/activate?zip=07302&offerId=${offerId}`, { credentials: 'include' } ) }) }) })().then()`