Giter Site home page Giter Site logo

lidl-plus's Introduction

This python package is unofficial and is not related in any way to Lidl. It was developed by reversed engineered requests and can stop working at anytime!

Python Lidl Plus API

GitHub Workflow Status PyPI - Status PyPI PyPI - Python Version PyPI - License PyPI - Downloads Buy Me a Coffee

Fetch receipts and more from Lidl Plus.

Installation

pip install lidl-plus

Authentication

To login in Lidl Plus we need to simulate the app login. This is a bit complicated, we need a web browser and some additional python packages. After we have received the token once, we can use it for further requestes and we don't need a browser anymore.

Prerequisites

  • Check you have installed one of the supported web browser
    • Chromium
    • Google Chrome
    • Mozilla Firefox
    • Microsoft Edge
  • Install additional python packages
    pip install "lidl-plus[auth]"

Commandline-Tool

$ lidl-plus auth
Enter your language (de, en, ...): de
Enter your country (DE, AT, ...): AT
Enter your lidl plus username (phone number): +4915784632296
Enter your lidl plus password:
Enter the verify code you received via phone: 590287
------------------------- refresh token ------------------------
2D4FC2A699AC703CAB8D017012658234917651203746021A4AA3F735C8A53B7F
----------------------------------------------------------------

Python

from lidlplus import LidlPlusApi

lidl = LidlPlusApi(language="de", country="AT")
lidl.login(phone="+4915784632296", password="password", verify_token_func=lambda: input("Insert code: "))
print(lidl.refresh_token)

Usage

Currently, the only features are fetching receipts and activating coupons

Receipts

Get your receipts as json and receive a list of bought items like:

{
    "currentUnitPrice": "2,19",
    "quantity": "1",
    "isWeight": false,
    "originalAmount": "2,19",
    "name": "Vegane Frikadellen",
    "taxGroup": "1",
    "taxGroupName": "A",
    "codeInput": "4023456245134",
    "discounts": [
        {
            "description": "5€ Coupon",
            "amount": "0,21"
        }
    ],
    "deposit": null,
    "giftSerialNumber": null
},

Commandline-Tool

$ lidl-plus --language=de --country=AT --refresh-token=XXXXX receipt --all > data.json

Python

from lidlplus import LidlPlusApi

lidl = LidlPlusApi("de", "AT", refresh_token="XXXXXXXXXX")
for receipt in lidl.tickets():
    pprint(lidl.ticket(receipt["id"]))

Coupons

You can list all coupons and activate/deactivate them by id

