import math from time import time import requests import os import redis from logging import getLogger logger = getLogger(__name__) api_token = os.getenv('FUDO_API_KEY') api_secret = os.getenv('FUDO_API_SECRET') token = None token_exp = None # Configuración de Redis redis_client = redis.Redis( host=os.getenv('REDIS_HOST', 'localhost'), port=int(os.getenv('REDIS_PORT', 6379)), db=int(os.getenv('REDIS_DB', 0)), decode_responses=True ) REDIS_TOKEN_KEY = 'fudo_api_token' def get_token(): """ Obtiene el token de autenticación de Fudo API. Primero verifica si existe un token válido en Redis. Si no existe o ha expirado, solicita uno nuevo y lo guarda en Redis con expiración automática. """ global token, token_exp # Intento de obtener el token desde la RAM if token and token_exp and time() < token_exp: logger.info("Token obtenido desde variable global") return token try: # Intentar obtener el token desde Redis cached_token = redis_client.get(REDIS_TOKEN_KEY) if cached_token: logger.info("Token obtenido desde Redis cache") token = cached_token ttl = redis_client.ttl(REDIS_TOKEN_KEY) if ttl is None or int(str(ttl)) < 0: token_exp = None else: token_exp = int(str(ttl)) + int(time()) return str(cached_token) except Exception as e: logger.error(f"Error al conectar con Redis: {e}") logger.info("Fallback: obteniendo token sin cache") # Si no hay token en cache, solicitar uno nuevo url = 'https://auth.fu.do/api' data = { "apiKey": api_token, "apiSecret": api_secret } r = requests.post(url, data=data) response_data = r.json() token = response_data['token'] expiration_timestamp = response_data['exp'] # Calcular TTL en segundos para Redis current_time = int(time()) ttl_seconds = expiration_timestamp - current_time try: # Guardar el token en Redis con expiración automática if ttl_seconds > 0: redis_client.setex(REDIS_TOKEN_KEY, ttl_seconds, token) logger.info(f"Token nuevo guardado en Redis (expira en {ttl_seconds} segundos)") else: logger.warning("Warning: El token ya está expirado") except Exception as e: logger.error(f"Error al guardar en Redis: {e}") return token def get_categories(): token = get_token() url = 'https://api.fu.do/v1alpha1/product-categories' headers = { 'Authorization': 'Bearer ' + token } r = requests.get(url, headers=headers) return r.json() def get_product(id_category:int): url = 'https://api.fu.do/v1alpha1/products/{}'.format(id_category) token = get_token() headers = { 'Authorization': 'Bearer ' + token } r = requests.get(url, headers=headers) if r.status_code != 200: logger.error(f"Error al obtener producto: {r.json()['errors']}") return r.json() def get_products(): url = 'https://api.fu.do/v1alpha1/products' token = get_token() headers = { 'Authorization': 'Bearer ' + token } r = requests.get(url, headers=headers) return list(filter(lambda x: x['relationships']['productCategory']['data']['id'] == '1', r.json()['data'])) def get_table(number:int): n_per_page = 10 page = math.ceil(number / n_per_page) url = 'https://api.fu.do/v1alpha1/tables?page[number]={}&page[size]={}&include=activeSales&sort=number'.format(page, n_per_page) token = get_token() headers = { 'Authorization': 'Bearer ' + token } r = requests.get(url, headers=headers) if r.status_code != 200: logger.error('Error al obtener tablas:' + str(r.json()['errors'])) return None try: return list(filter(lambda x: x['attributes']['number'] == number, r.json()['data']))[0] except: logger.error('Error al obtener tabla') logger.error(r.json()) return None def get_sale(sale_id:int): url = 'https://api.fu.do/v1alpha1/sales/{}'.format(sale_id) token = get_token() headers = { 'Authorization': 'Bearer ' + token } r = requests.get(url, headers=headers) if r.status_code != 200: logger.error('Error al obtener tablas:' + str(r.json()['errors'])) return None return r.json() def create_sale(table_id:int): url = 'https://api.fu.do/v1alpha1/sales' token = get_token() headers = { 'Authorization': 'Bearer ' + token } data = { "data": { "type": "Sale", "attributes": { "people": 1, "saleType": "EAT-IN", "comment": "Pedido desde la app pedidos express" }, "relationships": { "table": { "data": { "id": str(table_id), "type": "Table" } }, "waiter": { "data": { "type": "User", "id": "76" } } } } } r = requests.post(url, headers=headers, json=data) if r.status_code != 201: logger.error('Error al crear la venta:', r.json()) return None return r.json()["data"] def create_item(product_id:int, quantity:int, sale_id:int, comment = None): url = 'https://api.fu.do/v1alpha1/items' token = get_token() headers = { 'Authorization': 'Bearer ' + token } data = { "quantity": quantity, "origin": "MOBILE", "comment": "Pedido desde pedidos express" + (f" - {comment}" if comment else ""), } data = { "data":{ "type": "Item", "attributes": data, "relationships": { "product": { "data": { "type": "Product", "id": str(product_id) } }, "sale": { "data": { "type": "Sale", "id": str(sale_id) } } }, } } r = requests.post(url, headers=headers, json=data) if r.status_code != 201: logger.error(r.json()) return None return r.json()["data"] def get_active_sale(table): data = table['relationships']['activeSales']['data'] if len(data) == 0: return None return data[0] def clear_token(): """ Elimina el token cached de Redis. Útil cuando el token es inválido o se necesita forzar una renovación. """ try: redis_client.delete(REDIS_TOKEN_KEY) logger.info("Token eliminado del cache") except Exception as e: logger.error(f"Error al eliminar token de Redis: {e}") if __name__ == "__main__": table = get_table(107) if table is None: logger.error('No se pudo obtener la mesa') exit() activeSale = get_active_sale(table) if not activeSale: logger.error('No hay una venta activa para la mesa') activeSale = create_sale(table['id']) if activeSale is None: logger.error('No se pudo crear la venta') exit() else: activeSale = activeSale[0] logger.info('Venta activa: %s', activeSale['id']) """ Instrucciones para hacer un pedido: 1. Obtener el token de autenticación con `get_token()` (ahora usa Redis cache). 2. Obtener la mesa con `get_table(numero_de_mesa)`. 3. Ver si tiene una activeSale, en caso contrario crear una con `create_sale(id_mesa)`. 4. Agregar los items a la venta con `create_item(id_producto, cantidad, id_venta, comentario)`. Configuración de Redis: - Host: REDIS_HOST (default: localhost) - Puerto: REDIS_PORT (default: 6379) - Base de datos: REDIS_DB (default: 0) """