Completed
Branch dadyarri/6.1.0/divide-apis (ba35d6)
by Daniil
02:59
created

bot.Bot._get_users_info()   A

Complexity

Conditions 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nop 2
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
import json
29
import os
30
from binascii import Error as binErr
31
from typing import NoReturn
32
from typing import Tuple
33
34
import gspread
35
from oauth2client.service_account import ServiceAccountCredentials
36
37
from keyboard import Keyboards
38
from logger import Logger
39
from vk import Vk
40
41
42
def auth(func):
43
    def wrapper(self):
44
        if not self.current_is_admin():
45
            self.send_gui(text="У тебя нет доступа к этой функции.")
46
        else:
47
            func(self)
48
49
    return wrapper
50
51
52
class Bot:
53
    """
54
    Класс, описывающий объект бота, включая авторизацию в API, и все методы бота.
55
    """
56
57
    def __init__(self) -> None:
58
59
        self.log = Logger()
60
        self.log.log.info("Инициализация...")
61
62
        self.table = os.environ["TABLE_ID"]
63
64
        self.kbs = Keyboards()
65
        self.vk = Vk()
66
67
        # Инициализация дополнительных переменных
68
        self.event = {}
69
        self.admins = os.environ["ADMINS_IDS"].split(",")
70
71
        # Переменные состояния сессии (для администраторов)
72
        self.col = 0
73
74
        # Авторизация в API Google Sheets и подключение к заданной таблице
75
        self.log.log.info("Авторизация в Google Cloud...")
76
        self.scope = [
77
            "https://spreadsheets.google.com/feeds",
78
            "https://www.googleapis.com/auth/drive",
79
        ]
80
        try:
81
            credentials = ServiceAccountCredentials.from_json_keyfile_dict(
82
                keyfile_dict=json.loads(os.environ["GOOGLE_CREDS"]), scopes=self.scope
83
            )
84
        except binErr:
85
            self.log.log.error("Неудача.")
86
        else:
87
            self.gc = gspread.authorize(credentials=credentials)
88
            self.table_auth = self.gc.open_by_key(key=self.table)
89
            self.sh = self.table_auth.get_worksheet(0)
90
            self.sh_sch = self.table_auth.get_worksheet(1)
91
            self.log.log.info("Успех.")
92
93
        self.log.log.info("Инициализация завершена.")
94
95
    def _handle_table(self, col: int) -> Tuple[str, str, str]:
96
        """
97
        Обрабатывает гугл-таблицу и составляет кортеж с данными о должниках
98
        """
99
        men, cash, goal = None, None, None
100
        try:
101
            self.gc.login()
102
            debtor_ids = []
103
            for i in range(5, 38):
104
                if self.sh.cell(i, col).value != self.sh.cell(41, col).value:
105
                    debtor_ids.append(self.sh.cell(i, 3).value)
106
        except gspread.exceptions.APIError as e:
107
            self.log.log.error(
108
                f"[ERROR]: [{e.response.error.code}] – {e.response.error.message}"
109
            )
110
            self._handle_table(col)
111
        except (AttributeError, KeyError, ValueError):
112
            self.log.log.error("Херню ты натворил, Даня!")
113
        else:
114
            debtor_ids = ",".join(debtor_ids)
115
            men = self.generate_mentions(debtor_ids, True)
116
            cash = self.sh.cell(41, col).value
117
            goal = self.sh.cell(4, col).value
118
        if men is not None and cash is not None and goal is not None:
119
            return men, cash, goal
120
        else:
121
            self._handle_table(col)
122
123
    @auth
124
    def get_debtors(self):
125
        """
126
        Призывает должников
127
        """
128
        self.vk.send_message(
129
            msg="Эта команда может работать медленно. Прошу немного подождать.",
130
            pid=self.event.object.from_id,
131
        )
132
        men, cash, goal = self._handle_table(self.col)
133
        msg = f"{men} вам нужно принести по {cash} на {goal.lower()}."
134
        self.vk.send_message(msg=msg, pid=self.vk.cid)
135
        self.send_gui(text="Команда успешно выполнена.")
136
137
    def generate_mentions(self, ids: str, names: bool) -> str:
138
        """
139
        Генерирует строку с упоминаниями из списка идентификаторов
140
        """
141
        ids = list(filter(bool, ids.replace(" ", "").split(",")))
142
        users_info = self.vk.get_users_info(ids)
143
        users_names = [
144
            users_info[i]["first_name"] if names else "!" for i in range(len(ids))
145
        ]
146
        result = (", " if names else "").join(
147
            [f"@id{_id}({users_names[i]})" for (i, _id) in enumerate(ids)]
148
        )
149
        return result
150
151
    def current_is_admin(self) -> bool:
152
        """
153
        Проверяет, является ли текущий пользователь администратором бота
154
        """
155
        return str(self.event.object.from_id) in self.admins
156
157
    def send_gui(self, text: str = "Привет!") -> NoReturn:
158
        """
159
        Отправляет клавиатуру в зависимости от статуса пользователя
160
        """
161
        self.vk.send_message(
162
            msg=text,
163
            pid=self.event.object.from_id,
164
            keyboard=self.kbs.generate_main_menu(self.current_is_admin()),
165
        )
166
167
    def show_msg(self, text: str):
168
        self.vk.send_message(
169
            msg=text, pid=self.event.object.from_id,
170
        )
171