diff --git a/code/sql.py b/code/sql.py index f1c1d8f..16ec067 100644 --- a/code/sql.py +++ b/code/sql.py @@ -1,5 +1,16 @@ +import logging import sqlite3 +# номер счётчика в обработчиках → колонка таблицы статистики +COLUMNS = {1: "mes", 2: "rep", 3: "com", 4: "url", 5: "med", 6: "sti", 7: "voi"} + +# Таблица на каждый чат — так устроены рабочие базы бота; имя таблицы — внутренний числовой id чата, +# поэтому перед подстановкой в SQL он приводится к int + + +def table(group_id): + return f"[{int(group_id)}]" + class Base: def __init__(self, database): @@ -7,55 +18,66 @@ class Base: self.connection = sqlite3.connect(database) self.cursor = self.connection.cursor() + # на новой установке таблиц ещё нет + stat_columns = ", ".join(f"{c} INTEGER" for c in COLUMNS.values()) + with self.connection: + self.cursor.execute("CREATE TABLE IF NOT EXISTS work (group_id INTEGER NOT NULL, " + "state BOOLEAN NOT NULL DEFAULT (True))") + self.cursor.execute(f"CREATE TABLE IF NOT EXISTS stat (group_id INTEGER NOT NULL, {stat_columns})") + self.cursor.execute(f"CREATE TABLE IF NOT EXISTS month (group_id INTEGER NOT NULL, {stat_columns})") + self.cursor.execute("CREATE TABLE IF NOT EXISTS edit (user_id INTEGER NOT NULL)") + self.cursor.execute("CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)") + + def get_meta(self, key): + with self.connection: + row = self.cursor.execute("SELECT value FROM meta WHERE key = ?", (key,)).fetchone() + return row[0] if row else None + + def set_meta(self, key, value): + with self.connection: + self.cursor.execute("INSERT INTO meta (key, value) VALUES (?, ?) " + "ON CONFLICT(key) DO UPDATE SET value = excluded.value", (key, value)) + + def reset_month(self): + """Обнуляем месячную статистику групп""" + with self.connection: + self.cursor.execute("UPDATE month SET " + ", ".join(f"{c} = 0" for c in COLUMNS.values())) + + def increment(self, name, group_id, var_id): + """Счётчик +1 одним запросом: раньше SELECT и UPDATE с ручным +1 теряли сообщения, + пришедшие одновременно""" + column = COLUMNS[var_id] + try: + with self.connection: + self.cursor.execute(f"UPDATE {name} SET {column} = {column} + 1 WHERE group_id = ?", (group_id,)) + except sqlite3.Error: + # статистика не должна мешать остальной обработке сообщения + logging.exception("Не удалось обновить статистику %s", name) + # КОМАНДЫ def add_group(self, group_id): """Добавляем нового пользователя""" with self.connection: - self.cursor.execute(f"INSERT INTO `work` (`group_id`) VALUES(?)", (group_id,)) - self.cursor.execute(f"INSERT INTO `stat` (`group_id`, `mes`, `rep`, `com`, `url`, `med`, `sti`, " - f"`voi`) VALUES(?,?,?,?,?,?,?,?)", (group_id, 0, 0, 0, 0, 0, 0, 0)) + self.cursor.execute("INSERT INTO `work` (`group_id`) VALUES(?)", (group_id,)) + self.cursor.execute("INSERT INTO `stat` (`group_id`, `mes`, `rep`, `com`, `url`, `med`, `sti`, " + "`voi`) VALUES(?,?,?,?,?,?,?,?)", (group_id, 0, 0, 0, 0, 0, 0, 0)) return def group_exists(self, group_id): """Проверяем, есть ли уже пользователь в базе""" with self.connection: - result = self.cursor.execute(f'SELECT * FROM `work` WHERE `group_id` = ?', (group_id,)).fetchall() + result = self.cursor.execute('SELECT * FROM `work` WHERE `group_id` = ?', (group_id,)).fetchall() return bool(len(result)) # ТАБЛИЦА STAT def update_stat(self, group_id, var_id): """Обновляем статистику""" - try: - with self.connection: - self.cursor.execute(f"SELECT * FROM `stat` WHERE `group_id` = ?", (group_id,)) - if var_id == 1: - return self.cursor.execute(f"UPDATE `stat` SET `mes` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[1] + 1), group_id)) - elif var_id == 2: - return self.cursor.execute(f"UPDATE `stat` SET `rep` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[2] + 1), group_id)) - elif var_id == 3: - return self.cursor.execute(f"UPDATE `stat` SET `com` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[3] + 1), group_id)) - elif var_id == 4: - return self.cursor.execute(f"UPDATE `stat` SET `url` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[4] + 1), group_id)) - elif var_id == 5: - return self.cursor.execute(f"UPDATE `stat` SET `med` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[5] + 1), group_id)) - elif var_id == 6: - return self.cursor.execute(f"UPDATE `stat` SET `sti` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[6] + 1), group_id)) - elif var_id == 7: - return self.cursor.execute(f"UPDATE `stat` SET `voi` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[7] + 1), group_id)) - except Exception as e: - print(repr(e)) + self.increment("stat", group_id, var_id) def stat_group(self, group_id): """Получение данных пользователя""" with self.connection: - self.cursor.execute(f"SELECT * FROM `stat` WHERE `group_id` = ?", (group_id,)) + self.cursor.execute("SELECT * FROM `stat` WHERE `group_id` = ?", (group_id,)) data = self.cursor.fetchone() return data[1:] @@ -74,37 +96,12 @@ class Base: def update_month_stat(self, group_id, var_id): """Обновляем статистику""" - try: - with self.connection: - self.cursor.execute(f"SELECT * FROM `month` WHERE `group_id` = ?", (group_id,)) - if var_id == 1: - return self.cursor.execute(f"UPDATE `month` SET `mes` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[1] + 1), group_id)) - elif var_id == 2: - return self.cursor.execute(f"UPDATE `month` SET `rep` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[2] + 1), group_id)) - elif var_id == 3: - return self.cursor.execute(f"UPDATE `month` SET `com` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[3] + 1), group_id)) - elif var_id == 4: - return self.cursor.execute(f"UPDATE `month` SET `url` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[4] + 1), group_id)) - elif var_id == 5: - return self.cursor.execute(f"UPDATE `month` SET `med` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[5] + 1), group_id)) - elif var_id == 6: - return self.cursor.execute(f"UPDATE `month` SET `sti` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[6] + 1), group_id)) - elif var_id == 7: - return self.cursor.execute(f"UPDATE `month` SET `voi` = ? WHERE `group_id` = ?", - ((self.cursor.fetchone()[7] + 1), group_id)) - except Exception as e: - print(repr(e)) + self.increment("month", group_id, var_id) def month_stat_group(self, group_id): """Получение данных пользователя""" with self.connection: - self.cursor.execute(f"SELECT * FROM `month` WHERE `group_id` = ?", (group_id,)) + self.cursor.execute("SELECT * FROM `month` WHERE `group_id` = ?", (group_id,)) data = self.cursor.fetchone() return data[1:] @@ -123,19 +120,19 @@ class Base: def del_edit_user(self, user_id): """Удаление пользователя""" with self.connection: - return self.cursor.execute(f'DELETE FROM `edit` WHERE `user_id` = ?', (user_id,)) + return self.cursor.execute('DELETE FROM `edit` WHERE `user_id` = ?', (user_id,)) # ТАБЛИЦА WORK def check_status(self, group_id): """Получаем статус""" with self.connection: - return self.cursor.execute(f'SELECT `state` FROM `work` WHERE `group_id` = ?', (group_id,)).fetchone()[0] + return self.cursor.execute('SELECT `state` FROM `work` WHERE `group_id` = ?', (group_id,)).fetchone()[0] def update_status(self, group_id): """Обновляем статус""" with self.connection: - state = self.cursor.execute(f'SELECT `state` FROM `work` WHERE `group_id` = ?', (group_id,)).fetchone()[0] - return self.cursor.execute(f"UPDATE `work` SET `state` = ? WHERE `group_id` = ?", (not state, group_id)) + state = self.cursor.execute('SELECT `state` FROM `work` WHERE `group_id` = ?', (group_id,)).fetchone()[0] + return self.cursor.execute("UPDATE `work` SET `state` = ? WHERE `group_id` = ?", (not state, group_id)) # ЗАКРЫТИЕ ВЫЗОВА def close(self): @@ -149,27 +146,33 @@ class User: self.connection = sqlite3.connect(database) self.cursor = self.connection.cursor() + with self.connection: + self.cursor.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, " + "user_id INTEGER NOT NULL)") + self.cursor.execute("CREATE TABLE IF NOT EXISTS \"groups\" (id INTEGER PRIMARY KEY AUTOINCREMENT, " + "group_id INTEGER NOT NULL)") + # СВЯЗКА ПОЛЬЗОВАТЕЛЯ def user_exists(self, user_id): """Проверяем, есть ли уже пользователь в базе""" with self.connection: - result = self.cursor.execute(f'SELECT * FROM `users` WHERE `user_id` = ?', (user_id,)).fetchall() + result = self.cursor.execute('SELECT * FROM `users` WHERE `user_id` = ?', (user_id,)).fetchall() return bool(len(result)) def add_user(self, user_id): """Добавляем нового пользователя""" with self.connection: - return self.cursor.execute(f"INSERT INTO `users` (`user_id`) VALUES(?)", (user_id,)) + return self.cursor.execute("INSERT INTO `users` (`user_id`) VALUES(?)", (user_id,)) def get_user_id(self, user_id): """Получаем короткое айди юзера""" with self.connection: - return self.cursor.execute(f'SELECT `id` FROM `users` WHERE `user_id` = ?', (user_id,)).fetchone()[0] + return self.cursor.execute('SELECT `id` FROM `users` WHERE `user_id` = ?', (user_id,)).fetchone()[0] def get_first_user_id(self, user_id): """Получаем длинное айди юзера""" with self.connection: - return self.cursor.execute(f'SELECT `user_id` FROM `users` WHERE `id` = ?', (user_id,)).fetchone()[0] + return self.cursor.execute('SELECT `user_id` FROM `users` WHERE `id` = ?', (user_id,)).fetchone()[0] # СВЯЗКА ГРУППЫ def group_exists(self, group_id): @@ -186,12 +189,12 @@ class User: def get_group_id(self, group_id): """Получаем короткое айди юзера""" with self.connection: - return self.cursor.execute(f'SELECT `id` FROM `groups` WHERE `group_id` = ?', (group_id,)).fetchone()[0] + return self.cursor.execute('SELECT `id` FROM `groups` WHERE `group_id` = ?', (group_id,)).fetchone()[0] def get_first_group_id(self, group_id): """Получаем длинное айди юзера""" with self.connection: - return self.cursor.execute(f'SELECT `group_id` FROM `groups` WHERE `id` = ?', (group_id,)).fetchone()[0] + return self.cursor.execute('SELECT `group_id` FROM `groups` WHERE `id` = ?', (group_id,)).fetchone()[0] def update_group_id(self, from_id, to_id): """Заменяем на новый айди""" @@ -214,7 +217,7 @@ class Group: def created_group(self, group_id): """Создаём новую таблицу""" with self.connection: - return self.cursor.execute(f"""CREATE TABLE IF NOT EXISTS [{group_id}] ( + return self.cursor.execute(f"""CREATE TABLE IF NOT EXISTS {table(group_id)} ( user_id INTEGER NOT NULL, first_name STRING, mes INTEGER, @@ -228,76 +231,57 @@ class Group: def add_user(self, group_id, user_id, name): """Добавляем нового пользователя""" with self.connection: - return self.cursor.execute(f"INSERT INTO `{group_id}` (`user_id`, `first_name`, `mes`, `rep`, `com`, `url`, " + return self.cursor.execute(f"INSERT INTO {table(group_id)} (`user_id`, `first_name`, `mes`, `rep`, `com`, `url`, " f"`med`, `sti`, `voi`) VALUES(?,?,?,?,?,?,?,?,?)", (user_id, name, 0, 0, 0, 0, 0, 0, 0)) def all_names(self, group_id): """Список имён""" with self.connection: - return self.cursor.execute(f'SELECT `first_name` FROM `{group_id}`').fetchall() + return self.cursor.execute(f'SELECT `first_name` FROM {table(group_id)}').fetchall() def all_ids(self, group_id): """Список айди""" with self.connection: - return self.cursor.execute(f'SELECT `user_id` FROM `{group_id}`').fetchall() + return self.cursor.execute(f'SELECT `user_id` FROM {table(group_id)}').fetchall() def update_stat(self, user_id, group_id, var_id): """Обновляем статистику""" + column = COLUMNS[var_id] try: with self.connection: - self.cursor.execute(f"SELECT * FROM `{group_id}` WHERE `user_id` = ?", (user_id,)) - if var_id == 1: - return self.cursor.execute(f"UPDATE `{group_id}` SET `mes` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[2] + 1), user_id)) - elif var_id == 2: - return self.cursor.execute(f"UPDATE `{group_id}` SET `rep` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[3] + 1), user_id)) - elif var_id == 3: - return self.cursor.execute(f"UPDATE `{group_id}` SET `com` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[4] + 1), user_id)) - elif var_id == 4: - return self.cursor.execute(f"UPDATE `{group_id}` SET `url` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[5] + 1), user_id)) - elif var_id == 5: - return self.cursor.execute(f"UPDATE `{group_id}` SET `med` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[6] + 1), user_id)) - elif var_id == 6: - return self.cursor.execute(f"UPDATE `{group_id}` SET `sti` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[7] + 1), user_id)) - elif var_id == 7: - return self.cursor.execute(f"UPDATE `{group_id}` SET `voi` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[8] + 1), user_id)) - except Exception as e: - print(repr(e)) + self.cursor.execute(f"UPDATE {table(group_id)} SET {column} = {column} + 1 WHERE user_id = ?", + (user_id,)) + except sqlite3.Error: + logging.exception("Не удалось обновить статистику участника") def user_exists(self, user_id, group_id): """Проверяем, есть ли уже пользователь в базе""" with self.connection: - result = self.cursor.execute(f'SELECT * FROM `{group_id}` WHERE `user_id` = ?', (user_id,)).fetchall() + result = self.cursor.execute(f'SELECT * FROM {table(group_id)} WHERE `user_id` = ?', (user_id,)).fetchall() return bool(len(result)) def update_name(self, user_id, group_id, name): """Обновляем имя пользователя""" with self.connection: - return self.cursor.execute(f"UPDATE `{group_id}` SET `first_name` = ? WHERE `user_id` = ?", (name, user_id)) + return self.cursor.execute(f"UPDATE {table(group_id)} SET `first_name` = ? WHERE `user_id` = ?", (name, user_id)) def user_name(self, user_id, group_id): """Получаем имя пользователя по айди""" with self.connection: - return self.cursor.execute(f"SELECT `first_name` FROM `{group_id}` WHERE `user_id` = ?", (user_id,)).fetchone()[0] + return self.cursor.execute(f"SELECT `first_name` FROM {table(group_id)} WHERE `user_id` = ?", (user_id,)).fetchone()[0] def stat_user(self, user_id, group_id): """Получение данных пользователя""" with self.connection: - self.cursor.execute(f"SELECT * FROM `{group_id}` WHERE `user_id` = ?", (user_id,)) + self.cursor.execute(f"SELECT * FROM {table(group_id)} WHERE `user_id` = ?", (user_id,)) data = self.cursor.fetchone() return data[2:] def del_user(self, group_id, user_id): """Удаление пользователя""" with self.connection: - return self.cursor.execute(f'DELETE FROM `{group_id}` WHERE `user_id` = ?', (user_id,)) + return self.cursor.execute(f'DELETE FROM {table(group_id)} WHERE `user_id` = ?', (user_id,)) # ЗАКРЫТИЕ ВЫЗОВА def close(self): @@ -311,40 +295,29 @@ class Month: self.connection = sqlite3.connect(database) self.cursor = self.connection.cursor() + def reset_all(self): + """Обнуляем месячную статистику участников во всех чатах""" + with self.connection: + tables = [r[0] for r in self.cursor.execute("SELECT name FROM sqlite_master WHERE type = 'table'")] + for name in tables: + if name.lstrip("-").isdigit(): + self.cursor.execute(f"UPDATE {table(name)} SET " + ", ".join(f"{c} = 0" for c in COLUMNS.values())) + # КОМАНДЫ def update_stat(self, user_id, group_id, var_id): """Обновляем статистику""" + column = COLUMNS[var_id] try: with self.connection: - self.cursor.execute(f"SELECT * FROM `{group_id}` WHERE `user_id` = ?", (user_id,)) - if var_id == 1: - return self.cursor.execute(f"UPDATE `{group_id}` SET `mes` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[1] + 1), user_id)) - elif var_id == 2: - return self.cursor.execute(f"UPDATE `{group_id}` SET `rep` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[2] + 1), user_id)) - elif var_id == 3: - return self.cursor.execute(f"UPDATE `{group_id}` SET `com` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[3] + 1), user_id)) - elif var_id == 4: - return self.cursor.execute(f"UPDATE `{group_id}` SET `url` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[4] + 1), user_id)) - elif var_id == 5: - return self.cursor.execute(f"UPDATE `{group_id}` SET `med` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[5] + 1), user_id)) - elif var_id == 6: - return self.cursor.execute(f"UPDATE `{group_id}` SET `sti` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[6] + 1), user_id)) - elif var_id == 7: - return self.cursor.execute(f"UPDATE `{group_id}` SET `voi` = ? WHERE `user_id` = ?", - ((self.cursor.fetchone()[7] + 1), user_id)) - except Exception as e: - print(repr(e)) + self.cursor.execute(f"UPDATE {table(group_id)} SET {column} = {column} + 1 WHERE user_id = ?", + (user_id,)) + except sqlite3.Error: + logging.exception("Не удалось обновить статистику участника") def created_group(self, group_id): """Создаём новую таблицу""" with self.connection: - return self.cursor.execute(f"""CREATE TABLE IF NOT EXISTS [{group_id}] ( + return self.cursor.execute(f"""CREATE TABLE IF NOT EXISTS {table(group_id)} ( user_id INTEGER NOT NULL, mes INTEGER, rep INTEGER, @@ -357,31 +330,31 @@ class Month: def add_user(self, group_id, user_id): """Добавляем нового пользователя""" with self.connection: - return self.cursor.execute(f"INSERT INTO `{group_id}` (`user_id`, `mes`, `rep`, `com`, `url`, `med`, " + return self.cursor.execute(f"INSERT INTO {table(group_id)} (`user_id`, `mes`, `rep`, `com`, `url`, `med`, " f"`sti`, `voi`) VALUES(?,?,?,?,?,?,?,?)", (user_id, 0, 0, 0, 0, 0, 0, 0)) def all_ids(self, group_id): """Список айди""" with self.connection: - return self.cursor.execute(f'SELECT `user_id` FROM `{group_id}`').fetchall() + return self.cursor.execute(f'SELECT `user_id` FROM {table(group_id)}').fetchall() def user_exists(self, user_id, group_id): """Проверяем, есть ли уже пользователь в базе""" with self.connection: - result = self.cursor.execute(f'SELECT * FROM `{group_id}` WHERE `user_id` = ?', (user_id,)).fetchall() + result = self.cursor.execute(f'SELECT * FROM {table(group_id)} WHERE `user_id` = ?', (user_id,)).fetchall() return bool(len(result)) def stat_user(self, user_id, group_id): """Получение данных пользователя""" with self.connection: - self.cursor.execute(f"SELECT * FROM `{group_id}` WHERE `user_id` = ?", (user_id,)) + self.cursor.execute(f"SELECT * FROM {table(group_id)} WHERE `user_id` = ?", (user_id,)) data = self.cursor.fetchone() return data[1:] def del_user(self, group_id, user_id): """Удаление пользователя""" with self.connection: - return self.cursor.execute(f'DELETE FROM `{group_id}` WHERE `user_id` = ?', (user_id,)) + return self.cursor.execute(f'DELETE FROM {table(group_id)} WHERE `user_id` = ?', (user_id,)) # ЗАКРЫТИЕ ВЫЗОВА def close(self): diff --git a/db/base.db b/db/base.db deleted file mode 100644 index 515e7aa..0000000 Binary files a/db/base.db and /dev/null differ diff --git a/db/groups.db b/db/groups.db deleted file mode 100644 index e69de29..0000000 diff --git a/db/month.db b/db/month.db deleted file mode 100644 index e69de29..0000000 diff --git a/db/users.db b/db/users.db deleted file mode 100644 index 0e31838..0000000 Binary files a/db/users.db and /dev/null differ