tg2/main.py

210 lines
6.7 KiB
Python
Raw Normal View History

2024-01-21 19:25:07 +01:00
import tgtg
2023-03-29 20:00:10 +02:00
from tgtg import TgtgClient
from tgtg.exceptions import TgtgAPIError
2023-03-29 20:00:10 +02:00
import os
import json
import telegram
import asyncio
import time
import datetime
2023-12-12 09:20:22 +01:00
import requests
import random
2023-03-29 20:00:10 +02:00
2024-01-22 16:40:02 +01:00
def get_env(name, default=None):
content = os.environ.get(name, default)
if type(default) == bool:
return content.lower() in ('true', '1')
if type(default) == int:
return int(content)
if type(default) == float:
return float(content)
return content
initial_waiting_time = get_env('INITIAL_WAITING_TIME', 60)
waiting_time = initial_waiting_time
2024-01-22 16:40:02 +01:00
waiting_time_limit = get_env('WAITING_TIME_LIMIT', 60 * 60 * 24)
waiting_time_increase = get_env('WAITING_TIME_FACTOR', 2)
2024-01-22 16:40:02 +01:00
randomness = get_env('RANDOMNESS', 0.1)
2024-01-22 16:40:02 +01:00
interval = get_env('INTERVAL', 60)
2023-03-29 20:00:10 +02:00
2024-01-21 19:25:07 +01:00
# Reduce frequency of polling to avoid rate limiting
2024-01-22 16:40:02 +01:00
tgtg.POLLING_WAIT_TIME = get_env('LOGIN_POLLING_WAIT_TIME', 30)
tgtg.MAX_POLLING_TRIES = get_env('LOGIN_MAX_POLLING_TRIES', 10)
2024-01-21 19:25:07 +01:00
2023-05-19 22:01:51 +02:00
client = None
2023-12-12 09:20:22 +01:00
telegram_bot = None
2024-01-22 16:40:02 +01:00
removal_notification = get_env('REMOVAL_NOTIFICATION', False)
2023-05-19 22:01:51 +02:00
2024-01-22 16:40:02 +01:00
TOKEN_PATH = get_env('TOKEN_PATH', '/data/token')
2023-05-19 22:01:51 +02:00
def parse_duration(seconds):
seconds = int(seconds)
minutes = seconds // 60
seconds = seconds % 60
hours = minutes // 60
minutes = minutes % 60
if hours > 0:
return f'{hours}h {minutes}m {seconds}s'
elif minutes > 0:
return f'{minutes}m {seconds}s'
else:
return f'{seconds}s'
2023-05-19 22:01:51 +02:00
def check_env():
2024-01-22 16:40:02 +01:00
if get_env('TGTG_EMAIL') is None:
2023-12-12 09:20:22 +01:00
return False
2024-01-22 16:40:02 +01:00
if get_env('TELEGRAM') is None and os.environ.get('MATRIX') is None:
2023-12-12 09:20:22 +01:00
return False
2024-01-22 16:40:02 +01:00
if get_env('TELEGRAM') is not None and (os.environ.get('TELEGRAM_TOKEN') is None or os.environ.get('TELEGRAM_ID') is None):
2023-12-12 09:20:22 +01:00
return False
2024-01-22 16:40:02 +01:00
if get_env('MATRIX') is not None and os.environ.get('MATRIX_URL') is None:
2023-12-12 09:20:22 +01:00
return False
return True
2023-05-19 22:01:51 +02:00
2024-01-21 19:25:07 +01:00
def retry_on_api_error(message):
def decorator(func):
def wrapper(*args, **kwargs):
global waiting_time
while True:
try:
return func(*args, **kwargs)
except TgtgAPIError as e:
print('ERROR: ', e)
real_waiting_time = round(waiting_time + randomness * waiting_time * (2 * random.random() - 1))
print(f'ls, retrying in {parse_duration(real_waiting_time)}')
time.sleep(real_waiting_time)
if waiting_time < waiting_time_limit:
waiting_time *= waiting_time_increase
return wrapper
return decorator
@retry_on_api_error('tg² failed to get credentials')
def get_credentials():
return client.get_credentials()
2023-05-19 22:01:51 +02:00
def load_creds():
2023-12-12 09:20:22 +01:00
global client, telegram_bot
2023-05-19 22:01:51 +02:00
2024-01-22 16:40:02 +01:00
if not os.path.exists(TOKEN_PATH):
client = TgtgClient(email=get_env('TGTG_EMAIL'))
2023-05-29 16:58:15 +02:00
print('Waiting for credentials ...')
2024-01-21 19:25:07 +01:00
credentials = get_credentials()
2024-01-22 16:40:02 +01:00
with open(TOKEN_PATH, 'w') as file:
2023-05-13 23:13:33 +02:00
file.write(str(credentials))
2023-05-29 16:58:15 +02:00
print('Credentials stored in file')
2023-03-29 20:00:10 +02:00
else:
2024-01-22 16:40:02 +01:00
with open(TOKEN_PATH, 'r') as file:
2023-05-13 23:13:33 +02:00
credentials = json.loads(file.read().replace('\'', '"'))
2023-05-29 16:58:15 +02:00
print('Credentials loaded from file')
2023-03-29 20:00:10 +02:00
2023-05-13 23:13:33 +02:00
client = TgtgClient(**credentials)
2024-01-22 16:40:02 +01:00
if get_env('TELEGRAM').lower() == 'true':
2023-12-12 09:20:22 +01:00
telegram_bot = telegram.Bot(os.environ['TELEGRAM_TOKEN'])
2023-05-19 22:01:51 +02:00
2023-05-19 21:36:12 +02:00
2023-05-19 22:01:51 +02:00
async def send_message(text):
2024-01-22 16:40:02 +01:00
if get_env('TELEGRAM').lower() == 'true':
2023-12-12 09:20:22 +01:00
async with telegram_bot:
2024-01-22 16:40:02 +01:00
await telegram_bot.send_message(chat_id=get_env('TELEGRAM_ID'), text='\n'.join(['Too good to Go'] + text))
if get_env('MATRIX').lower() == 'true':
2023-12-12 09:20:22 +01:00
dic = {
'title': 'Too Good to Go',
'list': text,
}
response = requests.post(
2024-01-22 16:40:02 +01:00
url=get_env('MATRIX_URL'),
2023-12-12 09:20:22 +01:00
headers={
'Content-Type': 'application/json',
},
2024-01-22 16:40:02 +01:00
auth=(get_env('MATRIX_BASIC_AUTH_USER'), os.environ.get('MATRIX_BASIC_AUTH_PASS')),
2023-12-19 09:22:17 +01:00
json=dic
2023-12-12 09:20:22 +01:00
)
2023-05-19 22:01:51 +02:00
async def main():
2023-12-19 09:22:17 +01:00
await send_message(['tg² telegram_bot is watching!'])
2023-03-29 20:00:10 +02:00
last = []
while True:
try:
items = client.get_items()
texts = []
next = []
for item in items:
if item['items_available'] > 0:
next.append(item["item"]["item_id"])
if item["item"]["item_id"] not in last:
amount = item["items_available"]
item_name = item["item"]["name"]
price = item["item"]["price_including_taxes"]["minor_units"]/(10**item["item"]["price_including_taxes"]["decimals"])
store_name = item["store"]["store_name"]
store_branch = item["store"]["branch"]
2023-03-29 20:00:10 +02:00
name = ', '.join(filter(bool, [item_name, store_name, store_branch]))
2023-03-29 20:00:10 +02:00
if not name:
name = "Panier anti-gaspi"
2023-03-29 20:00:10 +02:00
texts.append(f'{amount} x "{name}" ({price:.2f}€)')
elif removal_notification and item["item"]["item_id"] in last:
amount = item["items_available"]
name = item["item"]["name"]
price = item["item"]["price_including_taxes"]["minor_units"]/(10**item["item"]["price_including_taxes"]["decimals"])
store_name = item["store"]["store_name"]
store_branch = item["store"]["branch"]
2023-03-29 20:00:10 +02:00
name = ', '.join(filter(bool, [item_name, store_name, store_branch]))
2023-03-29 20:00:10 +02:00
if not name:
name = "Panier anti-gaspi"
2023-12-12 09:20:22 +01:00
texts.append(f'no more "{name}"')
2023-12-12 09:20:22 +01:00
if len(texts) > 1:
2023-03-29 20:00:10 +02:00
print(f'\n{datetime.datetime.now()}: {len(texts)-1} new items available')
2023-03-29 20:00:10 +02:00
await send_message(texts)
else:
print('-', end='', flush=True)
last = next
2024-01-22 16:40:02 +01:00
real_interval = interval + randomness * interval * (2 * random.random() - 1)
time.sleep(interval)
2023-03-29 20:00:10 +02:00
waiting_time = initial_waiting_time
2023-03-29 20:00:10 +02:00
except TgtgAPIError as e:
print(e)
real_waiting_time = round(waiting_time + randomness * waiting_time * (2 * random.random() - 1))
await send_message([f'tg² failed to fetch data, retrying in {parse_duration(real_waiting_time)}'])
time.sleep(real_waiting_time)
2023-03-29 20:00:10 +02:00
if waiting_time < waiting_time_limit:
waiting_time *= waiting_time_increase
2023-03-29 20:00:10 +02:00
if __name__ == '__main__':
2024-01-21 19:25:07 +01:00
if not check_env():
print('Missing environment variables')
exit(1)
2023-05-19 22:01:51 +02:00
load_creds()
2023-05-19 21:36:12 +02:00
asyncio.run(main())