add abstraction ob platform and article + vinted

"

Signed-off-by: Omar Sánchez Pizarro <omar.sanchez@pistacero.net>
This commit is contained in:
Omar Sánchez Pizarro
2025-10-10 14:58:27 +02:00
parent b5178f415b
commit 4111f57564
16 changed files with 1890 additions and 102 deletions

View File

@@ -13,6 +13,7 @@ from datetime import datetime
ITEM_HTML = """
<b>Artículo:</b> {title}
<b>Plataforma:</b> {platform}
{description}
@@ -73,6 +74,7 @@ class TelegramManager:
message = ITEM_HTML.format(
search_name=self.escape_html(search_name),
title=self.escape_html(article.get_title()),
platform=self.escape_html(article.get_platform()),
description=self.escape_html(article.get_description()),
location=self.escape_html(article.get_location()),
price=self.escape_html(article.get_price()),
@@ -91,16 +93,16 @@ class TelegramManager:
keyboard = [
[
InlineKeyboardButton("⭐ Añadir a favoritos", callback_data=f"fav_{article.get_id()}_{search_name}"),
InlineKeyboardButton("Ir al anuncio", url=f"https://es.wallapop.com/item/{article.get_url()}")
InlineKeyboardButton("Ir al anuncio", url=f"{article.get_url()}")
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
# Enviar un mensaje adicional con los botones (reply al primer mensaje del grupo)
await self._bot.send_message(
await self._bot.send_photo(
chat_id=self._channel,
photo=first_image_url,
text=message,
caption=message,
parse_mode="HTML",
reply_markup=reply_markup,
message_thread_id=thread_id
@@ -174,8 +176,8 @@ class TelegramManager:
article_id = parts[1]
search_name = parts[2]
# Obtener el mensaje original (el que tiene reply)
original_message = query.message.reply_to_message
# Ahora el mensaje original es el mismo mensaje del keyboard
original_message = query.message
if not original_message:
await query.edit_message_text("❌ No se pudo encontrar el mensaje original")
@@ -198,7 +200,7 @@ class TelegramManager:
# Verificar si ya existe
if any(fav["id"] == article_id for fav in favorites):
await query.edit_message_text(" Este artículo ya está en favoritos")
await query.message.reply_text(" Este artículo ya está en favoritos")
return
# Añadir nuevo favorito
@@ -211,7 +213,6 @@ class TelegramManager:
[InlineKeyboardButton("🗑️ Quitar de favoritos", callback_data=f"unfav_{article_id}")]
]
await query.edit_message_text(
text="💾 Acciones:",
reply_markup=InlineKeyboardMarkup(new_keyboard)
)
@@ -283,7 +284,7 @@ class TelegramManager:
[InlineKeyboardButton("⭐ Añadir a favoritos", callback_data=f"fav_{article_id}_unknown")]
]
await query.edit_message_text(
text="💾 Acciones:",
text="💾 Acciones",
reply_markup=InlineKeyboardMarkup(keyboard)
)

View File

@@ -1,13 +1,9 @@
import time
import requests
import logging
from datalayer.wallapop_article import WallapopArticle
import traceback
from platforms.platform_factory import PlatformFactory
REQUEST_SLEEP_TIME = 30
REQUEST_RETRY_TIME = 5
ERROR_SLEEP_TIME = 60
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36'
class Worker:
def __init__(self, item_to_monitor, general_args, queue_manager):
@@ -15,64 +11,25 @@ class Worker:
self._item_monitoring = item_to_monitor
self._general_args = general_args
self._queue_manager = queue_manager
# Initialize the platform based on item_to_monitor configuration
platform_name = self._item_monitoring.get_platform()
try:
self._platform = PlatformFactory.create_platform(platform_name, item_to_monitor)
self.logger.info(f"Initialized platform: {platform_name}")
except ValueError as e:
self.logger.error(f"Failed to initialize platform: {e}")
raise
# Initialize the queue with existing articles
self._queue_manager.add_to_notified_articles(self._request_articles())
def _create_url(self):
url = (
f"http://api.wallapop.com/api/v3/search"
f"?source=search_box"
f"&keywords={self._item_monitoring._search_query}"
f"&order_by=newest"
f"&language=es_ES"
)
# Only include latitude and longitude if both are not 0
if self._item_monitoring._latitude != 0 and self._item_monitoring._longitude != 0:
url += (
f"&latitude={self._item_monitoring._latitude}"
f"&longitude={self._item_monitoring._longitude}"
)
if self._item_monitoring._min_price != 0:
url += f"&min_sale_price={self._item_monitoring._min_price}"
if self._item_monitoring._max_price != 0:
url += f"&max_sale_price={self._item_monitoring._max_price}"
if self._item_monitoring._max_distance != 0:
url += f"&distance_in_km={self._item_monitoring._max_distance}"
if self._item_monitoring.get_condition() != "all":
url += f"&condition={self._item_monitoring.get_condition()}" # new, as_good_as_new, good, fair, has_given_it_all
return url
def _request_articles(self):
url = self._create_url()
while True:
try:
headers = {
'X-DeviceOS': '0',
'User-Agent': USER_AGENT
}
response = requests.get(url, headers=headers)
response.raise_for_status()
break
except requests.exceptions.RequestException as err:
self.logger.error(f"Request Exception: {err}")
time.sleep(REQUEST_RETRY_TIME)
json_response = response.json()
json_items = json_response['data']['section']['payload']['items']
articles = self._parse_json_response(json_items)
return articles
def _parse_json_response(self, json_response):
articles = []
for json_article in json_response:
articles.append(WallapopArticle.load_from_json(json_article))
return articles
"""
Request articles from the configured platform
Platform-specific logic is delegated to the platform implementation
"""
return self._platform.fetch_articles()
def _has_words(self, text, word_list):
return any(word in text for word in word_list)
@@ -136,7 +93,8 @@ class Worker:
def run(self):
while True:
try:
self.logger.info(f"Wallapop monitor worker started - {self._item_monitoring.get_name()}")
platform_name = self._platform.get_platform_name()
self.logger.info(f"{platform_name.capitalize()} monitor worker started - {self._item_monitoring.get_name()}")
self.work()
except Exception as e:
self.logger.error(f"{''.join(traceback.format_exception(None, e, e.__traceback__))}")