diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d080e8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*json diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..527bca5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3.8-alpine +WORKDIR /usr/src/app +COPY ./requirements.txt . +RUN pip3 install --no-cache-dir -r requirements.txt +COPY . ./ +CMD ["python", "bot.py"] diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..3ce3931 --- /dev/null +++ b/bot.py @@ -0,0 +1,132 @@ +import _thread +import os +import logging +import telebot +from telebot import types +from pymongo import MongoClient + +telebot.logger.setLevel(logging.DEBUG) + +# Bot & config +bot = telebot.TeleBot(os.environ['TOKEN']) +admin = int(os.environ['ADMIN']) + +# Acceso a bd +client = MongoClient('database:27017') +db = client.bot +tests = db.tests +users = db.users + +# Funciones de utilidad +def is_user(cid): + return users.find_one(str(cid)) is not None + +def add_user(cid): + users.insert_one({ + "_id": str(cid), + "taken_tests": [], + "current_tests": [] + }) + +def restart_process(): + _thread.interrupt_main() + +def is_admin(cid): + return str(cid) == str(admin) + +def split(a, n): + k, m = divmod(len(a), n) + return (a[i*k+min(i, m):(i+1)*k+min(i+1, m)] for i in range(n)) + +def find_question(test, question): + questions = tests.find_one({'test': int(test)}, {'preguntas':{'$elemMatch': {'pregunta': int(question)}}, '_id':0}) + if questions: + return questions['preguntas'][0] + return {} + +def generate_tests_keyboard(): + keyboard = types.InlineKeyboardMarkup(row_width=4) + buttons = [] + for test in tests.find({},{'test':1, '_id':0}): + buttons.append(types.InlineKeyboardButton('Test {}'.format(test['test']-100), callback_data='test {}'.format(test['test']))) + for chunk in split(buttons, 4): + keyboard.add(*chunk) + return keyboard + +def generate_test_keyboard(question, test): + keyboard = types.InlineKeyboardMarkup() + for i,answer in enumerate(question.get('respuestas')): + keyboard.add(types.InlineKeyboardButton('{}'.format(answer['texto']), callback_data='pregunta {} {} {}'.format(test, question['pregunta'], i))) + next_prev_buttons = [] + if question.get('pregunta') == 1: + next_prev_buttons.append(types.InlineKeyboardButton('⏩', callback_data='pregunta {} {} +'.format(test, question['pregunta']))) + elif question.get('pregunta') == 20: + next_prev_buttons.append(types.InlineKeyboardButton('⏪', callback_data='pregunta {} {} -'.format(test, question['pregunta']))) + else: + next_prev_buttons.append(types.InlineKeyboardButton('⏪', callback_data='pregunta {} {} -'.format(test, question['pregunta']))) + next_prev_buttons.append(types.InlineKeyboardButton('⏩', callback_data='pregunta {} {} +'.format(test, question['pregunta']))) + keyboard.add(*next_prev_buttons) + return keyboard + +# Manejadores de comandos +@bot.message_handler(commands=['start']) +def start_handler(m): + cid = m.chat.id + if not is_user(cid): + add_user(cid) + bot.send_message(cid, "Bienvenid@") + +@bot.message_handler(commands=['reload']) +def reload_handler(m): + cid = m.chat.id + if is_admin(cid): + restart_process() + +@bot.message_handler(commands=['tests']) +def tests_handler(m): + cid = m.chat.id + keyboard = generate_tests_keyboard() + bot.send_message(cid, "Haz click en un test para empezar a resolverlo", reply_markup=keyboard) + +@bot.callback_query_handler(func=lambda call: call.data.startswith('test')) +def test_callback_handler(call): + cid = call.message.chat.id + test = call.data.split()[-1] + question = find_question(test, 1) + keyboard = generate_test_keyboard(question, test) + txt = "Test: {}\nPregunta: {}\n\n{}" + bot.send_photo(cid, question.get('img'), caption=txt.format(test, 1, question['encabezado']), reply_markup=keyboard) + # TODO: Añadir el test a current_tests del usuario + bot.answer_callback_query(call.id) + +@bot.callback_query_handler(func=lambda call: call.data.startswith('pregunta') and (call.data.endswith('+') or call.data.endswith('-'))) +def question_answer_callback_handler(call): + cid = call.message.chat.id + mid = call.message.id + _, test, q, option = call.data.split() + question = find_question(test, int(q)+1) + keyboard = generate_test_keyboard(question, test) + txt = "Test: {}\nPregunta: {}\n\n{}" + + + +@bot.callback_query_handler(func=lambda call: call.data.startswith('pregunta') and (call.data.endswith('+') or call.data.endswith('-'))) +def question_pager_callback_handler(call): + cid = call.message.chat.id + mid = call.message.id + _, test, q, action = call.data.split() + question = find_question(test, eval(q+action+'1')) + keyboard = generate_test_keyboard(question, test) + txt = "Test: {}\nPregunta: {}\n\n{}" + try: + bot.edit_message_media(types.InputMediaPhoto(question.get('img'), caption=txt.format(test, question['pregunta'], question['encabezado'])), cid, mid, reply_markup=keyboard) + bot.answer_callback_query(call.id) + except: + bot.answer_callback_query(call.id, "No se pudo cargar la pregunta") + + + + +# Iniciar bot +bot.send_message(admin, "Reiniciado") +bot.polling(skip_pending=True) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f85a849 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +version: '3' + +services: + bot: + build: + context: . + dockerfile: Dockerfile + restart: unless-stopped + environment: + - TOKEN=$TOKEN + - ADMIN=52033876 + volumes: + - ./:/usr/src/app + depends_on: + - database + database: + image: "mvertes/alpine-mongo" + restart: unless-stopped + volumes: + - db:/data/db + +volumes: + db: {} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6f16d41 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +pytelegrambotapi +pymongo +requests