diff --git a/bot.py b/bot.py index 87ce90c..da53bfa 100644 --- a/bot.py +++ b/bot.py @@ -1,5 +1,6 @@ from database.l9 import L9_DB from database.tg import TG_DB +from database.shedule import Shedule_DB from utils.config import * import telegram from tg.keyboards import Keyboard @@ -31,14 +32,47 @@ def initLogger(): class Bot: - def __init__(self, token: str, db: L9_DB, tg_db: TG_DB, limit=150): + def __init__( + self, + token: str, + db: L9_DB, + tg_db: TG_DB, + shedule: Shedule_DB, + limit=150, + ): self.l9lk = db self.tg_db = tg_db + self.shedule = shedule self.tg = telegram.Bot(token) self.limit = limit self.udpate_id = None self.isWork = True + def checkMessages(self): + """Проверка и обработка входящих сообщений""" + + updates = self.tg.get_updates(offset=self.udpate_id, timeout=5) + for update in updates: + self.udpate_id = update.update_id + 1 + if update.message: + query = update.message + tag, l9Id, log = self.tg_db.getTag(query) + logger.info(log) + tgId = query.from_user.id + + if tag == 'not_started': + self.start(query) + + if tag == 'add': + self.addGroup(l9Id, query) + + else: + self.tg.sendMessage( + tgId, + "Ой!", + reply_markup=Keyboard.menu(), + ) + def start(self, query: telegram.Message): """Обработка нового пользователя""" @@ -67,27 +101,54 @@ class Bot: ), ) - def checkMessages(self): - """Проверка и обработка входящих сообщений""" + def addGroup(self, l9Id: int, query: telegram.Message): + """Процесс добавления группы""" - updates = self.tg.get_updates(offset=self.udpate_id, timeout=5) - for update in updates: - self.udpate_id = update.update_id + 1 - if update.message: - query = update.message - tag, l9Id, log = self.tg_db.getTag(query) - logger.info(log) - tgId = query.from_user.id + groupName = query.text + tgId = query.from_user.id - if tag == 'not_started': - self.start(query) + result = self.shedule.checkGroupExists(groupName, l9Id) + if 'OK' in result: + _, groupName, specName = result.split(';') + self.tg_db.changeTag(tgId, 'ready') + self.tg.sendMessage( + tgId, + f'Поздравляем, твоя группа {groupName}, направление "{specName}", подключена!', + reply_markup=Keyboard.menu(), + ) - else: - self.tg.sendMessage( - tgId, - "Ой!", - reply_markup=Keyboard.menu(), - ) + elif result == 'Exists': + self.tg.sendMessage( + tgId, + '❗️Эта группа у тебя уже подключена', + reply_markup=Keyboard.cancel(), + ) + + elif result == 'Error': + self.tg.sendMessage( + tgId, + '❗У меня этой группы пока нет, а сайте возникла какая-то ошибка.\nПопробуйте позже', + reply_markup=Keyboard.cancel(), + ) + + elif 'ssau.ru' in result: + self.tg_db.changeTag(tgId, f'conf_{result[21:]}') + self.tg.sendMessage( + tgId, + ( + 'Такой группы у меня пока нет в базе, но она есть на сайте\n' + f'{result}\n' + 'Проверь, пожалуйста, что это твоя группа и нажми кнопку\n' + ), + reply_markup=Keyboard.confirm(), + ) + + else: + self.tg.sendMessage( + tgId, + 'К сожалению, такой группы нет ни в моей базе, ни на сайте университета :(', + reply_markup=Keyboard.cancel(), + ) if __name__ == "__main__": @@ -97,7 +158,10 @@ if __name__ == "__main__": config = loadJSON("config") l9lk = L9_DB(**config['sql']) tg_db = TG_DB(l9lk) - bot = Bot(config['tg']['token'], l9lk, tg_db, config['tg']['limit']) + shedule = Shedule_DB(l9lk) + bot = Bot( + config['tg']['token'], l9lk, tg_db, shedule, config['tg']['limit'] + ) logger.info("Bot ready!") diff --git a/database/asql.py b/database/a_sql.py similarity index 96% rename from database/asql.py rename to database/a_sql.py index 371df0c..65b6a7d 100644 --- a/database/asql.py +++ b/database/a_sql.py @@ -25,6 +25,7 @@ class Database: query.lower().find("drop") == -1 and query.lower().find("truncate") == -1 ): + print(query) self.cursor.execute(query) if commit: self.database.commit() @@ -42,7 +43,7 @@ class Database: with open(f'database/{filename}.sql', encoding='utf-8') as f: query = f.read() - return self.execute(query, commit) + return self.execute(query, commit).fetchall() def initDatabase(self, name: str): """Создать базу данных, если таковая отсутствует, @@ -51,8 +52,8 @@ class Database: :name: название базы данных """ - self.execute(f"CREATE DATABASE IF NOT EXISTS {name}") - self.execute(f"USE {name}") + self.execute(f"CREATE DATABASE IF NOT EXISTS {name};") + self.execute(f"USE {name};") def initTable(self, name: str, head: str): """Создать таблицу, если таковая отсутствует diff --git a/database/a_ssau_parser.py b/database/a_ssau_parser.py new file mode 100644 index 0000000..f17b26f --- /dev/null +++ b/database/a_ssau_parser.py @@ -0,0 +1,34 @@ +import requests +from bs4 import BeautifulSoup +from ast import literal_eval +import time +import logging +logger = logging.getLogger('bot') + +def findInRasp(req) -> dict: + """Поиск группы (преподавателя) в расписании""" + logger.debug(f'Find {req}') + + rasp = requests.Session() + rasp.headers['User-Agent'] = 'Mozilla/5.0' + hed = rasp.get("https://ssau.ru/rasp/") + if hed.status_code == 200: + soup = BeautifulSoup(hed.text, 'lxml') + csrf_token = soup.select_one('meta[name="csrf-token"]')['content'] + else: + return 'Error' + + time.sleep(1) + + rasp.headers['Accept'] = 'application/json' + rasp.headers['X-CSRF-TOKEN'] = csrf_token + result = rasp.post("https://ssau.ru/rasp/search", data = {'text':req}) + if result.status_code == 200: + num = literal_eval(result.text) + else: + return 'Error' + + if len(num) == 0: + return None + else: + return num[0] \ No newline at end of file diff --git a/database/database.sql b/database/database.sql deleted file mode 100644 index 9d6a362..0000000 --- a/database/database.sql +++ /dev/null @@ -1,30 +0,0 @@ -CREATE DATABASE IF NOT EXISTS `l9_db`; -USE `l9_db`; - --- Пользователи системы -CREATE TABLE IF NOT EXISTS `users` ( - `l9Id` bigint NOT NULL, - -- Идентификатор пользователя системы - - PRIMARY KEY (`l9Id`) - ); - --- Сведения о пользователях бота Telegram -CREATE TABLE IF NOT EXISTS `tg_users` ( - `l9Id` bigint NOT NULL, - -- Идентификатор пользователя системы - - `tgId` bigint NOT NULL, - -- ID пользователя в Telegram - - `name` TEXT, - -- (optional) Имя пользователя в Telegram - - `posTag` varchar(30) DEFAULT 'not_started', - -- Позиция пользователя в диалоге с ботом: - -- (default) not_started - не вступил в диалог - -- started - вступил в диалог - - PRIMARY KEY (`l9Id`), - CONSTRAINT `l9_tg` FOREIGN KEY (`l9Id`) REFERENCES `users` (`l9Id`) ON DELETE CASCADE ON UPDATE CASCADE - ); \ No newline at end of file diff --git a/database/l9.py b/database/l9.py index 97ea124..af74c37 100644 --- a/database/l9.py +++ b/database/l9.py @@ -1,4 +1,4 @@ -from .asql import Database +from .a_sql import Database class L9_DB: diff --git a/database/shedule.py b/database/shedule.py new file mode 100644 index 0000000..93a7415 --- /dev/null +++ b/database/shedule.py @@ -0,0 +1,60 @@ +from .l9 import L9_DB +from .a_ssau_parser import * + + +class Shedule_DB: + """Класс взаимодействия с базой расписания""" + + def __init__(self, l9lk: L9_DB): + self.l9lk = l9lk + self.db = l9lk.db + self.db.executeFile('shedule') + + def checkGroupExists(self, groupName: str, l9Id: str) -> str: + """Проверка наличия группы в БД и на сайте, а также проверка, + что пользователь ещё не подключен к группе + + 'OK;N_группы;Назв-е_спец-сти' - пользователь успешно подключен \n + 'Exists' - пользователь уже подключен к данной группе \n + 'ssau.ru/groupId=*' - группа отсутствует в базе, но есть на сайте \n + 'Error' - ошибка на стороне сайта + 'Empty' - группа нигде не обнаружена + """ + + groupIdInDB = self.l9lk.db.get( + 'groups', + f'groupName LIKE "%{groupName}%"', + ['groupId', 'groupName', 'specName'], + ) + + if groupIdInDB != []: + groupIdInDB = groupIdInDB[0] + + exists = self.l9lk.db.get( + 'groups_users', + f'l9Id = {l9Id} AND groupId = {groupIdInDB[0]}', + ) + if exists == []: + self.l9lk.db.insert( + 'groups_users', + {'l9Id': l9Id, 'groupId': groupIdInDB[0]}, + ) + return f'OK;{groupIdInDB[1]};{groupIdInDB[2]}' + + else: + return 'Exists' + + else: + group = findInRasp(groupName) + if group != None: + group_url = f'ssau.ru/{group["url"][2:]}' + gr_num = group["text"] + groupId = group["id"] + + return group_url + + elif group == 'Error': + return 'Error' + + else: + return 'Empty' diff --git a/database/shedule.sql b/database/shedule.sql new file mode 100644 index 0000000..5a85297 --- /dev/null +++ b/database/shedule.sql @@ -0,0 +1,45 @@ +-- Учебные группы +CREATE TABLE IF NOT EXISTS `groups` ( + `groupId` bigint NOT NULL, + -- Идентификатор группы, соответствует ID в расписании на сайте + + `groupName` varchar(4) DEFAULT '0000', + -- Учебный номер (наименование) группы + + `specName` text, + -- Код и название направления подготовки + + PRIMARY KEY (`groupId`) + ); + + +-- Сведения о группах пользователя и настройках каждой группы +CREATE TABLE IF NOT EXISTS `groups_users` ( + `guId` bigint NOT NULL AUTO_INCREMENT, + -- (service) Идентификатор сведения, устанавливается автоматически + + `l9Id` bigint NOT NULL, + -- Идентификатор пользователя системы + + `groupId` bigint NOT NULL, + -- ID группы, которой принадлежит пользователь + + `firstTime` int DEFAULT '45', + -- Время в минутах, за которое приходит уведомление о начале занятий + + `firstNote` tinyint DEFAULT '1', + -- Состояние уведомлений о начале занятий: + -- 0 - выключены + -- ненулевое значение - включены + + `nextNote` tinyint DEFAULT '1', + -- Состояние уведомлений о первой или следующей паре + -- 0 - выключены + -- ненулевое значение - включены + + PRIMARY KEY (`guId`), + KEY `guid_idx` (`l9Id`), + KEY `gid_idx` (`groupId`), + CONSTRAINT `gr_gu` FOREIGN KEY (`groupId`) REFERENCES `groups` (`groupId`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `l9_gu` FOREIGN KEY (`l9Id`) REFERENCES `users` (`l9Id`) ON DELETE CASCADE ON UPDATE CASCADE + ); \ No newline at end of file diff --git a/database/tg.sql b/database/tg.sql index 38f161c..5a90a8d 100644 --- a/database/tg.sql +++ b/database/tg.sql @@ -11,7 +11,7 @@ CREATE TABLE IF NOT EXISTS `tg_users` ( `posTag` varchar(30) DEFAULT 'not_started', -- Позиция пользователя в диалоге с ботом: - -- (default) not_started - только что в диалог + -- (default) not_started - только что вступил в диалог -- add - добавляет группу PRIMARY KEY (`l9Id`),