Skip to content

Commit 7a54b61

Browse files
authored
Merge pull request #276 from Der-Henning/dev
Merge dev to main
2 parents 891d3ec + 2e2e1de commit 7a54b61

File tree

14 files changed

+138
-90
lines changed

14 files changed

+138
-90
lines changed

requirements-build.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
-r requirements.txt
22
altgraph==0.17.3
3-
pyinstaller==5.7.0
3+
pyinstaller==5.8.0
44
pyinstaller-hooks-contrib==2022.15

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ certifi==2022.12.7
44
charset-normalizer==3.0.1
55
colorlog==6.7.0
66
cron-descriptor==1.2.35
7+
humanize==4.6.0
78
idna==3.4
89
packaging==23.0
910
prometheus-client==0.16.0

sample.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ ITEM_IDS=
66
METRICS=false
77
METRICS_PORT=8000
88
DISABLE_TESTS=false
9+
LOCALE=en_US
910

1011
TGTG_USERNAME=
1112
TGTG_ACCESS_TOKEN=

scanner.spec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ from PyInstaller.utils.hooks import collect_data_files
33

44
datas = []
55
datas += collect_data_files('cron_descriptor')
6+
datas += collect_data_files('humanize')
67

78

89
block_cipher = None

src/config.sample.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ MetricsPort = 8000
1919
DisableTests = false
2020
## Disable all console outputs. only displays errors or Console notifier messages
2121
quiet = false
22+
locale = en_US
2223

2324
[TGTG]
2425
## TGTG Username / Login EMail

src/main.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
import http.client as http_client
33
import json
44
import logging
5+
import platform
6+
import signal
57
import sys
68
from os import path
7-
from typing import NoReturn
9+
from typing import Any, NoReturn
810

911
import colorlog
1012
import requests
@@ -28,9 +30,13 @@
2830
# set to 1 to debug http headers
2931
http_client.HTTPConnection.debuglevel = 0
3032

33+
SYS_PLATFORM = platform.system()
34+
IS_WINDOWS = SYS_PLATFORM.lower() in ('windows', 'cygwin')
35+
3136

