Added constants. Added image creation. Refactored send found article

This commit is contained in:
Joan
2023-03-08 22:46:19 +01:00
parent f7b6512500
commit 67b1ee50ea
9 changed files with 224 additions and 88 deletions

BIN
data/fonts/Roboto-Bold.ttf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

36
wallamanta/constants.py Normal file
View File

@@ -0,0 +1,36 @@
import os
#TELEGRAM_ESCAPE_CHARACTERS = ['_', '*', '[', ']', '(', ')', '~', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!']
TELEGRAM_ESCAPE_CHARACTERS = ['_', '*', '[', ']', '(', ')', '~', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!']
ADMIN_IDS = [10101691]
TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN")
DB = "/app/data/wallamanta.db"
LATITUDE = os.getenv("LATITUDE")
LONGITUDE = os.getenv("LONGITUDE")
SLEEP_TIME = int(os.getenv("SLEEP_TIME"))
CATEGORIES = {'coches': 100,
'motos': 14000,
'motor y accesorios': 12800,
'moda y accesorios': 12465,
'inmobiliaria': 200,
'tv audio y foto': 12545,
'móviles y telefonía': 16000,
'informática y electrónica': 15000,
'informática': 15000,
'electrónica': 15000,
'deporte y ocio': 12579,
'bicicletas': 17000,
'consolas y videojuegos': 12900,
'consolas': 12900,
'videojuegos': 12900,
'hogar y jardín': 12467,
'electrodomésticos': 13100,
'cine libros y música': 12463,
'niños y bebés': 12461,
'coleccionismo': 18000,
'construcción y reformas': 19000,
'industria y agricultura': 20000,
'empleo': 21000,
'servicios': 13200,
'otros': 12485}

View File

@@ -1,11 +1,22 @@
import time
import random
import requests
import logging
import os
import constants
from PIL import Image, ImageDraw, ImageFont
from datetime import datetime, timedelta from datetime import datetime, timedelta
#TELEGRAM_ESCAPE_CHARACTERS = ['_', '*', '[', ']', '(', ')', '~', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'] # Enable logging
TELEGRAM_ESCAPE_CHARACTERS = ['_', '*', '[', ']', '(', ')', '~', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'] logging.basicConfig(
ADMIN_IDS = [10101691] format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
def telegram_escape_characters(text): def telegram_escape_characters(text):
for character in TELEGRAM_ESCAPE_CHARACTERS: for character in constants.TELEGRAM_ESCAPE_CHARACTERS:
try: try:
text = text.replace(character, f'\\{character}') text = text.replace(character, f'\\{character}')
except: except:
@@ -13,11 +24,86 @@ def telegram_escape_characters(text):
return text return text
def is_user_admin(telegram_user_id): def is_user_admin(telegram_user_id):
return telegram_user_id in ADMIN_IDS return telegram_user_id in constants.ADMIN_IDS
def get_telegram_user_id(update): def get_telegram_user_id(update):
return update.message.chat_id return update.message.chat_id
def get_date_ahead(add_days): def get_date_ahead(add_days):
date_ahead = datetime.today() + timedelta(days=add_days) date_ahead = datetime.today() + timedelta(days=add_days)
return date_ahead.strftime("%d/%m/%Y") return date_ahead.strftime("%d/%m/%Y")
def random_wait():
time.sleep(random.random())
def download_image(article):
r = requests.get(article['images'][0]['original'])
if r.status_code == 200:
image = open(f"/app/data/images/products/{article['id']}.jpg", "wb")
image.write(r.content)
image.close()
def create_image(article):
download_image(article)
currency = '?'
if article['currency'] == 'EUR':
currency = ''
price = str(article['price']) + currency
wallamanta_text = "@wallamanta_bot"
width = 1280
height = 800
baseheight = int(height * 0.85)
# límite de ancho para la parte izquierda (producto)
wlimit = int(((width / 3) * 2) - 80)
# límite de ancho para los logos (homelabers y amazon)
wlogo = int(width * 0.2)
# fuente y tamaño
font = ImageFont.truetype("/app/data/fonts/Roboto-Bold.ttf", 90)
wallamanta_text_font = ImageFont.truetype("/app/data/fonts/Roboto-Bold.ttf", 40)
# inicializamos canvas
image = Image.new('RGBA', (width, height), (255, 255, 255))
# logo homelabers, redimensionamos y ponemos en la parte derecha arriba
#logo_image = Image.open("/app/data/images/logo.png").convert("RGBA")
#hlogo = int((float(logo_image.size[1]) * float(lpercent)))
#lpercent = wlogo / float(logo_image.size[0])
#logo_image = logo_image.resize((wlogo, hlogo), Image.Resampling.LANCZOS)
#image.paste(logo_image, (int((width / 6) * 5 - logo_image.size[0] / 2), int(height * 0.1)), logo_image)
# logo wallamanta, redimensionamos y ponemos en la parte derecha abajo
#wallamanta_logo = Image.open("/app/data/images/wallamanta_logo.png").convert("RGBA")
#lpercent = wlogo / float(wallamanta_logo.size[0])
#hlogo = int((float(wallamanta_logo.size[1]) * float(lpercent)))
#wallamanta_logo = wallamanta_logo.resize((wlogo, hlogo), Image.Resampling.LANCZOS)
#image.paste(wallamanta_logo, (int((width / 6) * 5 - wallamanta_logo.size[0] / 2), int(height - height * 0.2)), wallamanta_logo)
draw = ImageDraw.Draw(image)
# escribimos @wallamanta_bot
wtext, htext = draw.textsize(wallamanta_text, font=wallamanta_text_font)
draw.text(((width / 6) * 5 - wtext / 2, int(height - height * 0.2)), wallamanta_text, "#13C1AC", font=wallamanta_text_font)
# escribimos el precio
wtext, htext = draw.textsize(price, font=font)
draw.text(((width / 6) * 5 - wtext / 2, height / 2 - htext / 2), price, (0, 0, 0), font=font)
# dibujamos rectángulo verde externo, con un margen externo y ancho determinado
draw.rectangle([15, 15, width - 15, height - 15], width = 15, outline="#13C1AC")
# ponemos la imagen del producto en la parte izquierda y se redimensiona dependiendo de lo ancho
product_image = Image.open(f"/app/data/images/products/{article['id']}.jpg")
hpercent = (baseheight / float(product_image.size[1]))
wsize = int((float(product_image.size[0]) * float(hpercent)))
if wsize < wlimit:
product_image = product_image.resize((wsize, baseheight), Image.Resampling.LANCZOS)
else:
wpercent = wlimit / float(product_image.size[0])
hsize = int((float(product_image.size[1]) * float(wpercent)))
product_image = product_image.resize((wlimit, hsize), Image.Resampling.LANCZOS)
image.paste(product_image, (int((width/3)-(product_image.size[0]/2)), int((height/2) - (product_image.size[1]/2))))
# guardamos la imagen con otro nombre
image.save(f"/app/data/images/products/{article['id']}_composed.png", quality=95)
def send_article(article, product):
create_image(article)
text = f"*{telegram_escape_characters(article['title'])}*\n\n*Descripción*: {telegram_escape_characters(article['description'])}\\nn*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)
def get_category_id(category):
ret = constants.CATEGORIES.get(category, '')
return ret

View File

@@ -1,3 +1,4 @@
python-telegram-bot==20.1 python-telegram-bot==20.1
requests==2.28.1 requests==2.28.1
prettytable==3.6.0 prettytable==3.6.0
Pillow==9.4.0

View File

@@ -1,11 +1,7 @@
import sqlite3 import sqlite3
import os import os
import logging import logging
import constants
LATITUDE = os.getenv("LATITUDE")
LONGITUDE = os.getenv("LONGITUDE")
DB = "/app/data/wallamanta.db"
# Enable logging # Enable logging
logging.basicConfig( logging.basicConfig(
@@ -21,16 +17,16 @@ def dict_factory(cursor, row):
return d return d
def setup_db(): def setup_db():
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
cur = con.cursor() cur = con.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS users(telegram_user_id, active, type, until)") cur.execute("CREATE TABLE IF NOT EXISTS users(telegram_user_id, active, type, until)")
cur.execute("CREATE TABLE IF NOT EXISTS products(product_name, distance, \ cur.execute("CREATE TABLE IF NOT EXISTS products(product_name, distance, \
latitude, longitude, condition, min_price, max_price, \ latitude, longitude, condition, min_price, max_price, category, \
title_exclude, title_description_exclude, telegram_user_id)") title_exclude, title_description_exclude, telegram_user_id)")
con.close() con.close()
def is_user_valid(telegram_user_id): def is_user_valid(telegram_user_id):
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
cur = con.cursor() cur = con.cursor()
res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id} AND active=True") res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id} AND active=True")
ret = res.fetchone() != None ret = res.fetchone() != None
@@ -38,7 +34,7 @@ def is_user_valid(telegram_user_id):
return ret return ret
def is_user_premium(telegram_user_id): def is_user_premium(telegram_user_id):
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
cur = con.cursor() cur = con.cursor()
res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id} AND active=True AND type='premium'") res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id} AND active=True AND type='premium'")
ret = res.fetchone() != None ret = res.fetchone() != None
@@ -46,7 +42,7 @@ def is_user_premium(telegram_user_id):
return ret return ret
def is_user_testing(telegram_user_id): def is_user_testing(telegram_user_id):
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
cur = con.cursor() cur = con.cursor()
res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id} AND active=True AND type='testing'") res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id} AND active=True AND type='testing'")
ret = res.fetchone() != None ret = res.fetchone() != None
@@ -55,7 +51,7 @@ def is_user_testing(telegram_user_id):
def add_premium_user(telegram_user_id, until): def add_premium_user(telegram_user_id, until):
found = False found = False
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
cur = con.cursor() cur = con.cursor()
res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id}") res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id}")
if res.fetchone() is None: if res.fetchone() is None:
@@ -70,7 +66,7 @@ def add_premium_user(telegram_user_id, until):
def add_test_user(telegram_user_id, until): def add_test_user(telegram_user_id, until):
found = False found = False
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
cur = con.cursor() cur = con.cursor()
res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id}") res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id}")
if res.fetchone() is None: if res.fetchone() is None:
@@ -82,7 +78,7 @@ def add_test_user(telegram_user_id, until):
return not found return not found
def remove_valid_user(telegram_user_id): def remove_valid_user(telegram_user_id):
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
cur = con.cursor() cur = con.cursor()
res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id}") res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id}")
if res.fetchone() != None: if res.fetchone() != None:
@@ -91,17 +87,33 @@ def remove_valid_user(telegram_user_id):
con.close() con.close()
def get_user_list(): def get_user_list():
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
cur = con.cursor() cur = con.cursor()
res = cur.execute(f"SELECT * FROM users") res = cur.execute(f"SELECT * FROM users")
ret = res.fetchall() ret = res.fetchall()
con.close() con.close()
return ret return ret
def get_user_type(telegram_user_id):
con = sqlite3.connect(constants.DB)
cur = con.cursor()
res = cur.execute(f"SELECT type FROM users WHERE telegram_user_id={telegram_user_id}")
ret = res.fetchone()
con.close()
return ret[0]
def get_user_until(telegram_user_id):
con = sqlite3.connect(constants.DB)
cur = con.cursor()
res = cur.execute(f"SELECT until FROM users WHERE telegram_user_id={telegram_user_id}")
ret = res.fetchone()
con.close()
return ret[0]
def get_product(product): def get_product(product):
product_name = product.get('product_name') product_name = product.get('product_name')
telegram_user_id = product.get('telegram_user_id') telegram_user_id = product.get('telegram_user_id')
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
con.row_factory = dict_factory con.row_factory = dict_factory
cur = con.cursor() cur = con.cursor()
res = cur.execute(f"SELECT * FROM products WHERE telegram_user_id={telegram_user_id} \ res = cur.execute(f"SELECT * FROM products WHERE telegram_user_id={telegram_user_id} \
@@ -111,7 +123,7 @@ def get_product(product):
return ret return ret
def get_products_from_user(telegram_user_id): def get_products_from_user(telegram_user_id):
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
con.row_factory = dict_factory con.row_factory = dict_factory
cur = con.cursor() cur = con.cursor()
res = cur.execute(f"SELECT * FROM products WHERE telegram_user_id={telegram_user_id}") res = cur.execute(f"SELECT * FROM products WHERE telegram_user_id={telegram_user_id}")
@@ -120,7 +132,7 @@ def get_products_from_user(telegram_user_id):
return ret return ret
def get_all_products(): def get_all_products():
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
con.row_factory = dict_factory con.row_factory = dict_factory
cur = con.cursor() cur = con.cursor()
res = cur.execute(f"SELECT * FROM products") res = cur.execute(f"SELECT * FROM products")
@@ -132,31 +144,32 @@ def add_product(product):
condition = 'all' condition = 'all'
product_name = product.get('product_name') product_name = product.get('product_name')
distance = product.get('distance', 0) distance = product.get('distance', 0)
latitude = product.get('latitude', LATITUDE) latitude = product.get('latitude', constants.LATITUDE)
longitude = product.get('longitude', LONGITUDE) longitude = product.get('longitude', constants.LONGITUDE)
min_price = product.get('min_price') min_price = product.get('min_price')
max_price = product.get('max_price') max_price = product.get('max_price')
title_exclude = product.get('title_exclude', '') title_exclude = product.get('title_exclude', '')
title_description_exclude = product.get('title_description_exclude', '') title_description_exclude = product.get('title_description_exclude', '')
category = product.get('category', '')
telegram_user_id = product.get('telegram_user_id') telegram_user_id = product.get('telegram_user_id')
params = (product_name, \ params = (product_name, \
distance, latitude, longitude, condition, min_price, \ distance, latitude, longitude, condition, min_price, \
max_price, title_exclude, title_description_exclude, telegram_user_id) max_price, category, title_exclude, title_description_exclude, telegram_user_id)
logging.info(f"Trying to add: {product_name}, {title_exclude}, {title_description_exclude}, {telegram_user_id}") logging.info(f"Trying to add: {product_name}, {title_exclude}, {title_description_exclude}, {telegram_user_id}")
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
cur = con.cursor() cur = con.cursor()
res = cur.execute(f"SELECT * FROM products WHERE telegram_user_id={product.get('telegram_user_id')} \ res = cur.execute(f"SELECT * FROM products WHERE telegram_user_id={product.get('telegram_user_id')} \
AND product_name='{product.get('product_name')}'") AND product_name='{product.get('product_name')}'")
if res.fetchone() is None: if res.fetchone() is None:
cur.execute("INSERT INTO products VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", params) cur.execute("INSERT INTO products VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", params)
con.commit() con.commit()
con.close() con.close()
def remove_product(product): def remove_product(product):
removed = False removed = False
if get_product(product): if get_product(product):
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
cur = con.cursor() cur = con.cursor()
res = cur.execute(f"DELETE FROM products WHERE telegram_user_id={product.get('telegram_user_id')} \ res = cur.execute(f"DELETE FROM products WHERE telegram_user_id={product.get('telegram_user_id')} \
AND product_name='{product.get('product_name')}'") AND product_name='{product.get('product_name')}'")
@@ -166,7 +179,7 @@ def remove_product(product):
return removed return removed
def count_user_products(telegram_user_id): def count_user_products(telegram_user_id):
con = sqlite3.connect(DB) con = sqlite3.connect(constants.DB)
cur = con.cursor() cur = con.cursor()
res = cur.execute(f"SELECT Count() FROM products WHERE telegram_user_id={telegram_user_id}") res = cur.execute(f"SELECT Count() FROM products WHERE telegram_user_id={telegram_user_id}")
ret = res.fetchone()[0] ret = res.fetchone()[0]

View File

@@ -5,17 +5,12 @@ import logging
import prettytable import prettytable
import helpers import helpers
import walladb import walladb
import constants
from worker import Worker from worker import Worker
from telegram import Update from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes from telegram.ext import Application, CommandHandler, ContextTypes
TELEGRAM_CHANNEL_ID = os.getenv("TELEGRAM_CHANNEL_ID")
TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN")
LATITUDE = os.getenv("LATITUDE")
LONGITUDE = os.getenv("LONGITUDE")
SLEEP_TIME = os.getenv("SLEEP_TIME")
# Enable logging # Enable logging
logging.basicConfig( logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
@@ -33,24 +28,33 @@ def save_json_file(products):
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if walladb.is_user_valid(helpers.get_telegram_user_id(update)): if walladb.is_user_valid(helpers.get_telegram_user_id(update)):
message = """Añade un producto con `/add producto;precio_mínimo;precio_máximo,excluir_título(opcional, separado por comas);excluir_descripción_y_título(opciona, separado por comas);latitud(opcional);longitud(opcional),distancia(opcional)`\n message = """Añade un producto con `/add producto;precio_mínimo;precio_máximo,categoría,excluir_título(opcional, separado por comas);
Ejemplo: `/add placa base itx;0;150`\n excluir_descripción_y_título(opciona, separado por comas);latitud(opcional);longitud(opcional),distancia(opcional)`\n
Ejemplo 2: `/add cpu;10;30;;intel,core 2 duo,celeron;;;100`\n Ejemplo: `/add placa base itx;0;150`\n
Los campos opcionales que se dejen vacíos tomarán el valor configurado en el archivo `.env`\n Ejemplo 2: `/add cpu;10;30;;intel,core 2 duo,celeron;;;100`\n
Lista los productos con `/list` o obtén la información de uno en concreto con `/list nombre del producto`\n Los campos opcionales que se dejen vacíos tomarán el valor configurado en el archivo `.env`\n
Borra un producto con `/remove nombre del producto`""" Lista los productos con `/list` o obtén la información de uno en concreto con `/list nombre del producto`\n
Borra un producto con `/remove nombre del producto`\n
`/status` muestra tu tipo de membresía y fecha de caducidad"""
else: else:
message = """Activa tu periodo de prueba de 7 días con `/test` o contacta con @jocarduck para más información.""" message = """Activa tu periodo de prueba de 7 días con `/test` o contacta con @jocarduck para más información."""
await update.message.reply_markdown_v2(helpers.telegram_escape_characters(message)) await update.message.reply_markdown_v2(helpers.telegram_escape_characters(message))
async def categories_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if walladb.is_user_valid(helpers.get_telegram_user_id(update)):
message = ''
for category in constants.CATEGORIES:
message = f"{message}, {category}"
await update.message.reply_markdown_v2(f"```{helpers.telegram_escape_characters(message)}```")
async def add_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: async def add_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
telegram_user_id = helpers.get_telegram_user_id(update) telegram_user_id = helpers.get_telegram_user_id(update)
if walladb.is_user_valid(telegram_user_id): if walladb.is_user_valid(telegram_user_id):
number_of_products = walladb.count_user_products(telegram_user_id) number_of_products = walladb.count_user_products(telegram_user_id)
message = """Tienes que pasar el número correcto de parámetros: `/add producto;precio_mínimo;precio_máximo,excluir_título(opcional, separado por comas);excluir_descripción_y_título(opciona, separado por comas);latitud(opcional);longitud(opcional),distancia(opcional)`\n message = """Tienes que pasar el número correcto de parámetros: `/add producto;precio_mínimo;precio_máximo,categoría,excluir_título(opcional, separado por comas);excluir_descripción_y_título(opciona, separado por comas);latitud(opcional);longitud(opcional),distancia(opcional)`\n
Ejemplo: `/add placa base itx;0;150`\n Ejemplo: `/add placa base itx;0;150`\n
Ejemplo 2: `/add cpu;10;30;;intel,core 2 duo,celeron;;;100`\n Ejemplo 2: `/add cpu;10;30;;intel,core 2 duo,celeron;;;100`\n
Los campos opcionales que se dejen vacíos tomarán el valor configurado en el archivo `.env`""" Los campos opcionales que se dejen vacíos tomarán el valor configurado en el archivo `.env`"""
valid = False valid = False
if walladb.is_user_testing(telegram_user_id): if walladb.is_user_testing(telegram_user_id):
valid = True valid = True
@@ -72,15 +76,17 @@ async def add_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> Non
args = args[1].split(";") args = args[1].split(";")
product['product_name'], product['min_price'], product['max_price'] = args[0:3] product['product_name'], product['min_price'], product['max_price'] = args[0:3]
if len(args) > 3 and args[3]: if len(args) > 3 and args[3]:
product['title_exclude'] = args[3] product['category'] = helpers.get_category_id(args[3])
if len(args) > 4 and args[4]: if len(args) > 4 and args[4]:
product['title_description_exclude'] = args[4] product['title_exclude'] = args[4]
if len(args) > 5 and args[5]: if len(args) > 5 and args[5]:
product['latitude'] = args[5] product['title_description_exclude'] = args[5]
if len(args) > 6 and args[6]: if len(args) > 6 and args[6]:
product['longitude'] = args[6] product['latitude'] = args[6]
if len(args) > 7 and args[7]: if len(args) > 7 and args[7]:
product['distance'] = args[7] product['longitude'] = args[7]
if len(args) > 8 and args[8]:
product['distance'] = args[8]
logging.info(f'Adding: {product}') logging.info(f'Adding: {product}')
if not walladb.get_product(product): if not walladb.get_product(product):
@@ -156,12 +162,14 @@ async def remove_user_command(update: Update, context: ContextTypes.DEFAULT_TYPE
await update.message.reply_markdown_v2(helpers.telegram_escape_characters(f"{telegram_user_id} desactivado.")) await update.message.reply_markdown_v2(helpers.telegram_escape_characters(f"{telegram_user_id} desactivado."))
async def status_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: async def status_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
# TODO: Obtener estado del usuario y fecha de caducidad de suscripción en el caso de haberla telegram_user_id = helpers.get_telegram_user_id(update)
message = '' if walladb.is_user_valid(telegram_user_id):
await update.message.reply_markdown_v2(helpers.telegram_escape_characters(message)) type = walladb.get_user_type(telegram_user_id)
until = walladb.get_user_until(telegram_user_id)
message = f"Tu cuenta es tipo: {type} y caduca el {until}."
await update.message.reply_markdown_v2(helpers.telegram_escape_characters(message))
async def test_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: async def test_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
# TODO: Activar usuario en modo testing y responder con fecha de caducidad
telegram_user_id = helpers.get_telegram_user_id(update) telegram_user_id = helpers.get_telegram_user_id(update)
if not walladb.is_user_valid(telegram_user_id): if not walladb.is_user_valid(telegram_user_id):
until = helpers.get_date_ahead(7) until = helpers.get_date_ahead(7)
@@ -186,10 +194,11 @@ def main()->None:
"""Start the bot.""" """Start the bot."""
# Create the Application and pass it your bot's token. # Create the Application and pass it your bot's token.
application = Application.builder().token(TELEGRAM_TOKEN).build() application = Application.builder().token(constants.TELEGRAM_TOKEN).build()
# on different commands - answer in Telegram # on different commands - answer in Telegram
application.add_handler(CommandHandler("help", help_command)) application.add_handler(CommandHandler("help", help_command))
application.add_handler(CommandHandler("categories", categories_command))
application.add_handler(CommandHandler("add", add_command)) application.add_handler(CommandHandler("add", add_command))
application.add_handler(CommandHandler("remove", remove_command)) application.add_handler(CommandHandler("remove", remove_command))
application.add_handler(CommandHandler("list", list_command)) application.add_handler(CommandHandler("list", list_command))

View File

@@ -6,13 +6,7 @@ import logging
import json import json
import helpers import helpers
import walladb import walladb
import constants
TELEGRAM_CHANNEL_ID = os.getenv("TELEGRAM_CHANNEL_ID")
TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN")
LATITUDE = os.getenv("LATITUDE")
LONGITUDE = os.getenv("LONGITUDE")
SLEEP_TIME = int(os.getenv("SLEEP_TIME"))
# Enable logging # Enable logging
logging.basicConfig( logging.basicConfig(
@@ -24,16 +18,14 @@ logger = logging.getLogger(__name__)
class Worker: class Worker:
def is_valid_request(self, product): def is_valid_request(self, product):
is_valid = True is_valid = False
if not walladb.get_product(product): if walladb.get_product(product):
is_valid = False if walladb.is_user_valid(product['telegram_user_id']):
if not walladb.is_user_valid(product['telegram_user_id']): if walladb.is_user_premium(product['telegram_user_id']) or walladb.is_user_testing(product['telegram_user_id']):
is_valid = False is_valid = True
if not walladb.is_user_premium(product['telegram_user_id']):
is_valid = False
return is_valid return is_valid
def request(self, product_name, n_articles, latitude=LATITUDE, longitude=LONGITUDE, distance='0', condition='all', min_price=0, max_price=10000000): def request(self, product_name, n_articles, latitude=constants.LATITUDE, longitude=constants.LONGITUDE, distance='0', condition='all', min_price=0, max_price=10000000, category=""):
url = (f"http://api.wallapop.com/api/v3/general/search?keywords={product_name}" url = (f"http://api.wallapop.com/api/v3/general/search?keywords={product_name}"
f"&order_by=newest&latitude={latitude}" f"&order_by=newest&latitude={latitude}"
f"&longitude={longitude}" f"&longitude={longitude}"
@@ -41,14 +33,17 @@ class Worker:
f"&min_sale_price={min_price}" f"&min_sale_price={min_price}"
f"&max_sale_price={max_price}" f"&max_sale_price={max_price}"
f"&filters_source=quick_filters&language=es_ES") f"&filters_source=quick_filters&language=es_ES")
if condition != "all": if condition != "all":
url = url + f"&condition={condition}" # new, as_good_as_new, good, fair, has_given_it_all url = url + f"&condition={condition}" # new, as_good_as_new, good, fair, has_given_it_all
if category != "":
url = url + f"&category_ids={category}"
for step in range(15): for step in range(15):
while True: while True:
time.sleep(0.5) helpers.random_wait()
response = requests.get(url+ f"&step={step+1}") response = requests.get(url+f"&step={step+1}")
try: try:
if response.status_code == 200: if response.status_code == 200:
break break
@@ -62,45 +57,41 @@ class Worker:
return json_data['search_objects'] return json_data['search_objects']
def first_run(self, product): def first_run(self, product):
for i in range(5):
helpers.random_wait()
list = [] list = []
if not self.is_valid_request(product): if not self.is_valid_request(product):
return list return list
articles = self.request(product['product_name'], 0, product['latitude'], product['longitude'], product['distance'], product['condition'], product['min_price'], product['max_price']) articles = self.request(product['product_name'], 0, product['latitude'], product['longitude'], product['distance'], product['condition'], product['min_price'], product['max_price'], product['category'])
for article in articles: for article in articles:
list.insert(0, article['id']) list.insert(0, article['id'])
return list return list
def work(self, product, list): 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 = [] exec_times = []
bot = telegram.Bot(token = TELEGRAM_TOKEN)
while True: while True:
if not self.is_valid_request(product): if not self.is_valid_request(product):
logging.info(f"{product['product_name']} not valid anymore, exiting worker")
break # Exits and ends worker thread break # Exits and ends worker thread
start_time = time.time() start_time = time.time()
articles = self.request(product['product_name'], 0, product['latitude'], product['longitude'], product['distance'], product['condition'], product['min_price'], product['max_price']) articles = self.request(product['product_name'], 0, product['latitude'], product['longitude'], product['distance'], product['condition'], product['min_price'], product['max_price'], product['category'])
for article in articles: for article in articles:
if not article['id'] in list: if not article['id'] in list:
logging.info(f"Found article {article['title']}") logging.info(f"Found article {article['title']}")
try: 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']): 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: try:
text = f"*Artículo*: {helpers.telegram_escape_characters(article['title'])}\n*Descripción*: {helpers.telegram_escape_characters(article['description'])}\n*Precio*: {helpers.telegram_escape_characters(str(article['price']))} {helpers.telegram_escape_characters(article['currency'])}\n[Ir al anuncio](https://es\.wallapop\.com/item/{helpers.telegram_escape_characters(article['web_slug'])})" helpers.send_article(article, product)
url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage?chat_id={TELEGRAM_CHANNEL_ID}&text={text}&parse_mode=MarkdownV2"
logging.info(text)
logging.info(requests.get(url).json())
except: except:
text = f"*Artículo*: {helpers.telegram_escape_characters(article['title'])}\n*Descripción*: {helpers.telegram_escape_characters(article['description'])}\n*Precio*: {helpers.telegram_escape_characters(str(article['price']))} {helpers.telegram_escape_characters(article['currency'])}\n[Ir al anuncio](https://es\.wallapop\.com/item/{helpers.telegram_escape_characters(article['web_slug'])})" helpers.send_article(article, product)
url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage?chat_id={TELEGRAM_CHANNEL_ID}&text={text}&parse_mode=MarkdownV2"
logging.info(text)
logging.info(requests.get(url).json())
time.sleep(1) # Avoid Telegram flood restriction time.sleep(1) # Avoid Telegram flood restriction
list.insert(0, article['id']) list.insert(0, article['id'])
except Exception as e: except Exception as e:
logging.info("---------- EXCEPTION -----------") logging.info("---------- EXCEPTION -----------")
logging.info(f"{product['product_name']} worker crashed. {e}") logging.info(f"{product['product_name']} worker crashed. {e}")
logging.info(f"{product['product_name']}: Trying to parse {article['id']}: {article['title']} .\n") logging.info(f"{product['product_name']}: Trying to parse {article['id']}: {article['title']} .\n")
time.sleep(constants.SLEEP_TIME)
time.sleep(SLEEP_TIME)
exec_times.append(time.time() - start_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']}\' node-> last: {exec_times[-1]} max: {self.get_max_time(exec_times)} avg: {self.get_average_time(exec_times)}")