Skip to content

Commit

Permalink
Merge pull request #8 from Woyken/main
Browse files Browse the repository at this point in the history
Added coupon activation API
  • Loading branch information
Andre0512 authored Sep 30, 2023
2 parents 272534f + 96c6ca5 commit b8606e5
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 3 deletions.
74 changes: 71 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/lidl-plus)](https://www.python.org/)
[![PyPI - License](https://img.shields.io/pypi/l/lidl-plus)](https://github.com/Andre0512/lidl-plus/blob/main/LICENCE)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/lidl-plus)](https://pypistats.org/packages/lidl-plus)

Fetch receipts and more from Lidl Plus.
## Installation
```bash
Expand Down Expand Up @@ -50,10 +50,10 @@ lidl.login(phone="+4915784632296", password="password", verify_token_func=lambda
print(lidl.refresh_token)
```
## Usage
Currently, the only feature is fetching receipts
Currently, the only features are fetching receipts and activating coupons
### Receipts

Get your receipts as json and receive a list of bought items like:
Get your receipts as json and receive a list of bought items like:
```json
{
"currentUnitPrice": "2,19",
Expand Down Expand Up @@ -89,6 +89,73 @@ for receipt in lidl.tickets():
pprint(lidl.ticket(receipt["id"]))
```
### Coupons
You can list all coupons and activate/deactivate them by id
```json
{
"sections": [
{
"name": "FavoriteStore",
"coupons": []
},
{
"name": "AllStores",
"coupons": [
{
"id": "2c9b3554-a09c-412c-8be4-d41cbff13572",
"image": "https://lidlplusprod.blob.core.windows.net/images/coupons/LT/IDISC0000254911.png?t=1695452076",
"type": "Standard",
"offerTitle": "1 + 1",
"title": "👨🏻‍🍳 Frozen 👨🏻‍🍳",
"offerDescriptionShort": "FREE",
"isSegmented": false,
"startValidityDate": "2023-09-24T21:00:00Z",
"endValidityDate": "2023-10-01T20:59:59Z",
"isActivated": false,
"apologizeText": "Xxxxxxxxxxxxxxxxx",
"apologizeStatus": false,
"apologizeTitle": "Xxxxxxxxxxxxxxxxxxx",
"promotionId": "DISC0000254911",
"tagSpecial": "",
"firstColor": "#ffc700",
"secondaryColor": null,
"firstFontColor": "#4a4a4a",
"secondaryFontColor": null,
"isSpecial": false,
"hasAsterisk": false,
"isHappyHour": false,
"stores": []
},
.......
]
},
{
"name": "OtherStores",
"coupons": []
}
]
}
```
#### Commandline-Tool
Activate all available coupons
```bash
$ lidl-plus --language=de --country=AT --refresh-token=XXXXX coupon --all
```
#### Python
```python
from lidlplus import LidlPlusApi
lidl = LidlPlusApi("de", "AT", refresh_token="XXXXXXXXXX")
for section in lidl.coupons()["sections"]:
for coupon in section["coupons"]:
print("found coupon: ", coupon["title"], coupon["id"])
```
## Help
#### Commandline-Tool
```commandline
Expand All @@ -110,4 +177,5 @@ options:
commands:
auth authenticate and get token
receipt output last receipts as json
coupon activate coupons
```
26 changes: 26 additions & 0 deletions lidlplus/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import sys
from getpass import getpass
from pathlib import Path
from datetime import datetime

if __name__ == "__main__":
sys.path.insert(0, str(Path(__file__).parent.parent))
Expand Down Expand Up @@ -47,6 +48,9 @@ def get_arguments():
receipt = subparser.add_parser("receipt", help="output last receipts as json")
receipt.add_argument("receipt", help="output last receipts as json", action="store_true")
receipt.add_argument("-a", "--all", help="fetch all receipts", action="store_true")
coupon = subparser.add_parser("coupon", help="activate coupons")
coupon.add_argument("coupon", help="activate coupons", action="store_true")
coupon.add_argument("-a", "--all", help="activate all coupons", action="store_true", required=True)
return vars(parser.parse_args())


Expand Down Expand Up @@ -120,13 +124,35 @@ def print_tickets(args):
print(json.dumps(tickets, indent=4))


def activate_coupons(args):
"""Activate all available coupons"""
lidl_plus = lidl_plus_login(args)
if not args.get("all"):
return
i = 0
for section in lidl_plus.coupons().get("sections", {}):
for coupon in section.get("coupons", {}):
if coupon["isActivated"]:
continue
if datetime.fromisoformat(coupon["startValidityDate"]) > datetime.now():
continue
if datetime.fromisoformat(coupon["endValidityDate"]) < datetime.now():
continue
print("activating coupon: ", coupon["title"])
lidl_plus.activate_coupon(coupon["id"])
i += 1
print(f"Activated {i} coupons")


def main():
"""argument commands"""
args = get_arguments()
if args.get("auth"):
print_refresh_token(args)
elif args.get("receipt"):
print_tickets(args)
elif args.get("coupon"):
activate_coupons(args)


def start():
Expand Down
26 changes: 26 additions & 0 deletions lidlplus/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import logging
import re
from datetime import datetime, timedelta
from typing import Literal

import requests

Expand Down Expand Up @@ -38,6 +39,7 @@ class LidlPlusApi:
_CLIENT_ID = "LidlPlusNativeClient"
_AUTH_API = "https://accounts.lidl.com"
_TICKET_API = "https://tickets.lidlplus.com/api/v2"
_COUPONS_API = "https://coupons.lidlplus.com/api"
_APP = "com.lidlplus.app"
_OS = "iOs"
_TIMEOUT = 10
Expand Down Expand Up @@ -198,6 +200,16 @@ def _check_2fa_auth(self, browser, wait, verify_mode="phone", verify_token_func=
browser.find_element(By.NAME, "VerificationCode").send_keys(verify_code)
self._click(browser, (By.CLASS_NAME, "role_next"))

def _switch_coupon_activation(self, coupon_id: str, action: Literal["activate", "deactivate"]):
url = f"{self._COUPONS_API}/v1/{self._country}/{coupon_id}/activation"
kwargs = {"headers": self._default_headers(), "timeout": self._TIMEOUT}
if action == "activate":
return requests.post(url, **kwargs).json()
elif action == "deactivate":
return requests.delete(url, **kwargs).json()
else:
raise ValueError(f'Unknown action "{action}" - Only "activate" or "deactivate" supported')

def login(self, phone, password, **kwargs):
"""Simulate app auth"""
browser = self._get_browser(headless=kwargs.get("headless", True))
Expand Down Expand Up @@ -252,3 +264,17 @@ def ticket(self, ticket_id):
kwargs = {"headers": self._default_headers(), "timeout": self._TIMEOUT}
url = f"{self._TICKET_API}/{self._country}/tickets"
return requests.get(f"{url}/{ticket_id}", **kwargs).json()

def coupons(self):
"""Get list of all coupons"""
url = f"{self._COUPONS_API}/v2/{self._country}"
kwargs = {"headers": self._default_headers(), "timeout": self._TIMEOUT}
return requests.get(url, **kwargs).json()

def activate_coupon(self, coupon_id: str):
"""Activate single coupon by id"""
return self._switch_coupon_activation(coupon_id, "activate")

def deactivate_coupon(self, coupon_id: str):
"""Deactivate single coupon by id"""
return self._switch_coupon_activation(coupon_id, "deactivate")

0 comments on commit b8606e5

Please sign in to comment.