Created
May 24, 2025 16:22
-
-
Save snacsnoc/c1982e57ec720a66d87c8391a29b38dd to your computer and use it in GitHub Desktop.
Load Dollar General coupons from file (JSON) and apply to account
This file contains hidden or 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
| #!/usr/bin/env python3 | |
| # load_dg_coupons.py | |
| import argparse, json, sys, textwrap | |
| from pathlib import Path | |
| from urllib.parse import urlencode | |
| import requests | |
| # ---------- constants you normally NEVER edit in-file ---------- | |
| BASE = "https://www.dollargeneral.com/bin/omni/coupons" | |
| ACTIV = f"{BASE}/activate" | |
| HEAD = { | |
| "User-Agent": "Mozilla/5.0", | |
| "Accept": "application/json, text/javascript, */*; q=0.01", | |
| "X-Requested-With": "XMLHttpRequest", | |
| "Referer": "https://www.dollargeneral.com/deals/coupons", | |
| "X-CSRF-Token": "NEEDTHIS", #enter your own | |
| "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", | |
| "Cookie": ( | |
| "form_key=6MzGRJsdB2ZZXMXA; " # probably need this | |
| "c_uuid=2501015138020100101138051050168030; " # probably need this | |
| "authType=1; " #probably need this too | |
| ), | |
| } | |
| BATCH = 20 # offers per POST | |
| # ----------------------------------------------------------------- | |
| def debug(msg, *, on=True): | |
| if on: | |
| print(msg, file=sys.stderr) | |
| def extract_ids(obj): | |
| """ | |
| Accepts either a dict that contains 'Coupons': […] or a plain | |
| list of strings, returns list[str]. | |
| """ | |
| if isinstance(obj, list): | |
| return [str(x) for x in obj] | |
| if isinstance(obj, dict) and "Coupons" in obj: | |
| return [c["OfferID"] for c in obj["Coupons"] if "OfferID" in c] | |
| raise ValueError("Unrecognised JSON structure") | |
| def activate(sess, offer_ids, store, device, mobile=False): | |
| data = { | |
| "offerGuids": ",".join(offer_ids), | |
| "storeNumber": store, | |
| "deviceId": device, | |
| "isMobileDevice": str(mobile).lower(), | |
| "clientOriginStoreNumber": "", | |
| } | |
| r = sess.post(ACTIV, headers=HEAD, data=urlencode(data), timeout=20) | |
| if r.status_code != 200: | |
| sys.stderr.write( | |
| f"\n--- HTTP {r.status_code} ---\n{r.text[:500]}\n-------------\n" | |
| ) | |
| r.raise_for_status() | |
| return r.json() | |
| # ----------------------------------------------------------------- | |
| def main(): | |
| ap = argparse.ArgumentParser( | |
| formatter_class=argparse.RawDescriptionHelpFormatter, | |
| description="Load multiple Dollar General coupons from a JSON file.", | |
| epilog=textwrap.dedent("""\ | |
| The file can be: | |
| • the raw /search response (object with a 'Coupons' key), **or** | |
| • a simple JSON list of OfferID strings. | |
| """), | |
| ) | |
| ap.add_argument("-f", "--file", default="coupons.json", | |
| help="JSON input file (default: coupons.json)") | |
| ap.add_argument("--store", required=True, help="Store number") | |
| ap.add_argument("--device", required=True, help="deviceId (same one in your cookies)") | |
| ap.add_argument("-n", "--dry-run", action="store_true", | |
| help="Show what would be sent but do NOT POST") | |
| args = ap.parse_args() | |
| j = json.loads(Path(args.file).read_text()) | |
| offers = extract_ids(j) | |
| if not offers: | |
| sys.exit("No OfferID values found – aborting") | |
| debug(f"Found {len(offers)} coupons in {args.file}") | |
| sess = requests.Session() | |
| for i in range(0, len(offers), BATCH): | |
| chunk = offers[i:i+BATCH] | |
| debug(f"→ Activating {len(chunk)} offers [{i}..{i+len(chunk)-1}]") | |
| if args.dry_run: | |
| debug(" DRY-RUN " + ",".join(chunk)) | |
| continue | |
| resp = activate(sess, chunk, args.store, args.device) | |
| for item in resp.get("result", []): | |
| ok = item.get("Success") | |
| cid = item.get("OfferId") | |
| note = "✓" if ok else f"✗ ({item.get('ErrorCode')})" | |
| print(f"{note} {cid}") | |
| if args.dry_run: | |
| print("-- dry-run completed, nothing sent --") | |
| # ----------------------------------------------------------------- | |
| if __name__ == "__main__": | |
| main() | |
| ''' | |
| Sample JSON | |
| { | |
| "Coupons": [ | |
| { | |
| "DiscountIndicator": 0, | |
| "UPCs": [], | |
| "OfferID": "69766dbc-d23d-43f8-9584-470cc349497b", | |
| "Image1": "https://cdn.cpnscdn.com/static.coupons.com/ext/bussys/cpa/pod/86/241/5286241_a822edc1-790c-4a0a-a7e4-1eaa72857d30.gif", | |
| "Image2": "https://cdn.cpnscdn.com/static.coupons.com/ext/bussys/cpa/pod/86/241/5286241_a822edc1-790c-4a0a-a7e4-1eaa72857d30_high.jpg", | |
| "OfferType": "AmountOff", | |
| "RecemptionFrequency": "OnceTimeOffer", | |
| "OfferGS1": "81101005280005980133001105003250614", | |
| "OfferCode": "23014133", | |
| "OfferActivationDate": "2025-05-03T00:01:00", | |
| "OfferExpirationDate": "2025-06-14T23:59:59", | |
| "OfferDescription": "on any ONE (1) LUBRIDERM® Product, any variety (16 fl. oz. or larger)", | |
| "BrandName": "LUBRIDERM®", | |
| "Companyname": "KENVUE BRANDS LLC.-SKILLMAN", | |
| "OfferSummary": "Save $3.00", | |
| "OfferFinePrint": "", | |
| "OfferDisclaimer": "CONSUMER: This coupon good only on purchase of product indicated. Any other use constitutes fraud. COUPON CANNOT BE BOUGHT, TRANSFERRED OR SOLD. LIMIT ONE COUPON PER PURCHASE AND ONE COUPON PER CUSTOMER. VOID IF TAXED, RESTRICTED OR PROHIBITED BY LAW. VOID IF COPIED OR EXPIRED.\n\nRETAILER: Kenvue Brands LLC. will reimburse you for the face value of this coupon plus 8¢ handling if submitted in compliance with Kenvue Brands LLC. Coupon Redemption Policy. Coupons are void where prohibited, taxed, or otherwise restricted by law. Cash Redemption Value 1/100 cent. Send coupons to: Kenvue Brands LLC. 1453, NCH, P.O. Box 880001, El Paso, TX 88588-0001.\n\n© Kenvue Brands LLC 2025", | |
| "OfferFeaturedText": "", | |
| "IsAutoActivated": 0, | |
| "TargetType": "recommended", | |
| "Visible": "", | |
| "ActivationDate": "0001-01-01T00:00:00", | |
| "AssociationCode": "", | |
| "RedemptionLimitQuantity": 1, | |
| "MinBasketValue": 0.0, | |
| "MinTripCount": 0, | |
| "TimesShopQuantity": 0, | |
| "MinQuantity": 1, | |
| "MinQuantityDescription": "", | |
| "RewaredOfferValue": 3.0, | |
| "RewaredCategoryName": "Personal Care", | |
| "RewardQuantity": 0, | |
| "IsClipped": 0, | |
| "IsManufacturerCoupon": 1, | |
| "offerSourceType": 1, | |
| "isProductEligible": true, | |
| "CouponType": 1, | |
| "IsBasketLevel": false | |
| }, | |
| } | |
| ''' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment