Modified categories. Remove buttons when used. Added timeout to conversation. Check if user is expired. Added telegram name. Fixed bug with lowercase.
This commit is contained in:
@@ -10,28 +10,26 @@ 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}
|
||||
CATEGORIES = {'🚗 Coches 🚗': 100,
|
||||
'🏍️ Motos 🏍️': 14000,
|
||||
'🚙 Motor 🚙': 12800,
|
||||
'👗 Moda 👗': 12465,
|
||||
'🏘️ Inmobiliaria 🏘️': 200,
|
||||
'📺 TV, audio y foto 📺': 12545,
|
||||
'📱 Móviles 📱': 16000,
|
||||
'🖥 Informática 🖥️': 15000,
|
||||
'🏅 Deportes y ocio 🏅': 12579,
|
||||
'🚴 Bicicletas 🚴': 17000,
|
||||
'🎮 Consolas y 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,
|
||||
'Todas': 0}
|
||||
|
||||
@@ -35,10 +35,18 @@ def get_telegram_user_id(update):
|
||||
#return update.message.chat_id
|
||||
return update.effective_chat.id
|
||||
|
||||
def get_telegram_user_name(update):
|
||||
return update.message.from_user
|
||||
|
||||
def get_date_ahead(add_days):
|
||||
date_ahead = datetime.today() + timedelta(days=add_days)
|
||||
return date_ahead.strftime("%d/%m/%Y")
|
||||
|
||||
def is_date_expired(until):
|
||||
until_date = datetime.strptime(until, "%d/%m/%Y")
|
||||
difference = until_date - datetime.today()
|
||||
return difference.days < 0
|
||||
|
||||
def random_wait():
|
||||
time.sleep(random.random())
|
||||
|
||||
@@ -114,32 +122,40 @@ def get_category_id(category):
|
||||
ret = constants.CATEGORIES.get(category, '')
|
||||
return ret
|
||||
|
||||
def get_category_name(category):
|
||||
category_name = ''
|
||||
for i_category in constants.CATEGORIES:
|
||||
if constants.CATEGORIES[i_category] == category:
|
||||
category_name = i_category
|
||||
break
|
||||
return category_name
|
||||
|
||||
def create_category_keyboard():
|
||||
keyboard = [
|
||||
[
|
||||
InlineKeyboardButton("🖥 Informática", callback_data='15000'),
|
||||
InlineKeyboardButton("Motor", callback_data='12800'),
|
||||
InlineKeyboardButton("Coches", callback_data='100'),
|
||||
InlineKeyboardButton("🖥 Informática 🖥️ ", callback_data='15000'),
|
||||
InlineKeyboardButton("🚙 Motor 🚙", callback_data='12800'),
|
||||
InlineKeyboardButton("🚗 Coches 🚗", callback_data='100'),
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton("Motos", callback_data='14000'),
|
||||
InlineKeyboardButton("Moda", callback_data='12465'),
|
||||
InlineKeyboardButton("Inmobiliaria", callback_data='200'),
|
||||
InlineKeyboardButton("🏍️ Motos 🏍️", callback_data='14000'),
|
||||
InlineKeyboardButton("👗 Moda 👗", callback_data='12465'),
|
||||
InlineKeyboardButton("🏘️ Inmobiliaria 🏘️", callback_data='200'),
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton("TV, audio y foto", callback_data='12545'),
|
||||
InlineKeyboardButton("Móviles", callback_data='16000'),
|
||||
InlineKeyboardButton("Deportes y ocio", callback_data='12579'),
|
||||
InlineKeyboardButton("📺 TV, audio y foto 📺", callback_data='12545'),
|
||||
InlineKeyboardButton("📱 Móviles 📱", callback_data='16000'),
|
||||
InlineKeyboardButton("🏅 Deportes y ocio 🏅", callback_data='12579'),
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton("Bicicletas", callback_data='17000'),
|
||||
InlineKeyboardButton("Consolas y videojuegos", callback_data='12900'),
|
||||
InlineKeyboardButton("Hogar y jardín", callback_data='12467'),
|
||||
InlineKeyboardButton("🚴 Bicicletas 🚴", callback_data='17000'),
|
||||
InlineKeyboardButton("🎮 Consolas y videojuegos 🎮", callback_data='12900'),
|
||||
InlineKeyboardButton("🏡 Hogar y jardín 🏡", callback_data='12467'),
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton("Electrodomésticos", callback_data='13100'),
|
||||
InlineKeyboardButton("Cine, libros y música", callback_data='12463'),
|
||||
InlineKeyboardButton("Niños y bebés", callback_data='12461'),
|
||||
InlineKeyboardButton("🎥 Cine, libros y música 🎥", callback_data='12463'),
|
||||
InlineKeyboardButton("🧒 Niños y bebés 🧒", callback_data='12461'),
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton("Coleccionismo", callback_data='18000'),
|
||||
@@ -164,7 +180,7 @@ def create_continue_keyboard():
|
||||
InlineKeyboardButton("Descartar", callback_data='remove')
|
||||
],[
|
||||
InlineKeyboardButton("Exclusión de título", callback_data='title_exclude'),
|
||||
InlineKeyboardButton("Exclusión de descripción", callback_data='description_exclude')
|
||||
InlineKeyboardButton("Exclusión de descripción", callback_data='title_description_exclude')
|
||||
],[
|
||||
InlineKeyboardButton("Coordenadas", callback_data='coords'),
|
||||
InlineKeyboardButton("Distancia", callback_data='distance')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
python-telegram-bot==20.1
|
||||
python-telegram-bot[job-queue]==20.1
|
||||
requests==2.28.1
|
||||
prettytable==3.6.0
|
||||
Pillow==9.4.0
|
||||
@@ -1,7 +1,7 @@
|
||||
import sqlite3
|
||||
import os
|
||||
import logging
|
||||
import constants
|
||||
import helpers
|
||||
|
||||
# Enable logging
|
||||
logging.basicConfig(
|
||||
@@ -19,7 +19,7 @@ def dict_factory(cursor, row):
|
||||
def setup_db():
|
||||
con = sqlite3.connect(constants.DB)
|
||||
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, telegram_name)")
|
||||
cur.execute("CREATE TABLE IF NOT EXISTS products(product_name, distance, \
|
||||
latitude, longitude, condition, min_price, max_price, category, \
|
||||
title_exclude, title_description_exclude, telegram_user_id)")
|
||||
@@ -33,6 +33,18 @@ def is_user_valid(telegram_user_id):
|
||||
con.close()
|
||||
return ret
|
||||
|
||||
def is_user_expired(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}")
|
||||
q_res = res.fetchone()
|
||||
ret = True
|
||||
if q_res != None:
|
||||
if not helpers.is_date_expired(q_res[0]):
|
||||
ret = False
|
||||
con.close()
|
||||
return ret
|
||||
|
||||
def is_user_premium(telegram_user_id):
|
||||
con = sqlite3.connect(constants.DB)
|
||||
cur = con.cursor()
|
||||
@@ -49,13 +61,14 @@ def is_user_testing(telegram_user_id):
|
||||
con.close()
|
||||
return ret
|
||||
|
||||
def add_premium_user(telegram_user_id, until):
|
||||
def add_premium_user(telegram_user_id, telegram_name, until):
|
||||
found = False
|
||||
con = sqlite3.connect(constants.DB)
|
||||
cur = con.cursor()
|
||||
res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id}")
|
||||
if res.fetchone() is None:
|
||||
cur.execute(f"INSERT INTO users VALUES ({telegram_user_id}, True, 'premium', '{until}')")
|
||||
params = (telegram_user_id, True, 'premium', until, telegram_name.first_name)
|
||||
cur.execute("INSERT INTO users VALUES (?, ?, ?, ?, ?)", params)
|
||||
con.commit()
|
||||
else:
|
||||
cur.execute(f"UPDATE users SET active = True, type = 'premium', until = '{until}' WHERE telegram_user_id={telegram_user_id}")
|
||||
@@ -65,13 +78,14 @@ def add_premium_user(telegram_user_id, until):
|
||||
logging.info(f"Added premium user {telegram_user_id} until {until}")
|
||||
return found
|
||||
|
||||
def add_test_user(telegram_user_id, until):
|
||||
def add_test_user(telegram_user_id, telegram_name, until):
|
||||
found = False
|
||||
con = sqlite3.connect(constants.DB)
|
||||
cur = con.cursor()
|
||||
res = cur.execute(f"SELECT * FROM users WHERE telegram_user_id={telegram_user_id}")
|
||||
if res.fetchone() is None:
|
||||
cur.execute(f"INSERT INTO users VALUES ({telegram_user_id}, True, 'testing', '{until}')")
|
||||
params = (telegram_user_id, True, 'testing', until, telegram_name.first_name)
|
||||
cur.execute("INSERT INTO users VALUES (?, ?, ?, ?, ?)", params)
|
||||
con.commit()
|
||||
else:
|
||||
found = True
|
||||
@@ -114,7 +128,7 @@ def get_user_until(telegram_user_id):
|
||||
return ret[0]
|
||||
|
||||
def get_product(product):
|
||||
product_name = product.get('product_name')
|
||||
product_name = product.get('product_name').lower()
|
||||
telegram_user_id = product.get('telegram_user_id')
|
||||
con = sqlite3.connect(constants.DB)
|
||||
con.row_factory = dict_factory
|
||||
|
||||
@@ -31,28 +31,11 @@ logging.basicConfig(
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def parse_json_file():
|
||||
f = open("/app/data/products.json")
|
||||
return json.load(f)
|
||||
|
||||
def save_json_file(products):
|
||||
with open('/app/data/products.json', 'w') as outfile:
|
||||
json.dump(products, outfile, indent=2)
|
||||
|
||||
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
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,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 2: `/add cpu;10;30;;;intel,core 2 duo,celeron;;;100`\n
|
||||
Ejemplo 3: `/add tiny;0;100;informática`\n
|
||||
Los campos opcionales que se dejen vacíos tomarán el valor configurado en el archivo `.env`\n
|
||||
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\n
|
||||
`/categories` muestra las categorías disponibles"""
|
||||
message = "Utiliza el menú de la conversación para añadir un producto y sigue los pasos indicados"
|
||||
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))
|
||||
|
||||
async def main_menu(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
@@ -68,6 +51,7 @@ async def main_menu(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
return ACTION
|
||||
|
||||
async def menu_click_handler(update: Update, context: CallbackContext):
|
||||
await update.callback_query.edit_message_reply_markup(None)
|
||||
telegram_user_id = helpers.get_telegram_user_id(update)
|
||||
if walladb.is_user_valid(telegram_user_id):
|
||||
context.user_data['telegram_user_id'] = telegram_user_id
|
||||
@@ -99,11 +83,10 @@ async def menu_click_handler(update: Update, context: CallbackContext):
|
||||
return REMOVE_PRODUCT
|
||||
if query.data == 'list':
|
||||
await send_list(update, context)
|
||||
#await context.bot.send_message(chat_id=update.effective_chat.id, text='Send your name', reply_markup=ForceReply())
|
||||
return ConversationHandler.END
|
||||
else:
|
||||
await context.bot.send_message(chat_id=update.effective_chat.id,
|
||||
text='Activa tu periodo de prueba de 7 días con `/test` o contacta con @jocarduck para más información.', parse_mode=ParseMode.MARKDOWN_V2)
|
||||
text=helpers.telegram_escape_characters('Activa tu periodo de prueba de 7 días con `/test` o contacta con @jocarduck para más información.'), parse_mode=ParseMode.MARKDOWN_V2)
|
||||
return ConversationHandler.END
|
||||
|
||||
async def add_product_name(update: Update, context: CallbackContext):
|
||||
@@ -162,6 +145,7 @@ async def add_product_max_price(update: Update, context: CallbackContext):
|
||||
return ADD_PRODUCT_CATEGORY
|
||||
|
||||
async def add_product_category(update: Update, context: CallbackContext):
|
||||
await update.callback_query.edit_message_reply_markup(None)
|
||||
query = update.callback_query
|
||||
if query.data == 'category':
|
||||
markup = InlineKeyboardMarkup(helpers.create_category_keyboard())
|
||||
@@ -177,6 +161,8 @@ async def add_product_category(update: Update, context: CallbackContext):
|
||||
return ConversationHandler.END
|
||||
|
||||
async def continue_or_finish(update: Update, context: CallbackContext):
|
||||
if update.callback_query != None:
|
||||
await update.callback_query.edit_message_reply_markup(None)
|
||||
markup = InlineKeyboardMarkup(helpers.create_continue_keyboard())
|
||||
last_step = context.user_data.get('last_step', '')
|
||||
query = update.callback_query
|
||||
@@ -195,6 +181,8 @@ async def continue_or_finish(update: Update, context: CallbackContext):
|
||||
|
||||
if last_step == 'category':
|
||||
category = int(query.data)
|
||||
await context.bot.send_message(chat_id=update.effective_chat.id,
|
||||
text=f"Categoría escogida: {helpers.get_category_name(category)}")
|
||||
context.user_data['category'] = category
|
||||
context.user_data['last_step'] = ''
|
||||
|
||||
@@ -207,13 +195,13 @@ async def continue_or_finish(update: Update, context: CallbackContext):
|
||||
context.user_data['title_exclude'] = update.message.text
|
||||
context.user_data['last_step'] = ''
|
||||
|
||||
if qd == 'description_exclude':
|
||||
if qd == 'title_description_exclude':
|
||||
await context.bot.send_message(chat_id=update.effective_chat.id,
|
||||
text='Exclusión de descripción', reply_markup=ForceReply())
|
||||
context.user_data['last_step'] = 'description_exclude'
|
||||
context.user_data['last_step'] = 'title_description_exclude'
|
||||
return CONTINUE_OR_FINISH
|
||||
if last_step == 'description_exclude':
|
||||
context.user_data['description_exclude'] = update.message.text
|
||||
if last_step == 'title_description_exclude':
|
||||
context.user_data['title_description_exclude'] = update.message.text
|
||||
context.user_data['last_step'] = ''
|
||||
|
||||
if qd == 'coords':
|
||||
@@ -255,6 +243,7 @@ async def continue_or_finish(update: Update, context: CallbackContext):
|
||||
return CONTINUE_OR_FINISH
|
||||
|
||||
async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
logging.info('Conversation cancelled')
|
||||
context.user_data.clear()
|
||||
await context.bot.send_message(chat_id=update.effective_chat.id, text="Cancelado.")
|
||||
return ConversationHandler.END
|
||||
@@ -306,7 +295,7 @@ async def add_premium_user_command(update: Update, context: ContextTypes.DEFAULT
|
||||
telegram_user_id = update.message.text.split('/add_premium_user ')[1].split(' ')[0]
|
||||
days = update.message.text.split('/add_premium_user ')[1].split(' ')[1]
|
||||
until = helpers.get_date_ahead(int(days))
|
||||
if not walladb.add_premium_user(telegram_user_id, until):
|
||||
if not walladb.add_premium_user(telegram_user_id, '', until):
|
||||
products = walladb.get_products_from_user(telegram_user_id)
|
||||
|
||||
for product in products:
|
||||
@@ -333,9 +322,10 @@ async def status_command(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
||||
|
||||
async def test_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
telegram_user_id = helpers.get_telegram_user_id(update)
|
||||
telegram_user_name = helpers.get_telegram_user_name(update)
|
||||
if not walladb.is_user_valid(telegram_user_id):
|
||||
until = helpers.get_date_ahead(7)
|
||||
walladb.add_test_user(telegram_user_id, until)
|
||||
walladb.add_test_user(telegram_user_id, telegram_user_name, until)
|
||||
message = f"Periodo de prueba activado hasta el {until}."
|
||||
else:
|
||||
message = "Ya has utilizado el periodo de prueba."
|
||||
@@ -345,7 +335,7 @@ async def test_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
|
||||
message = "Ya eres premium. No puedes volver al periodo de prueba."
|
||||
await update.message.reply_markdown_v2(helpers.telegram_escape_characters(message))
|
||||
|
||||
async def add_to_db_and_send(update, context):
|
||||
async def add_to_db_and_send(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
logging.info(f"Adding product with context: {context.user_data}")
|
||||
walladb.add_product(context.user_data)
|
||||
p = threading.Thread(target=Worker.run, args=(walladb.get_product(context.user_data), ))
|
||||
@@ -355,6 +345,12 @@ async def add_to_db_and_send(update, context):
|
||||
def error(update, context):
|
||||
logging.error(f'Update ---{update}--- caused error ---{context.error}---')
|
||||
|
||||
async def conv_timeout(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
await context.bot.send_message(chat_id=update.effective_chat.id, text="Se ha superado el tiempo de espera, vuelve a usar el menú si quieres añadir otro producto.")
|
||||
|
||||
async def conv_finish(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
await context.bot.send_message(chat_id=update.effective_chat.id, text="Vuelve a usar el menú si quieres añadir otro producto.")
|
||||
|
||||
def main()->None:
|
||||
walladb.setup_db()
|
||||
products = walladb.get_all_products()
|
||||
@@ -366,7 +362,7 @@ def main()->None:
|
||||
|
||||
"""Start the bot."""
|
||||
# Create the Application and pass it your bot's token.
|
||||
application = Application.builder().token(constants.TELEGRAM_TOKEN).build()
|
||||
application = Application.builder().get_updates_http_version('1.1').http_version('1.1').token(constants.TELEGRAM_TOKEN).build()
|
||||
|
||||
# on different commands - answer in Telegram
|
||||
application.add_handler(CommandHandler("help", help_command))
|
||||
@@ -377,6 +373,7 @@ def main()->None:
|
||||
application.add_handler(CommandHandler("test", test_command))
|
||||
|
||||
conv_handler = ConversationHandler(
|
||||
conversation_timeout=60,
|
||||
entry_points=[CommandHandler("menu", main_menu)],
|
||||
states={
|
||||
ACTION: [CallbackQueryHandler(menu_click_handler)],
|
||||
@@ -388,20 +385,16 @@ def main()->None:
|
||||
CONTINUE_OR_FINISH: [CallbackQueryHandler(continue_or_finish),
|
||||
MessageHandler(filters.TEXT, continue_or_finish)],
|
||||
REMOVE_PRODUCT: [MessageHandler(filters.TEXT, remove_product)],
|
||||
ConversationHandler.TIMEOUT: [CallbackQueryHandler(conv_timeout),
|
||||
MessageHandler(filters.TEXT, conv_timeout)],
|
||||
ConversationHandler.END: [MessageHandler(filters.TEXT, conv_finish)],
|
||||
},
|
||||
fallbacks=[CommandHandler("cancel", cancel)],
|
||||
)
|
||||
|
||||
# Add ConversationHandler to application that will be used for handling updates
|
||||
application.add_handler(conv_handler)
|
||||
|
||||
#application.bot.set_chat_menu_button('Menú')
|
||||
|
||||
application.add_error_handler(error)
|
||||
# on non command i.e message - echo the message on Telegram
|
||||
#application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
|
||||
|
||||
# Run the bot until the user presses Ctrl-C
|
||||
application.run_polling()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -17,8 +17,10 @@ class Worker:
|
||||
def is_valid_request(self, product):
|
||||
is_valid = False
|
||||
if walladb.get_product(product):
|
||||
if not walladb.is_user_expired(product['telegram_user_id']):
|
||||
if 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']):
|
||||
if walladb.is_user_premium(product['telegram_user_id']) or \
|
||||
walladb.is_user_testing(product['telegram_user_id']):
|
||||
is_valid = True
|
||||
return is_valid
|
||||
|
||||
|
||||
Reference in New Issue
Block a user