smkent / safeway-coupons

🥫 🎫 Automatic coupon clipper for Safeway's online "Safeway for U" coupons
GNU General Public License v3.0
57 stars 17 forks source link

Support for other Albertson's stores, like Jewel #72

Open johnmyrda opened 1 year ago

johnmyrda commented 1 year ago

Hi @smkent , I love this project. However, I've moved away from Safeway-land and now live near Jewel-Osco. This application clips most but not all of my Just4U coupons.

I updated the URLs within client.py and session.py and was able to get the application to clip all of them. While I am happy maintaining my own local branch of this code, it would be neat if this application could support all Albertson's stores, like Jewel and Vons and others.

Here's the diff of the changes I made to get my Jewel coupons working.

diff --git a/safeway_coupons/client.py b/safeway_coupons/client.py
index 19a1f5e..9f6784a 100644
--- a/safeway_coupons/client.py
+++ b/safeway_coupons/client.py
@@ -26,7 +26,7 @@ class SafewayClient(BaseSession):
     def get_offers(self) -> List[Offer]:
         try:
             response = self.requests.get(
-                "https://www.safeway.com/abs/pub/xapi"
+                "https://www.jewelosco.com/abs/pub/xapi"
                 "/offers/companiongalleryoffer"
                 f"?storeId={self.session.store_id}"
                 f"&rand={random.randrange(100000,999999)}",
@@ -41,7 +41,7 @@ class SafewayClient(BaseSession):
         response: Optional[requests.Response] = None
         try:
             response = self.requests.post(
-                "https://www.safeway.com/abs/pub/web/j4u/api/offers/clip"
+                "https://www.jewelosco.com/abs/pub/web/j4u/api/offers/clip"
                 f"?storeId={self.session.store_id}",
                 data=json.dumps(request.to_dict(encode_json=True)),
                 headers={"Content-Type": "application/json"},
diff --git a/safeway_coupons/session.py b/safeway_coupons/session.py
index 30ccdeb..ee9ed0e 100644
--- a/safeway_coupons/session.py
+++ b/safeway_coupons/session.py
@@ -15,7 +15,7 @@ AUTHORIZE_URL = (

 OAUTH_CLIENT_ID = "0oap6ku01XJqIRdl42p6"
 OAUTH_REDIRECT_URI = (
-    "https://www.safeway.com/bin/safeway/unified/sso/authorize"
+    "https://www.jewelosco.com/bin/safeway/unified/sso/authorize"
 )
johnmyrda commented 1 year ago

Here's a quick PR that adds support for arbitrary store urls - https://github.com/johnmyrda/safeway-coupons/pull/1

I don't know your policy on contributors, so I made the PR against my own fork.

smkent commented 1 year ago

Hi @johnmyrda, thanks for the note! I am delighted to hear you find this useful and want to contribute.

Supporting more Albertson's brands is a great idea. A good way to do that would be to add another command line argument to select the brand to connect to, e.g.:

safeway-coupons --brand jewel-osco [...]

We could create a new file constants.py or brands.py with an enumeration of supported brand names/values/URLs. app.py's ArgumentParser could use that enumeration to validate command line options, and client.py's SafewayClient could also use it to get the URLs for the chosen brand.

johnmyrda commented 1 year ago

Thanks @smkent! I intended for the above PR to act as a proof of concept, and I'm glad you appreciated it.

I like the idea of enumerating valid brands - very user friendly. The downside of enumeration is that the brand list would have to be maintained and Albertsons owns A LOT of supermarket chains. A hybrid approach where it's possible to select the brand but also override the URL as an advanced option would be the most flexible, but I understand not wanting to let users enter arbitrary URLs since it's very easy to mess up.

I created 2 more PoCs

My opinion is that the config approach is slightly cleaner architecturally, as the account configuration is already being passed around to all the places it is needed.