2023-03-29 20:00:10 +02:00
|
|
|
from tgtg import TgtgClient
|
2024-01-21 18:27:36 +01:00
|
|
|
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
|
2024-01-21 18:27:36 +01:00
|
|
|
import random
|
2023-03-29 20:00:10 +02:00
|
|
|
|
2024-01-21 18:27:36 +01:00
|
|
|
initial_waiting_time = 60
|
|
|
|
waiting_time = initial_waiting_time
|
|
|
|
waiting_time_limit = 60 * 60 * 24
|
|
|
|
waiting_time_increase = 2
|
|
|
|
|
|
|
|
randomness = 0.1
|
|
|
|
|
|
|
|
interval = 60
|
2023-03-29 20:00:10 +02:00
|
|
|
|
2023-05-19 22:01:51 +02:00
|
|
|
client = None
|
2023-12-12 09:20:22 +01:00
|
|
|
telegram_bot = None
|
|
|
|
removal_notification = os.environ.get('REMOVAL_NOTIFICATION')
|
|
|
|
|
|
|
|
if removal_notification is None:
|
|
|
|
removal_notification = False
|
2023-05-19 22:01:51 +02:00
|
|
|
|
|
|
|
|
2024-01-21 18:27:36 +01: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():
|
2023-12-12 09:20:22 +01:00
|
|
|
if os.environ.get('TGTG_EMAIL') is None:
|
|
|
|
return False
|
|
|
|
if os.environ.get('TELEGRAM') is None and os.environ.get('MATRIX') is None:
|
|
|
|
return False
|
|
|
|
if os.environ.get('TELEGRAM') is not None and (os.environ.get('TELEGRAM_TOKEN') is None or os.environ.get('TELEGRAM_ID') is None):
|
|
|
|
return False
|
|
|
|
if os.environ.get('MATRIX') is not None and os.environ.get('MATRIX_URL') is None:
|
|
|
|
return False
|
|
|
|
return True
|
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
|
|
|
|
|
|
|
if not os.path.exists('/data/token'):
|
|
|
|
client = TgtgClient(email=os.environ.get('TGTG_EMAIL'))
|
2023-05-29 16:58:15 +02:00
|
|
|
print('Waiting for credentials ...')
|
2023-03-29 20:00:10 +02:00
|
|
|
credentials = client.get_credentials()
|
2023-05-19 22:01:51 +02:00
|
|
|
with open('/data/token', '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:
|
2023-05-19 22:01:51 +02:00
|
|
|
with open('/data/token', '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)
|
2023-12-12 09:20:22 +01:00
|
|
|
if os.environ.get('TELEGRAM') is not None:
|
|
|
|
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):
|
2023-12-12 09:20:22 +01:00
|
|
|
if os.environ.get('TELEGRAM') is not None:
|
|
|
|
async with telegram_bot:
|
|
|
|
await telegram_bot.send_message(chat_id=os.environ.get('TELEGRAM_ID'), text='\n'.join(['Too good to Go'] + text))
|
|
|
|
if os.environ.get('MATRIX') is not None:
|
|
|
|
dic = {
|
|
|
|
'title': 'Too Good to Go',
|
|
|
|
'list': text,
|
|
|
|
}
|
|
|
|
|
|
|
|
response = requests.post(
|
|
|
|
url=os.environ.get('MATRIX_URL'),
|
|
|
|
headers={
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
},
|
|
|
|
auth=(os.environ.get('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:
|
2024-01-21 18:27:36 +01:00
|
|
|
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
|
|
|
|
2024-01-21 18:27:36 +01:00
|
|
|
name = ', '.join(filter(bool, [item_name, store_name, store_branch]))
|
2023-03-29 20:00:10 +02:00
|
|
|
|
2024-01-21 18:27:36 +01:00
|
|
|
if not name:
|
|
|
|
name = "Panier anti-gaspi"
|
2023-03-29 20:00:10 +02:00
|
|
|
|
2024-01-21 18:27:36 +01: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
|
|
|
|
2024-01-21 18:27:36 +01:00
|
|
|
name = ', '.join(filter(bool, [item_name, store_name, store_branch]))
|
2023-03-29 20:00:10 +02:00
|
|
|
|
2024-01-21 18:27:36 +01:00
|
|
|
if not name:
|
|
|
|
name = "Panier anti-gaspi"
|
2023-12-12 09:20:22 +01:00
|
|
|
|
2024-01-21 18:27:36 +01:00
|
|
|
texts.append(f'no more "{name}"')
|
2023-12-12 09:20:22 +01:00
|
|
|
|
2024-01-21 18:27:36 +01:00
|
|
|
|
|
|
|
if len(texts) > 1:
|
2023-03-29 20:00:10 +02:00
|
|
|
|
2024-01-21 18:27:36 +01:00
|
|
|
print(f'\n{datetime.datetime.now()}: {len(texts)-1} new items available')
|
2023-03-29 20:00:10 +02:00
|
|
|
|
2024-01-21 18:27:36 +01:00
|
|
|
await send_message(texts)
|
|
|
|
else:
|
|
|
|
print('-', end='', flush=True)
|
|
|
|
|
|
|
|
last = next
|
|
|
|
time.sleep(interval + randomness * interval * (2 * random.random() - 1))
|
2023-03-29 20:00:10 +02:00
|
|
|
|
2024-01-21 18:27:36 +01:00
|
|
|
waiting_time = initial_waiting_time
|
2023-03-29 20:00:10 +02:00
|
|
|
|
2024-01-21 18:27:36 +01: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
|
|
|
|
2024-01-21 18:27:36 +01:00
|
|
|
if waiting_time < waiting_time_limit:
|
|
|
|
waiting_time *= waiting_time_increase
|
2023-03-29 20:00:10 +02:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2023-05-29 16:58:15 +02:00
|
|
|
print('Check environ:', check_env())
|
2023-05-19 22:01:51 +02:00
|
|
|
load_creds()
|
2023-05-19 21:36:12 +02:00
|
|
|
asyncio.run(main())
|