Quiz

Форк
0
/
tg_bot.py 
192 строки · 10.6 Кб
1
from django.contrib import auth
2
from typing import List
3
from telethon.sync import TelegramClient, events
4
from telethon.tl.custom import Button
5

6
from authapp.models import AuthUser
7
from questions.operations import SettingRatingToQuestionByUser, AlreadyRemarkedByThisUser
8

9

10
class BotLogic:
11

12
    def __init__(self, bot: TelegramClient, telegram_id: int, telegram_username: str):
13
        self.bot = bot
14
        self.telegram_id = telegram_id
15
        self.telegram_username = telegram_username
16

17
    @staticmethod
18
    async def _get_answer_from_conv(conv: TelegramClient.conversation, question: str):
19
        """
20
        Функция-обертка над фрагментом диалога
21
        (отправит пользователю уведомление, есил время ожилания ответа истечет)
22
        """
23

24
        timeout_message = 'Время ответа истекло'
25
        try:
26
            await conv.send_message(question)
27
            answer = await conv.get_response()
28
            return answer.text
29
        except Exception as e:
30
            await conv.send_message(timeout_message)
31

32
    @staticmethod
33
    def _press_event(user_id: int) -> events.CallbackQuery:
34
        """
35
        Вспомогательная функция для отслеживания кнопки, нажатой в диалоге
36
        """
37
        return events.CallbackQuery(func=lambda e: e.sender_id == user_id)
38

39
    @staticmethod
40
    def _authorize(username: str, password: str) -> bool:
41
        user = auth.authenticate(username=username, password=password)
42
        if user:
43
            return True
44
        return False
45

46
    async def _merge_accounts(self, conv: TelegramClient.conversation) -> None:
47
        """
48
        Функция связывания аккаунтов:
49
        - в случае успешной авторизации аккаунту в базе добавляется переданный telegram id
50
        """
51

52
        username = await self._get_answer_from_conv(conv=conv, question='Введи имя пользователя')
53
        if username:
54
            password = await self._get_answer_from_conv(conv=conv, question='Введи пароль')
55
            if password:
56
                # Авторизация базовым аккаунтом системы
57
                authorized = self._authorize(username=username, password=password)
58

59
                if authorized:
60
                    # Добавляем аккаунту telegram id
61
                    current_user = AuthUser.objects.get(username=username)
62
                    current_user.telegram_id = self.telegram_id
63
                    current_user.save()
64
                    await conv.send_message(
65
                        f'Аккаунты связаны: login {current_user.username}, telegram_id {current_user.telegram_id}')
66
                else:
67
                    await conv.send_message(f'Неверный логин или пароль')
68

69
    async def _create_account(self, conv: TelegramClient.conversation) -> None:
70
        """
71
        Функция создания аккаунта в системе на основе telegram id
72
        """
73

74
        new_user = AuthUser(telegram_id=self.telegram_id, username=self.telegram_username)
75
        new_user.save()
76
        await conv.send_message(f'Создан новый аккаунт: login {new_user.username}, telegram_id {new_user.telegram_id}')
77

78
    async def send_welcome_back(self):
79
        await self.bot.send_message(self.telegram_id, 'С возвращением!')
80

81
    async def create_or_merge_account(self):
82
        """
83
        Функция создания нового аккаунта на базе telegram id
84
        или связи telegram id с существующим аккаунтом
85
        """
86

87
        async with self.bot.conversation(self.telegram_id) as conv:
88
            buttons = [Button.inline('Да, связать аккаунты', b'merge'),
89
                       Button.inline('Нет, создать аккаунт', b'create')]
90
            await conv.send_message('Аккаунт не найден. Ты уже регистрировался на сайте?', buttons=buttons)
91

92
            try:
93
                press = await conv.wait_event(self._press_event(self.telegram_id))
94
                if press.data == b'merge':
95
                    # связывание аккаунтов
96
                    await self._merge_accounts(conv)
97
                elif press.data == b'create':
98
                    # создание аккаунта
99
                    await self._create_account(conv)
100
            except Exception as e:
101
                pass
102

103
    async def rate_questions(self, rating_process: SettingRatingToQuestionByUser):
104

105
        def get_question_buttons() -> List[Button]:
