def _send_png_sticker( user_session: UserSession, message: Optional[str]) -> Sequence[Union[Answer, CloseSession]]: """ Возвращает финальное сообщение. Опционально возвращает путь к созданному стикеру на диске """ alias = _send_png_sticker.alias if message in USER_COMMANDS['SERVICE_COMMANDS']: if message == 'step_back': return (Answer(text=get_message( step=alias, message_type='unsupported_command')), ) else: message = 'НЕТ' message = message.strip().upper() if message not in ('ДА', 'НЕТ'): return (Answer(text='Пришлите "Да" либо "Нет"'), ) else: if message == 'ДА': document = (Answer(content_type='document', content_path=user_session.file_path), ) else: document = tuple() return document + ( CloseSession(), Answer(text=get_message(step=alias, message_type='end_message')), )
def _handle_admin_commands(self, user_id: int, command: str) -> Answer: user_role = get_user_role(user_id) if user_role != 'admin': return Answer(text=self._get_default_answer('unsupported_command')) if command == 'create_deep_link': deep_link = create_deep_link(user_id) return Answer(text=f'Ссылка успешно создана.\n{deep_link}')
def command_handler(self, chat_id: int, command: str) -> Sequence[Union[Answer, CloseSession]]: """ Промежуточный обработчик для команд из бота """ command = command[1:] if command in USER_COMMANDS['RESET_COMMANDS']: self.close_session(chat_id) return self._get_init_message() if command in USER_COMMANDS['START_COMMANDS']: try: user_session = self._create_session(chat_id) except NotClosedUserSession as exc: return (Answer(text=str(exc)), ) else: return self._user_session_handler.handle_session(user_session) if command in USER_COMMANDS['SERVICE_COMMANDS']: try: user_session = self._get_session(chat_id) except NotCreatedUserSession as exc: return (Answer(text=str(exc)), ) else: return self._user_session_handler.handle_session( user_session, message=command)
def _language_test_execution( session: UserSession, message: Optional[str] ) -> Union[Answer, Tuple[Answer, Answer], Tuple[Answer, CloseSession]]: if message is None: current_question = session.language_test.current_question question = session.language_test.get_current_question() fmt_question, keyboard = _get_formatted_question( question, current_question + 1) number_questions = len(session.language_test.questions) start_message = (f'Давайте начнём!\n' f'Выберите ваш вариант ответа вместо пропусков.\n' f'Тест состоит из {number_questions} вопросов.') return (Answer(text=start_message), Answer(text=fmt_question, keyboard=keyboard)) language_test = session.language_test if _is_correct_question_answer(message, language_test.number_answers): return _process_user_answer(session.user_id, language_test, int(message)) else: max_number = language_test.number_answers keyboard = get_keyboard([i for i in range(1, max_number + 1)], row_width=2) return Answer(text=f'Ответ д. б. в диапазоне от 1 до {max_number}', keyboard=keyboard)
def _handle_information_commands(self, user_id: int, command: str) -> Answer: if get_user_role(user_id) == 'user': return Answer(text=self._get_default_answer('unsupported_command')) if command == 'languages_list': return Answer(text=get_formatted_languages_list()) if command == 'test_types_list': return Answer(text=get_formatted_test_types_list())
def _set_splitting_numbers(user_session: UserSession, message: Optional[str]) -> Sequence[Answer]: """ Проверяет правильность присланной разбивки текста по строкам, обновляет разбивку в экземпляре пользовательской сессии """ alias = _set_splitting_numbers.alias if user_session.data_class.splitting_numbers is None: user_session.data_class.splitting_numbers = _set_splitting_numbers_helper( user_session.data_class.text) if message is None and len(user_session.data_class.splitting_numbers) > 1: splitting_numbers = _get_str_splitting_numbers( user_session.data_class.splitting_numbers) split_pattern = _get_split_pattern( len(user_session.data_class.splitting_numbers)) return (Answer(text=get_message(step=alias, splitting_numbers=splitting_numbers, split_pattern=split_pattern)), ) if message in USER_COMMANDS['SERVICE_COMMANDS'] or ( message is None and len(user_session.data_class.splitting_numbers) == 1): message = message or 'next_step' handler.update_current_step(user_session, command=message) return handler.handle_session(user_session) try: user_session.data_class.splitting_numbers = _get_splitting_numbers( message, user_session.data_class.splitting_numbers) except NotCorrectSplittingText as exc: return (Answer(text=str(exc)), ) else: handler.update_current_step(user_session) return handler.handle_session(user_session)
def _handle_language_test_creator_commands( self, user_id: int, command: str, date: datetime ) -> Union[Answer, Tuple[Answer, CloseSession]]: handler_alias = 'language_test_creator_session_handler' user_role = get_user_role(user_id) if user_role == 'user': return Answer(text=self._get_default_answer('unsupported_command')) handler = self._get_handler(handler_alias) handler.alias = handler_alias try: session = self._create_session(user_id, date, handler) except UnclosedSessionError as e: return Answer(text=str(e)) return handler.handle_session(session, command)
def _handle_text( self, user_id: int, text: str ) -> Union[Answer, Tuple[Answer, Answer], Tuple[Answer, CloseSession]]: if user_id in self._sessions: handler_alias = self._sessions[user_id].handler_alias handler = self._get_handler(handler_alias) return handler.handle_session(self._sessions[user_id], message=text) return Answer(text=self._get_default_answer('invalid_message'))
def handle_document( self, user_id: int, document: io.BytesIO ) -> Union[Answer, Tuple[Answer, CloseSession]]: if user_id in self._sessions: handler_alias = self._sessions[user_id].handler_alias handler = self._get_handler(handler_alias) return handler.handle_session(self._sessions[user_id], message=document) return Answer(text=self._get_default_answer('invalid_message'))
def _handle_user_commands( self, user_id: int, date: datetime ) -> Answer: handler_alias = 'user_session_handler' handler = self._get_handler(handler_alias) try: session = self._create_session(user_id, date, handler) except UnclosedSessionError as e: return Answer(text=str(e)) return handler.handle_session(session)
def _set_background_color(user_session: UserSession, message: Optional[str]) -> Sequence[Answer]: """ Проверяет правильность присланного RGB-кода цвета для заливки фона стикера, обновляет RGB-код цвета в экземпляре пользовательской сессии """ alias = _set_background_color.alias if message is None: return (Answer(text=get_message(step=alias), keyboard=kb_colors), ) if message in USER_COMMANDS['SERVICE_COMMANDS']: handler.update_current_step(user_session, command=message) return handler.handle_session(user_session) try: user_session.data_class.background_color = _set_color_helper(message) except NotCorrectRGBCode as exc: return (Answer(text=str(exc), keyboard=kb_colors), ) else: handler.update_current_step(user_session) return handler.handle_session(user_session)
def _set_text(user_session: UserSession, message: Optional[str]) -> Sequence[Answer]: """ Проверяет правильность присланного текста для стикера, обновляет текст в экземпляре пользовательской сессии """ alias = _set_text.alias if message is None: return (Answer(text=get_message(step=alias)), ) if message in USER_COMMANDS['SERVICE_COMMANDS']: return (Answer( text=get_message(step=alias, message_type='unsupported_command')), ) if emoji_count(message) > 0: return (Answer( text='Текст стикера не может содержать в себе смайлики, ' 'пришлите текст без смайликов'), ) user_session.data_class.text = message handler.update_current_step(user_session) return handler.handle_session(user_session)
def _select_test_type(session: UserSession, message: Optional[str]) -> Answer: if message is None or not is_supported_test_type(message): test_types_list = get_test_types(session.language_id) keyboard = get_keyboard(test_types_list, row_width=1) text = 'Выберите один из доступных типов теста.' if message is not None: text = f'Вы прислали неверный тип теста\n{text}' return Answer(text=text, keyboard=keyboard) else: session.test_type_id = get_test_type_id(message.strip()) _ush.update_current_step(session) return _ush.handle_session(session)
def _select_language(session: UserSession, message: Optional[str]) -> Answer: if message is None or not is_supported_language(message): current_languages = get_current_languages() keyboard = get_keyboard(current_languages, row_width=1) text = 'Выберите один из доступных языков.' if message is not None: text = f'Вы прислали неподдерживаемый язык.\n{text}' return Answer(text=text, keyboard=keyboard) else: session.language_id = get_language_id(message.strip()) _ush.update_current_step(session) return _ush.handle_session(session)
def _get_init_message() -> Sequence[Answer]: """ Возвращает стартовое сообщение """ return (Answer(text='Начните работу с ботом с помощью одной из ' 'доступных стартовых команд.\n\n' 'Стартовые команды:\n' '/create_sticker - начать создание стикера\n\n' 'Сервисные команды:\n' '/next_step - пропустить текущий шаг (будет ' 'установлено значение по умолчанию)\n' '/step_back - вернуться к предыдущему шагу\n' '/reset - начать создание стикера сначала'), )
def _get_test_result( language_test: LanguageTest) -> Tuple[Answer, CloseSession]: test_score, wrong_answers = _get_test_score(language_test) number_questions = len(language_test.questions) percent_test_score = test_score / number_questions * 100 number_right_answers = _get_fmt_count_right_answers(test_score) final_message = (f'Вы ответили верно на {number_right_answers} из ' f'{number_questions}. Ваш результат: ' f'{percent_test_score:.0f} %') if percent_test_score < 100: fmt_wrong_answers = _get_fmt_wrong_answers(wrong_answers) final_message = f'{final_message}\n{fmt_wrong_answers}' return (Answer(text=final_message), CloseSession())
def _process_user_answer( user_id: int, language_test: LanguageTest, answer: int) -> Union[Answer, Tuple[Answer, CloseSession]]: language_test.register_answer(answer) if len(language_test.questions) - 1 > language_test.current_question: language_test.current_question += 1 fmt_question, keyboard = _get_formatted_question( language_test.get_current_question(), language_test.current_question + 1) return Answer(text=fmt_question, keyboard=keyboard) else: values = generate_answer_values(user_id, language_test) insert_user_answers(values) return _get_test_result(language_test)
def message_handler(self, chat_id: int, message: str) -> Sequence[Union[Answer, CloseSession]]: """ Промежуточный обработчик для сообщений из бота """ if chat_id not in self._sessions: return (Answer( text='Для начала работы с ботом используйте одну из ' 'доступных стартовых команд.\n\n' 'Стартовые команды:\n' '/create_sticker - начать создание стикера'), ) user_session = self._sessions[chat_id] return self._user_session_handler.handle_session( user_session=user_session, message=message)
def _set_font(user_session: UserSession, message: Optional[str]) -> Sequence[Answer]: """ Проверяет правильность присланного номера шрифта, обновляет шрифт в экземпляре пользовательской сессии """ alias = _set_font.alias if message is None: return (Answer(content_type='photo', content_path=EXAMPLE_FONTS_PATH, content_location='telegram_server', text=get_message(step=alias), keyboard=kb_fonts_numbers), ) if message in USER_COMMANDS['SERVICE_COMMANDS']: handler.update_current_step(user_session, command=message) return handler.handle_session(user_session) try: _set_font_helper(message, user_session) except NotCorrectFontNumber as exc: return (Answer(text=str(exc)), ) else: handler.update_current_step(user_session) return handler.handle_session(user_session)
def _send_sticker(user_session: UserSession, message: Optional[str]) -> Sequence[Answer]: """ Создаёт и сохраняет изображение, возращает путь к изображению """ alias = _send_sticker.alias if message is None: text = _get_splitting_text(user_session.data_class.text, user_session.data_class.splitting_numbers) sticker = create_sticker( text=text, background_color=user_session.data_class.background_color, font_name=user_session.data_class.font_name, font_color=user_session.data_class.font_color, picture_width=512, picture_height=512) file_name = f'{_get_date_formatted(user_session.created)}_{_generate_filename()}.png' user_session.file_path = os.path.join(CONTENT_DIR, file_name) sticker.save(user_session.file_path) handler.update_current_step(user_session) return ( Answer(content_type='photo', content_path=user_session.file_path), Answer(text=get_message(step=alias), keyboard=kb_yesno), )
def _handle_command( self, user_id: int, text: str, date: datetime ) -> Union[Answer, Tuple[Answer, Answer]]: command, deep_link = self._get_bot_command(text) if command in COMMANDS['start_commands']: return self._handle_start_commands(user_id, command, date, deep_link) if command in COMMANDS['user_commands']: return self._handle_user_commands(user_id, date) if command in COMMANDS['test_creator_commands']: return self._handle_language_test_creator_commands(user_id, command, date) if command in COMMANDS['information_commands']: return self._handle_information_commands(user_id, command) if command in COMMANDS['admin_commands']: return self._handle_admin_commands(user_id, command) return Answer(text=self._get_default_answer('unsupported_command'))
def _get_start_message(command: str, role: str) -> Answer: start_message = ( 'Привет!\n' 'С помощью этого бота вы сможете проверить ваши знания грамматики ' 'и лексики иностранного языка.\n\n' ) user_commands = 'Для того, чтобы начать тест, введите /begin_test.\n' test_creator_commands = ( 'Для того, чтобы получить список языков, введите /languages_list.\n' 'Для того, чтобы получить список доступных типов тестов, введите ' '/test_types_list.\nДля того, чтобы добавить вопросы, введите ' '/add_questions, чтобы обновить - /update_questions, чтобы удалить ' '- /delete_questions.\n' ) admin_commands = 'Для того, чтобы создать deeplink, введите /create_deep_link.' if role == 'user': text = user_commands elif role == 'test_creator': text = f'{user_commands}{test_creator_commands}' else: text = f'{user_commands}{test_creator_commands}{admin_commands}' if command == 'start': text = f'{start_message}{text}' return Answer(text=text)
def _get_start_message(session: LanguageTestCreatorSession, message: str) -> Answer: session.command = message text = _get_start_message_text(message) _ltcsh.update_current_step(session) return Answer(text=text)
def _handle_language_test( session: LanguageTestCreatorSession, message: [str, io.BytesIO]) -> Tuple[Answer, CloseSession]: answer_text = _handle_test_creator_message(session.command, session.user_id, message) return (Answer(text=answer_text), CloseSession())