diff options
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | README.md | 8 | ||||
-rw-r--r-- | helpers.py | 70 | ||||
-rw-r--r-- | server.py | 82 | ||||
-rw-r--r-- | static/favicon.ico | bin | 0 -> 193310 bytes | |||
-rw-r--r-- | templates/failure.html | 150 | ||||
-rw-r--r-- | templates/index.html | 182 |
7 files changed, 496 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..428598d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__ +*.sh +*.log +*.txt
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..425e829 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +Koda za spletno stran za preverjanje e-vinjet + +## Kako uporabljati +Namestite vse potrebne pakete s `pip install` nato `python3 server.py`. +Nato morate stran nekako spraviti na internet, ena opcija je nakup domene, druga je ngrok(zeeeelo enostavno in zastonj ampak da popup ob uporabi). Ko spletno stran postavite ne bo delovala ker mora dobiti pravilne headerje, zato nam njen naslov pošljite na `brajdic.roman@gmail.com` da jih boste prejemali in da promet preusmerimo na vašo stran. + + + diff --git a/helpers.py b/helpers.py new file mode 100644 index 0000000..414cb87 --- /dev/null +++ b/helpers.py @@ -0,0 +1,70 @@ +import requests +import json +from datetime import datetime, timezone, timedelta +from functools import lru_cache + +def formatEndDate(st): + if st is None: + return (None, None) + dt = datetime.strptime(st, "%Y-%m-%dT%H:%M:%S.%f0%z") + formatted = dt.strftime("%d.%m.%Y") + + # print days from today to date + daysRemaining = (dt - datetime.now(timezone.utc)).days + + return (formatted, daysRemaining) + + +purl = "https://evinjeta.dars.si/selfcare/api/eshop/shopping-cart/validate" + +json_dump = open("payload.txt", "r").read() +payload = json.loads(json_dump) + + +def get_headers(): + hdump = [x.split(":") for x in [t for t in open("headers.txt", "r")][1:]] + headers = {h[0]: h[1].strip() for h in hdump} + return headers + +@lru_cache(maxsize=None) +def veljavnost(registrska: str): + today = datetime.now() + one_year_after = today + timedelta(days=365) + payload["registrationNumber"] = registrska + payload["registrationNumberAgain"] = registrska + payload["vignetteValidityStart"] = today.strftime("%Y-%m-%dT%H:%M:%S.%fZ") + payload["vignetteValidityEnd"] = one_year_after.strftime( + "%Y-%m-%dT%H:%M:%S.%f0%z") + r = requests.post(purl, json=payload, headers=get_headers(), verify=False) + # print("\n"+r.text+"\n------------------\n\n\n") + print(r.status_code, r.reason, r.url) + return r.json() + + +def aux(registrska): + registrska = registrska.upper().replace(" ", "").replace("-", "").strip() + if len(registrska) > 8 or len(registrska) < 5 or not registrska.isalnum(): + return None + if registrska == "FTEST": + t = 1/0 + if registrska[:3] == "XXX": + return None + jdump = veljavnost(registrska)["vignetteValidationResult"] + if "exemptedVehicles" in jdump and len(jdump["exemptedVehicles"]) > 0: + return f'Oproščeno {jdump["exemptedVehicles"][0]["exemptionReasonId"]["text"]}' + + # for k, v in jdump.items(): + # print(f"{k}: {v}") + max_date = None + for v in jdump["vignettes"]: + print(v) + date_1 = v["vignetteValidityStart"] + date_2 = v["vignetteValidityEnd"] + if max_date is None or date_2 > max_date: + max_date = date_2 + # resp.append(f"Obstaja vinjeta veljavna od {formatEndDate(date_1)[0]} do {formatEndDate(date_2)[0]}, torej je veljavna še {formatEndDate(date_2)[1]} dni") + return formatEndDate(max_date)[0] + + +if __name__ == "__main__": + pass diff --git a/server.py b/server.py new file mode 100644 index 0000000..aebf98c --- /dev/null +++ b/server.py @@ -0,0 +1,82 @@ +from flask import Flask, jsonify, render_template, request, send_from_directory +import os +from helpers import aux +from discord_webhook import DiscordWebhook +import datetime +from flask_limiter import Limiter +from flask_limiter.util import get_remote_address +import logging + +# Logs to both console and app.log file +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s") +formatter = logging.Formatter("%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s") +logger = logging.getLogger('server') +handler = logging.FileHandler('app.log') +handler.setFormatter(formatter) +handler.setLevel(logging.INFO) +logger.addHandler(handler) + +def niceDateTime(): + time = datetime.datetime.now() + return time.strftime("%H:%M:%S -- %d/%m/%Y") +def niceTime(): + time = datetime.datetime.now() + return time.strftime("%H:%M:%S") + +def spamDiscord(logs = ""): + content = f"@everyone, napaka na strani ob {niceDateTime()}: \n {logs}" + webhook = DiscordWebhook(url='https://discord.com/api/webhooks/1062432166950219838/tgl04gFFIMkrASeWX2AlETdsgdTHU3CWufzE1NZQMLQ2eZGVeQyGXj4AAt0k4Dj467B'+'d', content=content) + webhook.execute() + + +def discordLog(msg): + DiscordWebhook(url="https://discord.com/api/webhooks/1062474351196262571/V6M_vfhPy9QEEbM-b1W7qxBr2k5olpyeOY1O2RoKZ178c0Fs9_vYzKqJwES3o3suLSF"+"o", content=msg).execute() + + +app = Flask(__name__, static_folder="static") +limiter = Limiter( + get_remote_address, + app=app, + default_limits=["50 per minute"], + storage_uri="memory://", +) + + +def setHeaders(request): + headers = request.form["headers"] + with open("headers.txt", "w") as f: + f.write(headers) + discordLog(f"Headers so bili spremenjeni ob {niceDateTime()}") + return "OK" + +@app.route('/favicon.ico') +@app.route('/robots.txt') +@app.route('/sitemap.xml') +def static_from_root(): + return send_from_directory(app.static_folder, request.path[1:]) + +@app.route('/', methods=['POST']) +def apcall(): + if "headers" in request.form: + setHeaders(request) + return "OK" + registrska = request.form["registrska"] + try: + ar = aux(registrska) + app.logger.info(f"Registrska \"{registrska}\" je veljavna do {ar} ob {niceTime()}") + # discordLog(f"Registrska \"{registrska}\" je veljavna do {ar} ob {niceTime()}") + return render_template('index.html', valid_until = ar, license_plate = registrska) + except Exception as e: + spamDiscord(str(e)) + app.logger.error(f"\nNapaka pri {registrska} ob {niceTime()}") + # discordLog(f"!!!!!!!!!!!!!!!!!!!!!!!!!!!\nNapaka pri {registrska} ob {niceTime()}") + return render_template('failure.html') + +@app.route('/', methods=['GET']) +def index(): + app.logger.info("Zahteva za domačo stran") + # discordLog(f"Zahteva za domačo stran ob {niceTime()}") + return render_template('index.html') + +if __name__ == '__main__': + app.run()
\ No newline at end of file diff --git a/static/favicon.ico b/static/favicon.ico Binary files differnew file mode 100644 index 0000000..cac1eb8 --- /dev/null +++ b/static/favicon.ico diff --git a/templates/failure.html b/templates/failure.html new file mode 100644 index 0000000..7cff5eb --- /dev/null +++ b/templates/failure.html @@ -0,0 +1,150 @@ +<!doctype html> +<html> + <head> + + <!-- Google tag (gtag.js) --> + <script async src="https://www.googletagmanager.com/gtag/js?id=G-FJS1R8KSQD"></script> + <script> + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-FJS1R8KSQD'); + </script> + + <style> + body { + font-family: 'Courier New', Courier, monospace; + font-size: 1.3rem; + width: 100%; + height: 100%; + margin: auto; + justify-content: center; + align-items: center; + display: flex; + } + .input { + font-size: 1rem; + border-radius: 5px; + outline: none; + padding: 0.4rem; + height: 1.6rem; + } + .inner-content { + display: flex; + flex-direction: column; + margin: 10px; + padding: 7px; + max-width: 800px; + width: 95%; + } + .button { + height: 36px; + padding: 5px 20px; + border: 1px solid; + border-radius: 5px; + font-size: 1rem; + font-weight: 300; + margin-top: 15px; + } + .input:focus { + outline: auto; + border-color: none; + } + .info { + background-color: lightgoldenrodyellow; + border: 1px solid yellow; + border-radius: 10px; + padding: 20px; + margin-top: 20px; + margin-bottom: 20px; + display: flex; + flex-direction: column; + } + .message { + padding: 10px 20px; + margin-top: 2rem; + border: 2px solid rgb(190, 190, 190); + border-radius: 5px; + } + .error { + border-color: lightcoral; + } + .label { + font-weight: 700; + margin: 15px 0px ; + margin-right: 20px; + } + .title { + text-align: center; + } + .share-icon { + border-radius: 5px; + height: 2rem; + width: 2rem; + display: flex; + justify-content: center; + align-items: center; + } + .share-icon:hover { + cursor: pointer; + transform: scale(1.2); + transition-duration: 200ms; + } + .share-icons-container { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + margin-top: 40px; + } + a { + text-decoration: none; + } + .input-container { + display: flex; + flex-direction: row; + align-items: center; + } + + @media screen and (max-width: 400px) { + .input-container { + flex-direction: column; + justify-content: center; + } + .label { + margin-right: 0px; + } + } + </style> + <title>eVinjeta</title> + <meta name="description" content="Preveri veljavnost svoje e-vinjete." /> + <meta name="keywords" content="vinjeta evinjeta veljavnost" /> + <meta property="og:url" content="http://evinjeta.eu" /> + <meta property="og:type" content="website" /> + <meta property="og:title" content="eVinjeta" /> + <meta property="og:description" content="Enostavno preveri veljavnost svoje eVinjete!" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> + </head> + <body> + </script> + <div class="inner-content"> + <h1 class="title">eVINJETA</h1> + <div class="info"> + Žal je s strežnikom nekaj narobe, DARS občasno ovira delovanje naše strani. Morda jim je dolgčas.<br> + <br> + Za informacije o vinjeti pokličite na <a href="tel:015188350,1,1">01 51 88 350</a> + številka deluje 24/7, ko se klic poveže dvakrat pritisnite 1, nato pa operaterju povejte svojo registrsko številko.<br> + <br> + Drugi način je da sledite navodilom na posnetku spodaj.<br> + <br> + Lahko pa spletno stran ponovno pogledate kasneje, nismo še obupali! + </div> + <div style="align-self: center; margin-top: 20px; max-width: 95%;"> + <blockquote class="twitter-tweet"><p lang="sl" dir="ltr">[E-VINJETA] 🤔🧾 Ste izgubili potrdilo o nakupu letne e-vinjete in se sprašujete, do kdaj vam velja? 📆 V kratkem videu si oglejte, kako lahko na hiter in enostaven način preverite veljavnost vaše e-vinjete! 👇 <a href="https://t.co/buHbFp9NgV">pic.twitter.com/buHbFp9NgV</a></p>— DARS (@DARS_SI) <a href="https://twitter.com/DARS_SI/status/1613109303648346114?ref_src=twsrc%5Etfw">January 11, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> + </div> + </div> + </body> +</html> diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..d552e94 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,182 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + + <!-- Google tag (gtag.js) --> + <script async src="https://www.googletagmanager.com/gtag/js?id=G-FJS1R8KSQD"></script> + <script> + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-FJS1R8KSQD'); + </script> + <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6895462190731164" + crossorigin="anonymous"></script> + </script> + + <style> + body { + font-family: 'Courier New', Courier, monospace; + font-size: 1.6rem; + width: 100%; + height: 100%; + margin: auto; + justify-content: center; + align-items: center; + display: flex; + } + .input { + font-size: 1.6rem; + border-radius: 5px; + outline: none; + padding: 0.4rem; + height: 1.6rem; + max-width: 90%; + } + .inner-content { + display: flex; + flex-direction: column; + margin: 10px; + padding: 7px; + max-width: 800px; + width: 95%; + } + .button { + height: 45px; + padding: 5px 20px; + border: 1px solid; + border-radius: 5px; + font-size: 1.7rem; + margin-top: 15px; + } + .input:focus { + outline: auto; + border-color: none; + } + .info { + background-color: lightgoldenrodyellow; + border: 1px solid yellow; + border-radius: 10px; + padding: 20px; + margin-bottom: 10px; + display: flex; + flex-direction: column; + font-size: 1.6rem; + } + .message { + padding: 10px 20px; + margin-top: 2rem; + border: 2px solid rgb(190, 190, 190); + border-radius: 5px; + } + .error { + border-color: lightcoral; + } + .label { + margin: 15px 0px ; + margin-right: 20px; + font-weight: 700; + } + .title { + text-align: center; + font-size: 3rem; + } + .share-icon { + border-radius: 5px; + height: 2rem; + width: 2rem; + display: flex; + justify-content: center; + align-items: center; + } + .share-icon:hover { + cursor: pointer; + transform: scale(1.2); + transition-duration: 200ms; + } + .share-icons-container { + width: 90%; + display: flex; + justify-content: center; + align-items: center; + margin-top: 40px; + padding-left: 5%; + } + a { + text-decoration: none; + } + .input-container { + display: flex; + flex-direction: row; + align-items: center; + } + + @media screen and (max-width: 700px) { + .input-container { + flex-direction: column; + justify-content: center; + } + .label { + margin-right: 0px; + } + } + </style> + <title>eVinjeta: Hitro preveri veljavnost evinjete</title> + <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"> + <meta name="description" content="Preveri evinjeto. Brez registracije, potrebujemo le registrsko številko. Rezultat takoj. Kdaj kupiti evinjeto?" /> + <meta name="keywords" content="vinjeta evinjeta veljavnost DARS" /> + <meta property="og:url" content="http://evinjeta.eu" /> + <meta property="og:type" content="website" /> + <meta property="og:title" content="eVinjeta" /> + <meta property="og:description" content="Preveri evinjeto. Brez registracije, potrebujemo le registrsko številko. Rezultat takoj. Kdaj kupiti evinjeto?" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> + </head> + <body> + <div class="inner-content"> + <h1 class="title">eVINJETA</h1> + <div class="info"> + Preveri veljavnost svoje eVinjete: + <span style="font-size: 0.8rem; font-weight: 200;">* Podatke dobimo iz DARS strežnikov. Nismo povezani z DARSom.</span> + </div> + <form method="POST"> + <div class="input-container"> + <p class="label">Registrska tablica:</p> + <input class="input" type="text" name="registrska" placeholder="Primer: LJABC23" /> + </div> + <div style="justify-content: center; display: flex;"> + <input type="submit" value="Preveri" class="button" /> + </div> + </form> + {% if valid_until %} + <div class="message"> + <p>Vinjeta za registrsko tablico <b>{{ license_plate }}</b> je veljavna vključno do: <b>{{ valid_until }}</b></p> + </div> + {% elif license_plate %} + <div class="message error"> + <p>Registrska tablica "<b>{{ license_plate }}</b>" nima veljavne vinjete!</p> + </div> + {% endif %} + <div class="share-icons-container"> + <div> + <span style="font-size: 1.4rem;">Če vam je spletna stran prišla prav, jo prosim delite z ostalimi:</span> + <div style="display: flex; justify-content: center; margin-top: 10px;"> + <a href="https://twitter.com/intent/tweet?text=Enostavno%20preveri%20veljavnost%20svoje%20vinjete%21%0Ahttp%3A%2F%2Fevinjeta.eu%0A%23vinjeta%20%23evinjeta" onclick="window.open(this.href, 'newwindow1', 'width=600,height=600'); return false;"> + <div class="share-icon" id="twitter"><i class="fa fa-twitter" style="font-size:32px; color: #1DA1F2"></i></div> + </a> + <div style="width: 10px;"></div> + <a href="https://www.facebook.com/sharer/sharer.php?u=http://evinjeta.eu" onclick="window.open(this.href, 'newwindow2', 'width=600,height=600'); return false;"> + <div class="share-icon" id="facebook"><i class="fa fa-facebook" style="font-size:32px; color: #4267B2"></i></div> + </a> + </div> + </div> + </div> + <div style="align-self: center; margin-top: 20px; max-width: 100%;"> + <blockquote class="twitter-tweet"><p lang="sl" dir="ltr">[E-VINJETA] 🤔🧾 Ste izgubili potrdilo o nakupu letne e-vinjete in se sprašujete, do kdaj vam velja? 📆 V kratkem videu si oglejte, kako lahko na hiter in enostaven način preverite veljavnost vaše e-vinjete! 👇 <a href="https://t.co/buHbFp9NgV">pic.twitter.com/buHbFp9NgV</a></p>— DARS (@DARS_SI) <a href="https://twitter.com/DARS_SI/status/1613109303648346114?ref_src=twsrc%5Etfw">January 11, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> + </div> + </div> + </body> +</html>
\ No newline at end of file |