diff --git a/bot_modules/access.py b/bot_modules/access.py index 4160a28..d1d044c 100644 --- a/bot_modules/access.py +++ b/bot_modules/access.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Права пользователей @@ -16,16 +16,17 @@ class FSMRequestToBDAccess(StatesGroup): # --------------------------------------------------------- # БД -table_name = 'module_access' module_name = 'access' +table_name = 'module_access' + init_bd_cmds = [f"""CREATE TABLE IF NOT EXISTS {table_name}( modName TEXT, modAccess TEXT, itemDefaultAccess TEXT, UNIQUE(modName) );""", -f"INSERT OR IGNORE INTO {table_name} (modName, modAccess, itemDefaultAccess) VALUES ('{module_name}', '{user_access.user_access_group_all}=-', '{user_access.user_access_group_all}=-');" +f"INSERT OR IGNORE INTO {table_name} (modName, modAccess, itemDefaultAccess) VALUES ('{module_name}', '{user_access.user_access_group_new}=-', '{user_access.user_access_group_new}=-');" ] # --------------------------------------------------------- @@ -47,8 +48,9 @@ request_start_message = ''' ''' help_message = ''' -📄 Существует БД для работы с правами -`module_access (modName, modAccess)` - содержит права для модулей +📄 Существует одна БД для работы с правами для модулей +Имя БД: module_access +Поля:(modName, modAccess) modAccess - строка ''' + user_access.user_access_readme diff --git a/bot_modules/backup.py b/bot_modules/backup.py index a942dde..8184d7a 100644 --- a/bot_modules/backup.py +++ b/bot_modules/backup.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Бэкапы пользователя @@ -20,7 +20,7 @@ bot = Bot(token=config.GetTelegramBotApiToken(), parse_mode=types.ParseMode.HTML module_name = 'backup' init_bd_cmds = [ -f"INSERT OR IGNORE INTO module_access (modName, modAccess, itemDefaultAccess) VALUES ('{module_name}', '{user_access.user_access_group_all}=-', '{user_access.user_access_group_all}=-');" +f"INSERT OR IGNORE INTO module_access (modName, modAccess, itemDefaultAccess) VALUES ('{module_name}', '{user_access.user_access_group_new}=-', '{user_access.user_access_group_new}=-');" ] # --------------------------------------------------------- diff --git a/bot_modules/groups.py b/bot_modules/groups.py index 89b54d4..d6a22aa 100644 --- a/bot_modules/groups.py +++ b/bot_modules/groups.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Группы пользователей @@ -18,17 +18,32 @@ class FSMRequestToBD(StatesGroup): # БД module_name = 'groups' -init_bd_cmds = ["""CREATE TABLE IF NOT EXISTS user_groups( - group_id INTEGER PRIMARY KEY NOT NULL, - groupName TEXT, - UNIQUE(group_id) +table_groups_name = 'user_groups' +table_user_in_groups_name = 'user_in_groups' + +key_table_groups_name = 'group_id' +name_table_groups_field = 'groupName' +user_id_field = 'user_id' +access_field = 'access' +create_datetime_field = 'createDateTime' + +init_bd_cmds = [f"""CREATE TABLE IF NOT EXISTS {table_groups_name}( + {key_table_groups_name} INTEGER PRIMARY KEY NOT NULL, + {name_table_groups_field} TEXT, + {access_field} TEXT, + {create_datetime_field} TEXT, + UNIQUE({key_table_groups_name}), + UNIQUE({name_table_groups_field}) );""", -"""CREATE TABLE IF NOT EXISTS user_in_groups( - user_id INTEGER, - group_id INTEGER, - UNIQUE(user_id, group_id) +f"""CREATE TABLE IF NOT EXISTS {table_user_in_groups_name}( + {user_id_field} INTEGER, + {key_table_groups_name} INTEGER, + {access_field} TEXT, + {create_datetime_field} TEXT, + UNIQUE({user_id_field}, {key_table_groups_name}) );""", -f"INSERT OR IGNORE INTO module_access (modName, modAccess, itemDefaultAccess) VALUES ('{module_name}', '{user_access.user_access_group_all}=-', '{user_access.user_access_group_all}=-');" +f"INSERT OR IGNORE INTO module_access (modName, modAccess, itemDefaultAccess) VALUES ('{module_name}', '{user_access.user_access_group_new}=-', '{user_access.user_access_group_new}=-');", +f"INSERT OR IGNORE INTO {table_groups_name} ({name_table_groups_field}, {access_field}, {create_datetime_field}) VALUES ('{user_access.user_access_group_new}', '{user_access.user_access_group_new}=-', {bot_bd.GetBDDateTimeNow()});" ] # --------------------------------------------------------- diff --git a/bot_modules/profile.py b/bot_modules/profile.py index 8bca46c..b90d5a6 100644 --- a/bot_modules/profile.py +++ b/bot_modules/profile.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Профиль пользователя @@ -14,23 +14,42 @@ from aiogram.dispatcher import Dispatcher # БД module_name = 'profile' -init_bd_cmds = ["""CREATE TABLE IF NOT EXISTS users( - user_id INTEGER, - userName TEXT, - userAccess TEXT, - UNIQUE(user_id) +table_name = 'users' +key_name = 'user_id' +name_field = 'userName' +name1_field = 'userFirstName' +name2_field = 'userLastName' +is_bot_field = 'userIsBot' +language_code_field = 'userLanguageCode' +access_field = 'userAccess' +create_datetime_field = 'createDateTime' + +init_bd_cmds = [f"""CREATE TABLE IF NOT EXISTS {table_name}( + {key_name} INTEGER, + {name_field} TEXT, + {name1_field} TEXT, + {name2_field} TEXT, + {is_bot_field} TEXT, + {language_code_field} TEXT, + {access_field} TEXT, + {create_datetime_field} TEXT, + UNIQUE({key_name}) );""", -f"INSERT OR IGNORE INTO module_access (modName, modAccess, itemDefaultAccess) VALUES ('{module_name}', '{user_access.user_access_group_all}=+', '{user_access.user_access_group_all}=+');" +f"INSERT OR IGNORE INTO module_access (modName, modAccess, itemDefaultAccess) VALUES ('{module_name}', '{user_access.user_access_group_new}=+', '{user_access.user_access_group_new}=+');" ] # --------------------------------------------------------- # Сообщения -profile_message = ''' +profile_message = f''' 📰 Профиль: -ID: @user_id -Имя: @user_name +ID: #{key_name} +Имя: #{name_field} +Имя1: #{name1_field} +Имя2: #{name2_field} +Код языка: #{language_code_field} +Дата добавления: #{create_datetime_field} ''' user_profile_button_name = "📰 Профиль" @@ -49,15 +68,34 @@ async def ProfileOpen(a_Message, state = None): user_info = GetUserInfo(a_Message.from_user.id) msg = profile_message if not user_info is None: - msg = msg.replace('@user_id', str(user_info[0])).replace('@user_name', str(user_info[1])) - return simple_message.WorkFuncResult(msg, item_access = user_info[2]) + msg = msg.\ + replace(f'#{key_name}', str(user_info[0])).\ + replace(f'#{name_field}', str(user_info[1])).\ + replace(f'#{name1_field}', str(user_info[2])).\ + replace(f'#{name2_field}', str(user_info[3])).\ + replace(f'#{is_bot_field}', str(user_info[4])).\ + replace(f'#{language_code_field}', str(user_info[5])).\ + replace(f'#{access_field}', str(user_info[6])).\ + replace(f'#{create_datetime_field}', str(user_info[7])) + return simple_message.WorkFuncResult(msg, item_access = str(user_info[6])) + return simple_message.WorkFuncResult(msg) # --------------------------------------------------------- # Работа с базой данных пользователей # Добавление пользователя, если он уже есть, то игнорируем -def AddUser(a_UserID, a_UserName): - bot_bd.SQLRequestToBD("INSERT OR IGNORE INTO users (user_id, userName, userAccess) VALUES (?, ?, ?);", commit=True, param = (a_UserID, a_UserName, access.GetItemDefaultAccessForModule(module_name))) +def AddUser(a_UserID, a_UserName, a_UserName1, a_UserName2, a_UserIsBot, a_LanguageCode): + bot_bd.SQLRequestToBD(f"INSERT OR IGNORE INTO users ({key_name}, {name_field}, {name1_field}, {name2_field}, {is_bot_field}, {language_code_field}, {access_field}, {create_datetime_field}) VALUES (?, ?, ?, ?, ?, ?, ?, {bot_bd.GetBDDateTimeNow()});", + commit=True, param = (a_UserID, a_UserName, a_UserName1, a_UserName2, a_UserIsBot, a_LanguageCode, access.GetItemDefaultAccessForModule(module_name))) + + user_groups = groups.GetUserGroupData(a_UserID) + # Если пользователь не состоит ни в одной группе, то добавляем его в группу user_access.user_access_group_new + if len(user_groups.group_names_list) == 0: + new_group_id = bot_bd.SQLRequestToBD(f'SELECT {groups.key_table_groups_name} FROM {groups.table_groups_name} WHERE {groups.name_table_groups_field} = ?', + param = [user_access.user_access_group_new]) + if new_group_id and new_group_id[0]: + bot_bd.SQLRequestToBD(f"INSERT OR IGNORE INTO {groups.table_user_in_groups_name} ({groups.user_id_field}, {groups.key_table_groups_name}, {groups.access_field}, {groups.create_datetime_field}) VALUES (?, ?, ?, {bot_bd.GetBDDateTimeNow()});", + commit=True, param = (a_UserID, new_group_id[0][0], access.GetItemDefaultAccessForModule(module_name))) def GetUserInfo(a_UserID): user_info = bot_bd.SQLRequestToBD('SELECT * FROM users WHERE user_id = ?', param = [a_UserID]) diff --git a/bot_modules/projects.py b/bot_modules/projects.py index 5eb60fe..5b0b6a3 100644 --- a/bot_modules/projects.py +++ b/bot_modules/projects.py @@ -1,21 +1,19 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Проекты -from bot_sys import bot_bd, log, config, keyboard, user_access +from bot_sys import bot_bd, log, keyboard, user_access from bot_modules import start, access, groups from template import bd_item_view, simple_message, bd_item_delete, bd_item_edit, bd_item, bd_item_add -from aiogram import Bot, types +from aiogram import types from aiogram.dispatcher import FSMContext from aiogram.dispatcher.filters.state import State, StatesGroup from aiogram.dispatcher import Dispatcher import sqlite3 -bot = Bot(token=config.GetTelegramBotApiToken(), parse_mode = types.ParseMode.HTML) - class FSMCreateProject(StatesGroup): name = State() desc = State() @@ -33,6 +31,9 @@ class FSMEditDeskItem(StatesGroup): item_id = State() item_field = State() +class FSMEditAccessItem(StatesGroup): + item_id = State() + item_field = State() # --------------------------------------------------------- # БД module_name = 'projects' @@ -53,7 +54,7 @@ init_bd_cmds = [f'''CREATE TABLE IF NOT EXISTS {table_name}( {access_field} TEXT, {create_datetime_field} TEXT )''', -f"INSERT OR IGNORE INTO module_access (modName, modAccess, itemDefaultAccess) VALUES ('{module_name}', '{user_access.user_access_group_all}=va', '{user_access.user_access_group_all}=va');" +f"INSERT OR IGNORE INTO module_access (modName, modAccess, itemDefaultAccess) VALUES ('{module_name}', '{user_access.user_access_group_new}=va', '{user_access.user_access_group_new}=va');" ] # --------------------------------------------------------- @@ -128,6 +129,15 @@ project_edit_desc_message = f''' Введите новое описание проекта: ''' +project_edit_access_message = f''' +Текущий доступ к проекту: +#{access_field} + +{user_access.user_access_readme} + +Введите новое описание проекта: +''' + project_select_to_edit_message = ''' Выберите проект, который вы хотите отредактировать. ''' @@ -147,6 +157,7 @@ edit_project_button_name = "🛠 Редактировать проект" edit_project_photo_button_name = "☐ Изменить изображение" edit_project_name_button_name = "≂ Изменить название" edit_project_desc_button_name = "𝌴 Изменить описание" +edit_project_access_button_name = "✋ Изменить доступ" # --------------------------------------------------------- # Работа с кнопками @@ -156,6 +167,7 @@ def GetEditProjectKeyboardButtons(a_UserGroups): keyboard.ButtonWithAccess(edit_project_photo_button_name, user_access.AccessMode.EDIT, GetAccess()), keyboard.ButtonWithAccess(edit_project_name_button_name, user_access.AccessMode.EDIT, GetAccess()), keyboard.ButtonWithAccess(edit_project_desc_button_name, user_access.AccessMode.EDIT, GetAccess()), + keyboard.ButtonWithAccess(edit_project_access_button_name, user_access.AccessMode.ACCEES_EDIT, GetAccess()), ] mods = [start] return keyboard.MakeKeyboard(keyboard.GetButtons(mods) + cur_buttons, a_UserGroups) @@ -196,7 +208,7 @@ def ShowMessageTemplate(a_StringMessage): photo_id = a_Item[3] access = a_Item[4] create_time = a_Item[5] - msg = a_StringMessage.replace(f'#{name_field}', name).replace(f'#{desc_field}', desc).replace(f'#{create_datetime_field}', create_time) + msg = a_StringMessage.replace(f'#{name_field}', name).replace(f'#{desc_field}', desc).replace(f'#{create_datetime_field}', create_time).replace(f'#{access_field}', access) return simple_message.WorkFuncResult(msg, photo_id = photo_id, item_access = access) return ShowProject @@ -221,7 +233,7 @@ async def ProjectPostDelete(a_CallbackQuery : types.CallbackQuery, a_ItemID): # Работа с базой данных проектов def AddBDItemFunc(a_ItemData): - res, error = bot_bd.SQLRequestToBD(f'INSERT INTO {table_name}({photo_field}, {name_field}, {desc_field}, {access_field}, {create_datetime_field}) VALUES(?, ?, ?, ?, datetime(\'now\'))', + res, error = bot_bd.SQLRequestToBD(f'INSERT INTO {table_name}({photo_field}, {name_field}, {desc_field}, {access_field}, {create_datetime_field}) VALUES(?, ?, ?, ?, {bot_bd.GetBDDateTimeNow()})', commit = True, return_error = True, param = (a_ItemData[photo_field], a_ItemData[name_field], a_ItemData[desc_field], access.GetItemDefaultAccessForModule(module_name))) if error: @@ -264,4 +276,5 @@ def RegisterHandlers(dp : Dispatcher): bd_item_edit.EditBDItemRegisterHandlers(dp, FSMEditPhotoItem, edit_project_photo_button_name, project_select_to_edit_message, ShowMessageTemplate(project_edit_photo_message), ShowMessageTemplate(project_success_edit_message), table_name, key_name, photo_field, GetButtonNameAndKeyValueAndAccess, GetAccess, GetEditProjectKeyboardButtons, access_mode = user_access.AccessMode.EDIT, field_type = bd_item.FieldType.photo) bd_item_edit.EditBDItemRegisterHandlers(dp, FSMEditNameItem, edit_project_name_button_name, project_select_to_edit_message, ShowMessageTemplate(project_edit_name_message), ShowMessageTemplate(project_success_edit_message), table_name, key_name, name_field, GetButtonNameAndKeyValueAndAccess, GetAccess, GetEditProjectKeyboardButtons, access_mode = user_access.AccessMode.EDIT, field_type = bd_item.FieldType.text) bd_item_edit.EditBDItemRegisterHandlers(dp, FSMEditDeskItem, edit_project_desc_button_name, project_select_to_edit_message, ShowMessageTemplate(project_edit_desc_message), ShowMessageTemplate(project_success_edit_message), table_name, key_name, desc_field, GetButtonNameAndKeyValueAndAccess, GetAccess, GetEditProjectKeyboardButtons, access_mode = user_access.AccessMode.EDIT, field_type = bd_item.FieldType.text) + bd_item_edit.EditBDItemRegisterHandlers(dp, FSMEditAccessItem, edit_project_access_button_name, project_select_to_edit_message, ShowMessageTemplate(project_edit_access_message), ShowMessageTemplate(project_success_edit_message), table_name, key_name, access_field, GetButtonNameAndKeyValueAndAccess, GetAccess, GetEditProjectKeyboardButtons, access_mode = user_access.AccessMode.ACCEES_EDIT, field_type = bd_item.FieldType.text) diff --git a/bot_modules/start.py b/bot_modules/start.py index e00662e..80ce400 100644 --- a/bot_modules/start.py +++ b/bot_modules/start.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Стартовое меню @@ -41,7 +41,11 @@ def GetStartKeyboardButtons(a_UserGroups): async def StartMenu(a_Message, state = None): user_id = str(a_Message.from_user.id) user_name = str(a_Message.from_user.username) - profile.AddUser(user_id, user_name) + first_name = str(a_Message.from_user.first_name) + last_name = str(a_Message.from_user.last_name) + is_bot = str(a_Message.from_user.is_bot) + language_code = str(a_Message.from_user.language_code) + profile.AddUser(user_id, user_name, first_name, last_name, is_bot, language_code) log.Info(f'Пользователь {user_id} {user_name} авторизовался в боте. Полные данные {a_Message.from_user}.') return simple_message.WorkFuncResult(start_message) diff --git a/bot_sys/bot_bd.py b/bot_sys/bot_bd.py index 3dbfc35..95f7d11 100644 --- a/bot_sys/bot_bd.py +++ b/bot_sys/bot_bd.py @@ -1,5 +1,5 @@ #-*-coding utf-8-*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) import sqlite3 from bot_sys import log @@ -16,14 +16,12 @@ def GetBDFileName(): # --------------------------------------------------------- +def GetBDDateTimeNow(): + return 'datetime(\'now\')' + def BDExecute(a_Commands): - db = sqlite3.connect(GetBDFileName()) - cursor = db.cursor() for cmd in a_Commands: - cursor.execute(cmd) - db.commit() - cursor.close() - db.close() + SQLRequestToBD(cmd, commit = True) def SelectBDTemplate(a_TableName): def SelectBD(): @@ -48,6 +46,7 @@ def SQLRequestToBD(a_Request : str, commit = False, return_error = False, param error = "Ошибка sqlite3:" + str(e) cursor.close() db.close() + if not error: log.Success(f'Выполнен запроса [{a_Request}]') if return_error: return result, error return result diff --git a/bot_sys/keyboard.py b/bot_sys/keyboard.py index b7a7a9e..b045991 100644 --- a/bot_sys/keyboard.py +++ b/bot_sys/keyboard.py @@ -1,5 +1,5 @@ #-*-coding utf-8-*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Работа с кнопками и клавиатурой diff --git a/bot_sys/user_access.py b/bot_sys/user_access.py index 84165ce..2823c7d 100644 --- a/bot_sys/user_access.py +++ b/bot_sys/user_access.py @@ -1,5 +1,5 @@ #-*-coding utf-8-*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Доступ пользователей diff --git a/main.py b/main.py index 08e76aa..7dbaf12 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) log_start_message = 'Бот успешно запущен!' diff --git a/template/bd_item.py b/template/bd_item.py index a555e5d..fc3d9f1 100644 --- a/template/bd_item.py +++ b/template/bd_item.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Работа с элементом в БД from enum import Enum diff --git a/template/bd_item_add.py b/template/bd_item_add.py index 8af6317..3da3527 100644 --- a/template/bd_item_add.py +++ b/template/bd_item_add.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Добавление элемента в БД diff --git a/template/bd_item_delete.py b/template/bd_item_delete.py index 59f9791..40a7d96 100644 --- a/template/bd_item_delete.py +++ b/template/bd_item_delete.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # удаление элемента в БД diff --git a/template/bd_item_edit.py b/template/bd_item_edit.py index 8dda913..5229abf 100644 --- a/template/bd_item_edit.py +++ b/template/bd_item_edit.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Редактирование элемента в БД @@ -11,6 +11,7 @@ from aiogram import types from aiogram.dispatcher import FSMContext from aiogram.dispatcher.filters.state import State, StatesGroup +#TODO: Переделать на FinishOrNextAddBDItemTemplate StartAddBDItemTemplate cancel_message = ''' 🚫 Редактирование отменено diff --git a/template/bd_item_select.py b/template/bd_item_select.py index 0bbc638..13bf91e 100644 --- a/template/bd_item_select.py +++ b/template/bd_item_select.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Просмотр элемента в БД diff --git a/template/bd_item_view.py b/template/bd_item_view.py index 47daf2f..7380a8e 100644 --- a/template/bd_item_view.py +++ b/template/bd_item_view.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Просмотр элемента в БД diff --git a/template/simple_message.py b/template/simple_message.py index 59a49f9..e05f15f 100644 --- a/template/simple_message.py +++ b/template/simple_message.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Простые информационные сообщения diff --git a/template/sql_request.py b/template/sql_request.py index f4c7c49..447a078 100644 --- a/template/sql_request.py +++ b/template/sql_request.py @@ -1,5 +1,5 @@ # -*- coding: utf8 -*- -# Общественное достояние 2023, Алексей Безбородов (Alexei Bezborodov) +# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) # Сообщения для работы с sql запросами