3237
def main() -> NoReturn:
3338
"""Wrapper for Scanner and Helper functions."""
39+
_register_signals()
3440
parser = argparse.ArgumentParser(
3541
description="TooGoodToGo scanner and notifier.",
3642
prog="scanner"
@@ -245,6 +251,24 @@ def _print_welcome_message() -> None:
245251
log.info("")
246252

247253

254+
def _register_signals() -> None:
255+
# TODO: Define SIGUSR1, SIGUSR2
256+
signal.signal(signal.SIGINT, _handle_exit_signal)
257+
signal.signal(signal.SIGTERM, _handle_exit_signal)
258+
if hasattr(signal, "SIGBREAK"):
259+
signal.signal(getattr(signal, "SIGBREAK"), _handle_exit_signal)
260+
if not IS_WINDOWS:
261+
signal.signal(signal.SIGHUP, _handle_exit_signal)
262+
# TODO: SIGQUIT is ideally meant to terminate with core dumps
263+
signal.signal(signal.SIGQUIT, _handle_exit_signal)
264+
265+
266+
def _handle_exit_signal(signum: int, _frame: Any) -> None:
267+
log = logging.getLogger("tgtg")
268+
log.debug('Received signal %d' % signum)
269+
raise KeyboardInterrupt
270+
271+
248272
def query_yes_no(question, default="yes") -> bool:
249273
"""Ask a yes/no question via raw_input() and return their answer.
250274

src/models/config.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from pathlib import Path
88
from typing import Any
99

10+
import humanize
11+
1012
from models.cron import Cron
1113
from models.errors import ConfigurationError
1214

@@ -18,6 +20,7 @@
1820
'sleep_time': 60,
1921
'schedule_cron': Cron('* * * * *'),
2022
'debug': False,
23+
'locale': "en_US",
2124
'metrics': False,
2225
'metrics_port': 8000,
2326
'disable_tests': False,
@@ -75,7 +78,7 @@
7578
'url': '',
7679
'method': 'POST',
7780
'body': '',
78-
'type': '',
81+
'type': 'text/plain',
7982
'headers': {},
8083
'timeout': 60,
8184
'cron': Cron('* * * * *')
@@ -105,6 +108,7 @@ class Config():
105108
sleep_time: int
106109
schedule_cron: str
107110
debug: bool
111+
locale: str
108112
metrics: bool
109113
metrics_port: int
110114
disable_tests: bool
@@ -135,6 +139,8 @@ def __init__(self, file: str = None):
135139

136140
self.token_path = environ.get("TGTG_TOKEN_PATH", None)
137141
self._load_tokens()
142+
if (self.locale and not self.locale.startswith('en')):
143+
humanize.i18n.activate(self.locale)
138144

139145
def _open(self, file: str, mode: str) -> TextIOWrapper:
140146
return open(Path(self.token_path, file), mode, encoding='utf-8')
@@ -238,6 +244,7 @@ def _read_ini(self) -> None:
238244
self._ini_get_boolean(config, "MAIN", "DisableTests",
239245
"disable_tests")
240246
self._ini_get_boolean(config, "MAIN", "quiet", "quiet")
247+
self._ini_get(config, "MAIN", "locale", "locale")
241248

242249
self._ini_get(config, "TGTG", "Username", "tgtg.username")
243250
self._ini_get(config, "TGTG", "AccessToken", "tgtg.access_token")
@@ -350,6 +357,7 @@ def _read_env(self) -> None:
350357
self._env_get_int("METRICS_PORT", "metrics_port")
351358
self._env_get_boolean("DISABLE_TESTS", "disable_tests")
352359
self._env_get_boolean("QUIET", "quiet")
360+
self._env_get("LOCALE", "locale")
353361

354362
self._env_get("TGTG_USERNAME", "tgtg.username")
355363
self._env_get("TGTG_ACCESS_TOKEN", "tgtg.access_token")
@@ -419,7 +427,7 @@ def set(self, section: str, option: str, value: Any) -> bool:
419427
try:
420428
config = configparser.ConfigParser()
421429
config.optionxform = str
422-
config.read(self.file)
430+
config.read(self.file, encoding='utf-8')
423431
if section not in config.sections():
424432
config.add_section(section)
425433
config.set(section, option, str(value))
@@ -440,7 +448,7 @@ def save_tokens(self, access_token: str, refresh_token: str,
440448
try:
441449
config = configparser.ConfigParser()
442450
config.optionxform = str
443-
config.read(self.file)
451+
config.read(self.file, encoding='utf-8')
444452
if "TGTG" not in config.sections():
445453
config.add_section("TGTG")
446454
config.set("TGTG", "AccessToken", access_token)

src/models/item.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import datetime
22
import re
33

4+
import humanize
5+
46
from models.errors import MaskConfigurationError
57

68
ATTRS = ["item_id", "items_available", "display_name", "description",
@@ -17,6 +19,7 @@ class Item():
1719
"""
1820

1921
def __init__(self, data: dict):
22+
2023
self.items_available = data.get("items_available", 0)
2124
self.display_name = data.get("display_name", "-")
2225
self.favorite = "Yes" if data.get("favorite", False) else "No"
@@ -96,9 +99,10 @@ def pickupdate(self) -> str:
9699
pto = self._datetimeparse(self.pickup_interval_end)
97100
prange = (f"{pfr.hour:02d}:{pfr.minute:02d} - "
98101
f"{pto.hour:02d}:{pto.minute:02d}")
102+
tommorow = now + datetime.timedelta(days=1)
99103
if now.date() == pfr.date():
100-
return f"Today, {prange}"
104+
return f"{humanize.naturalday(now)}, {prange}"
101105
if (pfr.date() - now.date()).days == 1:
102-
return f"Tomorrow, {prange}"
106+
return f"{humanize.naturalday(tommorow)}, {prange}"
103107
return f"{pfr.day}/{pfr.month}, {prange}"
104108
return "-"

src/scanner.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ def _job(self) -> None:
6464
for item_id in self.item_ids:
6565
try:
6666
if item_id != "":
67-
items.append(Item(self.tgtg_client.get_item(item_id)))
67+
items.append(
68+
Item(self.tgtg_client.get_item(item_id)))
6869
except TgtgAPIError as err:
6970
log.error(err)
7071
items += self._get_favorites()

src/tgtg/tgtg_client.py

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -173,34 +173,31 @@ def _post(self, path, **kwargs) -> requests.Response:
173173
if response.status_code in (HTTPStatus.OK, HTTPStatus.ACCEPTED):
174174
self.captcha_error_count = 0
175175
return response
176-
try:
177-
response.json()
178-
except ValueError:
179-
# Status Code == 403 and no json contend
180-
# --> Blocked due to rate limit / wrong user_agent.
181-
# 1. Try: Get latest APK Version from google
182-
# 2. Try: Reset session
183-
# 3. Try: Delete datadome cookie and reset session
184-
# 10.Try: Sleep 10 minutes, and reset session
185-
if response.status_code == 403:
186-
log.debug("Captcha Error 403!")
187-
self.captcha_error_count += 1
188-
if self.captcha_error_count == 1:
189-
self.user_agent = self._get_user_agent()
190-
elif self.captcha_error_count == 2:
191-
self.session = self._create_session()
192-
elif self.captcha_error_count == 4:
193-
self.datadome_cookie = None
194-
self.session = self._create_session()
195-
elif self.captcha_error_count >= 10:
196-
log.warning(
197-
"Too many captcha Errors! Sleeping for 10 minutes...")
198-
time.sleep(10 * 60)
199-
log.info("Retrying ...")
200-
self.captcha_error_count = 0
201-
self.session = self._create_session()
202-
time.sleep(1)
203-
return self._post(path, **kwargs)
176+
# Status Code == 403
177+
# --> Blocked due to rate limit / wrong user_agent.
178+
# 1. Try: Get latest APK Version from google
179+
# 2. Try: Reset session
180+
# 3. Try: Delete datadome cookie and reset session
181+
# 10.Try: Sleep 10 minutes, and reset session
182+
if response.status_code == 403:
183+
log.debug("Captcha Error 403!")
184+
self.captcha_error_count += 1
185+
if self.captcha_error_count == 1:
186+
self.user_agent = self._get_user_agent()
187+
elif self.captcha_error_count == 2:
188+
self.session = self._create_session()
189+
elif self.captcha_error_count == 4:
190+
self.datadome_cookie = None
191+
self.session = self._create_session()
192+
elif self.captcha_error_count >= 10:
193+
log.warning(
194+
"Too many captcha Errors! Sleeping for 10 minutes...")
195+
time.sleep(10 * 60)
196+
log.info("Retrying ...")
197+
self.captcha_error_count = 0
198+
self.session = self._create_session()
199+
time.sleep(1)
200+
return self._post(path, **kwargs)
204201
raise TgtgAPIError(response.status_code, response.content)
205202

206203
def _get_user_agent(self) -> str:

0 commit comments

Comments
 (0)