Passed
Push — master ( 7083c9...302a05 )
by Daniil
01:56
created

bot.Bot.is_admin()   A

Complexity

Conditions 1

Size

Total Lines 9
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 2
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
"""
2
:project: ralph
3
:version: see VERSION.txt
4
:authors: dadyarri, 6a16ec
5
:contact: https://vk.me/dadyarri, https://vk.me/6a16ec
6
:license: MIT
7
8
:copyright: (c) 2019 - 2020 dadyarri, 6a16ec
9
10
Info about logging levels:
11
12
DEBUG: Detailed information, typically of interest only when diagnosing problems.
13
14
INFO: Confirmation that things are working as expected.
15
16
WARNING: An indication that something unexpected happened, or indicative of some
17
problem in the near future (e.g. ‘disk space low’). The software is still working
18
as expected.
19
20
ERROR: Due to a more serious problem, the software has not been able to perform
21
some function.
22
23
CRITICAL: A serious error, indicating that the program itself may be unable to
24
continue running.
25
26
"""
27
28
29
import os
30
import random
31
from typing import List
32
from typing import NoReturn
33
34
import requests
35
import vk_api
36
from vk_api.bot_longpoll import VkBotEventType
37
38
from database import Database
39
from keyboard import Keyboards
40
from logger import init_logger
41
from vkbotlongpoll import RalphVkBotLongPoll
42
43
44
class Bot:
45
    """Класс, описывающий объект бота, включая авторизацию в API, и все методы бота.
46
    
47
    Attributes:
48
        token (str): Токен доступа к сообществу ВКонтакте
49
        user_token (str): Токен доступа пользователя-администратора ВКонтакте (используется для обновления статуса сообщества)
50
        gid (str): Идентификатор сообщества ВКонтакте
51
        cid (str): Идентификатор активной беседы (используется в рассылке расписания)
52
        kbs (Keyboards): Объект класса Keyboards, содержащий генераторы клавиатур
53
        db (Database): Объект класса Database, инициирующий подключение к базе данных
54
        admins (List[str]): Список идентификаторов ВКонтакте пользователей, имеющих доступ администратора бота 
55
    
56
    Example:
57
        .. code-block:: python
58
        
59
            from bot import Bot
60
        
61
            bot = Bot()
62
        
63
    Todo:
64
        * Почистить атрибуты
65
    """
66
67
    def __init__(self) -> None:
68
69
        log = init_logger()
70
71
        log.info("Инициализация...")
72
73
        self.token = os.environ["VK_TOKEN"]
74
        self.user_token = os.environ["VK_USER_TOKEN"]
75
        self.gid = os.environ["GID_ID"]
76
        self.cid = os.environ["CID_ID"]
77
78
        self.kbs = Keyboards()
79
80
        self.db = Database(os.environ["DATABASE_URL"])
81
82
        log.info("Авторизация ВКонтакте...")
83
        try:
84
            self.bot_session = vk_api.VkApi(token=self.token, api_version="5.103")
85
            self.user_session = vk_api.VkApi(token=self.user_token, api_version="5.103")
86
        except vk_api.exceptions.AuthError:
87
            log.exception("Неудача. Ошибка авторизации.")
88
        else:
89
            try:
90
                self.bot_vk = self.bot_session.get_api()
91
                self.user_vk = self.user_session.get_api()
92
                self.longpoll = RalphVkBotLongPoll(
93
                    vk=self.bot_session, group_id=self.gid
94
                )
95
            except requests.exceptions.ConnectionError:
96
                log.exception("Неудача. Превышен лимит попыток подключения.")
97
            except vk_api.exceptions.ApiError:
98
                log.exception("Неудача. Ошибка доступа.")
99
            else:
100
                log.info("Успех.")
101
102
        # Инициализация дополнительных переменных
103
        self.admins = os.environ["ADMINS_IDS"].split(",")
104
105
        # Переименование обрабатываемых типов событий
106
        self.NEW_MESSAGE = VkBotEventType.MESSAGE_NEW
107
108
        log.info(f"Беседа... {'Тестовая' if self.cid.endswith('1') else 'Основная'}")
