From 2091a17cb93d9fed61ffa5a503f84bdead5e23a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Delfino?= Date: Sat, 22 Feb 2025 01:00:16 -0300 Subject: [PATCH 01/10] Add repository URL to project --- src/pycamp_bot/commands/manage_pycamp.py | 1 + src/pycamp_bot/commands/projects.py | 166 +++++++++++++++++++++-- src/pycamp_bot/commands/voting.py | 11 +- src/pycamp_bot/models.py | 1 + 4 files changed, 161 insertions(+), 18 deletions(-) diff --git a/src/pycamp_bot/commands/manage_pycamp.py b/src/pycamp_bot/commands/manage_pycamp.py index 61f2e2d..4df2be9 100644 --- a/src/pycamp_bot/commands/manage_pycamp.py +++ b/src/pycamp_bot/commands/manage_pycamp.py @@ -152,6 +152,7 @@ async def define_duration(update, context): chat_id=update.message.chat_id, text=msg ) + return ConversationHandler.END async def cancel(update, context): diff --git a/src/pycamp_bot/commands/projects.py b/src/pycamp_bot/commands/projects.py index 035f508..a5f2dde 100644 --- a/src/pycamp_bot/commands/projects.py +++ b/src/pycamp_bot/commands/projects.py @@ -1,6 +1,7 @@ import logging import peewee -from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters +from telegram import InlineKeyboardButton, InlineKeyboardMarkup +from telegram.ext import CallbackQueryHandler, ConversationHandler, CommandHandler, MessageHandler, filters from pycamp_bot.models import Pycampista, Project, Slot, Vote from pycamp_bot.commands.base import msg_to_active_pycamp_chat from pycamp_bot.commands.manage_pycamp import active_needed, get_active_pycamp @@ -10,7 +11,15 @@ current_projects = {} -NOMBRE, DIFICULTAD, TOPIC = ["nombre", "dificultad", "topic"] + +NOMBRE = "nombre" +DIFICULTAD = "dificultad" +TOPIC = "topic" +CHECK_REPOSITORIO = "check_repositorio" +REPOSITORIO = "repositorio" + +REPO_EXISTS_PATTERN = 'repoexists' +PROJECT_NAME_PATTERN = 'projectname' logger = logging.getLogger(__name__) @@ -57,7 +66,11 @@ async def naming_project(update, context): username = update.message.from_user.username name = update.message.text + user = Pycampista.get_or_create(username=username, chat_id=update.message.chat_id)[0] + new_project = Project(name=name) + new_project.owner = user + current_projects[username] = new_project await context.bot.send_message( @@ -118,26 +131,81 @@ async def project_topic(update, context): new_project = current_projects[username] new_project.topic = text - chat_id = update.message.chat_id - user = Pycampista.get_or_create(username=username, chat_id=chat_id)[0] + await context.bot.send_message( + chat_id=update.message.chat_id, + text="Excelente {}! La temática de tu proyecto es: {}.".format(username, text) + ) + + keyboard = [ + [ + InlineKeyboardButton("Sí", callback_data=f"{REPO_EXISTS_PATTERN}:si"), + InlineKeyboardButton("No", callback_data=f"{REPO_EXISTS_PATTERN}:no"), + ] + ] + reply_markup = InlineKeyboardMarkup(keyboard) + + await context.bot.send_message( + chat_id=update.message.chat_id, + text="¿Existe un repositorio para tu proyecto?", + reply_markup=reply_markup, + ) + + return CHECK_REPOSITORIO + + +async def ask_if_repository_exists(update, context): + '''Dialog to ask if a repository exists''' + callback_query = update.callback_query + chat = callback_query.message.chat + + if callback_query.data.split(':')[1] == "si": + await context.bot.send_message( + chat_id=chat.id, + text="¿Cuál es la URL del repositorio?", + ) + return REPOSITORIO + else: + await context.bot.send_message( + chat_id=chat.id, + text="Si creás un repositorio, podés agregarlo con /agregar_repositorio." + ) + await save_project(callback_query.from_user.username, chat.id, context) + return ConversationHandler.END + + +async def save_project(username, chat_id, context): + '''Save project to database''' + new_project = current_projects[username] - new_project.owner = user try: new_project.save() except peewee.IntegrityError: await context.bot.send_message( - chat_id=update.message.chat_id, + chat_id=chat_id, text="Ups ese proyecto ya fue cargado" ) - return ConversationHandler.END + else: + await context.bot.send_message( + chat_id=chat_id, + text="Tu proyecto ha sido cargado" + ) + + +async def project_repository(update, context): + '''Dialog to set project repository''' + username = update.message.from_user.username + text = update.message.text + + new_project = current_projects[username] + new_project.repository_url = text await context.bot.send_message( chat_id=update.message.chat_id, - text="Excelente {}! La temática de tu proyecto es: {}.".format(username, text)) - await context.bot.send_message( - chat_id=update.message.chat_id, - text="Tu proyecto ha sido cargado" + text=f"El repositorio de tu proyecto es: {text}." ) + + await save_project(username, update.message.chat_id, context) + return ConversationHandler.END @@ -148,6 +216,61 @@ async def cancel(update, context): return ConversationHandler.END +@active_needed +async def ask_project_name(update, context): + '''Command to start the agregar_repositorio dialog''' + username = update.message.from_user.username + + projects = Project.select().join(Pycampista).where(Pycampista.username == username) + + keyboard = [ + [InlineKeyboardButton(project.name, callback_data=f"{PROJECT_NAME_PATTERN}:{project.name}") for project in projects] + ] + reply_markup = InlineKeyboardMarkup(keyboard) + + await context.bot.send_message( + chat_id=update.message.chat_id, + text="¿A qué proyecto querés agregar un repositorio?", + reply_markup=reply_markup, + ) + + return 1 + + +async def ask_repository_name(update, context): + '''Dialog to set project name''' + callback_query = update.callback_query + chat = callback_query.message.chat + + username = callback_query.from_user.username + + current_projects[username] = callback_query.data.split(':')[1] + + await context.bot.send_message( + chat_id=chat.id, + text="¿Cuál es la URL del repositorio?", + ) + return 2 + + +@active_needed +async def add_repository(update, context): + '''Dialog to set repository''' + username = update.message.from_user.username + text = update.message.text + + project = Project.select().where(Project.name == current_projects[username]).get() + + project.repository_url = text + project.save() + + await context.bot.send_message( + chat_id=update.message.chat_id, + text=f'Repositorio "{text}" agregado al proyecto "{current_projects[username]}"', + ) + return ConversationHandler.END + + @active_needed @admin_needed async def start_project_load(update, context): @@ -186,7 +309,10 @@ async def end_project_load(update, context): states={ NOMBRE: [MessageHandler(filters.TEXT, naming_project)], DIFICULTAD: [MessageHandler(filters.TEXT, project_level)], - TOPIC: [MessageHandler(filters.TEXT, project_topic)]}, + TOPIC: [MessageHandler(filters.TEXT, project_topic)], + CHECK_REPOSITORIO: [CallbackQueryHandler(ask_if_repository_exists, pattern=REPO_EXISTS_PATTERN)], + REPOSITORIO: [MessageHandler(filters.TEXT, project_repository)], + }, fallbacks=[CommandHandler('cancel', cancel)]) @@ -236,11 +362,12 @@ async def show_projects(update, context): projects = Project.select() text = [] for project in projects: - project_text = "{} \n Owner: {} \n Temática: {} \n Nivel: {}".format( + project_text = "{} \n Owner: {} \n Temática: {} \n Nivel: {} \n Repositorio: {}".format( project.name, project.owner.username, project.topic, - project.difficult_level + project.difficult_level, + project.repository_url, ) participants_count = Vote.select().where( (Vote.project == project) & (Vote.interest)).count() @@ -336,7 +463,18 @@ async def show_my_projects(update, context): await update.message.reply_text(text, parse_mode='MarkdownV2') def set_handlers(application): + add_repository_handler = ConversationHandler( + entry_points=[CommandHandler('agregar_repositorio', ask_project_name)], + states={ + 1: [CallbackQueryHandler(ask_repository_name, pattern=PROJECT_NAME_PATTERN)], + 2: [MessageHandler(filters.TEXT, add_repository)], + }, + fallbacks=[CommandHandler('cancel', cancel)], + ) + application.add_handler(load_project_handler) + application.add_handler(add_repository_handler) + application.add_handler( CommandHandler('empezar_carga_proyectos', start_project_load)) application.add_handler( diff --git a/src/pycamp_bot/commands/voting.py b/src/pycamp_bot/commands/voting.py index fb90647..4424a9a 100644 --- a/src/pycamp_bot/commands/voting.py +++ b/src/pycamp_bot/commands/voting.py @@ -9,6 +9,9 @@ from pycamp_bot.logger import logger +VOTE_PATTERN = 'vote' + + def vote_authorized(func): @functools.wraps(func) async def wrap(*args): @@ -61,7 +64,7 @@ async def button(update, context): # Save vote in the database and confirm the chosen proyects. - if query.data == "si": + if query.data.split(':')[1] == "si": result = f"✅ Sumade a {project_name}!" new_vote.interest = True else: @@ -96,8 +99,8 @@ async def vote(update, context): # ask user for each project in the database for project in Project.select(): - keyboard = [[InlineKeyboardButton("Me Sumo!", callback_data="si"), - InlineKeyboardButton("Paso", callback_data="no")]] + keyboard = [[InlineKeyboardButton("Me Sumo!", callback_data=f"{VOTE_PATTERN}:si"), + InlineKeyboardButton("Paso", callback_data=f"{VOTE_PATTERN}:no")]] reply_markup = InlineKeyboardMarkup(keyboard) @@ -129,7 +132,7 @@ async def vote_count(update, context): def set_handlers(application): application.add_handler( - CallbackQueryHandler(button)) + CallbackQueryHandler(button, pattern=VOTE_PATTERN)) application.add_handler( CommandHandler('empezar_votacion_proyectos', start_voting)) application.add_handler( diff --git a/src/pycamp_bot/models.py b/src/pycamp_bot/models.py index b5e2bb2..3313111 100644 --- a/src/pycamp_bot/models.py +++ b/src/pycamp_bot/models.py @@ -158,6 +158,7 @@ class Project(BaseModel): topic = pw.CharField(null=True) slot = pw.ForeignKeyField(Slot, null=True) owner = pw.ForeignKeyField(Pycampista) + repository_url = pw.CharField(null=True) class Vote(BaseModel): From ff6e62cac1d7d9b7c7abea73a930288f0f9138c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Delfino?= Date: Sat, 22 Feb 2025 01:03:23 -0300 Subject: [PATCH 02/10] Minor changes --- src/pycamp_bot/commands/projects.py | 37 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/pycamp_bot/commands/projects.py b/src/pycamp_bot/commands/projects.py index a5f2dde..158fb05 100644 --- a/src/pycamp_bot/commands/projects.py +++ b/src/pycamp_bot/commands/projects.py @@ -153,6 +153,24 @@ async def project_topic(update, context): return CHECK_REPOSITORIO +async def save_project(username, chat_id, context): + '''Save project to database''' + new_project = current_projects[username] + + try: + new_project.save() + except peewee.IntegrityError: + await context.bot.send_message( + chat_id=chat_id, + text="Ups ese proyecto ya fue cargado" + ) + else: + await context.bot.send_message( + chat_id=chat_id, + text="Tu proyecto ha sido cargado" + ) + + async def ask_if_repository_exists(update, context): '''Dialog to ask if a repository exists''' callback_query = update.callback_query @@ -173,24 +191,6 @@ async def ask_if_repository_exists(update, context): return ConversationHandler.END -async def save_project(username, chat_id, context): - '''Save project to database''' - new_project = current_projects[username] - - try: - new_project.save() - except peewee.IntegrityError: - await context.bot.send_message( - chat_id=chat_id, - text="Ups ese proyecto ya fue cargado" - ) - else: - await context.bot.send_message( - chat_id=chat_id, - text="Tu proyecto ha sido cargado" - ) - - async def project_repository(update, context): '''Dialog to set project repository''' username = update.message.from_user.username @@ -253,7 +253,6 @@ async def ask_repository_name(update, context): return 2 -@active_needed async def add_repository(update, context): '''Dialog to set repository''' username = update.message.from_user.username From 6260db2f3fe98a5ebb0d5b7c68d64fc234c247cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Delfino?= Date: Sun, 23 Feb 2025 21:19:22 -0300 Subject: [PATCH 03/10] Show one project per row --- src/pycamp_bot/commands/projects.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pycamp_bot/commands/projects.py b/src/pycamp_bot/commands/projects.py index 158fb05..1228bba 100644 --- a/src/pycamp_bot/commands/projects.py +++ b/src/pycamp_bot/commands/projects.py @@ -223,9 +223,9 @@ async def ask_project_name(update, context): projects = Project.select().join(Pycampista).where(Pycampista.username == username) - keyboard = [ - [InlineKeyboardButton(project.name, callback_data=f"{PROJECT_NAME_PATTERN}:{project.name}") for project in projects] - ] + keyboard = [] + for project in projects: + keyboard.append([InlineKeyboardButton(project.name, callback_data=f"{PROJECT_NAME_PATTERN}:{project.name}")]) reply_markup = InlineKeyboardMarkup(keyboard) await context.bot.send_message( From 7e63b82dd93f378ab207e88456db13c7315d8816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Delfino?= Date: Sun, 23 Feb 2025 21:30:56 -0300 Subject: [PATCH 04/10] Abort ask_project_name if the user didn't load projects --- src/pycamp_bot/commands/projects.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pycamp_bot/commands/projects.py b/src/pycamp_bot/commands/projects.py index 1228bba..cd7afce 100644 --- a/src/pycamp_bot/commands/projects.py +++ b/src/pycamp_bot/commands/projects.py @@ -223,6 +223,13 @@ async def ask_project_name(update, context): projects = Project.select().join(Pycampista).where(Pycampista.username == username) + if not projects: + await context.bot.send_message( + chat_id=update.message.chat_id, + text="No cargaste ningún proyecto", + ) + return ConversationHandler.END + keyboard = [] for project in projects: keyboard.append([InlineKeyboardButton(project.name, callback_data=f"{PROJECT_NAME_PATTERN}:{project.name}")]) From 65cd0be81d0e7e179d5fa6acd68c7a1e04a9e055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Delfino?= Date: Sun, 23 Feb 2025 21:31:18 -0300 Subject: [PATCH 05/10] Print a nicer string than None --- src/pycamp_bot/commands/projects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pycamp_bot/commands/projects.py b/src/pycamp_bot/commands/projects.py index cd7afce..6aa985c 100644 --- a/src/pycamp_bot/commands/projects.py +++ b/src/pycamp_bot/commands/projects.py @@ -373,7 +373,7 @@ async def show_projects(update, context): project.owner.username, project.topic, project.difficult_level, - project.repository_url, + project.repository_url or '(ninguno)', ) participants_count = Vote.select().where( (Vote.project == project) & (Vote.interest)).count() From 7151c3b8539a5a0362630ae2df77dc69f44739dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Delfino?= Date: Sun, 23 Feb 2025 21:36:01 -0300 Subject: [PATCH 06/10] Polish CallbackQueryHandler patterns --- src/pycamp_bot/commands/projects.py | 4 ++-- src/pycamp_bot/commands/voting.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pycamp_bot/commands/projects.py b/src/pycamp_bot/commands/projects.py index 6aa985c..273af3b 100644 --- a/src/pycamp_bot/commands/projects.py +++ b/src/pycamp_bot/commands/projects.py @@ -316,7 +316,7 @@ async def end_project_load(update, context): NOMBRE: [MessageHandler(filters.TEXT, naming_project)], DIFICULTAD: [MessageHandler(filters.TEXT, project_level)], TOPIC: [MessageHandler(filters.TEXT, project_topic)], - CHECK_REPOSITORIO: [CallbackQueryHandler(ask_if_repository_exists, pattern=REPO_EXISTS_PATTERN)], + CHECK_REPOSITORIO: [CallbackQueryHandler(ask_if_repository_exists, pattern=f'^{REPO_EXISTS_PATTERN}:')], REPOSITORIO: [MessageHandler(filters.TEXT, project_repository)], }, fallbacks=[CommandHandler('cancel', cancel)]) @@ -472,7 +472,7 @@ def set_handlers(application): add_repository_handler = ConversationHandler( entry_points=[CommandHandler('agregar_repositorio', ask_project_name)], states={ - 1: [CallbackQueryHandler(ask_repository_name, pattern=PROJECT_NAME_PATTERN)], + 1: [CallbackQueryHandler(ask_repository_name, pattern=f'^{PROJECT_NAME_PATTERN}:')], 2: [MessageHandler(filters.TEXT, add_repository)], }, fallbacks=[CommandHandler('cancel', cancel)], diff --git a/src/pycamp_bot/commands/voting.py b/src/pycamp_bot/commands/voting.py index 4424a9a..b14e7f3 100644 --- a/src/pycamp_bot/commands/voting.py +++ b/src/pycamp_bot/commands/voting.py @@ -132,7 +132,7 @@ async def vote_count(update, context): def set_handlers(application): application.add_handler( - CallbackQueryHandler(button, pattern=VOTE_PATTERN)) + CallbackQueryHandler(button, pattern=f'^{VOTE_PATTERN}:')) application.add_handler( CommandHandler('empezar_votacion_proyectos', start_voting)) application.add_handler( From b60196cd551a519746c6d6362d96223b227f2a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Delfino?= Date: Sun, 23 Feb 2025 21:38:08 -0300 Subject: [PATCH 07/10] Simplify CallbackQueryHandler patterns --- src/pycamp_bot/commands/projects.py | 4 ++-- src/pycamp_bot/commands/voting.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pycamp_bot/commands/projects.py b/src/pycamp_bot/commands/projects.py index 273af3b..76d2ad6 100644 --- a/src/pycamp_bot/commands/projects.py +++ b/src/pycamp_bot/commands/projects.py @@ -316,7 +316,7 @@ async def end_project_load(update, context): NOMBRE: [MessageHandler(filters.TEXT, naming_project)], DIFICULTAD: [MessageHandler(filters.TEXT, project_level)], TOPIC: [MessageHandler(filters.TEXT, project_topic)], - CHECK_REPOSITORIO: [CallbackQueryHandler(ask_if_repository_exists, pattern=f'^{REPO_EXISTS_PATTERN}:')], + CHECK_REPOSITORIO: [CallbackQueryHandler(ask_if_repository_exists, pattern=f'{REPO_EXISTS_PATTERN}:')], REPOSITORIO: [MessageHandler(filters.TEXT, project_repository)], }, fallbacks=[CommandHandler('cancel', cancel)]) @@ -472,7 +472,7 @@ def set_handlers(application): add_repository_handler = ConversationHandler( entry_points=[CommandHandler('agregar_repositorio', ask_project_name)], states={ - 1: [CallbackQueryHandler(ask_repository_name, pattern=f'^{PROJECT_NAME_PATTERN}:')], + 1: [CallbackQueryHandler(ask_repository_name, pattern=f'{PROJECT_NAME_PATTERN}:')], 2: [MessageHandler(filters.TEXT, add_repository)], }, fallbacks=[CommandHandler('cancel', cancel)], diff --git a/src/pycamp_bot/commands/voting.py b/src/pycamp_bot/commands/voting.py index b14e7f3..db8c143 100644 --- a/src/pycamp_bot/commands/voting.py +++ b/src/pycamp_bot/commands/voting.py @@ -132,7 +132,7 @@ async def vote_count(update, context): def set_handlers(application): application.add_handler( - CallbackQueryHandler(button, pattern=f'^{VOTE_PATTERN}:')) + CallbackQueryHandler(button, pattern=f'{VOTE_PATTERN}:')) application.add_handler( CommandHandler('empezar_votacion_proyectos', start_voting)) application.add_handler( From ba02b7e5e700588956eb51d73477d658d630fc41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Delfino?= Date: Tue, 4 Mar 2025 22:56:56 -0300 Subject: [PATCH 08/10] Add comment --- src/pycamp_bot/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pycamp_bot/models.py b/src/pycamp_bot/models.py index 3313111..93d4b81 100644 --- a/src/pycamp_bot/models.py +++ b/src/pycamp_bot/models.py @@ -152,6 +152,7 @@ class Project(BaseModel): topic: string comma separated with the pertinences slot: ForeignKey with the slot asigned owner: ForeignKey with the pycamp user asigned + repository_url: URL of the repository of the project ''' name = pw.CharField(unique=True) difficult_level = pw.IntegerField(default=1) From 8d091228fcd4d5520c731cceadf86962a5604e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Delfino?= Date: Wed, 5 Mar 2025 20:09:30 -0300 Subject: [PATCH 09/10] Add group handling --- src/pycamp_bot/commands/base.py | 13 +- src/pycamp_bot/commands/projects.py | 198 ++++++++++++++++++++-------- src/pycamp_bot/models.py | 2 + 3 files changed, 155 insertions(+), 58 deletions(-) diff --git a/src/pycamp_bot/commands/base.py b/src/pycamp_bot/commands/base.py index fc3badb..bac80c5 100644 --- a/src/pycamp_bot/commands/base.py +++ b/src/pycamp_bot/commands/base.py @@ -2,13 +2,16 @@ from pycamp_bot.commands.help_msg import get_help from pycamp_bot.logger import logger +import os + async def msg_to_active_pycamp_chat(bot, text): - chat_id = -1001404878013 # Prueba - await bot.send_message( - chat_id=chat_id, - text=text - ) + if 'TEST_CHAT_ID' in os.environ: + chat_id = -1001404878013 # Prueba + await bot.send_message( + chat_id=os.environ['TEST_CHAT_ID'], + text=text + ) async def start(update, context): diff --git a/src/pycamp_bot/commands/projects.py b/src/pycamp_bot/commands/projects.py index 76d2ad6..dc2f484 100644 --- a/src/pycamp_bot/commands/projects.py +++ b/src/pycamp_bot/commands/projects.py @@ -1,7 +1,9 @@ import logging +import textwrap + import peewee -from telegram import InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import CallbackQueryHandler, ConversationHandler, CommandHandler, MessageHandler, filters +from telegram import InlineKeyboardButton, InlineKeyboardMarkup, LinkPreviewOptions +from telegram.ext import CallbackQueryHandler, CommandHandler, ConversationHandler, MessageHandler, filters from pycamp_bot.models import Pycampista, Project, Slot, Vote from pycamp_bot.commands.base import msg_to_active_pycamp_chat from pycamp_bot.commands.manage_pycamp import active_needed, get_active_pycamp @@ -9,7 +11,6 @@ from pycamp_bot.commands.schedule import DIAS from pycamp_bot.utils import escape_markdown - current_projects = {} NOMBRE = "nombre" @@ -17,9 +18,12 @@ TOPIC = "topic" CHECK_REPOSITORIO = "check_repositorio" REPOSITORIO = "repositorio" +CHECK_GRUPO = "check_grupo" +GRUPO = "grupo" REPO_EXISTS_PATTERN = 'repoexists' PROJECT_NAME_PATTERN = 'projectname' +GROUP_EXISTS_PATTERN = 'groupexists' logger = logging.getLogger(__name__) @@ -47,15 +51,11 @@ async def load_project(update, context): logger.info("Adding project") username = update.message.from_user.username - logger.info("Load autorized. Starting dialog") - await context.bot.send_message( - chat_id=update.message.chat_id, - text="Usuario: " + username - ) + logger.info("Load authorized. Starting dialog") await context.bot.send_message( chat_id=update.message.chat_id, - text="Ingresá el Nombre del Proyecto a proponer", + text="¿Cuál es el nombre del proyecto?", ) return NOMBRE @@ -75,18 +75,13 @@ async def naming_project(update, context): await context.bot.send_message( chat_id=update.message.chat_id, - text="Estamos cargando tu proyecto: {}!".format(username) - ) - await context.bot.send_message( - chat_id=update.message.chat_id, - text="Tu proyecto se llama: {}".format(name) - ) - await context.bot.send_message( - chat_id=update.message.chat_id, - text="""Cual es el nivel de dificultad? - 1 = newbie friendly - 2 = intermedio - 3 = python avanzado""" + text=textwrap.dedent(""" + ¿Cuál es el nivel de dificultad del proyecto? + + 1: newbie friendly + 2: intermedio + 3: python avanzado""" + ) ) return DIFICULTAD @@ -102,17 +97,16 @@ async def project_level(update, context): await context.bot.send_message( chat_id=update.message.chat_id, - text="Ok! Tu proyecto es nivel: {}".format(text) - ) - await context.bot.send_message( - chat_id=update.message.chat_id, - text="""Ahora necesitamos que nos digas la temática de tu proyecto. - Algunos ejemplos pueden ser: - - flask - - django - - telegram - - inteligencia artificial - - recreativo""" + text=textwrap.dedent(""" + ¿Cuál es la temática del proyecto? + + Ejemplos: + - flask + - django + - telegram + - inteligencia artificial + - recreativo""" + ) ) return TOPIC else: @@ -131,11 +125,6 @@ async def project_topic(update, context): new_project = current_projects[username] new_project.topic = text - await context.bot.send_message( - chat_id=update.message.chat_id, - text="Excelente {}! La temática de tu proyecto es: {}.".format(username, text) - ) - keyboard = [ [ InlineKeyboardButton("Sí", callback_data=f"{REPO_EXISTS_PATTERN}:si"), @@ -146,7 +135,7 @@ async def project_topic(update, context): await context.bot.send_message( chat_id=update.message.chat_id, - text="¿Existe un repositorio para tu proyecto?", + text="¿El proyecto tiene un repositorio?", reply_markup=reply_markup, ) @@ -167,10 +156,25 @@ async def save_project(username, chat_id, context): else: await context.bot.send_message( chat_id=chat_id, - text="Tu proyecto ha sido cargado" + text="Proyecto cargado" ) +async def present_group_inline_keyboard(chat_id, context): + keyboard = [ + [ + InlineKeyboardButton("Sí", callback_data=f"{GROUP_EXISTS_PATTERN}:si"), + InlineKeyboardButton("No", callback_data=f"{GROUP_EXISTS_PATTERN}:no"), + ] + ] + reply_markup = InlineKeyboardMarkup(keyboard) + await context.bot.send_message( + chat_id=chat_id, + text="¿Tu proyecto tiene un grupo de Telegram?", + reply_markup=reply_markup, + ) + + async def ask_if_repository_exists(update, context): '''Dialog to ask if a repository exists''' callback_query = update.callback_query @@ -179,7 +183,7 @@ async def ask_if_repository_exists(update, context): if callback_query.data.split(':')[1] == "si": await context.bot.send_message( chat_id=chat.id, - text="¿Cuál es la URL del repositorio?", + text="¿Cuál es la URL del repositorio del proyecto?", ) return REPOSITORIO else: @@ -187,6 +191,31 @@ async def ask_if_repository_exists(update, context): chat_id=chat.id, text="Si creás un repositorio, podés agregarlo con /agregar_repositorio." ) + + await present_group_inline_keyboard( + chat_id=chat.id, + context=context, + ) + + return CHECK_GRUPO + + +async def ask_if_group_exists(update, context): + '''Dialog to ask if a group exists''' + callback_query = update.callback_query + chat = callback_query.message.chat + + if callback_query.data.split(':')[1] == "si": + await context.bot.send_message( + chat_id=chat.id, + text="¿Cuál es la URL del grupo del proyecto?", + ) + return GRUPO + else: + await context.bot.send_message( + chat_id=chat.id, + text="Si creás un grupo, podés agregarlo con /agregar_grupo." + ) await save_project(callback_query.from_user.username, chat.id, context) return ConversationHandler.END @@ -199,13 +228,23 @@ async def project_repository(update, context): new_project = current_projects[username] new_project.repository_url = text - await context.bot.send_message( + await present_group_inline_keyboard( chat_id=update.message.chat_id, - text=f"El repositorio de tu proyecto es: {text}." + context=context, ) - await save_project(username, update.message.chat_id, context) + return CHECK_GRUPO + + +async def project_group(update, context): + '''Dialog to set project group''' + username = update.message.from_user.username + text = update.message.text + + new_project = current_projects[username] + new_project.group_url = text + await save_project(username, update.message.chat_id, context) return ConversationHandler.END @@ -218,7 +257,7 @@ async def cancel(update, context): @active_needed async def ask_project_name(update, context): - '''Command to start the agregar_repositorio dialog''' + '''Command to start the agregar_repositorio/agregar_grupo dialogs''' username = update.message.from_user.username projects = Project.select().join(Pycampista).where(Pycampista.username == username) @@ -237,14 +276,14 @@ async def ask_project_name(update, context): await context.bot.send_message( chat_id=update.message.chat_id, - text="¿A qué proyecto querés agregar un repositorio?", + text="¿Qué proyecto querés modificar?", reply_markup=reply_markup, ) return 1 -async def ask_repository_name(update, context): +async def ask_repository_url(update, context): '''Dialog to set project name''' callback_query = update.callback_query chat = callback_query.message.chat @@ -260,6 +299,22 @@ async def ask_repository_name(update, context): return 2 +async def ask_group_url(update, context): + '''Dialog to set project name''' + callback_query = update.callback_query + chat = callback_query.message.chat + + username = callback_query.from_user.username + + current_projects[username] = callback_query.data.split(':')[1] + + await context.bot.send_message( + chat_id=chat.id, + text="¿Cuál es la URL del grupo?", + ) + return 2 + + async def add_repository(update, context): '''Dialog to set repository''' username = update.message.from_user.username @@ -272,7 +327,24 @@ async def add_repository(update, context): await context.bot.send_message( chat_id=update.message.chat_id, - text=f'Repositorio "{text}" agregado al proyecto "{current_projects[username]}"', + text=f'Repositorio agregado', + ) + return ConversationHandler.END + + +async def add_group(update, context): + '''Dialog to set group''' + username = update.message.from_user.username + text = update.message.text + + project = Project.select().where(Project.name == current_projects[username]).get() + + project.group_url = text + project.save() + + await context.bot.send_message( + chat_id=update.message.chat_id, + text=f'Grupo agregado', ) return ConversationHandler.END @@ -287,7 +359,7 @@ async def start_project_load(update, context): pycamp.project_load_authorized = True pycamp.save() - await update.message.reply_text("Autorizadx \nCarga de proyectos Abierta") + await update.message.reply_text("Carga de proyectos Abierta") await msg_to_active_pycamp_chat(context.bot, "Carga de proyectos Abierta") else: await update.message.reply_text("La carga de proyectos ya estaba abierta") @@ -318,6 +390,8 @@ async def end_project_load(update, context): TOPIC: [MessageHandler(filters.TEXT, project_topic)], CHECK_REPOSITORIO: [CallbackQueryHandler(ask_if_repository_exists, pattern=f'{REPO_EXISTS_PATTERN}:')], REPOSITORIO: [MessageHandler(filters.TEXT, project_repository)], + CHECK_GRUPO: [CallbackQueryHandler(ask_if_group_exists, pattern=f'{GROUP_EXISTS_PATTERN}:')], + GRUPO: [MessageHandler(filters.TEXT, project_group)], }, fallbacks=[CommandHandler('cancel', cancel)]) @@ -368,12 +442,13 @@ async def show_projects(update, context): projects = Project.select() text = [] for project in projects: - project_text = "{} \n Owner: {} \n Temática: {} \n Nivel: {} \n Repositorio: {}".format( + project_text = "{}\n Owner: @{}\n Temática: {}\n Nivel: {}\n Repositorio: {}\n Grupo de Telegram: {}".format( project.name, project.owner.username, project.topic, project.difficult_level, project.repository_url or '(ninguno)', + project.group_url or '(ninguno)', ) participants_count = Vote.select().where( (Vote.project == project) & (Vote.interest)).count() @@ -386,7 +461,7 @@ async def show_projects(update, context): else: text = "Todavía no hay ningún proyecto cargado" - await update.message.reply_text(text) + await update.message.reply_text(text, link_preview_options=LinkPreviewOptions(is_disabled=True)) @@ -470,16 +545,33 @@ async def show_my_projects(update, context): def set_handlers(application): add_repository_handler = ConversationHandler( - entry_points=[CommandHandler('agregar_repositorio', ask_project_name)], + entry_points=[ + CommandHandler('agregar_repositorio', ask_project_name), + ], states={ - 1: [CallbackQueryHandler(ask_repository_name, pattern=f'{PROJECT_NAME_PATTERN}:')], + 1: [CallbackQueryHandler(ask_repository_url, pattern=f'{PROJECT_NAME_PATTERN}:')], 2: [MessageHandler(filters.TEXT, add_repository)], }, - fallbacks=[CommandHandler('cancel', cancel)], + fallbacks=[ + CommandHandler('cancel', cancel), + ], + ) + add_group_handler = ConversationHandler( + entry_points=[ + CommandHandler('agregar_grupo', ask_project_name), + ], + states={ + 1: [CallbackQueryHandler(ask_group_url, pattern=f'{PROJECT_NAME_PATTERN}:')], + 2: [MessageHandler(filters.TEXT, add_group)], + }, + fallbacks=[ + CommandHandler('cancel', cancel), + ], ) application.add_handler(load_project_handler) application.add_handler(add_repository_handler) + application.add_handler(add_group_handler) application.add_handler( CommandHandler('empezar_carga_proyectos', start_project_load)) diff --git a/src/pycamp_bot/models.py b/src/pycamp_bot/models.py index 93d4b81..a931da1 100644 --- a/src/pycamp_bot/models.py +++ b/src/pycamp_bot/models.py @@ -153,6 +153,7 @@ class Project(BaseModel): slot: ForeignKey with the slot asigned owner: ForeignKey with the pycamp user asigned repository_url: URL of the repository of the project + group_url: URL of the Telegram group of the project ''' name = pw.CharField(unique=True) difficult_level = pw.IntegerField(default=1) @@ -160,6 +161,7 @@ class Project(BaseModel): slot = pw.ForeignKeyField(Slot, null=True) owner = pw.ForeignKeyField(Pycampista) repository_url = pw.CharField(null=True) + group_url = pw.CharField(null=True) class Vote(BaseModel): From 09a04ceb3178444dc89a10c7f4649b7e55342c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Delfino?= Date: Wed, 5 Mar 2025 21:03:43 -0300 Subject: [PATCH 10/10] Add migration --- .../migrate_to_repository_and_group_urls.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 migrations/migrate_to_repository_and_group_urls.py diff --git a/migrations/migrate_to_repository_and_group_urls.py b/migrations/migrate_to_repository_and_group_urls.py new file mode 100644 index 0000000..f1ba4c4 --- /dev/null +++ b/migrations/migrate_to_repository_and_group_urls.py @@ -0,0 +1,24 @@ +# https://docs.peewee-orm.com/en/latest/peewee/playhouse.html#schema-migrations + +import peewee +import playhouse.migrate + +import pycamp_bot.models + + +my_db = peewee.SqliteDatabase('pycamp_projects.db') +migrator = playhouse.migrate.SqliteMigrator(my_db) + + +playhouse.migrate.migrate( + migrator.add_column( + pycamp_bot.models.Project._meta.table_name, + 'repository_url', + pycamp_bot.models.Project.repository_url, + ), + migrator.add_column( + pycamp_bot.models.Project._meta.table_name, + 'group_url', + pycamp_bot.models.Project.group_url, + ), +)