{
    "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

$ lidl-plus --language=de --country=AT --refresh-token=XXXXX coupon --all

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

Lidl Plus API

options:
  -h, --help                show this help message and exit
  -c CC, --country CC       country (DE, BE, NL, AT, ...)
  -l LANG, --language LANG  language (de, en, fr, it, ...)
  -u USER, --user USER      Lidl Plus login username
  -p XXX, --password XXX    Lidl Plus login password
  --2fa {phone,email}       choose two factor auth method
  -r TOKEN, --refresh-token TOKEN
                            refresh token to authenticate
  --skip-verify             skip ssl verification
  --not-accept-legal-terms  not auto accept legal terms updates
  -d, --debug               debug mode

commands:
  auth                      authenticate and get token
  receipt                   output last receipts as json
  coupon                    activate coupons

Support

If you find this project helpful and would like to support its development, you can buy me a coffee! β˜•

"Buy Me A Coffee"

Don't forget to star the repository if you found it useful! ⭐

lidl-plus's People

Contributors

1-byte avatar andre0512 avatar dakse avatar dependabot[bot] avatar pawel-grad avatar vorostamas avatar woyken avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

lidl-plus's Issues

API change

Just letting you know that API version 1 became unavailable (at least here in NL), so I did some research, and here are the updated endpoints:

  • Ticket list
    Old: https://tickets.lidlplus.com/api/v1/NL/tickets/list
    New: https://tickets.lidlplus.com/api/v2/NL/tickets
    You can use the query parameter pageNumber to select the page of the paginated response, and onlyFavorite (true/false) to filter by favourite status.

  • Ticket details
    Old: https://tickets.lidlplus.com/api/v1/NL/tickets/{id}
    New: https://tickets.lidlplus.com/api/v2/NL/tickets/{id}

Response data structure also slightly changed, for example records in the list response is now tickets instead.

Also, methods for these aren't currently implemented in your library, but you might be interested in them:

  • Mark ticket (receipt) as favourite:
    POST https://tickets.lidlplus.com/api/v2/NL/tickets/favorite, with JSON body ["ticketidhere"]
  • Mark ticket (receipt) as not favourite:
    POST https://tickets.lidlplus.com/api/v2/NL/tickets/unfavorite, with JSON body ["ticketidhere"]
  • Coupon list: https://coupons.lidlplus.com/api/v2/NL
  • Coupon details: https://coupons.lidlplus.com/api/v2/NL/{id}
  • Coupon activation: POST (activate) or DELETE (deactivate) to https://coupons.lidlplus.com/api/v1/NL/{id}/activation

There's a bunch more endpoints for announcements, store details, shopping lists, etc, I'll document them all at some point, but hopefully the above will save you some trouble fixing the current methods in your library.

Suggestion: Dockerization

I would be most appreciative if an example in docs or in practical implementation would be shown on how to run code from the library inside a docker container (preferably with docker-compose).

When running it on a desktop computer seems no problem at all, I have spent an entire day on trying to dockerize the lib but I still didn't manage to run it on a ubuntu server.

Unable to get refresh token with Firefox

Describe the bug
I'm not familiar with selenium at all, but lidl-plus auth was reporting that I don't have firefox installed (I do). Removing executable_path, firefox_binary and firefox_profile arguments from webdriver.Firefox() in _init_firefox() fixed this issue.

Expected behavior
I'm supposed to get a refresh token.

Environment (please complete the following information):

  • Python-Version: 3.11
  • Release-Version: 0.2.6
  • OS: Fedora 37
  • Browser: Firefox and Chromium
  • Country: CZ
  • Language: cs

Cannot get refresh-token

After adding .items() inside the _register_link function in lidlplus/api.py here:

kΓ©p

I was able to navigate to the page, but after providing the e-mail and password, then hitting the next button nothing happened:

kΓ©p

HTTP 403 during auth

It seems lidl's CDN is blocking python connecting.

DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): accounts.lidl.com:443
send: b'GET /.well-known/openid-configuration HTTP/1.1\r\nHost: accounts.lidl.com\r\nUser-Agent: python-requests/2.31.0\r\nAccept-Encoding: gzip, deflate, br, zstd\r\nAccept: */*\r\nConnection: keep-alive\r\n\r\n'
reply: 'HTTP/1.1 403 Forbidden\r\n'
header: Server: AkamaiGHost
header: Mime-Version: 1.0
header: Content-Type: text/html
header: Content-Length: 419
header: Expires: Sat, 30 Mar 2024 15:54:23 GMT
header: Date: Sat, 30 Mar 2024 15:54:23 GMT
header: Connection: close
DEBUG:urllib3.connectionpool:https://accounts.lidl.com:443 "GET /.well-known/openid-configuration HTTP/1.1" 403 419
Traceback (most recent call last):
  File "/home/sd/venv/bin/lidl-plus", line 8, in <module>
    sys.exit(start())
             ^^^^^^^
  File "/home/sd/venv/lib/python3.11/site-packages/lidlplus/__main__.py", line 187, in start
    main()
  File "/home/sd/venv/lib/python3.11/site-packages/lidlplus/__main__.py", line 175, in main
    print_refresh_token(args)
  File "/home/sd/venv/lib/python3.11/site-packages/lidlplus/__main__.py", line 114, in print_refresh_token
    lidl_plus = lidl_plus_login(args)
                ^^^^^^^^^^^^^^^^^^^^^
  File "/home/sd/venv/lib/python3.11/site-packages/lidlplus/__main__.py", line 92, in lidl_plus_login
    lidl_plus.login(
  File "/home/sd/venv/lib/python3.11/site-packages/lidlplus/api.py", line 215, in login
    browser.get(self._register_link)
                ^^^^^^^^^^^^^^^^^^^
  File "/home/sd/venv/lib/python3.11/site-packages/lidlplus/api.py", line 158, in _register_link
    return f"{self._register_oauth_client()}&{params}"
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/sd/venv/lib/python3.11/site-packages/lidlplus/api.py", line 73, in _register_oauth_client
    client.provider_config(self._AUTH_API)
  File "/home/sd/venv/lib/python3.11/site-packages/oic/oauth2/__init__.py", line 1173, in provider_config
    raise CommunicationError("Trying '%s', status %s" % (url, r.status_code))
oic.exception.CommunicationError: Trying 'https://accounts.lidl.com/.well-known/openid-configuration', status 403

Note that accessing the same URL from a browser works fine. Taking the headers from the browser and running the request via cURL doesn't work. Running the request via wget works. Changing user agents doesn't seem to solve it.

TL:DR: This is probably some tls fingerprinting level anti-bot protection. It's hard to bypass. Search 'AkamaiGHost 403' on Google to find others with the same issue. I tried some of the proposed fixes with no luck.

blinker._saferef Issue

"It turns out that selenium-wire is no longer maintained as of January 2024 and the project depends on package blinker, specifically file blinker._saferef that is no longer available in latest blinker versions 1.8.0 and 1.8.1.

The solution is to add direct dependency on blinker<1.8.0 into your project in order to prevent selenium-wire from automatically downloading the latest blinker version."

Can be fixed by installing an older version with
pip install blinker==1.7.0

API works just fine afterwards

HTTP 500 on ticket API

Describe the bug
I'm currently getting a HTTP 500 with the body Object reference not set to an instance of an object.. This happens when fetching a single ticket using ticket(), but fetching multiple tickets using tickets() works fine. I'm not sure whether this is an issue of this library or of the actual Lidl API.

Expected behavior
ticket() actually returns a valid response.

Environment:

  • Python-Version: 3.12.2
  • Release-Version: 0.3.5
  • OS: Windows 11
  • Browser: Firefox
  • Country: cs
  • Language: CZ

canΒ΄t enter my password

Describe the bug
when i get to the enter password part, I am unable to enter any letter

Environment (please complete the following information):

  • Python-Version: [3.11]
  • Release-Version: [e.g. 0.2.1]

For auth problems:

  • OS [Windows 11 pro]
  • Browser: [Chrome, Firefox]
  • Country: [sv]
  • Language: [SE]

Can't connect to Web Browser

Everytime I try to use lidl-plus auth I am running into a Webbrowser Exeption after entering the password. I tried with Chrome, Chromium and Firefox.

A clear and concise description of what you expected to happen.

  • Python-Version: 3.12.4
  • Release-Version: 0.3.5

For auth problems:

  • OS : Windows11, MacOS 13.5.1
  • Browser: Chromium, Chrome, Firefox
  • Country: DE
  • Language: de

Set Favorite Market

Hi,

thanks for providing this nice package.
With feature #7 it's possible to automatically activate all coupons available to all markets. However, there may be coupons that are only available to the selected favorite market. In the app, this favorite market can be selected under settings.

Without setting a favorite market, the first part of the response of LidlPlusApi.coupons() looks like this: {'name': 'FavoriteStore', 'coupons': []}
To automatically enable the coupons for the favorite market as well, it would be nice to support the selection of the favorite market per API.

By the way, in case someone runs into the same problem:
Some LIDL coupon names are not UTF-8 compliant. This may lead to such an error when printing the coupon name:
UnicodeEncodeError: 'latin-1' codec can't encode character '\u20ac' in position 1120: ordinal not in range(256)
Therefore, it might be worth sanitizing the string before using it further. For me, the following encoding of the string worked:
coupon["title"].encode('utf8', 'ignore')

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.