109
110
        log.info("Инициализация завершена.")
111
112
    def send_message(
113
        self,
114
        msg: str,
115
        pid: int = None,
116
        keyboard: str = "",
117
        attachments: str = None,
118
        user_ids: str = None,
119
        forward: str = "",
120
    ) -> NoReturn:
121
122
        """Обёртка над API ВКонтакте, отправляющая сообщения
123
        
124
        Arguments:
125
            msg: Текст отправляемого сообщения
126
            pid: Идентификатор пользователя/беседы/сообщества получателя сообщения (*не нужен, если указан user_ids*)
127
            keyboard: JSON-подобная строка со встроенной клавиатурой
128
            attachments: Вложения к сообщению (**не работает**)
129
            user_ids: Перечень адресатов для отправки одного сообщения (*не нужен, если указан pid*)
130
            forward: Перечень идентификаторов сообщений для пересылки
131
        """
132
133
        log = init_logger()
134
135
        try:
136
            self.bot_vk.messages.send(
137
                peer_id=pid,
138
                random_id=random.getrandbits(64),
139
                message=msg,
140
                keyboard=keyboard,
141
                attachments=attachments,
142
                user_ids=user_ids,
143
                forward_messages=forward,
144
            )
145
146
        except vk_api.exceptions.ApiError as e:
147
            log.exception(msg=e.__str__())
148
149
    def get_users_names(self, ids: list) -> List[str]:
150
        """Получает имена пользователей  из базы данных по идентификаторам из списка
151
        
152
        Arguments:
153
            ids: Список идентификаторов для формирования списка имён
154
        
155
        Returns:
156
            List[str]: Список имён пользователей
157
        """
158
        user_ids = [
159
            self.db.query(f"SELECT id FROM users WHERE vk_id={i}")[0][0] for i in ids
160
        ]
161
        user_names = [
162
            self.db.query(f"SELECT first_name FROM users_info WHERE user_id={i}")[0][0]
163
            for i in user_ids
164
        ]
165
        return user_names
166
167
    def generate_mentions(self, ids: str, names: bool) -> str:
168
        """Генерирует строку с упоминаниями из списка идентификаторов
169
        
170
        Arguments:
171
            ids: Перечень идентификаторов пользователей
172
            names: Флаг, указывающий на необходимость использования имён
173
            
174
        Returns:
175
            str: Сообщение, упоминающее выбранных пользователей
176
        """
177
        ids = ids.replace(" ", "").split(",")[:-1]
178
        if names:
179
            users_names = self.get_users_names(ids)
180
        else:
181
            users_names = ["!"] * len(ids)
182
        result = (", " if names else "").join(
183
            [f"@id{_id}({users_names[i]})" for (i, _id) in enumerate(ids)]
184
        )
185
        return result
186
187
    def is_admin(self, _id: int) -> bool:
188
        """Проверяет, является ли пользователь администратором бота
189
190
        Arguments:
191
            _id: Идентификатор пользователя для проверки привелегий
192
        Returns:
193
            bool: Флаг, указывающий на принадлежность текущего пользователя к касте Администраторов
194
        """
195
        return str(_id) in self.admins
196
197
    def send_gui(self, pid: int, text: str = "Привет!") -> NoReturn:
198
        """Отправляет клавиатуру главного меню
199
        
200
        Arguments:
201
            pid: Получатель клавиатуры
202
            text: Сообщение, вместе с которым будет отправлена клавиатура
203
        """
204
        self.send_message(
205
            msg=text, pid=pid, keyboard=self.kbs.generate_main_menu(self.is_admin(pid)),
206
        )
207
208
    def update_version(self):
209
        """
210
        Обновляет версию в статусе группы с ботом
211
        """
212
        log = init_logger()
213
214
        log.info("Обновление версии в статусе группы...")
215
        try:
216
            with open("VERSION.txt", "r") as f:
217
                v = f"Версия: {f.read()}"
218
            self.user_vk.status.set(text=v, group_id=self.gid)
219
        except vk_api.exceptions.ApiError as e:
220
            log.error(f"Ошибка {e.__str__()}")
221
        else:
222
            log.info(f"Успех.")
223