summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend_api.py40
-rw-r--r--bot.py115
-rw-r--r--keyboards.py69
-rw-r--r--states.py109
-rw-r--r--utils.py14
5 files changed, 259 insertions, 88 deletions
diff --git a/backend_api.py b/backend_api.py
index 2c8f268..4fb11cf 100644
--- a/backend_api.py
+++ b/backend_api.py
@@ -1,4 +1,5 @@
import requests
+import urllib
from config import BACKEND_URL
import logging
@@ -9,15 +10,20 @@ logger.setLevel(logging.DEBUG)
def register_user(tg_id, username, fullname) -> bool:
logger.debug(f"Registering user with id={tg_id}; username={username}")
- response = requests.post(f"{BACKEND_URL}/profiles/", data={
- "tg_id": tg_id,
- "username": username,
- "fullname": fullname
- })
+ try:
+ response = requests.post(f"{BACKEND_URL}/profiles/", data={
+ "tg_id": tg_id,
+ "username": username,
+ "fullname": fullname
+ })
+ except Exception as e:
+ logger.debug(f"Got exception while making request: {e}")
+ return False
+
logger.debug(
f"Got response from backend: "
f"Status={response.status_code}; "
- f"Text={response.text}"
+ f"Text={response.text[:200]}..."
)
return response.status_code == 201
@@ -26,3 +32,25 @@ def register_user(tg_id, username, fullname) -> bool:
def get_tasks():
response = requests.get(f"{BACKEND_URL}/tasks/")
return response.json()
+
+
+def get_task(title: str):
+ logger.debug(f"Trying to retrieve task with title={title}")
+ try:
+ response = requests.get(
+ "http://127.0.0.1:8000/api/get_task/"
+ + urllib.parse.quote(title)
+ )
+ task = response.json()
+
+ except Exception as e:
+ logger.debug(f"Got exception while making request: {e}")
+ return 500, {}
+
+ logger.debug(
+ f"Got response from backend: "
+ f"Status={response.status_code}; "
+ f"Text={response.text[:200]}..."
+ )
+
+ return response.status_code, task
diff --git a/bot.py b/bot.py
index 7fdcffb..e683152 100644
--- a/bot.py
+++ b/bot.py
@@ -9,6 +9,12 @@ from os import environ
from config import TG_TOKEN, REQUEST_KWARGS
import backend_api
+from keyboards import (
+ MenuKeyboard, TasksKeyboard, TaskChosenKeyboard, ContinueKeyboard,
+ AnsweringKeyboard,
+)
+from utils import *
+from states import States
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
@@ -17,91 +23,29 @@ logger.setLevel(logging.DEBUG)
def start(bot: Bot, update: Update, user_data):
update.message.reply_text(
- "Привет! Ты вошел в телеграм-квиз с мемами про наш любимый КНиИТ.",
+ "Привет! Ты вошел в телеграм-квиз с мемами про наш любимый КНиИТ!",
reply_markup=ReplyKeyboardRemove()
)
user: User = update.message.from_user
if user.username is None:
- update.message.reply_text(
- "По правилам квиза ты не можешь участвовать, если у тебя не указано "
- "имя пользователя, поэтому укажи его и возвращайся как только это сделаешь!",
- reply_markup=ReplyKeyboardMarkup([['Я указал имя пользователя']])
- )
- return USERNAME_CHECK
+ return States.wait_for_username(bot, update, user_data)
else:
logger.debug(backend_api.register_user(user.id, user.username, user.full_name))
update.message.reply_text("Ты успешно зарегистрирован в системе!")
-
- update.message.reply_text("Твой счет: 0")
- update.message.reply_text(
- "Выбери следующее действие...",
- reply_markup=ReplyKeyboardMarkup([
- ["Сдать задачу"],
- ["Топ-10", "Правила"]
- ])
- )
-
- return MAIN_MENU
+ return States.main_menu(bot, update, user_data)
def username_check(bot: Bot, update: Update, user_data):
user: User = update.message.from_user
if user.username is None:
- update.message.reply_text(
- "Ты все еще не указал имя пользователя!",
- reply_markup=ReplyKeyboardMarkup([['Я указал имя пользователя']])
- )
- return USERNAME_CHECK
+ return States.wait_for_username(bot, update, user_data)
else:
logger.debug(backend_api.register_user(user.id, user.username, user.full_name))
update.message.reply_text("Ты успешно зарегистрирован в системе!")
-
- update.message.reply_text("Твой счет: 0")
- update.message.reply_text(
- "Выбери следующее действие...",
- reply_markup=ReplyKeyboardMarkup([
- ["Сдать задачу"],
- ["Топ-10", "Правила"]
- ])
- )
-
- return MAIN_MENU
-
-
-def main_menu(bot, update, user_data):
- text = update.message.text
-
- if text == "Сдать задачу":
- update.message.reply_text("А пока что нельзя!!")
- return MAIN_MENU
-
- elif text == "Топ-10":
- update.message.reply_text("Топ-1:\n1.Андрей Гущин")
- return MAIN_MENU
-
- elif text == "Правила":
- update.message.reply_text("Какие-то правила!!!!")
- return MAIN_MENU
-
- return MAIN_MENU
-
-
-def rules(bot: Bot, update: Update, user_data):
- update.message.reply_text("Какие-то правила!!!!")
- return MAIN_MENU
-
-
-def top_10(bot: Bot, update: Update, user_data):
- update.message.reply_text("Топ-1:\n1.Андрей Гущин")
- return MAIN_MENU
-
-
-def task_choose(bot: Bot, update: Update, user_data):
- update.message.reply_text("А пока что нельзя!!")
- return MAIN_MENU
+ return States.main_menu(bot, update, user_data)
def stop(bot, update):
@@ -125,26 +69,33 @@ def main():
updater.idle()
-(
- USERNAME_CHECK, USERNAME_HOLD,
- MAIN_MENU, TOP_10, RULES,
- TASK_CHOOSE, CANCEL_CHOOSE,
- TASK_SHOW, CANCEL_SHOW,
- ENTER_ANSWER, CANCEL_ANSWER,
- *_
-) = range(100)
-
conversation_handler = ConversationHandler(
entry_points=[
- CommandHandler('start', start, pass_user_data=True)
+ CommandHandler('start', start, pass_user_data=True),
],
states={
- USERNAME_CHECK: [MessageHandler(Filters.text, username_check, pass_user_data=True)],
- MAIN_MENU: [MessageHandler(Filters.text, main_menu, pass_user_data=True)],
- RULES: [MessageHandler(Filters.text, rules, pass_user_data=True)],
- TOP_10: [MessageHandler(Filters.text, top_10, pass_user_data=True)],
- TASK_CHOOSE: [MessageHandler(Filters.text, task_choose, pass_user_data=True)],
+ WAIT_FOR_USERNAME: [MessageHandler(Filters.text, username_check, pass_user_data=True)],
+
+ MAIN_MENU: [
+ MessageHandler(Filters.regex(MenuKeyboard.CHOOSE_TASK), States.choose_task, pass_user_data=True),
+ MessageHandler(Filters.regex(MenuKeyboard.TOP_10), States.top_10, pass_user_data=True),
+ MessageHandler(Filters.regex(MenuKeyboard.RULES), States.rules, pass_user_data=True),
+ ],
+ TASK_CHOOSING: [
+ MessageHandler(Filters.regex(TasksKeyboard.CANCEL), States.main_menu, pass_user_data=True),
+ MessageHandler(Filters.text, States.show_task, pass_user_data=True),
+ ],
+ TASK_SHOWN: [
+ MessageHandler(Filters.regex(TaskChosenKeyboard.CANCEL), States.choose_task, pass_user_data=True),
+ MessageHandler(Filters.regex(TaskChosenKeyboard.TYPE_ANSWER), States.type_answer, pass_user_data=True),
+ ],
+ ANSWERING: [
+ MessageHandler(Filters.regex(AnsweringKeyboard.CANCEL), States.show_task, pass_user_data=True),
+ MessageHandler(Filters.text, States.accept_answer, pass_user_data=True),
+ ],
+ ANSWER_RIGHT: [MessageHandler(Filters.text, States.main_menu, pass_user_data=True)],
+ ANSWER_WRONG: [MessageHandler(Filters.text, States.show_task, pass_user_data=True)]
},
fallbacks=[CommandHandler('stop', stop)]
diff --git a/keyboards.py b/keyboards.py
new file mode 100644
index 0000000..5966d90
--- /dev/null
+++ b/keyboards.py
@@ -0,0 +1,69 @@
+import backend_api
+from abc import ABC
+
+
+class Keyboard(ABC):
+ @classmethod
+ def get_keyboard(cls, telegram_id=None):
+ pass
+
+
+class MenuKeyboard(Keyboard):
+ CHOOSE_TASK = "Выбрать задание"
+ TOP_10 = "Топ-10"
+ RULES = "Правила"
+ ADMIN = "/admin"
+
+ @classmethod
+ def get_keyboard(cls, telegram_id=None):
+ return [
+ [cls.CHOOSE_TASK],
+ [cls.TOP_10, cls.RULES],
+ ]
+
+
+class BackToMenuKeyboard(Keyboard):
+ CANCEL = "Вернуться в меню"
+
+ @classmethod
+ def get_keyboard(cls, telegram_id=None):
+ return [[cls.CANCEL]]
+
+
+class TasksKeyboard(Keyboard):
+ CANCEL = "Вернуться в меню"
+
+ @classmethod
+ def get_keyboard(cls, telegram_id=None):
+ tasks = backend_api.get_tasks()
+ titles_keyboard = [[cls.CANCEL]]
+ titles_keyboard.extend([task.get("title")] for task in tasks)
+ return titles_keyboard
+
+
+class TaskChosenKeyboard(Keyboard):
+ TYPE_ANSWER = "Ввести ответ"
+ CANCEL = "Назад"
+
+ @classmethod
+ def get_keyboard(cls, telegram_id=None):
+ return [
+ [cls.TYPE_ANSWER],
+ [cls.CANCEL],
+ ]
+
+
+class ContinueKeyboard(Keyboard):
+ CONTINUE = "Продолжить"
+
+ @classmethod
+ def get_keyboard(cls, telegram_id=None):
+ return [[cls.CONTINUE]]
+
+
+class AnsweringKeyboard(Keyboard):
+ CANCEL = "Отмена"
+
+ @classmethod
+ def get_keyboard(cls, telegram_id=None):
+ return [[cls.CANCEL]]
diff --git a/states.py b/states.py
new file mode 100644
index 0000000..ccd4318
--- /dev/null
+++ b/states.py
@@ -0,0 +1,109 @@
+from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove
+
+from keyboards import (
+ MenuKeyboard, TasksKeyboard, TaskChosenKeyboard, ContinueKeyboard,
+ AnsweringKeyboard,
+)
+from utils import *
+import backend_api
+
+# Typing
+from telegram import Update, User, Bot
+
+
+class States:
+ @staticmethod
+ def wait_for_username(bot: Bot, update: Update, user_data: dict):
+ update.message.reply_text(
+ "По правилам квиза ты не можешь участвовать, если у тебя не указано "
+ "имя пользователя, поэтому укажи его и возвращайся как только это сделаешь!",
+ reply_markup=ReplyKeyboardMarkup([['Я указал имя пользователя']])
+ )
+ return WAIT_FOR_USERNAME
+
+ @staticmethod
+ def main_menu(bot: Bot, update: Update, user_data: dict):
+ user_data["chosen_task"] = None
+
+ update.message.reply_text("Твой счет: 0")
+ update.message.reply_text(
+ "Выбери следующее действие...",
+ reply_markup=ReplyKeyboardMarkup(MenuKeyboard.get_keyboard())
+ )
+
+ return MAIN_MENU
+
+ @staticmethod
+ def choose_task(bot: Bot, update: Update, user_data: dict):
+ user_data["chosen_task"] = None
+
+ update.message.reply_text(
+ "Какую задачу ты хочешь сдать?",
+ reply_markup=ReplyKeyboardMarkup(TasksKeyboard.get_keyboard())
+ )
+ return TASK_CHOOSING
+
+ @staticmethod
+ def top_10(bot: Bot, update: Update, user_data: dict):
+ update.message.reply_text("какая то хуйня")
+ return MAIN_MENU
+
+ @staticmethod
+ def rules(bot: Bot, update: Update, user_data: dict):
+ update.message.reply_text("какая то хуйня")
+ return MAIN_MENU
+
+ @staticmethod
+ def show_task(bot: Bot, update: Update, user_data: dict):
+ if "chosen_task" in user_data and user_data["chosen_task"] is not None:
+ task_title = user_data["chosen_task"]
+ else:
+ task_title = get_input(update, user_data)
+ user_data["chosen_task"] = task_title
+
+ status_code, task = backend_api.get_task(task_title)
+ if status_code != 200:
+ message = "Произошла ошибка в работе квиза. Мы уже работаем над её устранением!"
+ keyboard = ContinueKeyboard.get_keyboard()
+
+ else:
+ message = '\n'.join([
+ f"*{task['title']}*",
+ f"{task['statement']}",
+ "",
+ f"_Теги: {task['tags']}_",
+ ])
+ keyboard = TaskChosenKeyboard.get_keyboard()
+
+ update.message.reply_text(
+ message, parse_mode="Markdown",
+ reply_markup=ReplyKeyboardMarkup(keyboard)
+ )
+
+ return TASK_SHOWN
+
+ @staticmethod
+ def type_answer(bot: Bot, update: Update, user_data: dict):
+ update.message.reply_text(
+ "Вводи свой ответ, я его проверю.",
+ reply_markup=ReplyKeyboardMarkup(AnsweringKeyboard.get_keyboard())
+ )
+ return ANSWERING
+
+ @staticmethod
+ def accept_answer(bot: Bot, update: Update, user_data: dict):
+ answer = get_input(update, user_data)
+ if answer == "хуй":
+ update.message.reply_text(
+ "Ты ввел правильный ответ! Возвращайся к другим задачам",
+ reply_markup=ReplyKeyboardMarkup(ContinueKeyboard.get_keyboard())
+ )
+
+ return ANSWER_RIGHT
+ else:
+ update.message.reply_text(
+ "К сожалению, твой ответ неверный =(",
+ reply_markup=ReplyKeyboardMarkup(ContinueKeyboard.get_keyboard())
+ )
+
+ return ANSWER_WRONG
diff --git a/utils.py b/utils.py
new file mode 100644
index 0000000..a25571e
--- /dev/null
+++ b/utils.py
@@ -0,0 +1,14 @@
+def get_input(update, user_data):
+ if "resuming" in user_data and user_data["resuming"] == True:
+ return user_data["text"]
+ else:
+ return update.message.text
+
+
+(
+ WAIT_FOR_USERNAME,
+ MAIN_MENU, TOP_10, RULES,
+ TASK_CHOOSING, TASK_SHOWN, ANSWERING,
+ ANSWER_RIGHT, ANSWER_WRONG,
+ *_
+) = range(100) \ No newline at end of file