106
            """
107
            Внутренняя функция для получения актуального состава inline-кнопок.
108
            Их состав меняется в зависимости от наличия замечаний вообще и наличия замечаний текущего пользователя.
109
            :return:
110
            """
111

112
            # Базовый набор кнопок вопроса
113
            question_buttons = [Button.inline('👍', b'like'),
114
                                Button.inline('👎', b'dislike'),
115
                                Button.inline('🔚', b'cancel')]
116

117
            # Если у вопроса есть замечания, добавляем кнопку для их просмотра
118
            remarks = rating_process.get_remarks_for_current_question()
119
            if remarks:
120
                question_buttons.append(Button.inline(f'👀 {len(remarks)}', b'remarks'), )
121

122
            # Если пользователь может добавить замечание (еще не добавлял на этот вопрос), добавляем кнопку для этого
123
            able_to_add_remark = rating_process.ability_to_remark_question()
124
            if able_to_add_remark:
125
                question_buttons.append(Button.inline('✏️', b'add_remark'))
126

127
            return question_buttons
128

129
        while rating_process.current_question:
130
            # Пока существует вопрос для оценки (не все вопросы оценены пользователем)
131

132
            question = rating_process.current_question.question
133
            answer = rating_process.current_question.answer.answer
134
            answer_type = rating_process.current_question.answer.subtype.type.name
135
            answer_subtype = rating_process.current_question.answer.subtype.name
136

137
            # Строка с описанием вопроса для вывода
138
            question_description_string = f'Вопрос: {question}\nОтвет: {answer}\nТип ответа: {answer_type}\nПодтип ответа: {answer_subtype}'
139

140
            async with self.bot.conversation(self.telegram_id) as conv:
141
                await conv.send_message(question_description_string, buttons=get_question_buttons())
142

143
                press = await conv.wait_event(self._press_event(self.telegram_id))
144
                if press.data == b'like':
145
                    # Если пользователь нажал Like, добавляем очко вопросу и переходим к следующему
146
                    rating_process.rate_current_question()
147
                    rating_process.get_next_question()
148

149
                elif press.data == b'dislike':
150
                    # Если пользователь нажал Like, забираем очко вопросу и переходим к следующему
151
                    rating_process.rate_current_question(bad=True)
152
                    rating_process.get_next_question()
153

154
                elif press.data == b'remarks':
155
                    # Выдаем пользователю замечания на оценку
156
                    for remark in rating_process.get_remarks_for_current_question():
157
                        remark_text = remark.text
158
                        remark_buttons = [Button.inline('👍', b'like_remark'),
159
                                          Button.inline('👎', b'dislike_remark'),
160
                                          Button.inline('🔚', b'cancel_remark')]
161
                        await conv.send_message(f'Замечание: {remark_text}', buttons=remark_buttons)
162

163
                        press = await conv.wait_event(self._press_event(self.telegram_id))
164
                        if press.data == b'like_remark':
165
                            # Если пользователь нажал Like, добавляем очко замечанию и переходим к следующему
166
                            rating_process.rate_remark(remark=remark)
167
                            await conv.send_message('Like замечания принят')
168
                            continue
169
                        elif press.data == b'dislike_remark':
170
                            # Если пользователь нажал Dislike, забираем очко у замечания и переходим к следующему
171
                            rating_process.rate_remark(remark=remark, bad=True)
172
                            await conv.send_message('Dislike замечания принят')
173
                            continue
174
                        elif press.data == b'cancel_remark':
175
                            # Если пользователь закончил смотреть замечания, возвращаемся к вопросу
176
                            await conv.send_message(question_description_string, buttons=get_question_buttons())
177
                            break
178

179
                    else:
180
                        # Если замечания закончились, возвращаемся к вопросу
181
                        await conv.send_message(question_description_string, buttons=get_question_buttons())
182

183
                elif press.data == b'add_remark':
184
                    # Если пользователь нажал добавление замечания, берем текст и добавлем замечание
185
                    remark_text = await self._get_answer_from_conv(conv=conv, question='Введи текст замечания')
186
                    if remark_text:
187
                        rating_process.add_remark_to_current_question(text=remark_text)
188

189
                elif press.data == b'cancel':
190
                    # Пользователь закончил оценку вопросов
191
                    await conv.send_message('Оценка вопросов завершена')
192
                    await conv.cancel()
193

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.