def live_question_handler(message): """ Задать вопрос преподавателю во время лекции. """ if student := Student.objects(user_id=message.chat.id): student = student.first() if student.status == "standby": time_delta = datetime.today() - cfg.FIRST_CLASS_DAY if time_delta.seconds <= cfg.CLASS_DURATION and time_delta.days % cfg.CLASS_OFFSET == 0: if time.time() - student.last_live_q >= cfg.LIVE_Q_DELAY: student.last_live_q = time.time() student.status = "live_question" student.save() bot.send_message(message.chat.id, "🖋️ Введите ваш вопрос:") else: spam_time = int(cfg.LIVE_Q_DELAY - (time.time() - student.last_live_q)) time_msg = f"⏰ Подождите {spam_time} секунд прежде чем еще раз задавать вопрос." bot.send_message(message.chat.id, time_msg) else: bot.send_message( message.chat.id, "⛔ Вопросы можно задавать только во время лекции.") elif student.status == "live_question": bot.send_message(message.chat.id, "🖋️ Введите ваш вопрос:") else: bot.send_message( message.chat.id, "⛔ Прежде чем задавать вопросы, ответьте на вопросы бота.")
def help_message(message): """ Помощь в использовании бота. """ student = Student.objects(user_id=message.from_user.id).first() if student.status == "standby": bot.send_message(message.chat.id, cfg.HELP_MSG, parse_mode="markdown") elif student.status == "registration": bot.send_message(message.chat.id, "️👮🏻♀️ Выберите группу.") elif student.status == "is_ready": answer = "📚 Нажмите кнопку готов, если готовы ответить на вопрос." bot.send_message(message.chat.id, answer) elif student.status == "question": variants = ["🅰️", "🅱️"] answer = f"Я ничего не понимаю на человеческом, но вариант {choice(variants)} " \ "выглядит привлекательно!" bot.send_message(message.chat.id, answer) elif student.status == "live_question": answer = "📚 Задайте свой вопрос:" bot.send_message(message.chat.id, answer) else: bot.send_message(message.chat.id, "Ничем не могу помочь, напишите разработчикам...")
def find_student(user_id, students): """ Поиск студента по user id в рейтинге всех студентов. """ student = Student.objects(user_id=user_id).first() student_info = list(filter(lambda x: x[0] == student.login, students))[0] return student_info, students.index(student_info) + 1
def query_handler_ready(call): """ Высылание вопроса с inline-кнопками тем, кто подтвердил готовность отвечать на вопрос. """ bot.answer_callback_query(call.id) student = Student.objects(user_id=call.message.chat.id).first() if student.status == "is_ready": send_question(student)
def questions_notification(): """ Функция рассылки информации о том, что можно задавать вопросы лектору. """ if date.today().isocalendar()[1] % 2: for student in Student.objects(status__ne="registration"): bot.send_message(student.user_id, "📬") bot.send_message( student.user_id, "Начиная с этого момента вы можете задать вопрос лектору.")
def question_sender(msg): """ Пересылка вопроса преподавателю. """ student = Student.objects(user_id=msg.chat.id).first() bot.send_message(cfg.LECTOR_ID, msg.text) bot.send_message(msg.chat.id, "📮 Ваш вопрос принят!") student.status = "standby" student.save()
def send_stat(message): """ Отправляет сообщение со статистикой при запросе пользователя (командой /stat). """ student = Student.objects(user_id=message.chat.id).first() if student.status == "standby": bot.send_message(message.chat.id, stat.stat_msg(student), parse_mode="markdown") else: bot.send_message( message.chat.id, "⛔️ Прежде чем вызвать статистику, ответьте на вопросы бота.")
def send_confirmation(): """ Отправка сообщения с вопросом о подтверждении готовности отвечать на вопрос. """ for student in Student.objects(): if student.status == "standby" and not len(student.queue) and \ student.queue[0]["days_left"] <= 0: student.status = "is_ready" # Функция возвращает измененный объект студента (имитация передачи по ссылке). # (p.s.: в функции записывается время отправления сообщения с вопросом о готовности). student = send_single_confirmation(student, True) student.save()
def info_message(message): """ Информация о боте. """ student = Student.objects(user_id=message.from_user.id).first() if student.status == "standby": bot.send_message(message.chat.id, cfg.INFO_MSG, parse_mode="markdown") else: bot.send_message( message.chat.id, "⛔️ Прежде чем посмотреть информацию, ответьте на вопросы бота.")
def query_handler_reg(call): """ Обработка нажатия inline-кнопок с выбором группы студентом. """ bot.answer_callback_query(call.id) student = Student.objects(user_id=call.message.chat.id).first() if student.status == "registration": bot.send_message(call.message.chat.id, "✅ Вы успешно зарегистрированы в системе.") student.group = call.data student.status = "standby" student.save()
def authorization(message): """ Выбор учебной группы для авторизации. """ if not Student.objects(user_id=message.chat.id): questions_queue = list() count_missed_questions = \ (datetime.today() - cfg.FIRST_QUESTION_DAY).days * cfg.QUESTION_PORTION if count_missed_questions > 0: questions_queue = [{ "question_day": i, "days_left": 0 } for i in range(count_missed_questions + cfg.QUESTION_PORTION)] login = message.chat.username if message.chat.username is None: login = f"[{generate_r2d2()}](tg://user?id={str(message.chat.id)})" student = Student(user_id=message.chat.id, login=login, status="registration", queue=questions_queue) bot.send_message(message.chat.id, "💬 Укажите свою учебную группу: ", reply_markup=create_markup(cfg.GROUPS_BTNS)) student.save() else: bot.send_message(message.chat.id, "⚠️ Вы уже зарегистрированы в системе.")
def rules_message(message): """ Правила работы бота. """ student = Student.objects(user_id=message.from_user.id).first() if student.status == "standby": bot.send_message(message.chat.id, cfg.RULES_MSG, parse_mode="markdown") elif student.status == "live_question": bot.send_message( message.chat.id, "⛔️ Прежде чем посмотреть правила, задайте свой вопрос.") else: bot.send_message( message.chat.id, "⛔️ Прежде чем посмотреть правила, ответьте на вопросы бота.")
def show_leaderboard(message): """ Вывод лидерборда среди учеников. """ student = Student.objects(user_id=message.from_user.id).first() if student.status == "standby" and int( time.time()) - student.lb_timeout > cfg.LB_TIMEOUT: student.lb_timeout = int(time.time()) student.save() page = create_leaderboard_page(cfg.SCROLL_BTNS[1], message.chat.id) if Student.objects.count() > cfg.LB_PAGE_SIZE: markup = telebot.types.InlineKeyboardMarkup() markup.add( telebot.types.InlineKeyboardButton( text=cfg.SCROLL_BTNS[1], callback_data=cfg.SCROLL_BTNS[1])) bot.send_message(message.chat.id, page, reply_markup=markup, parse_mode="Markdown") else: bot.send_message(message.chat.id, page, parse_mode="Markdown") elif student.status == "standby": bot.send_message( message.chat.id, "⏰ Вы недавно вызывали лидерборд. Повторите через " + f"{cfg.LB_TIMEOUT - (int(time.time()) - student.lb_timeout)} секунд." ) else: bot.send_message( message.chat.id, "⛔️ Прежде чем вызвать лидерборд, ответьте на вопросы бота.")
def update_queue(): """ Функция добавления "вопроса дня". """ today_question_day = (datetime.today() - cfg.FIRST_QUESTION_DAY).days for student in Student.objects(status__ne="registration"): if cfg.DEV_MODE_QUEUE: print( f"Daily update queue of user: {student.login}\nQueue before: {student.queue}" ) # Кол-во дней ожидания у вопросов, которые уже находятся в очереди, уменьшается на 1 # (p.s.: Если кол-во дней ожидания <= 0, то вопрос должен быть отправлен сегодня). need_miss_msg = False for questions in student.queue: questions["days_left"] -= 1 if questions["days_left"] <= -cfg.MISS_DAYS: need_miss_msg = True if need_miss_msg: bot.send_message(student.user_id, cfg.MISS_MESSAGE) # Вопрос дня добавляется на самое первое место for i in range(today_question_day * cfg.QUESTION_PORTION, (today_question_day + 1) * cfg.QUESTION_PORTION): student.queue.insert(0, {"question_day": i, "days_left": 0}) if cfg.DEV_MODE_QUEUE: print(f"Queue after: {student.queue}\n") student.save() # Предложения ответить будут разосланы тем, кто свободен. send_confirmation()
def get_rating(): """ Возвращает отсортированный массив кортежей-пар (nickname, summary). """ rating = dict() questions = Question.objects() for student in Student.objects(): summary = 0 datastore = json.loads(student.data) for question in questions: if len(datastore) > question.day: number_of_answers = 2 if len(datastore[question.day]["right"]) > 2 \ else len(datastore[question.day]["right"]) for i in range(number_of_answers): summary += answer_summary(student, question, i) else: break if student.login not in rating: rating[student.login] = (summary if summary != 0 else 0, student.group) else: i = 1 while student.login + f" ({i})" in rating: i += 1 rating[student.login + f" ({i})"] = (summary if summary != 0 else 0, student.group) if cfg.DEV_MODE_RATING: print("rating:\n", rating, end="\n\n") items = [(elem[0], elem[1][0], elem[1][1]) for elem in rating.items()] return sorted(items, key=lambda x: x[1], reverse=True)
(time.time() - student.last_live_q)) time_msg = f"⏰ Подождите {spam_time} секунд прежде чем еще раз задавать вопрос." bot.send_message(message.chat.id, time_msg) else: bot.send_message( message.chat.id, "⛔ Вопросы можно задавать только во время лекции.") elif student.status == "live_question": bot.send_message(message.chat.id, "🖋️ Введите ваш вопрос:") else: bot.send_message( message.chat.id, "⛔ Прежде чем задавать вопросы, ответьте на вопросы бота.") @bot.message_handler(func=lambda msg: Student.objects(user_id=msg.chat.id). first().status == "live_question") def question_sender(msg): """ Пересылка вопроса преподавателю. """ student = Student.objects(user_id=msg.chat.id).first() bot.send_message(cfg.LECTOR_ID, msg.text) bot.send_message(msg.chat.id, "📮 Ваш вопрос принят!") student.status = "standby" student.save() @bot.callback_query_handler(lambda call: call.data in cfg.GROUPS_BTNS)
def query_handler_questions(call): """ Обработка нажатия inline-кнопок с выбором ответа студентом. Обновление статистики после ответа на вопрос. """ bot.answer_callback_query(call.id) student = Student.objects(user_id=call.message.chat.id).first() if student.status == "question": day = student.queue[0]["question_day"] question = Question.objects(day=day).first() if cfg.DEV_MODE_QUEUE: print( f"Queue of {student.login} after answering the question (before)" + f": {student.queue}", f"Got day {day}", sep='\n', end='\n\n') datastore = json.loads(student.data) # 4 - emoji + вариант ответа (перед самим ответом) student_answer = call.message.text.split("\n")[ cfg.ANSWERS_BTNS[call.data] + 1][4:] correct_answer = question.answers[ cfg.ANSWERS_BTNS[question.correct_answer] - 1] # Очередь очищается от текущего вопроса (и обзаводится новым в некоторых случаях) # внутри handler'ов. if student_answer == correct_answer: datastore[ day], question, student.queue = stat.right_answer_handler( datastore[day], question, (time.time(), student.qtime_start, student.waiting_time), student.queue) bot.send_message(call.message.chat.id, "✅ Верно! Ваш ответ засчитан.") else: datastore[ day], question, student.queue = stat.wrong_answer_handler( datastore[day], question, student.queue) bot.send_message( call.message.chat.id, "❌ К сожалению, ответ неправильный, и он не будет засчитан.") question.save() # Обновить статистику. student.data = json.dumps(datastore) student.qtime_start = time.time() student.waiting_time = 0 if cfg.DEV_MODE_QUEUE: print( f"Queue of {student.login} after answering the question (after) " + f": {student.queue}", end='\n\n') print(f"Check update of the stat: {datastore[day]}\n") # Если есть вопросы, запланированные на сегодня, то еще раз спросить о готовности # и задать вопрос. if len(student.queue) != 0 and student.queue[0]["days_left"] <= 0: if cfg.DEV_MODE_QUEUE: print("Asking one more question\n") send_question(student) else: if cfg.DEV_MODE_QUEUE: print("No more questions for today") student.status = "standby" bot.send_message( call.message.chat.id, "🏁 На сегодня у меня нет больше к тебе вопросов, до завтра!") student.save()