From 8057bfdba34343a7fe78f21a40cb182c56cf9fd3 Mon Sep 17 00:00:00 2001 From: Joan Date: Mon, 13 Mar 2023 16:42:26 +0100 Subject: [PATCH] Added button to product found. Added product_name to know from which search provides the article found. --- wallamanta/helpers.py | 20 ++++++++++++++------ wallamanta/walladb.py | 15 ++++++++++++++- wallamanta/wallamanta.py | 37 ++++++++++++++++++++++++------------- wallamanta/worker.py | 39 ++++++++++++++++++++++++++++++--------- 4 files changed, 82 insertions(+), 29 deletions(-) diff --git a/wallamanta/helpers.py b/wallamanta/helpers.py index c1ab375..1f09ddc 100644 --- a/wallamanta/helpers.py +++ b/wallamanta/helpers.py @@ -6,7 +6,9 @@ import constants from PIL import Image, ImageDraw, ImageFont from datetime import datetime, timedelta -from telegram import InlineKeyboardButton +from telegram import InlineKeyboardButton, InlineKeyboardMarkup +from telegram.ext import Application +from telegram.constants import ParseMode # Enable logging logging.basicConfig( @@ -111,12 +113,18 @@ def create_image(article): # guardamos la imagen con otro nombre image.save(f"/app/data/images/products/{article['id']}_composed.png", quality=95) -def send_article(article, product): +async def send_article(article, product): + application = Application.builder().get_updates_http_version('1.1').http_version('1.1').token(constants.TELEGRAM_TOKEN).build() create_image(article) - text = f"*{telegram_escape_characters(article['title'])}*\n\n*Descripción*: {telegram_escape_characters(article['description'])}\n\n*Precio*: {telegram_escape_characters(str(article['price']))} {telegram_escape_characters(article['currency'])}\n\n[IR AL ANUNCIO](https://es\.wallapop\.com/item/{telegram_escape_characters(article['web_slug'])})" - url = f"https://api.telegram.org/bot{constants.TELEGRAM_TOKEN}/sendPhoto?chat_id={product['telegram_user_id']}&caption={text}&parse_mode=MarkdownV2" - files = {'photo':open(f"/app/data/images/products/{article['id']}_composed.png", 'rb')} - logging.info(requests.post(url, files=files).content) + text = f"*{telegram_escape_characters(article['title'])}*\n\n*Descripción*: {telegram_escape_characters(article['description'])}\n\n*Encontrado por la búsqueda de:*{telegram_escape_characters(product['product_name'])}\n\n*Precio*: {telegram_escape_characters(str(article['price']))} {telegram_escape_characters(article['currency'])}" + #url = f"https://api.telegram.org/bot{constants.TELEGRAM_TOKEN}/sendPhoto?chat_id={product['telegram_user_id']}&caption={text}&parse_mode=MarkdownV2" + #files = {'photo':open(f"/app/data/images/products/{article['id']}_composed.png", 'rb')} + keyboard = [[InlineKeyboardButton("Ir al anuncio", url=f"https://es.wallapop.com/item/{article['web_slug']}")]] + #InlineKeyboardButton("Listar productos", callback_data="list")] + markup = InlineKeyboardMarkup(keyboard) + response = await application.bot.send_photo(chat_id=product['telegram_user_id'], photo=open(f"/app/data/images/products/{article['id']}_composed.png", 'rb'), caption=text, parse_mode=ParseMode.MARKDOWN_V2, reply_markup=markup) + #logging.info(requests.post(url, files=files).content) + logging.info(response) def get_category_id(category): ret = constants.CATEGORIES.get(category, '') diff --git a/wallamanta/walladb.py b/wallamanta/walladb.py index 197d414..40511fe 100644 --- a/wallamanta/walladb.py +++ b/wallamanta/walladb.py @@ -88,7 +88,7 @@ def add_test_user(telegram_user_id, telegram_name, until): found = False con = sqlite3.connect(constants.DB) cur = con.cursor() - params = (telegram_name,) + params = (telegram_user_id,) res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id=?", params) if res.fetchone() is None: params = (telegram_user_id, True, 'testing', until, telegram_name.first_name) @@ -119,6 +119,19 @@ def get_user_list(): con.close() return ret +def get_user(telegram_user_id): + con = sqlite3.connect(constants.DB) + con.row_factory = dict_factory + cur = con.cursor() + params = (telegram_user_id,) + res = cur.execute(f"SELECT telegram_name FROM users WHERE telegram_user_id=?", params) + if res != None: + ret = res.fetchone()['telegram_name'] + else: + ret = 'NoName' + con.close() + return ret + def get_user_type(telegram_user_id): con = sqlite3.connect(constants.DB) cur = con.cursor() diff --git a/wallamanta/wallamanta.py b/wallamanta/wallamanta.py index 70f5302..55bf779 100644 --- a/wallamanta/wallamanta.py +++ b/wallamanta/wallamanta.py @@ -91,16 +91,21 @@ async def menu_click_handler(update: Update, context: CallbackContext): async def add_product_name(update: Update, context: CallbackContext): if context.user_data.get('product_name', '') == '': - answer = update.message.text - context.user_data['product_name'] = answer - if not walladb.get_product(context.user_data): + product_name = update.message.text + if len(product_name) > 200: await context.bot.send_message(chat_id=update.effective_chat.id, - text='Escribe el precio mínimo', reply_markup=ForceReply()) - return ADD_PRODUCT_MIN_PRICE + text='El nombre del producto a buscar es muy largo, pon otro nombre', reply_markup=ForceReply()) + return ADD_PRODUCT_NAME else: - await context.bot.send_message(chat_id=update.effective_chat.id, - text=f"¡{walladb.get_product(context.user_data)['product_name']} ya está en seguimiento!") - return ConversationHandler.END + context.user_data['product_name'] = product_name + if not walladb.get_product(context.user_data): + await context.bot.send_message(chat_id=update.effective_chat.id, + text='Escribe el precio mínimo', reply_markup=ForceReply()) + return ADD_PRODUCT_MIN_PRICE + else: + await context.bot.send_message(chat_id=update.effective_chat.id, + text=f"¡{walladb.get_product(context.user_data)['product_name']} ya está en seguimiento!") + return ConversationHandler.END async def add_product_min_price(update: Update, context: CallbackContext): if context.user_data.get('min_price', '') == '': @@ -111,9 +116,9 @@ async def add_product_min_price(update: Update, context: CallbackContext): await context.bot.send_message(chat_id=update.effective_chat.id, text='Pon un precio mínimo en números', reply_markup=ForceReply()) return ADD_PRODUCT_MIN_PRICE - if min_price < 0: + if min_price < 0 or min_price > 999999: await context.bot.send_message(chat_id=update.effective_chat.id, - text='Pon un precio mínimo en números que no sean negativos', reply_markup=ForceReply()) + text='Pon un precio mínimo entre 0 y 999999', reply_markup=ForceReply()) return ADD_PRODUCT_MIN_PRICE context.user_data['min_price'] = min_price await context.bot.send_message(chat_id=update.effective_chat.id, @@ -129,9 +134,13 @@ async def add_product_max_price(update: Update, context: CallbackContext): await context.bot.send_message(chat_id=update.effective_chat.id, text='Pon un precio máximo en números', reply_markup=ForceReply()) return ADD_PRODUCT_MAX_PRICE - if max_price < 0: + if max_price < 0 or max_price > 999999: await context.bot.send_message(chat_id=update.effective_chat.id, - text='Pon un precio máximo en números que no sean negativos', reply_markup=ForceReply()) + text='Pon un precio máximo entre 0 y 999999', reply_markup=ForceReply()) + return ADD_PRODUCT_MAX_PRICE + if max_price < context.user_data['min_price']: + await context.bot.send_message(chat_id=update.effective_chat.id, + text='Pon un precio máximo menor al precio mínimo', reply_markup=ForceReply()) return ADD_PRODUCT_MAX_PRICE context.user_data['max_price'] = max_price keyboard = [ @@ -340,7 +349,7 @@ async def add_to_db_and_send(update: Update, context: ContextTypes.DEFAULT_TYPE) walladb.add_product(context.user_data) p = threading.Thread(target=Worker.run, args=(walladb.get_product(context.user_data), )) p.start() - await context.bot.send_message(chat_id=update.effective_chat.id, text=f"¡*{context.user_data['product_name']}* añadido correctamente\!", parse_mode=ParseMode.MARKDOWN_V2) + await context.bot.send_message(chat_id=update.effective_chat.id, text=f"¡*{helpers.telegram_escape_characters(context.user_data['product_name'])}* añadido correctamente\!", parse_mode=ParseMode.MARKDOWN_V2) def error(update, context): logging.error(f'Update ---{update}--- caused error ---{context.error}---') @@ -371,6 +380,8 @@ def main()->None: application.add_handler(CommandHandler("remove_user", remove_user_command)) application.add_handler(CommandHandler("status", status_command)) application.add_handler(CommandHandler("test", test_command)) + #application.add_handler(CallbackQueryHandler("list", send_list())) + #application.add_handler(CallbackQueryHandler(pattern="list", callback=send_list())) conv_handler = ConversationHandler( conversation_timeout=60, diff --git a/wallamanta/worker.py b/wallamanta/worker.py index af88c3d..0f3ac9a 100644 --- a/wallamanta/worker.py +++ b/wallamanta/worker.py @@ -4,6 +4,7 @@ import logging import helpers import walladb import constants +import asyncio # Enable logging logging.basicConfig( @@ -56,6 +57,7 @@ class Worker: return json_data['search_objects'] def first_run(self, product): + logging.info(f"First run for {product['product_name']} for {walladb.get_user(product['telegram_user_id'])}") for i in range(5): helpers.random_wait() list = [] @@ -66,7 +68,7 @@ class Worker: list.insert(0, article['id']) return list - def work(self, product, list): + async def work(self, product, list): helpers.random_wait() # Random wait to make requests separated in time in order to prevent API rate limit exec_times = [] while True: @@ -81,18 +83,20 @@ class Worker: try: if not self.has_excluded_words(article['title'].lower(), article['description'].lower(), product['title_description_exclude']) and not self.is_title_key_word_excluded(article['title'].lower(), product['title_exclude']): try: - helpers.send_article(article, product) + await helpers.send_article(article, product) except: - helpers.send_article(article, product) - time.sleep(1) # Avoid Telegram flood restriction + await helpers.send_article(article, product) + await asyncio.sleep(1) + #time.sleep(1) # Avoid Telegram flood restriction list.insert(0, article['id']) except Exception as e: logging.info("---------- EXCEPTION -----------") logging.info(f"{product['product_name']} worker crashed. {e}") logging.info(f"{product['product_name']}: Trying to parse {article['id']}: {article['title']} .\n") - time.sleep(constants.SLEEP_TIME) + #time.sleep(constants.SLEEP_TIME) + await asyncio.sleep(constants.SLEEP_TIME) exec_times.append(time.time() - start_time) - logging.info(f"\'{product['product_name']}\' node-> last: {exec_times[-1]} max: {self.get_max_time(exec_times)} avg: {self.get_average_time(exec_times)}") + logging.info(f"\'{product['product_name']}\' for {walladb.get_user(product['telegram_user_id'])} ({product['telegram_user_id']}) node-> last: {exec_times[-1]:.2f} max: {self.get_max_time(exec_times):.2f} avg: {self.get_average_time(exec_times):.2f}") def has_excluded_words(self, title, description, excluded_words): if len(excluded_words) > 0: @@ -125,16 +129,33 @@ class Worker: largest = i return largest + async def old_run(product): + worker = Worker() + list = worker.first_run(product) + while True: + try: + logging.info(f"Wallapop monitor worker started. Checking for new items containing: \'{product['product_name']}\' for {walladb.get_user(product['telegram_user_id'])} ({product['telegram_user_id']}) with given parameters periodically") + await worker.work(product, list) + break + except Exception as e: + logging.info(f"Exception: {e}") + logging.info(f"{product['product_name']} worker crashed. Restarting worker...") + await asyncio.sleep(10) + #time.sleep(10) + logging.info(f"Wallapop monitor worker stopped for: \'{product['product_name']}\'") + def run(product): worker = Worker() list = worker.first_run(product) while True: try: - logging.info(f"Wallapop monitor worker started. Checking for new items containing: \'{product['product_name']}\' with given parameters periodically") - worker.work(product, list) + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete(worker.work(product, list)) + loop.close() break except Exception as e: logging.info(f"Exception: {e}") logging.info(f"{product['product_name']} worker crashed. Restarting worker...") time.sleep(10) - logging.info(f"Wallapop monitor worker stopped for: \'{product['product_name']}\'") \ No newline at end of file + logging.info(f"Wallapop monitor worker stopped for: \'{product['product_name']}\'")