From 17d10fbf78cd553a99de2b9d95af1cd16fabf59e Mon Sep 17 00:00:00 2001 From: Verum Date: Sun, 13 Apr 2025 06:50:23 +0700 Subject: [PATCH] =?UTF-8?q?3.0=20=D0=92=D1=8B=D0=BF=D1=83=D1=81=D0=BA=20?= =?UTF-8?q?=D0=B2=20PrimoRU?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/PRIMOWORLD.iml | 1 + BotCode/inline/reklama.py | 15 +- BotCode/routers/commands/adm_cmd/ban_cmd.py | 5 +- .../routers/commands/user_cmd/weather_cmd.py | 2 +- BotCode/routers/common/messages.py | 5 +- BotCode/utils/admin_list.py | 20 +- BotCode/utils/all_admins_hide.py | 14 +- BotCode/utils/notification/__init__.py | 0 BotCode/utils/weather_api.py | 17 +- BotLibrary/__init__.py | 32 +- BotLibrary/analytics/type_chat.py | 26 +- BotLibrary/analytics/type_msg.py | 14 +- BotLibrary/loggers/__init__.py | 1 - BotLibrary/loggers/custom_loggers.py | 207 ------- BotLibrary/loggers/logs.py | 380 +++++++++--- BotLibrary/samples/inline_kb_sample.py | 7 +- BotLibrary/samples/reply_kb_sample.py | 5 +- BotLibrary/samples/user_cmd_class.py | 45 +- BotLibrary/sql/__init__.py | 12 + BotLibrary/sql/db_class.py | 563 ++++++++++++++++++ BotLibrary/system/bot_edit.py | 218 +++---- BotLibrary/system/bots.py | 82 ++- BotLibrary/system/directory.py | 72 ++- BotLibrary/timer/start_time.py | 6 +- BotLibrary/validators/email_valid.py | 9 +- BotLibrary/validators/normal_word.py | 3 +- BotLibrary/validators/url_valid.py | 10 +- BotLibrary/validators/username.py | 2 - Documentation/docs.md | 9 - GUI/__init__.py | 4 - GUI/console.py | 167 ------ GUI/console_0.py | 74 --- ProjectsFiles/configs/.env | 2 +- ProjectsFiles/configs/config.py | 11 +- .../bd.db => ProjectsFiles/database/users.db | Bin 53248 -> 36864 bytes SQLite3/__init__.py | 5 - SQLite3/bd_func/__init__.py | 26 - SQLite3/bd_func/bd_add_user.py | 39 -- SQLite3/bd_func/bd_get_user.py | 16 - SQLite3/bd_func/bd_update_user.py | 39 -- SQLite3/bd_func/bd_update_user_msg.py | 78 --- SQLite3/bd_func/bd_user_create.py | 283 --------- SQLite3/bd_func/status_user.py | 41 -- SQLite3/bd_func/username_to_id.py | 3 - SQLite3/list_ids.json | 35 -- Test/commands/ban_cmd.py | 63 -- Test/commands/find_username.py | 25 - Test/old_files/__init__.py | 22 - Test/old_files/media_func.py | 55 -- main.py | 32 +- requirements.txt | Bin 0 -> 1466 bytes 51 files changed, 1191 insertions(+), 1611 deletions(-) delete mode 100644 BotCode/utils/notification/__init__.py delete mode 100644 BotLibrary/loggers/custom_loggers.py create mode 100644 BotLibrary/sql/__init__.py create mode 100644 BotLibrary/sql/db_class.py delete mode 100644 GUI/__init__.py delete mode 100644 GUI/console.py delete mode 100644 GUI/console_0.py rename SQLite3/bd.db => ProjectsFiles/database/users.db (56%) delete mode 100644 SQLite3/__init__.py delete mode 100644 SQLite3/bd_func/__init__.py delete mode 100644 SQLite3/bd_func/bd_add_user.py delete mode 100644 SQLite3/bd_func/bd_get_user.py delete mode 100644 SQLite3/bd_func/bd_update_user.py delete mode 100644 SQLite3/bd_func/bd_update_user_msg.py delete mode 100644 SQLite3/bd_func/bd_user_create.py delete mode 100644 SQLite3/bd_func/status_user.py delete mode 100644 SQLite3/bd_func/username_to_id.py delete mode 100644 SQLite3/list_ids.json delete mode 100644 Test/commands/ban_cmd.py delete mode 100644 Test/commands/find_username.py delete mode 100644 Test/old_files/__init__.py delete mode 100644 Test/old_files/media_func.py create mode 100644 requirements.txt diff --git a/.idea/PRIMOWORLD.iml b/.idea/PRIMOWORLD.iml index 541e011..e96faea 100644 --- a/.idea/PRIMOWORLD.iml +++ b/.idea/PRIMOWORLD.iml @@ -23,6 +23,7 @@ + diff --git a/BotCode/inline/reklama.py b/BotCode/inline/reklama.py index 8956fa9..be080a9 100644 --- a/BotCode/inline/reklama.py +++ b/BotCode/inline/reklama.py @@ -1,8 +1,9 @@ # BotCode/inline/reklama.py # Работа с инлайн запросами на рекламу -from aiogram import Router, types -from aiogram.types import InlineQueryResultPhoto +from aiogram import Router +from aiogram.types import (InlineQueryResultPhoto, InlineQuery, CallbackQuery, + InlineKeyboardMarkup, InlineKeyboardButton) from BotLibrary import bot # Настройка экспорта в модули @@ -14,13 +15,13 @@ f"""Это сообщение с изображением и инлайн кно @router.callback_query(lambda c: c.data == 'button_1') -async def process_callback_button(callback_query: types.CallbackQuery) -> None: +async def process_callback_button(callback_query: CallbackQuery) -> None: await bot.answer_callback_query(callback_query.id, text="Вы нажали первую кнопку!") await bot.send_message(callback_query.from_user.id, "Ответ на вашу кнопку.") @router.inline_query() -async def inline_echo(inline_query: types.InlineQuery) -> None: +async def inline_echo(inline_query: InlineQuery) -> None: # Содержимое запроса query = inline_query.query @@ -36,10 +37,10 @@ async def inline_echo(inline_query: types.InlineQuery) -> None: photo_url=image_url, # URL изображения thumbnail_url=image_url, # Миниатюра изображения caption=text_msg, # Текст, который будет показываться под изображением - reply_markup=types.InlineKeyboardMarkup( + reply_markup=InlineKeyboardMarkup( inline_keyboard=[ - [types.InlineKeyboardButton(text="Посмотреть инфо-канал", url="https://t.me/adeptusfiziks")], - [types.InlineKeyboardButton(text="Вторая кнопка", callback_data="button_1")], + [InlineKeyboardButton(text="Посмотреть инфо-канал", url="https://t.me/adeptusfiziks")], + [InlineKeyboardButton(text="Вторая кнопка", callback_data="button_1")], ] ) ) diff --git a/BotCode/routers/commands/adm_cmd/ban_cmd.py b/BotCode/routers/commands/adm_cmd/ban_cmd.py index b7cd099..12e2d64 100644 --- a/BotCode/routers/commands/adm_cmd/ban_cmd.py +++ b/BotCode/routers/commands/adm_cmd/ban_cmd.py @@ -2,15 +2,14 @@ # Работа с командой /stats, для получения информации о себе from aiogram import types -from BotLibrary import CommandHandler, bot -from SQLite3 import status_user +from BotLibrary import CommandHandler, bot, db # Настройки экспорта в модули __all__ = ("ban_cmd",) # Функция блокировки пользователя async def ban_user(message: types.Message, *args, **kwargs) -> None: - status = await status_user(message) + status = db.get_user_status(message) if status not in ('Пользователь', 'Забаннен'): # Проверка, что команда вызвана с упоминанием пользователя args = message.text.split() diff --git a/BotCode/routers/commands/user_cmd/weather_cmd.py b/BotCode/routers/commands/user_cmd/weather_cmd.py index 400d821..41264c0 100644 --- a/BotCode/routers/commands/user_cmd/weather_cmd.py +++ b/BotCode/routers/commands/user_cmd/weather_cmd.py @@ -1,5 +1,5 @@ # BotCode/routers/commands/user_cmd/start_time_cmd.py -# +# Команда на выдачу погоды определенного города from BotLibrary import CommandHandler from BotCode.utils import get_weather diff --git a/BotCode/routers/common/messages.py b/BotCode/routers/common/messages.py index d34778e..574880b 100644 --- a/BotCode/routers/common/messages.py +++ b/BotCode/routers/common/messages.py @@ -3,7 +3,6 @@ from aiogram import Router, types from BotLibrary import * -from SQLite3 import base_sql, status_user # Настройка экспорта модулей и роутера __all__ = ("router",) @@ -12,6 +11,6 @@ router = Router(name="common_msg_router") # Обработчик всех сообщений @router.message() async def all_messages(message: types.Message) -> None: - await base_sql(message) - await status_user(message) + db.update_user(message) + db.update_user_messages(message) Logs.msg(message) diff --git a/BotCode/utils/admin_list.py b/BotCode/utils/admin_list.py index 86e815d..40b7689 100644 --- a/BotCode/utils/admin_list.py +++ b/BotCode/utils/admin_list.py @@ -1,22 +1,34 @@ # BotCode/utils/admin_lists.py # Составления листа администраторов +from aiogram.types import Message from BotLibrary import bot +from ProjectsFiles import BotVar # Настройки экспорта в модули __all__ = ("admin_lists",) +async def admin_lists(chat_id: int = None, message: Message = None) -> str: + """ + Функция составления словаря администраторов. -# Функция составления словаря администраторов -async def admin_lists(chat_id: int) -> str: + :param message: Объект сообщения от пользователя. + :param chat_id: ID-чата в котором будет проводиться работа. + :return: Строка с юзерами администрации. + """ + chat_id = chat_id if isinstance(chat_id, int) else message.chat.id admins = await bot.get_chat_administrators(chat_id) + # Формируем список упоминаний администраторов admin_mentions = [] for admin in admins: if admin.user.is_bot: continue - admin_mentions.append( + if BotVar.parse_mode == "HTML": + admin_mentions.append( f"@{admin.user.username}" if admin.user.username else f"{admin.user.full_name}") - + elif BotVar.parse_mode == "MarkdownV2": + admin_mentions.append( + f"@{admin.user.username}" if admin.user.username else f"[{admin.user.full_name}](tg://user?id={admin.user.id})") admins_text = ", ".join(admin_mentions) if admin_mentions else "Нет администраторов" return admins_text diff --git a/BotCode/utils/all_admins_hide.py b/BotCode/utils/all_admins_hide.py index 2e25dbb..f1b95f9 100644 --- a/BotCode/utils/all_admins_hide.py +++ b/BotCode/utils/all_admins_hide.py @@ -8,18 +8,28 @@ from BotLibrary import bot # Настройки экспорта в модули __all__ = ("hidden_admins_message",) - -# Функция составления словаря администраторов async def hidden_admins_message(message: types.Message = None, chat_id: int = None, text: str = "", msg: bool = True, *args) -> str | None: + """ + Формирует скрытые ссылки на администраторов чата в Markdown-разметке. + + :param message: Объект сообщения от пользователя (если chat_id не указан, ID чата берется из него). + :param chat_id: ID чата, в котором нужно получить список администраторов (если не указан, берется из message). + :param text: Дополнительный текст, который будет добавлен к результату. + :param msg: Определяет, возвращать ли результат (True) или отправлять его в чат (False). + :param args: Дополнительные аргументы (не используются, оставлены для совместимости с шаблоном). + :return: Строка со скрытыми ссылками на администраторов и добавленным текстом (если msg=True), иначе None. + """ chat_id = chat_id if isinstance(chat_id, int) else message.chat.id admins = await bot.get_chat_administrators(chat_id) + hidden_links = "".join( markdown.hide_link(f"tg://user?id={admin.user.id}") for admin in admins if not admin.user.is_bot ) + result = f"{hidden_links}{text}" if msg: return result diff --git a/BotCode/utils/notification/__init__.py b/BotCode/utils/notification/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/BotCode/utils/weather_api.py b/BotCode/utils/weather_api.py index cbf3907..7734930 100644 --- a/BotCode/utils/weather_api.py +++ b/BotCode/utils/weather_api.py @@ -1,10 +1,17 @@ import aiohttp +from aiogram.types import Message from ProjectsFiles import weather_api_key # Настройки экспорта в модули __all__ = ("get_weather",) -async def get_weather(message, *args) -> str: +async def get_weather(message: Message, *args) -> str: + """ + Обрабатывает запрос о погоде для указанного города и возвращает информацию о текущей погоде. + + :param message: Объект сообщения от пользователя. + :return: Возвращает ответ о погоде в указанном городе. + """ # Извлекаем город из сообщения command_parts = message.text.split(maxsplit=1) print(command_parts[1]) @@ -33,10 +40,10 @@ async def get_weather(message, *args) -> str: wind = data["wind"]["speed"] weather_today: str = (f"Погода {city}\n" - f"☁️Погода: {weather}\n" - f"🌡Температура: {temp}°C\n" - f"💧Влажность: {humidity}%\n" - f"💨Скорость ветра: {wind} м/с") + f"☁️Погода: {weather}\n" + f"🌡Температура: {temp}°C\n" + f"💧Влажность: {humidity}%\n" + f"💨Скорость ветра: {wind} м/с") await message.answer(weather_today) return weather_today except Exception as e: diff --git a/BotLibrary/__init__.py b/BotLibrary/__init__.py index a294390..2317c8f 100644 --- a/BotLibrary/__init__.py +++ b/BotLibrary/__init__.py @@ -5,37 +5,7 @@ from .analytics import * from .loggers import * from .samples import * +from .sql import * from .system import * from .timer import * from .validators import * - -from SQLite3 import create_user_db -from ProjectsFiles import Permissions - - -# Функция установки -async def setup(): - # Запуск логеров - await setup_logger() - - # Получение информации о боте - await bot_get_info() - - # Вывод сообщение о запуске - Logs.start(text=f"Начало запуска бота @{BotInfo.username}...") - Logs.console() - - # Автоматическое создание базы данных при отсутствии - await create_user_db() - - # Создание пустых директорий - await setup_directories() - - # Нужно ли удалить веб-хук - if Permissions.delete_webhook: - await bot.delete_webhook() - - await set_adm_rights() - await set_bot_name() - await set_bot_description() - await set_bot_short_description() diff --git a/BotLibrary/analytics/type_chat.py b/BotLibrary/analytics/type_chat.py index 70cc965..31e4362 100644 --- a/BotLibrary/analytics/type_chat.py +++ b/BotLibrary/analytics/type_chat.py @@ -1,25 +1,23 @@ # BotLibrary/analytics/type_chat.py # Определение типа чата -from aiogram import types +from aiogram.types import Message # Настройка экспорта в модули __all__ = ("type_chat",) -# Проверка на тип чата -async def type_chat(message: types.Message) -> str: +async def type_chat(message: Message) -> str: """ - Преобразует информацию о чате в понятные значения. + Преобразует информацию о чате в его тип на русском языке. - :param message: Объект сообщения из aiogram. + :param message: Объект сообщения из aiogram, содержащий информацию о чате. :return: Тип чата строкой. """ - chat_type: str = message.chat.type - if chat_type == "private": - return "Личный" - elif chat_type == "group" or chat_type == "supergroup": - return "Группа" - elif chat_type == "channel": - return "Канал" - else: - return "Неизвестный тип чата." + chat_types: dict[str, str] = { + "private": "Личный", + "group": "Группа", + "supergroup": "Группа", + "channel": "Канал", + } + + return chat_types.get(message.chat.type, "Неизвестный тип чата") diff --git a/BotLibrary/analytics/type_msg.py b/BotLibrary/analytics/type_msg.py index 4a28287..f12e9da 100644 --- a/BotLibrary/analytics/type_msg.py +++ b/BotLibrary/analytics/type_msg.py @@ -6,7 +6,6 @@ from aiogram.types import ContentType, Message # Настройка экспорта из модуля __all__ = ("type_msg",) -# Функция определения типа сообщения def type_msg(message: Message) -> str: """ Функция для определения типа сообщения на основе его содержимого. @@ -50,7 +49,7 @@ def type_msg(message: Message) -> str: ContentType.INVOICE: "Счет", ContentType.SUCCESSFUL_PAYMENT: "Успешный платеж", ContentType.REFUNDED_PAYMENT: "Возврат платежа", - ContentType.USERS_SHARED: "Пользователи поделились", + ContentType.USERS_SHARED: "Пользователь поделился", ContentType.CHAT_SHARED: "Чат был передан", ContentType.CONNECTED_WEBSITE: "Подключенный веб-сайт", ContentType.WRITE_ACCESS_ALLOWED: "Разрешение на запись", @@ -77,9 +76,20 @@ def type_msg(message: Message) -> str: # Получение типа сообщения message_type: str = message.content_type + # Если это контакт, добавляем номер телефона if message_type == ContentType.CONTACT and message.contact: return f"{content_types.get(message_type, 'Контакт')}: {message.contact.phone_number}" + # Если это пользователи, добавляем их ID + if message_type == ContentType.USERS_SHARED and message.users_shared: + user_ids = ", ".join(map(str, message.users_shared.user_ids)) + return f"{content_types.get(message_type, 'Пользователь поделился')}: {user_ids}" + + # Если это переданный чат, добавляем его ID + if message_type == ContentType.CHAT_SHARED and message.chat_shared: + return f"{content_types.get(message_type, 'Чат был передан')}: {message.chat_shared.chat_id}" + + # Возвращаем описание типа сообщения, если оно есть в словаре, иначе "Неизвестный тип" return content_types.get(message_type, "Неизвестный тип") diff --git a/BotLibrary/loggers/__init__.py b/BotLibrary/loggers/__init__.py index c235a89..2e432b8 100644 --- a/BotLibrary/loggers/__init__.py +++ b/BotLibrary/loggers/__init__.py @@ -3,4 +3,3 @@ # Экспортирование модулей во внешние слои проекта from .logs import * -from .custom_loggers import * diff --git a/BotLibrary/loggers/custom_loggers.py b/BotLibrary/loggers/custom_loggers.py deleted file mode 100644 index c6e7134..0000000 --- a/BotLibrary/loggers/custom_loggers.py +++ /dev/null @@ -1,207 +0,0 @@ -# BotLibrary/loggers/custom_loggers.py -# Кастомные логгеры для проекта, с более стандартизированным использованием - -from time import sleep - -from aiogram import types -from colorama import Fore -from loguru import logger -from aiogram.types import Message - -from BotLibrary.system.bots import BotInfo -from BotLibrary.validators.username import username -from BotLibrary.analytics.type_msg import type_msg - -from ProjectsFiles import BotLogs, Permissions, ProjectPath, BotVar, bot_owner - -# Настройка экспорта из модуля -__all__ = ("Logs",) - - -class Logs: - """Класс для логирования с разными уровнями через loguru.""" - - @staticmethod - def start(text: str = "Логирование!", - system: str = "PRIMO", - log_type: str = "AEP", - user: str = "@Console") -> None: - """ - Логирует сообщение на уровне START. - - :param text: Сообщение для логирования. - :param system: Тип системы логирования. - :param log_type: Тип лога (например, "Help"). - :param user: Имя пользователя или источник вызова лога. - - :return: Вывод сообщения об старте бота - """ - logger.bind(system=system, user=user, log_type=log_type).log("START", text) - - - @staticmethod - def debug(text: str = "Логирование!", - system: str = "DEBUG", - log_type: str = "Logs", - user: str = "@Console", - message: Message = None) -> None: - """ - Логирует сообщение на уровне DEBUG. - - :param text: Сообщение для логирования. - :param system: Тип системы логирования. - :param log_type: Тип лога (например, "Help"). - :param user: Имя пользователя или источник вызова лога. - :param message: Сообщение от пользователя, если необходимо извлечь имя. - - :return: Вывод сообщения об дебаг-информации - """ - if message: - user = username(message) - logger.bind(system=system, log_type=log_type, user=user).debug(text) - - - @staticmethod - def info(text: str = "Логирование!", - system: str = "PRIMO", - log_type: str = "Logs", - user: str = "@Console", - message: Message = None) -> None: - """ - Логирует сообщение на уровне INFO. - - :param text: Сообщение для логирования. - :param system: Тип системы логирования. - :param log_type: Тип лога (например, "Logs"). - :param user: Имя пользователя или источник вызова лога. - :param message: Сообщение от пользователя, если необходимо извлечь имя. - - :return: Вывод сообщения об некой информации - """ - if message: - user = username(message) - logger.bind(system=system, log_type=log_type, user=user).info(text) - - - @staticmethod - def warning(text: str = "Логирование!", - system: str = "WARNING", - log_type: str = "Logs", - user: str = "@Console", - message: Message = None) -> None: - """ - Логирует сообщение на уровне WARNING. - - :param text: Сообщение для логирования. - :param system: Тип системы логирования. - :param log_type: Тип лога (например, "Logs"). - :param user: Имя пользователя или источник вызова лога. - :param message: Сообщение от пользователя, если необходимо извлечь имя. - - :return: Вывод сообщения об предупреждении - """ - if message: - user = username(message) - logger.bind(system=system, log_type=log_type, user=user).warning(text) - - - @staticmethod - def error(text: str = "Логирование!", - system: str = "ERROR", - log_type: str = "Logs", - user: str = "@Console", - message: Message = None) -> None: - """ - Логирует сообщение на уровне ERROR. - - :param text: Сообщение для логирования. - :param system: Тип системы логирования. - :param log_type: Тип лога (например, "Logs"). - :param user: Имя пользователя или источник вызова лога. - :param message: Сообщение от пользователя, если необходимо извлечь имя. - - :return: Вывод сообщения об ошибке - """ - if message: - user = username(message) - logger.bind(system=system, log_type=log_type, user=user).error(text) - - - @staticmethod - def msg(message: types.Message, - log_type: str = "Message", - user: str = None, - msg_type: str = None, - permission: bool = BotLogs.permission) -> None: - """ - Логирует сообщение, если оно не обработано. - - :param message: Сообщение от пользователя. - :param log_type: Тип лога (по умолчанию "Message"). - :param permission: Разрешение на логирование (config). - :param user: Получение пользователя (автоматически). - :param msg_type: Получение типа сообщения (автоматически). -. - :return: Вывод сообщения об обычном сообщении пользователя. - """ - # Получаем айди чата - chat_id = message.chat.id - - # Получаем username или id пользователя - if user is None: - user: str = f"@{message.from_user.username or message.from_user.id}" - if msg_type is None: - msg_type: str = type_msg(message) - - # Логирование только если разрешено - if permission: - # Проверка на наличие текста и его типа - if message.text is None and msg_type not in ("Новые участники чата", "Ушедший участник чата"): - Logs.info(log_type=log_type, user=user, text=f"Получено сообщение из ({chat_id}) : {msg_type}") - elif message.text is not None: - Logs.info(log_type=log_type, user=user, - text=f"Получено сообщение из ({chat_id}) : {message.text}") - - - @staticmethod - def console(stop_time: int = 1, - console: bool = Permissions.start_info_console, - file: bool = Permissions.start_info_to_file, - path: str = ProjectPath.bot_info_log_file) -> None: - """ - Собирает информацию о боте и выводит её в консоль, а также возвращает как строку. - - :param stop_time: Количество времени в секундах, после которых выведется информация (1 сек) - :param console: Разрешение на внесение информации в консоль (config) - :param file: Разрешение на внесение информации в файл (config) - :param path: Путь до файла для сохранения информации о боте (config) - - :return: Информация о боте в виде строки. - """ - # Собираем данные о боте - bot_name: str = f"Основное имя: {BotInfo.first_name}\n" - bot_post_name: str = f"Владельцы бота: {bot_owner}\n" - bot_username: str = f"Юзернейм: @{BotInfo.username}\n" - bot_id: str = f"ID: {BotInfo.id}\n" - bot_can_join_groups: str = f"Может ли вступать в группы: {BotInfo.can_join_groups}\n" - bot_can_read_all_group_messages: str = f"Чтение всех сообщений: {BotInfo.can_read_all_group_messages}\n" - bot_supports_inline_queries: str = f"Поддерживает инлайн-запросы: {BotInfo.supports_inline_queries}\n" - bot_can_connect_to_business: str = f"Подключение к бизнес-аккаунтам: {BotInfo.can_connect_to_business}\n" - bot_has_main_web_app: str = f"Основное веб-приложение: {BotInfo.has_main_web_app}\n" - - # Формируем полный текст с выводом информации о боте - bot_all_info: str = (f"{bot_name} {bot_post_name} {bot_username} {bot_id} " - f"{bot_can_join_groups} {bot_can_read_all_group_messages} " - f"{bot_supports_inline_queries} {bot_can_connect_to_business} " - f"{bot_has_main_web_app}") - - # Печатаем всю информацию в консоль с задержкой - if console: - sleep(stop_time) - print(Fore.CYAN + bot_all_info) - - # Печатаем всю информацию в файл - if file: - # Преобразуем словарь bot_all_info в строку и записываем в файл - with open(path, 'w', encoding=BotVar.encod) as file: - file.write(str(bot_all_info)) diff --git a/BotLibrary/loggers/logs.py b/BotLibrary/loggers/logs.py index 6007a3d..c366992 100644 --- a/BotLibrary/loggers/logs.py +++ b/BotLibrary/loggers/logs.py @@ -1,107 +1,295 @@ # BotLibrary/loggers/logs.py -# Создание логгеров и их шаблон для проекта +# Кастомные логгеры для проекта, с более стандартизированным использованием -import sys from loguru import logger -from ProjectsFiles import BotLogs, ProjectPath +from aiogram.types import Message + +from BotLibrary.system.bots import BotInfo +from BotLibrary.validators.username import username +from BotLibrary.analytics.type_msg import type_msg + +from ProjectsFiles import BotLogs, Permissions, ProjectPath, BotVar, bot_owner # Настройка экспорта из модуля -__all__ = ("setup_logger",) - -# Создание обычного логгера + логгер в файл -async def setup_logger(logging: bool = BotLogs.permission, - to_file: bool = BotLogs.permission_to_file) -> None: - """ - Настройка логгеров для проекта, выводящих логи в консоль и файлы. - Логгеры конфигурируются в зависимости от настроек в конфигах проекта. - - Если разрешено логирование, добавляются логи для уровней DEBUG, INFO, WARNING, ERROR. - И кастомные такие, как START, NEW_USER, LEAVE_USER - - :param logging: Разрешение на логирование в консоль (config) - :param to_file: Разрешение на логирование в файл (config) - - :return: Создание логеров под различные уровни - """ - logger.remove() # Удаляем все стандартные логгеры +__all__ = ("Logs",) - # Если есть разрешение, то он создает новые уровни - if logging and BotLogs.permission_to_file: - # Добавляем новый уровень START - logger.level("START", no=25, color="white", icon="🔸") - if logging and BotLogs.permission_new_user: - # Добавляем новый уровень NEW_USER - logger.level("NEW_USER", no=4, color="white", icon="👋") - if logging and BotLogs.permission_leave_user: - # Добавляем новый уровень LEAVE_USER - logger.level("LEAVE_USER", no=3, color="white", icon="🫰") +class Logs: + """Класс для логирования с разными уровнями через loguru.""" + @staticmethod + def setup(logging: bool = BotLogs.permission, + to_file: bool = BotLogs.permission_to_file) -> None: + """ + Настройка логгеров для проекта, выводящих логи в консоль и файлы. + Логгеры конфигурируются в зависимости от настроек в конфигах проекта. + + Если разрешено логирование, добавляются логи для уровней DEBUG, INFO, WARNING, ERROR. + И кастомные такие, как START, NEW_USER, LEAVE_USER + + :param logging: Разрешение на логирование в консоль (config) + :param to_file: Разрешение на логирование в файл (config) + :return: Создание логеров под различные уровни + """ + logger.remove() # Удаляем все стандартные логгеры + + # Если есть разрешение, то он создает новые уровни + if logging and BotLogs.permission_to_file: + # Добавляем новый уровень START + logger.level("START", no=25, color="white", icon="🔸") + if logging and BotLogs.permission_new_user: + # Добавляем новый уровень NEW_USER + logger.level("NEW_USER", no=4, color="white", icon="👋") + if logging and BotLogs.permission_leave_user: + # Добавляем новый уровень LEAVE_USER + logger.level("LEAVE_USER", no=3, color="white", icon="🫰") + + # Настройка логирования в консоль для каждого уровня + if logging: + from sys import stderr + logger.add(stderr, + colorize=True, + format=BotLogs.start_text, + level="START", + filter=lambda record: record["level"].name == "START" + ) + logger.add(stderr, + colorize=True, + format=BotLogs.debug_text, + level="DEBUG", + filter=lambda record: record["level"].name == "DEBUG") + logger.add(stderr, + colorize=True, + format=BotLogs.info_text, + level="INFO", + filter=lambda record: record["level"].name == "INFO") + logger.add(stderr, + colorize=True, + format=BotLogs.warning_text, + level="WARNING", + filter=lambda record: record["level"].name == "WARNING") + logger.add(stderr, + colorize=True, + format=BotLogs.error_text, + level="ERROR", + filter=lambda record: record["level"].name == "ERROR") + + # Добавление логгера для записи в файл + if to_file: + logger.add(ProjectPath.start_log_file, + rotation=BotLogs.max_size, + format=BotLogs.start_text, + backtrace=True, + diagnose=True, + level="START", + filter=lambda record: record["level"].name == "START") + logger.add(ProjectPath.debug_log_file, + rotation=BotLogs.max_size, + format=BotLogs.debug_text, + backtrace=True, + diagnose=True, + level="DEBUG", + filter=lambda record: record["level"].name == "DEBUG") + logger.add(ProjectPath.info_log_file, + rotation=BotLogs.max_size, + format=BotLogs.info_text, + backtrace=True, + diagnose=True, + level="INFO", + filter=lambda record: record["level"].name == "INFO") + logger.add(ProjectPath.warning_log_file, + rotation=BotLogs.max_size, + format=BotLogs.warning_text, + backtrace=True, + diagnose=True, + level="WARNING", + filter=lambda record: record["level"].name == "WARNING") + logger.add(ProjectPath.error_log_file, + rotation=BotLogs.max_size, + format=BotLogs.error_text, + backtrace=True, + diagnose=True, + level="ERROR", + filter=lambda record: record["level"].name == "ERROR") - # Настройка логирования в консоль для каждого уровня - if logging: - logger.add(sys.stderr, - colorize=True, - format=BotLogs.start_text, - level="START", - filter=lambda record: record["level"].name == "START" - ) - logger.add(sys.stderr, - colorize=True, - format=BotLogs.debug_text, - level="DEBUG", - filter=lambda record: record["level"].name == "DEBUG") - logger.add(sys.stderr, - colorize=True, - format=BotLogs.info_text, - level="INFO", - filter=lambda record: record["level"].name == "INFO") - logger.add(sys.stderr, - colorize=True, - format=BotLogs.warning_text, - level="WARNING", - filter=lambda record: record["level"].name == "WARNING") - logger.add(sys.stderr, - colorize=True, - format=BotLogs.error_text, - level="ERROR", - filter=lambda record: record["level"].name == "ERROR") + @staticmethod + def start(text: str = "Логирование!", + system: str = "PRIMO", + log_type: str = "AEP", + user: str = "@Console") -> None: + """ + Логирует сообщение на уровне START. + + :param text: Сообщение для логирования. + :param system: Тип системы логирования. + :param log_type: Тип лога (например, "Help"). + :param user: Имя пользователя или источник вызова лога. + + :return: Вывод сообщения об старте бота + """ + logger.bind(system=system, user=user, log_type=log_type).log("START", text) - # Добавление логгера для записи в файл - if to_file: - logger.add(ProjectPath.start_log_file, - rotation=BotLogs.max_size, - format=BotLogs.start_text, - backtrace=True, - diagnose=True, - level="START", - filter=lambda record: record["level"].name == "START") - logger.add(ProjectPath.debug_log_file, - rotation=BotLogs.max_size, - format=BotLogs.debug_text, - backtrace=True, - diagnose=True, - level="DEBUG", - filter=lambda record: record["level"].name == "DEBUG") - logger.add(ProjectPath.info_log_file, - rotation=BotLogs.max_size, - format=BotLogs.info_text, - backtrace=True, - diagnose=True, - level="INFO", - filter=lambda record: record["level"].name == "INFO") - logger.add(ProjectPath.warning_log_file, - rotation=BotLogs.max_size, - format=BotLogs.warning_text, - backtrace=True, - diagnose=True, - level="WARNING", - filter=lambda record: record["level"].name == "WARNING") - logger.add(ProjectPath.error_log_file, - rotation=BotLogs.max_size, - format=BotLogs.error_text, - backtrace=True, - diagnose=True, - level="ERROR", - filter=lambda record: record["level"].name == "ERROR") + @staticmethod + def debug(text: str = "Логирование!", + system: str = "DEBUG", + log_type: str = "Logs", + user: str = "@Console", + message: Message = None) -> None: + """ + Логирует сообщение на уровне DEBUG. + + :param text: Сообщение для логирования. + :param system: Тип системы логирования. + :param log_type: Тип лога (например, "Help"). + :param user: Имя пользователя или источник вызова лога. + :param message: Сообщение от пользователя, если необходимо извлечь имя. + + :return: Вывод сообщения об дебаг-информации + """ + if message: + user = username(message) + logger.bind(system=system, log_type=log_type, user=user).debug(text) + + + @staticmethod + def info(text: str = "Логирование!", + system: str = "PRIMO", + log_type: str = "Logs", + user: str = "@Console", + message: Message = None) -> None: + """ + Логирует сообщение на уровне INFO. + + :param text: Сообщение для логирования. + :param system: Тип системы логирования. + :param log_type: Тип лога (например, "Logs"). + :param user: Имя пользователя или источник вызова лога. + :param message: Сообщение от пользователя, если необходимо извлечь имя. + + :return: Вывод сообщения об некой информации + """ + if message: + user = username(message) + logger.bind(system=system, log_type=log_type, user=user).info(text) + + + @staticmethod + def warning(text: str = "Логирование!", + system: str = "WARNING", + log_type: str = "Logs", + user: str = "@Console", + message: Message = None) -> None: + """ + Логирует сообщение на уровне WARNING. + + :param text: Сообщение для логирования. + :param system: Тип системы логирования. + :param log_type: Тип лога (например, "Logs"). + :param user: Имя пользователя или источник вызова лога. + :param message: Сообщение от пользователя, если необходимо извлечь имя. + + :return: Вывод сообщения об предупреждении + """ + if message: + user = username(message) + logger.bind(system=system, log_type=log_type, user=user).warning(text) + + + @staticmethod + def error(text: str = "Логирование!", + system: str = "ERROR", + log_type: str = "Logs", + user: str = "@Console", + message: Message = None) -> None: + """ + Логирует сообщение на уровне ERROR. + + :param text: Сообщение для логирования. + :param system: Тип системы логирования. + :param log_type: Тип лога (например, "Logs"). + :param user: Имя пользователя или источник вызова лога. + :param message: Сообщение от пользователя, если необходимо извлечь имя. + + :return: Вывод сообщения об ошибке + """ + if message: + user = username(message) + logger.bind(system=system, log_type=log_type, user=user).error(text) + + + @staticmethod + def msg(message: Message, + log_type: str = "Message", + user: str = None, + msg_type: str = None, + permission: bool = BotLogs.permission) -> None: + """ + Логирует сообщение, если оно не обработано. + + :param message: Сообщение от пользователя. + :param log_type: Тип лога (по умолчанию "Message"). + :param permission: Разрешение на логирование (config). + :param user: Получение пользователя (автоматически). + :param msg_type: Получение типа сообщения (автоматически). +. + :return: Вывод сообщения об обычном сообщении пользователя. + """ + # Получаем айди чата + chat_id = message.chat.id + + # Получаем username или id пользователя + if user is None: + user: str = f"@{message.from_user.username or message.from_user.id}" + if msg_type is None: + msg_type: str = type_msg(message) + + # Логирование только если разрешено + if permission: + # Проверка на наличие текста и его типа + if message.text is None and msg_type not in ("Новые участники чата", "Ушедший участник чата"): + Logs.info(log_type=log_type, user=user, text=f"Получено сообщение из ({chat_id}) : {msg_type}") + elif message.text is not None: + Logs.info(log_type=log_type, user=user, + text=f"Получено сообщение из ({chat_id}) : {message.text}") + + + @staticmethod + def console(console: bool = Permissions.start_info_console, + file: bool = Permissions.start_info_to_file, + path: str = ProjectPath.bot_info_log_file) -> None: + """ + Собирает информацию о боте и выводит её в консоль, а также возвращает как строку. + + :param console: Разрешение на внесение информации в консоль (config) + :param file: Разрешение на внесение информации в файл (config) + :param path: Путь до файла для сохранения информации о боте (config) + :return: Информация о боте в виде строки. + """ + # Собираем данные о боте + bot_name: str = f"Основное имя: {BotInfo.first_name}\n" + bot_post_name: str = f"Владельцы бота: {bot_owner}\n" + bot_username: str = f"Юзернейм: @{BotInfo.username}\n" + bot_id: str = f"ID: {BotInfo.id}\n" + bot_can_join_groups: str = f"Может ли вступать в группы: {BotInfo.can_join_groups}\n" + bot_can_read_all_group_messages: str = f"Чтение всех сообщений: {BotInfo.can_read_all_group_messages}\n" + bot_supports_inline_queries: str = f"Поддерживает инлайн-запросы: {BotInfo.supports_inline_queries}\n" + bot_can_connect_to_business: str = f"Подключение к бизнес-аккаунтам: {BotInfo.can_connect_to_business}\n" + bot_has_main_web_app: str = f"Основное веб-приложение: {BotInfo.has_main_web_app}\n" + + # Формируем полный текст с выводом информации о боте + bot_all_info: str = (f"{bot_name} {bot_post_name} {bot_username} {bot_id} " + f"{bot_can_join_groups} {bot_can_read_all_group_messages} " + f"{bot_supports_inline_queries} {bot_can_connect_to_business} " + f"{bot_has_main_web_app}") + + # Печатаем всю информацию в консоль с задержкой + if console: + from colorama import Fore + print(Fore.CYAN + bot_all_info) + + # Печатаем всю информацию в файл + if file: + # Преобразуем словарь bot_all_info в строку и записываем в файл + with open(path, 'w', encoding=BotVar.encod) as file: + file.write(str(bot_all_info)) diff --git a/BotLibrary/samples/inline_kb_sample.py b/BotLibrary/samples/inline_kb_sample.py index c1dd60d..1d12783 100644 --- a/BotLibrary/samples/inline_kb_sample.py +++ b/BotLibrary/samples/inline_kb_sample.py @@ -1,10 +1,13 @@ # BotLibrary/samples/inline_kb_sample.py # Шаблон для создания инлайн клавиатур -from aiogram.types import InlineKeyboardMarkup, ReplyKeyboardRemove +from aiogram.types import InlineKeyboardMarkup from aiogram.utils.keyboard import InlineKeyboardBuilder from typing import List, Tuple, Optional +# Настройка экспорта в модули +__all__ = ("BaseInlineKeyboard",) + class BaseInlineKeyboard: def __init__(self, buttons: List[Tuple[str, Optional[str], Optional[str]]], row_width: int = 1): """ @@ -17,7 +20,7 @@ class BaseInlineKeyboard: def get_keyboard(self) -> InlineKeyboardMarkup: """ Создаёт инлайн-клавиатуру и возвращает её вместе с объектом для удаления reply-клавиатуры. - :return: кортеж (InlineKeyboardMarkup, ReplyKeyboardRemove) + :return: кортеж InlineKeyboardMarkup """ ikb = InlineKeyboardBuilder() for text, url, callback_data in self.buttons: diff --git a/BotLibrary/samples/reply_kb_sample.py b/BotLibrary/samples/reply_kb_sample.py index ad4b9c9..046ca21 100644 --- a/BotLibrary/samples/reply_kb_sample.py +++ b/BotLibrary/samples/reply_kb_sample.py @@ -5,6 +5,9 @@ from aiogram.types import ReplyKeyboardMarkup, KeyboardButton, KeyboardButtonPol from aiogram.utils.keyboard import ReplyKeyboardBuilder from typing import List, Union, Tuple, Optional, Dict, Any +# Настройка экспорта в модули +__all__ = ("BaseReplyKeyboard",) + class BaseReplyKeyboard: def __init__( self, @@ -100,4 +103,4 @@ class BaseReplyKeyboard: buttons.append(KeyboardButton(text=button)) rkb.row(*buttons) - return rkb.as_markup(resize_keyboard=self.resize_keyboard, one_time_keyboard=self.one_time_keyboard) \ No newline at end of file + return rkb.as_markup(resize_keyboard=self.resize_keyboard, one_time_keyboard=self.one_time_keyboard) diff --git a/BotLibrary/samples/user_cmd_class.py b/BotLibrary/samples/user_cmd_class.py index 3536223..6559032 100644 --- a/BotLibrary/samples/user_cmd_class.py +++ b/BotLibrary/samples/user_cmd_class.py @@ -11,16 +11,47 @@ from typing import Optional, Callable from ..loggers import Logs from ..validators import username, valid_url from ProjectsFiles import BotVar -from SQLite3 import base_sql +from ..sql import db # Настройки экспорта в модули __all__ = ("CommandHandler",) class CommandHandler: + """ + Класс для создания и управления командами Telegram-бота. + + Этот класс позволяет создавать команды с различными настройками, включая + текстовые сообщения, медиафайлы, клавиатуры, выполнение функций и обработку + callback-запросов. + + :param name: Название команды. + :param keywords: Список ключевых слов, вызывающих команду. + :param func: Список функций, которые выполняются при вызове команды. + :param text_msg: Текстовое сообщение, которое отправляется в ответ на команду. + :param chat_action: Флаг отправки анимации набора текста перед ответом. + :param description: Описание команды. + :param tg_links: Флаг обработки ссылок на пользователей в тексте. + :param keyboard: Клавиатура, привязанная к команде. + :param prefix: Префикс команды (по умолчанию `BotVar.prefix`). + :param callbackdata: Список callback-данных, связанных с командой. + :param only_admin: Флаг, разрешающий использование команды только администраторам. + :param ignore_case: Флаг игнорирования регистра в ключевых словах. + :param activate_keywords: Флаг активации команды по ключевым словам. + :param delete_msg: Флаг удаления исходного сообщения после обработки команды. + :param activate_commands: Флаг активации команды через стандартный обработчик команд. + :param activate_callback: Флаг активации обработки callback-запросов. + :param media: Тип медиафайла, который отправляется (`"message"`, `"photo"`, `"video"`, и т. д.). + :param path_to_media: Путь к медиафайлу (или список путей). + :param parse_mode: Форматирование текста (`Markdown`, `HTML` и т. д.). + :param disable_notification: Флаг отключения уведомлений при отправке сообщений. + :param protect: Флаг защиты контента сообщений. + + :return: Готовый шаблон для команды. + """ def __init__(self, name: str, keywords: list, func: Optional[list[Callable]] = None, - text_msg=None, + text_msg = None, chat_action: bool = False, description: str = "Описание команды", tg_links: bool = False, @@ -34,15 +65,16 @@ class CommandHandler: activate_commands: bool = True, activate_callback: bool = True, media: str = "message", - path_to_media=None, + path_to_media = None, parse_mode: str = BotVar.parse_mode, disable_notification: bool = BotVar.disable_notification, protect: bool = BotVar.protect_content): + self.router = Router(name=f"{name}_router") self.name = name self.log_type = name.capitalize() self.description = description - self.last_bot_message = {} # {chat_id: message_id} + self.last_bot_message = {} self.keywords = keywords self.text_msg = text_msg @@ -125,7 +157,10 @@ class CommandHandler: text = text.replace("", str(message.from_user.id)) Logs.info(log_type=self.log_type, user=username(message), text=f"использовал(а) команду /{self.name}") - await base_sql(message) + + # Работа с базами данных + db.update_user(message) + db.update_user_messages(message) # Обрабатываем текстовое сообщение if callable(self.text_msg): diff --git a/BotLibrary/sql/__init__.py b/BotLibrary/sql/__init__.py new file mode 100644 index 0000000..75eb74f --- /dev/null +++ b/BotLibrary/sql/__init__.py @@ -0,0 +1,12 @@ +# BotLibrary/system/__init__.py +# Инициализация пакета system, для библиотек запуска + +# Экспортирование модулей во внешние слои проекта +from .db_class import * +from ProjectsFiles import BotVar + +# Создание экземпляра класса +db = Database(BotVar.bd_names) + +# Создание базы данных +db.create_db() diff --git a/BotLibrary/sql/db_class.py b/BotLibrary/sql/db_class.py new file mode 100644 index 0000000..7d0de7f --- /dev/null +++ b/BotLibrary/sql/db_class.py @@ -0,0 +1,563 @@ +# BotLibrary/system/db_class.py +# Создание базы данных +import os +import sqlite3 +from datetime import datetime, timedelta, timezone +from aiogram import types +from typing import Optional, List, Tuple +from ProjectsFiles import BotVar + +# Настройка экспорта в модули +__all__ = ("Database",) + +class Database: + """Класс для управления базой данных пользователей чата с использованием SQLite3.""" + + def __init__(self, db_name: str = BotVar.bd_path) -> None: + """Инициализация класса с именем базы данных.""" + self.db_name = db_name + + # --- Основные методы из предоставленных функций --- + + def create_db(self) -> None: + """Создание базы данных и таблиц с начальными данными.""" + # Создание директории, если её нет + db_directory = os.path.dirname(self.db_name) + if db_directory and not os.path.exists(db_directory): + os.makedirs(db_directory) + + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + + # Таблица пользователей + cursor.execute(''' + CREATE TABLE IF NOT EXISTS users ( + user_id INTEGER PRIMARY KEY, + tg_id INTEGER NOT NULL UNIQUE, + username TEXT, + first_name TEXT, + last_name TEXT, + role TEXT DEFAULT NULL, + status TEXT DEFAULT 'active', + user TEXT DEFAULT 'user' + );''') + + # Таблица сообщений пользователей + cursor.execute(''' + CREATE TABLE IF NOT EXISTS user_messages ( + user_id INTEGER PRIMARY KEY, + last_message TEXT, + last_message_id INTEGER, + last_message_time TEXT, + messages_per_day INTEGER DEFAULT 0, + messages_per_week INTEGER DEFAULT 0, + messages_per_month INTEGER DEFAULT 0, + total_messages INTEGER DEFAULT 0, + FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE CASCADE + );''') + + # Таблица персонажей Genshin Impact + cursor.execute(''' + CREATE TABLE IF NOT EXISTS characters ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + region TEXT NOT NULL, + name TEXT NOT NULL, + status TEXT DEFAULT 'Свободно', + user_id INTEGER DEFAULT NULL, + comment TEXT DEFAULT '', + FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE SET NULL + );''') + + # Таблица персонажей Honkai: Star Rail + cursor.execute(''' + CREATE TABLE IF NOT EXISTS characters_hsr ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + region TEXT NOT NULL, + name TEXT NOT NULL, + status TEXT DEFAULT 'Свободно', + user_id INTEGER DEFAULT NULL, + comment TEXT DEFAULT '', + FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE SET NULL + );''') + + # Начальные данные для characters (Genshin Impact) + characters_genshin: list[tuple[str, str, str, str]] = [ + # Мондштадт + ("Мондштадт", "Венти", "Свободно", ""), + ("Мондштадт", "Кэйа", "Свободно", ""), + ("Мондштадт", "Альбедо", "Свободно", ""), + ("Мондштадт", "Дилюк", "Свободно", ""), + ("Мондштадт", "Мика", "Свободно", ""), + ("Мондштадт", "Беннет", "Свободно", ""), + ("Мондштадт", "Рэйзор", "Свободно", ""), + ("Мондштадт", "Эола", "Свободно", ""), + ("Мондштадт", "Мона", "Свободно", ""), + ("Мондштадт", "Джинн", "Свободно", ""), + ("Мондштадт", "Диона", "Свободно", ""), + ("Мондштадт", "Лиза", "Свободно", ""), + ("Мондштадт", "Ноэлль", "Свободно", ""), + ("Мондштадт", "Сахароза", "Свободно", ""), + ("Мондштадт", "Розария", "Свободно", ""), + ("Мондштадт", "Эмбер", "Свободно", ""), + ("Мондштадт", "Фишль", "Свободно", ""), + ("Мондштадт", "Барбара", "Свободно", ""), + + # Ли Юэ + ("Ли Юэ", "Чжун Ли", "Свободно", ""), + ("Ли Юэ", "Сяо", "Свободно", ""), + ("Ли Юэ", "Син Цю", "Свободно", ""), + ("Ли Юэ", "Чун Юнь", "Свободно", ""), + ("Ли Юэ", "Бай Чжу", "Свободно", ""), + ("Ли Юэ", "Е Лань", "Свободно", ""), + ("Ли Юэ", "Шень Хэ", "Свободно", ""), + ("Ли Юэ", "Гань Юй", "Свободно", ""), + ("Ли Юэ", "Ци Ци", "Свободно", ""), + ("Ли Юэ", "Кэ Цин", "Свободно", ""), + ("Ли Юэ", "Янь Фэй", "Свободно", ""), + ("Ли Юэ", "Нин Гуан", "Свободно", ""), + ("Ли Юэ", "Бэй Доу", "Свободно", ""), + ("Ли Юэ", "Яо Яо", "Свободно", ""), + ("Ли Юэ", "Ка Мин", "Свободно", ""), + ("Ли Юэ", "Сянь Юнь", "Свободно", ""), + ("Ли Юэ", "Юнь Цзинь", "Свободно", ""), + ("Ли Юэ", "Ху Тао", "Свободно", ""), + ("Ли Юэ", "Лань Янь", "Свободно", ""), + + # Инадзума + ("Инадзума", "Итто", "Свободно", ""), + ("Инадзума", "Горо", "Свободно", ""), + ("Инадзума", "Аято", "Свободно", ""), + ("Инадзума", "Хэйдзо", "Свободно", ""), + ("Инадзума", "Тома", "Свободно", ""), + ("Инадзума", "Кадзуха", "Свободно", ""), + ("Инадзума", "Кирара", "Свободно", ""), + ("Инадзума", "Кудзё Сара", "Свободно", ""), + ("Инадзума", "Ёимия", "Свободно", ""), + ("Инадзума", "Аяка", "Свободно", ""), + ("Инадзума", "Сангономия Кокоми", "Свободно", ""), + ("Инадзума", "Яэ Мико", "Свободно", ""), + ("Инадзума", "Райдэн Эи", "Свободно", ""), + ("Инадзума", "Саю", "Свободно", ""), + ("Инадзума", "Куки Синобу", "Свободно", ""), + ("Инадзума", "Мидзуки", "Свободно", ""), + + # Сумеру + ("Сумеру", "Аль-Хайтам", "Свободно", ""), + ("Сумеру", "Кавех", "Свободно", ""), + ("Сумеру", "Сайно", "Свободно", ""), + ("Сумеру", "Тигнари", "Свободно", ""), + ("Сумеру", "Сетос", "Свободно", ""), + ("Сумеру", "Нилу", "Свободно", ""), + ("Сумеру", "Нахида", "Свободно", ""), + ("Сумеру", "Лайла", "Свободно", ""), + ("Сумеру", "Кандакия", "Свободно", ""), + ("Сумеру", "Дори", "Свободно", ""), + ("Сумеру", "Дэхья", "Свободно", ""), + ("Сумеру", "Коллеи", "Свободно", ""), + ("Сумеру", "Фарузан", "Свободно", ""), + + # Фонтейн + ("Фонтейн", "Лини", "Свободно", ""), + ("Фонтейн", "Ризли", "Свободно", ""), + ("Фонтейн", "Невиллет", "Свободно", ""), + ("Фонтейн", "Фремине", "Свободно", ""), + ("Фонтейн", "Линетт", "Свободно", ""), + ("Фонтейн", "Эмилия", "Свободно", ""), + ("Фонтейн", "Клоринда", "Свободно", ""), + ("Фонтейн", "Навия", "Свободно", ""), + ("Фонтейн", "Шарлотта", "Свободно", ""), + ("Фонтейн", "Фурина", "Свободно", ""), + ("Фонтейн", "Тиори", "Свободно", ""), + ("Фонтейн", "Сиджвин", "Свободно", ""), + + # Натлан + ("Натлан", "Кинич", "Свободно", ""), + ("Натлан", "Оророн", "Свободно", ""), + ("Натлан", "Муалани", "Свободно", ""), + ("Натлан", "Ситлали", "Свободно", ""), + ("Натлан", "Шилонен", "Свободно", ""), + ("Натлан", "Иансан", "Свободно", ""), + ("Натлан", "Мавуика", "Свободно", ""), + ("Натлан", "Часка", "Свободно", ""), + + # Фатуи + ("Фатуи", "Тарталья", "Свободно", ""), + ("Фатуи", "Панталоне", "Свободно", ""), + ("Фатуи", "Дотторе", "Свободно", ""), + ("Фатуи", "Капитано", "Свободно", ""), + ("Фатуи", "Пьеро", "Свободно", ""), + ("Фатуи", "Пульничелла", "Свободно", ""), + ("Фатуи", "Синьора", "Свободно", ""), + ("Фатуи", "Арлекино", "Свободно", ""), + ("Фатуи", "Коломбина", "Свободно", ""), + ("Фатуи", "Царица", "Свободно", ""), + ("Фатуи", "Странник", "Свободно", ""), + + # Иные персонажи + ("Иные персонажи", "Итэр", "Свободно", ""), + ("Иные персонажи", "Люмин", "Свободно", ""), + ("Иные персонажи", "Элой", "Свободно", ""), + ("Иные персонажи", "Паймон", "Свободно", ""), + ("Иные персонажи", "Дайнслейф", "Свободно", ""), + ] + + # Начальные данные для characters_hsr (Honkai: Star Rail) + characters_hsr: list[tuple[str, str, str, str]] = [ + # Ярило-6 + ("Ярило-6", "Броня", "Свободно", ""), + ("Ярило-6", "Гепард", "Свободно", ""), + ("Ярило-6", "Зеле", "Свободно", ""), + ("Ярило-6", "Клара", "Свободно", ""), + ("Ярило-6", "Лука", "Свободно", ""), + ("Ярило-6", "Наташа", "Свободно", ""), + ("Ярило-6", "Пела", "Свободно", ""), + ("Ярило-6", "Рысь", "Свободно", ""), + ("Ярило-6", "Сампо", "Свободно", ""), + ("Ярило-6", "Сервал", "Свободно", ""), + ("Ярило-6", "Хук", "Свободно", ""), + + # Станция «Герта» + ("Станция «Герта»", "Арлан", "Свободно", ""), + ("Станция «Герта»", "Аста", "Свободно", ""), + ("Станция «Герта»", "Великая Герта", "Свободно", ""), + ("Станция «Герта»", "Кукла «Герта»", "Свободно", ""), + ("Станция «Герта»", "Жуань Мэй", "Свободно", ""), + + # Пенакония + ("Пенакония", "Авантюрин", "Свободно", ""), + ("Пенакония", "Ахерон", "Свободно", ""), + ("Пенакония", "Галлахер", "Свободно", ""), + ("Пенакония", "Зарянка", "Свободно", ""), + ("Пенакония", "Миша", "Свободно", ""), + ("Пенакония", "Мистер Река", "Свободно", ""), + ("Пенакония", "Раппа", "Свободно", ""), + ("Пенакония", "Чёрный Лебедь", "Свободно", ""), + ("Пенакония", "Яшма", "Свободно", ""), + ("Пенакония", "Воскресенье", "Свободно", ""), + + # Звёздный Экспресс + ("Звёздный Экспресс", "Вельт", "Свободно", ""), + ("Звёздный Экспресс", "Келус", "Свободно", ""), + ("Звёздный Экспресс", "Стелла", "Свободно", ""), + ("Звёздный Экспресс", "Дань Хэн", "Свободно", ""), + ("Звёздный Экспресс", "Март 7", "Свободно", ""), + ("Звёздный Экспресс", "Химеко", "Свободно", ""), + ("Звёздный Экспресс", "Пом Пом", "Свободно", ""), + + # Галактика + ("Галактика", "Аргенти", "Свободно", ""), + ("Галактика", "Блэйд", "Свободно", ""), + ("Галактика", "Бутхилл", "Свободно", ""), + ("Галактика", "Доктор Рацио", "Свободно", ""), + ("Галактика", "Кафка", "Свободно", ""), + ("Галактика", "Светлячок", "Свободно", ""), + ("Галактика", "Искорка", "Свободно", ""), + ("Галактика", "Серебряный Волк", "Свободно", ""), + ("Галактика", "Топаз", "Свободно", ""), + + # Амфореус + ("Амфореус", "Аглая", "Свободно", ""), + ("Амфореус", "Мидей", "Свободно", ""), + ("Амфореус", "Трибби", "Свободно", ""), + + # Альянс Сяньчжоу + ("Альянс Сяньчжоу", "Байлу", "Свободно", ""), + ("Альянс Сяньчжоу", "Гуйнайфей", "Свободно", ""), + ("Альянс Сяньчжоу", "Линша", "Свободно", ""), + ("Альянс Сяньчжоу", "Лоча", "Свободно", ""), + ("Альянс Сяньчжоу", "Моцзэ", "Свободно", ""), + ("Альянс Сяньчжоу", "Пожиратель Луны", "Свободно", ""), + ("Альянс Сяньчжоу", "Сушан", "Свободно", ""), + ("Альянс Сяньчжоу", "Сюзи", "Свободно", ""), + ("Альянс Сяньчжоу", "Фуга", "Свободно", ""), + ("Альянс Сяньчжоу", "Фэйсяо", "Свободно", ""), + ("Альянс Сяньчжоу", "Ханья", "Свободно", ""), + ("Альянс Сяньчжоу", "Хохо", "Свободно", ""), + ("Альянс Сяньчжоу", "Цзин Юань", "Свободно", ""), + ("Альянс Сяньчжоу", "Цзиннлю", "Свободно", ""), + ("Альянс Сяньчжоу", "Цзяоцю", "Свободно", ""), + ("Альянс Сяньчжоу", "Цинцюэ", "Свободно", ""), + ("Альянс Сяньчжоу", "Юйкун", "Свободно", ""), + ("Альянс Сяньчжоу", "Юньли", "Свободно", ""), + ("Альянс Сяньчжоу", "Яньцин", "Свободно", ""), + ] + + # Заполнение таблиц начальными данными, если они пусты + cursor.execute("SELECT COUNT(*) FROM characters") + if cursor.fetchone()[0] == 0: + cursor.executemany('INSERT INTO characters (region, name, status, comment) VALUES (?, ?, ?, ?)', + characters_genshin) + + cursor.execute("SELECT COUNT(*) FROM characters_hsr") + if cursor.fetchone()[0] == 0: + cursor.executemany('INSERT INTO characters_hsr (region, name, status, comment) VALUES (?, ?, ?, ?)', + characters_hsr) + + db.commit() + + def add_user(self, tg_id: int, username: str, first_name: str, last_name: str, role: str, status: str, + user: str) -> None: + """Добавление нового пользователя в базу данных.""" + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + + # Проверка на существование пользователя + cursor.execute("SELECT user_id FROM users WHERE tg_id = ?", (tg_id,)) + if cursor.fetchone(): + return # Пользователь уже существует + + # Определение нового user_id + cursor.execute("SELECT MAX(user_id) FROM users") + max_id = cursor.fetchone()[0] + new_user_id = 1 if max_id is None else max_id + 1 + + # Вставка пользователя + cursor.execute(''' + INSERT INTO users (user_id, tg_id, username, first_name, last_name, role, status, user) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ''', (new_user_id, tg_id, username, first_name, last_name, role, status, user)) + + # Создание записи в user_messages + cursor.execute('INSERT INTO user_messages (user_id) VALUES (?)', (new_user_id,)) + db.commit() + + def get_user(self, tg_id: int) -> Optional[Tuple]: + """Получение информации о пользователе по tg_id.""" + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + cursor.execute("SELECT * FROM users WHERE tg_id = ?", (tg_id,)) + return cursor.fetchone() + + def update_user(self, tg_id: int, username: Optional[str] = None, first_name: Optional[str] = None, + last_name: Optional[str] = None, role: Optional[str] = None, user: Optional[str] = None) -> None: + """Обновление данных о пользователе.""" + updates = [] + params = [] + + if username: + updates.append("username = ?") + params.append(username) + if first_name: + updates.append("first_name = ?") + params.append(first_name) + if last_name: + updates.append("last_name = ?") + params.append(last_name) + if role: + updates.append("role = ?") + params.append(role) + if user: + updates.append("user = ?") + params.append(user) + + if updates: + query = f"UPDATE users SET {', '.join(updates)} WHERE tg_id = ?" + params.append(tg_id) + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + cursor.execute(query, params) + db.commit() + + def update_user_messages(self, message: types.Message) -> None: + """Обновление статистики сообщений пользователя.""" + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + tg_id = message.from_user.id + + # Получение user_id + cursor.execute("SELECT user_id FROM users WHERE tg_id = ?", (tg_id,)) + result = cursor.fetchone() + if not result: + return # Пользователь не найден + user_id = result[0] + + # Проверка существующей записи в user_messages + cursor.execute( + "SELECT last_message_time, messages_per_day, messages_per_week, messages_per_month, total_messages " + "FROM user_messages WHERE user_id = ?", (user_id,)) + result = cursor.fetchone() + + # Время сообщения в московском часовом поясе + now = message.date.astimezone(timezone(timedelta(hours=3))) + today = now.date() + start_of_week = today - timedelta(days=today.weekday()) + current_month = now.month + current_year = now.year + + last_message = message.text or "Н/Д" # Замените на type_msg(message) при необходимости + last_message_id = message.message_id + + if result: + last_message_time, messages_per_day, messages_per_week, messages_per_month, total_messages = result + if last_message_time: + last_message_time = datetime.fromisoformat(last_message_time).astimezone( + timezone(timedelta(hours=3))) + last_date = last_message_time.date() + last_week = last_date - timedelta(days=last_date.weekday()) + last_month = last_message_time.month + last_year = last_message_time.year + + # Обнуление счетчиков при смене периода + if last_date != today: + messages_per_day = 0 + if last_week != start_of_week: + messages_per_week = 0 + if last_month != current_month or last_year != current_year: + messages_per_month = 0 + else: + messages_per_day, messages_per_week, messages_per_month = 0, 0, 0 + + # Увеличение счетчиков + messages_per_day += 1 + messages_per_week += 1 + messages_per_month += 1 + total_messages += 1 + else: + messages_per_day, messages_per_week, messages_per_month, total_messages = 1, 1, 1, 1 + cursor.execute(''' + INSERT INTO user_messages (user_id, last_message, last_message_id, last_message_time, + messages_per_day, messages_per_week, messages_per_month, total_messages) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ''', (user_id, last_message, last_message_id, now.isoformat(), messages_per_day, + messages_per_week, messages_per_month, total_messages)) + db.commit() + return + + # Обновление записи + cursor.execute(''' + UPDATE user_messages + SET last_message = ?, last_message_id = ?, last_message_time = ?, + messages_per_day = ?, messages_per_week = ?, messages_per_month = ?, + total_messages = ? + WHERE user_id = ? + ''', (last_message, last_message_id, now.isoformat(), messages_per_day, + messages_per_week, messages_per_month, total_messages, user_id)) + db.commit() + + def get_user_status(self, message: types.Message) -> str: + """Получение статуса пользователя.""" + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + cursor.execute("SELECT user FROM users WHERE tg_id = ?", (message.from_user.id,)) + row = cursor.fetchone() + status_map = { + "ban": "Забанен", + "user": "Пользователь", + "moderator": "Модератор", + "admin": "Администратор", + "so-owner": "Совладелец", + "owner": "Владелец", + } + return status_map.get(row[0], "Ошибка!") if row else "Пользователь не найден" + + # --- Новые и улучшенные методы --- + + def delete_user(self, tg_id: int) -> None: + """Удаление пользователя из базы данных.""" + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + cursor.execute("DELETE FROM users WHERE tg_id = ?", (tg_id,)) + db.commit() + + def get_all_users(self) -> List[Tuple]: + """Получение списка всех пользователей.""" + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + cursor.execute("SELECT * FROM users") + return cursor.fetchall() + + def get_user_messages(self, tg_id: int) -> Optional[Tuple]: + """Получение статистики сообщений пользователя.""" + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + cursor.execute("SELECT user_id FROM users WHERE tg_id = ?", (tg_id,)) + result = cursor.fetchone() + if not result: + return None + user_id = result[0] + cursor.execute("SELECT * FROM user_messages WHERE user_id = ?", (user_id,)) + return cursor.fetchone() + + def ban_user(self, tg_id: int) -> None: + """Блокировка пользователя.""" + self.update_user(tg_id, user="ban") + + def unban_user(self, tg_id: int) -> None: + """Разблокировка пользователя.""" + self.update_user(tg_id, user="user") + + def promote_user(self, tg_id: int, new_role: str) -> None: + """Изменение роли пользователя.""" + valid_roles = ["user", "moderator", "admin", "so-owner", "owner"] + if new_role in valid_roles: + self.update_user(tg_id, user=new_role) + + def get_top_active_users(self, limit: int = 10) -> List[Tuple]: + """Получение самых активных пользователей по количеству сообщений.""" + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + cursor.execute(''' + SELECT u.tg_id, u.username, um.total_messages + FROM users u + JOIN user_messages um ON u.user_id = um.user_id + ORDER BY um.total_messages DESC + LIMIT ? + ''', (limit,)) + return cursor.fetchall() + + def assign_character(self, tg_id: int, character_id: int, game: str = "genshin") -> bool: + """Назначение персонажа пользователю.""" + table = "characters" if game == "genshin" else "characters_hsr" + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + cursor.execute(f"SELECT user_id FROM {table} WHERE id = ?", (character_id,)) + result = cursor.fetchone() + if result and result[0] is not None: + return False # Персонаж уже занят + + cursor.execute("SELECT user_id FROM users WHERE tg_id = ?", (tg_id,)) + user_id = cursor.fetchone() + if not user_id: + return False # Пользователь не найден + + cursor.execute(f"UPDATE {table} SET user_id = ?, status = 'Занят' WHERE id = ?", (user_id[0], character_id)) + db.commit() + return True + + def free_character(self, character_id: int, game: str = "genshin") -> None: + """Освобождение персонажа.""" + table = "characters" if game == "genshin" else "characters_hsr" + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + cursor.execute(f"UPDATE {table} SET user_id = NULL, status = 'Свободно' WHERE id = ?", (character_id,)) + db.commit() + + def get_user_characters(self, tg_id: int, game: str = "genshin") -> List[Tuple]: + """Получение списка персонажей пользователя.""" + table = "characters" if game == "genshin" else "characters_hsr" + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + cursor.execute("SELECT user_id FROM users WHERE tg_id = ?", (tg_id,)) + user_id = cursor.fetchone() + if not user_id: + return [] + cursor.execute(f"SELECT * FROM {table} WHERE user_id = ?", (user_id[0],)) + return cursor.fetchall() + + def search_users(self, query: str) -> List[Tuple]: + """Поиск пользователей по имени или юзернейму.""" + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + cursor.execute(""" + SELECT * FROM users + WHERE username LIKE ? OR first_name LIKE ? OR last_name LIKE ? + """, (f"%{query}%", f"%{query}%", f"%{query}%")) + return cursor.fetchall() + + def get_character_by_name(self, name: str, game: str = "genshin") -> Optional[Tuple]: + """Поиск персонажа по имени.""" + table = "characters" if game == "genshin" else "characters_hsr" + with sqlite3.connect(self.db_name) as db: + cursor = db.cursor() + cursor.execute(f"SELECT * FROM {table} WHERE name = ?", (name,)) + return cursor.fetchone() diff --git a/BotLibrary/system/bot_edit.py b/BotLibrary/system/bot_edit.py index 9553b5d..1ad9c49 100644 --- a/BotLibrary/system/bot_edit.py +++ b/BotLibrary/system/bot_edit.py @@ -1,153 +1,115 @@ # BotLibrary/system/bot_edit.py # Под-пакет установки настроек бота -from aiogram.types import ChatAdministratorRights +from aiogram import Bot from ProjectsFiles import BotEdit -from .bots import bot - -# Настройка логирования -log_type = "Edit" # Настройка экспорта из модуля -__all__ = ("set_adm_rights", "set_bot_name", "set_bot_description", "set_bot_short_description") +__all__ = ("BotRights",) -# Функция установки прав администратора -async def set_adm_rights(anonym: bool = BotEdit.is_anonymous, - manage_chat: bool = BotEdit.manage_chat, - delete_msg: bool = BotEdit.delete_messages, - manage_video_chats: bool = BotEdit.manage_video_chats, - restrict_members: bool = BotEdit.restrict_members, - promote_members: bool = BotEdit.promote_members, - change_info: bool = BotEdit.change_info, - invite_users: bool = BotEdit.invite_users, - post_stories: bool = BotEdit.post_stories, - edit_stories: bool = BotEdit.edit_stories, - delete_stories: bool = BotEdit.delete_stories, - post_messages: bool = BotEdit.post_messages, - edit_messages: bool = BotEdit.edit_messages, - pin_messages: bool = BotEdit.pin_messages, - manage_topics: bool = BotEdit.manage_topics,) -> None: +class BotRights: """ - Устанавливает права администратора для бота, если они отличаются от текущих. - Все через конфиги!!! - - :param anonym: Позволяет ли боту быть анонимным. - :param manage_chat: Разрешение на управление чатом. - :param delete_msg: Разрешение на удаление сообщений. - :param manage_video_chats: Разрешение на управление видеочатами. - :param restrict_members: Разрешение на ограничение участников (мут, бан). - :param promote_members: Разрешение на назначение администраторов. - :param change_info: Разрешение на изменение информации о группе/канале. - :param invite_users: Разрешение на приглашение новых участников. - :param post_stories: Разрешение на публикацию историй. - :param edit_stories: Разрешение на редактирование историй. - :param delete_stories: Разрешение на удаление историй. - :param post_messages: Разрешение на публикацию сообщений (только для каналов). - :param edit_messages: Разрешение на редактирование сообщений (только для каналов). - :param pin_messages: Разрешение на закрепление сообщений. - :param manage_topics: Разрешение на управление темами (в супергруппах). - - :return: Изменение прав администратора + Класс для установки прав администратора и метаинформации бота (имя, описания). """ - rights = ChatAdministratorRights( - is_anonymous=anonym, - can_manage_chat=manage_chat, - can_delete_messages=delete_msg, - can_manage_video_chats=manage_video_chats, - can_restrict_members=restrict_members, - can_promote_members=promote_members, - can_change_info=change_info, - can_invite_users=invite_users, - can_post_stories=post_stories, - can_edit_stories=edit_stories, - can_delete_stories=delete_stories, - can_post_messages=post_messages, - can_edit_messages=edit_messages, - can_pin_messages=pin_messages, - can_manage_topics=manage_topics, - ) + @staticmethod + async def set_administrator_rights(bot: Bot) -> None: + """ + Установка прав администратора в чатах. - # Применяем права только в случае изменения - current_rights = await bot.get_my_default_administrator_rights() - if current_rights != rights: - await bot.set_my_default_administrator_rights(rights) - else: - return + :param bot: Базовый объект бота. + :return: Измененные права по конфигу. + """ + from aiogram.types import ChatAdministratorRights + rights: ChatAdministratorRights = ChatAdministratorRights( + is_anonymous=BotEdit.is_anonymous, + can_manage_chat=BotEdit.manage_chat, + can_delete_messages=BotEdit.delete_messages, + can_manage_video_chats=BotEdit.manage_video_chats, + can_restrict_members=BotEdit.restrict_members, + can_promote_members=BotEdit.promote_members, + can_change_info=BotEdit.change_info, + can_invite_users=BotEdit.invite_users, + can_post_stories=BotEdit.post_stories, + can_edit_stories=BotEdit.edit_stories, + can_delete_stories=BotEdit.delete_stories, + can_post_messages=BotEdit.post_messages, + can_edit_messages=BotEdit.edit_messages, + can_pin_messages=BotEdit.pin_messages, + can_manage_topics=BotEdit.manage_topics, + ) + current_rights: ChatAdministratorRights = await bot.get_my_default_administrator_rights() + if current_rights != rights: + await bot.set_my_default_administrator_rights(rights) -# Функция установки имени бота с проверкой на ограничения -async def set_bot_name(new_name: str = BotEdit.name) -> None: - """ - Устанавливает имя бота, если оно отличается от текущего и соответствует ограничениям. + @staticmethod + async def set_name(bot: Bot) -> None: + """ + Установка имени бота. - :param new_name: Новое имя бота (config) - :return: Имя бота - """ - # Импортируем Logs внутри функции, чтобы избежать циклического импорта - from ..loggers.custom_loggers import Logs + :param bot: Базовый объект бота. + :return: Измененное имя бота. + """ + current_name: str = (await bot.get_me()).first_name + new_name: str = str(BotEdit.name) - # Получаем текущее имя бота - current_name = (await bot.get_me()).first_name + if not (1 <= len(new_name) <= 32): + from ..loggers import Logs + Logs.error(log_type="SET_NAME", user="BOT", text="Имя бота должно быть от 1 до 32 символов.") + return - # Проверка длины имени - if len(new_name) < 1 or len(new_name) > 32: - Logs.error(log_type=log_type, user="NAME_BOT", text="Имя бота должно быть от 1 до 32 символов.") - return # Выходим из функции, если имя некорректно + if current_name != new_name: + await bot.set_my_name(new_name) - # Проверяем, совпадает ли текущее имя с тем, которое мы хотим установить - if current_name != new_name: - await bot.set_my_name(new_name) - else: - return + @staticmethod + async def set_description(bot: Bot) -> None: + """ + Установка описания бота. + :param bot: Базовый объект бота. + :return: Измененное описание бота. + """ + from aiogram.types import BotDescription + current_description: BotDescription = await bot.get_my_description() + new_description: str = str(BotEdit.description) -# Функция установки описания бота с проверкой на ограничения -async def set_bot_description(new_description: str = BotEdit.description) -> None: - """ - Устанавливает описание бота, если оно отличается от текущего и соответствует ограничениям. + if not (0 < len(new_description) <= 255): + from ..loggers import Logs + Logs.error(log_type="SET_DESCRIPTION", user="BOT", text="Описание должно быть от 1 до 255 символов.") + return - :param new_description: Новое описание для бота (config) - :return: Описание бота - """ - # Импортируем Logs внутри функции, чтобы избежать циклического импорта - from ..loggers.custom_loggers import Logs + if current_description != new_description: + await bot.set_my_description(description=new_description) - # Получаем текущее описание бота - current_description = await bot.get_my_description() + @staticmethod + async def set_short_description(bot: Bot) -> None: + """ + Установка описания виджета. - # Проверка длины описания - if len(new_description) > 255 or len(new_description)==0: - Logs.error(log_type=log_type, user="DISCRIPT", text="Короткое описание бота не может превышать 255 символов или быть равно 0.") - return # Выходим из функции, если описание некорректно + :param bot: Базовый объект бота. + :return: Измененное описание виджета бота. + """ + from aiogram.types import BotShortDescription + current_short_description: BotShortDescription = await bot.get_my_short_description() + new_short_description: str = str(BotEdit.short_description) - # Проверяем, совпадает ли текущее описание с тем, которое мы хотим установить - if current_description != new_description: - await bot.set_my_description(description=new_description) - else: - return + if not (0 < len(new_short_description) <= 512): + from ..loggers import Logs + Logs.error(log_type="SET_SHORT_DESCRIPTION", user="BOT", text="Короткое описание должно быть от 1 до 512 символов.") + return + if current_short_description != new_short_description: + await bot.set_my_short_description(short_description=new_short_description) -# Функция установки короткого описания бота с проверкой на ограничения -async def set_bot_short_description(new_short_description: str = BotEdit.short_description) -> None: - """ - Устанавливает короткое описание бота, если оно отличается от текущего и соответствует ограничениям. + @staticmethod + async def all(bot: Bot) -> None: + """ + Применяет все настройки: права, имя, описание и короткое описание. - :param new_short_description: Новое описание виджета для бота (config) - :return: Короткое описание бота - """ - # Импортируем Logs внутри функции, чтобы избежать циклического импорта - from ..loggers.custom_loggers import Logs - - # Получаем текущее короткое описание бота - current_short_description = await bot.get_my_short_description() - - # Проверка длины короткого описания - if len(new_short_description) > 512 or len(new_short_description) == 0: - Logs.error(log_type=log_type, user="SHORT_DISCRIPT", text="Описание виджета не может превышать 512 символов или быть равно 0.") - return # Выходим из функции, если короткое описание некорректно - - # Проверяем, совпадает ли текущее короткое описание с тем, которое мы хотим установить - if current_short_description != new_short_description: - await bot.set_my_short_description(short_description=new_short_description) - else: - return + :param bot: Базовый объект бота. + :return: Изменение всех основных параметров бота. + """ + await BotRights.set_administrator_rights(bot) + await BotRights.set_name(bot) + await BotRights.set_description(bot) + await BotRights.set_short_description(bot) diff --git a/BotLibrary/system/bots.py b/BotLibrary/system/bots.py index a377419..bf90244 100644 --- a/BotLibrary/system/bots.py +++ b/BotLibrary/system/bots.py @@ -17,23 +17,14 @@ dp = Dispatcher() dp["started_at"] = get_host_time() dp["started_at_city"] = get_city_time() dp["is_active"] = True # Флаг активности бота -dp["logs"] = [] -dp["users"] = {} -dp["admins"] = {} -dp["sessions"] = {} -dp["task_queue"] = [] dp["configs"] = {"max_connections": 100, "retry_interval": 5, "time_format": BotVar.time_format} dp["metrics"] = {"messages_received": 0, "messages_sent": 0, "errors": 0} -dp["modules"] = {} -dp["state"] = {} -dp["scheduler"] = [] dp["handlers"] = {"on_message": [], "on_error": []} -dp["storage"] = {} dp["database"] = "SQLite3" # Создание экземпляра бота и его настройка -bot = Bot(token=bot_token, default=DefaultBotProperties( +bot: Bot = Bot(token=bot_token, default=DefaultBotProperties( parse_mode=BotVar.parse_mode, disable_notification=BotVar.disable_notification, protect_content=BotVar.protect_content, @@ -57,27 +48,31 @@ class BotInfo: """ id: int = None first_name: str = None - bot_owner: str = BotVar last_name: str = None username: str = None - description: str = None - short_description: str = None - can_join_groups: bool = False - can_read_all_group_messages: bool = False + description: str = '' + short_description: str = '' language_code: str = BotVar.language prefix: str = BotVar.prefix + bot_owner: str = BotVar is_premium: bool = False added_to_attachment_menu: bool = False supports_inline_queries: bool = False can_connect_to_business: bool = False has_main_web_app: bool = False + can_join_groups: bool = False + can_read_all_group_messages: bool = False @classmethod - def update(cls, bot_info) -> None: + async def info(cls, bots: Bot = bot) -> dict: """ - Обновляет данные о боте. - :param bot_info: Объект с данными о боте, полученные через API Telegram. + Получает информацию о боте через API и обновляет данные в классе. + + :param bots: Объект бота + :return: Словарь с данными о боте """ + bot_info = await bots.get_me() + cls.id = bot_info.id cls.first_name = bot_info.first_name cls.last_name = bot_info.last_name @@ -93,31 +88,28 @@ class BotInfo: cls.can_join_groups = getattr(bot_info, 'can_join_groups', False) cls.can_read_all_group_messages = getattr(bot_info, 'can_read_all_group_messages', False) + return cls.to_dict() -# Функция получения информации о боте -async def bot_get_info(bots: Bot = bot) -> dict: - """ - Получает информацию о боте и обновляет данные в классе BotInfo. - :param bots: Получение объекта бота в функцию. - :return: Словарь с данными о боте. - """ - bot_info_data = await bots.get_me() - BotInfo.update(bot_info_data) - return { - 'bot_info': bot_info_data, - 'id': bot_info_data.id, - 'first_name': bot_info_data.first_name, - 'last_name': bot_info_data.last_name, - 'username': bot_info_data.username, - 'description': getattr(bot_info_data, 'description', ''), - 'short_description': getattr(bot_info_data, 'short_description', ''), - 'language_code': bot_info_data.language_code, - 'prefix': BotVar.prefix, - 'is_premium': bot_info_data.is_premium, - 'added_to_attachment_menu': bot_info_data.added_to_attachment_menu, - 'supports_inline_queries': bot_info_data.supports_inline_queries, - 'can_connect_to_business': bot_info_data.can_connect_to_business, - 'has_main_web_app': bot_info_data.has_main_web_app, - 'can_join_groups': getattr(bot_info_data, 'can_join_groups', False), - 'can_read_all_group_messages': getattr(bot_info_data, 'can_read_all_group_messages', False), - } + @classmethod + def to_dict(cls) -> dict: + """ + Возвращает текущие данные в виде словаря. + """ + return { + 'id': cls.id, + 'first_name': cls.first_name, + 'last_name': cls.last_name, + 'username': cls.username, + 'description': cls.description, + 'short_description': cls.short_description, + 'language_code': cls.language_code, + 'prefix': cls.prefix, + 'bot_owner': cls.bot_owner, + 'is_premium': cls.is_premium, + 'added_to_attachment_menu': cls.added_to_attachment_menu, + 'supports_inline_queries': cls.supports_inline_queries, + 'can_connect_to_business': cls.can_connect_to_business, + 'has_main_web_app': cls.has_main_web_app, + 'can_join_groups': cls.can_join_groups, + 'can_read_all_group_messages': cls.can_read_all_group_messages, + } diff --git a/BotLibrary/system/directory.py b/BotLibrary/system/directory.py index 89621e5..c582110 100644 --- a/BotLibrary/system/directory.py +++ b/BotLibrary/system/directory.py @@ -2,51 +2,47 @@ # Создание пустых директорий при первом запуске import os -from ProjectsFiles import ProjectPath, TypeDirectory from typing import List +from ProjectsFiles import ProjectPath, TypeDirectory # Настройка экспорта из модуля -__all__ = ("create_directories", "setup_directories", "create_directory") +__all__ = ("Directory",) +class Directory: + @staticmethod + async def create_directory(directory: str) -> None: + """ + Создает директории, если они еще не существуют. -# Функция создания директории -async def create_directory(directory : str) -> None: - """ - Создает директории, если они еще не существуют. + :param directory: Путь к базовой директории. + :return: Создание директорий по определенному пути. + """ + os.makedirs(directory, exist_ok=True) - :param directory: Путь к базовой директории. - :return: Создание директорий по определенному пути. - """ - os.makedirs(directory) + @staticmethod + async def create_directories(base_directory: str, subdirectories: List[str]) -> None: + """ + Создает указанные поддиректории в указанной базовой директории. + :param base_directory: Путь к базовой директории. + :param subdirectories: Список поддиректорий, которые необходимо создать. + :return: Создание директорий по определенному пути. + """ + # Создание директорий и файлов в каждой из них + for subdirectory in subdirectories: + directory_path = os.path.join(base_directory, subdirectory) -# Функция создания поддиректорий -async def create_directories(base_directory: str, subdirectories: List[str]) -> None: - """ - Создает указанные поддиректории в указанной базовой директории. + # Проверка, существует ли директория, если нет - создаём + os.makedirs(directory_path, exist_ok=True) - :param base_directory: Путь к базовой директории. - :param subdirectories: Список поддиректорий, которые необходимо создать. - :return: Создание директорий по определенному пути. - """ - # Создание директорий и файлов в каждой из них - for subdirectory in subdirectories: - directory_path = os.path.join(base_directory, subdirectory) + @staticmethod + async def setup() -> None: + """ + Настройка начальных пустых директорий для проекта. - # Проверка, существует ли директория, если нет - создаём - if not os.path.exists(directory_path): - os.makedirs(directory_path) - - -# Функция установки начальных директорий -async def setup_directories() -> None: - """ - Настройка начальных пустых директорий для проекта. - - :return: Создание системы директорий по определенному пути. - """ - # Создание директорий для медиа файлов - await create_directories(ProjectPath.personal_media, TypeDirectory.media_directories) - await create_directories(ProjectPath.received_media, TypeDirectory.media_directories) - await create_directories(ProjectPath.received_avatars, TypeDirectory.avatar_directories) - # await create_directories(ProjectPath.msg, TypeDirectory.msg_directories) + :return: Создание системы директорий по определенному пути. + """ + # Создание директорий для медиа файлов + await Directory.create_directories(ProjectPath.personal_media, TypeDirectory.media_directories) + await Directory.create_directories(ProjectPath.received_media, TypeDirectory.media_directories) + await Directory.create_directories(ProjectPath.received_avatars, TypeDirectory.avatar_directories) diff --git a/BotLibrary/timer/start_time.py b/BotLibrary/timer/start_time.py index 75c05cd..9ff9626 100644 --- a/BotLibrary/timer/start_time.py +++ b/BotLibrary/timer/start_time.py @@ -1,7 +1,6 @@ # BotLibrary/timer/start_time.py # Получение времени по разным часовым поясам -import pytz from datetime import datetime from tzlocal import get_localzone from apscheduler.schedulers.asyncio import AsyncIOScheduler @@ -14,7 +13,6 @@ __all__ = ("scheduler", "get_city_time", "get_host_time") scheduler = AsyncIOScheduler(timezone=get_localzone().key) -# Функция получение иного времени def get_city_time(city: str = 'Europe/Moscow', time_format: str = BotVar.time_format) -> str: """ @@ -24,13 +22,13 @@ def get_city_time(city: str = 'Europe/Moscow', :param time_format: Шаблон форматирования времени (конфиг). :return: Строка, представляющая время в формате, заданном в BotVar.time_format. """ + from pytz import timezone # Устанавливаем временную зону для Москвы - city_tz = pytz.timezone(city) + city_tz = timezone(city) # Возвращаем строку с форматом времени return datetime.now(city_tz).strftime(time_format) -# Функция получение времени хоста def get_host_time(time_format: str = BotVar.time_format) -> str: """ Получение текущего времени хоста (локального времени). diff --git a/BotLibrary/validators/email_valid.py b/BotLibrary/validators/email_valid.py index 9782d30..2d4bd8f 100644 --- a/BotLibrary/validators/email_valid.py +++ b/BotLibrary/validators/email_valid.py @@ -2,13 +2,10 @@ # Создание валидации почты для проекта from typing import Optional -from email_validator import validate_email, EmailNotValidError # Настройка экспорта из этого модуля __all__ = ("valid_email",) - -# Функция проверки почты на корректность def valid_email(email: str) -> Optional[str]: """ Делает почтовый адрес корректным. @@ -16,10 +13,12 @@ def valid_email(email: str) -> Optional[str]: :param email: Почтовый адрес в виде строки. :return: Нормализованный почтовый адрес, если он валиден, иначе None. """ + from email_validator import validate_email, EmailNotValidError try: return validate_email(email).normalized + except EmailNotValidError as e: # Импортируем Logs внутри функции, чтобы избежать циклического импорта - from ..loggers.custom_loggers import Logs + from ..loggers.logs import Logs Logs.error(text=f"Ошибка в нормализировании почты: {e}", log_type="NormalEmail") - return None \ No newline at end of file + return None diff --git a/BotLibrary/validators/normal_word.py b/BotLibrary/validators/normal_word.py index ab122cf..62ad0ef 100644 --- a/BotLibrary/validators/normal_word.py +++ b/BotLibrary/validators/normal_word.py @@ -13,8 +13,9 @@ async def normal_words(word: str) -> str: """ try: return word.lower().capitalize() + except Exception as e: # Импортируем Logs внутри функции, чтобы избежать циклического импорта - from ..loggers.custom_loggers import Logs + from ..loggers.logs import Logs Logs.error(text=f"Ошибка в нормализировании слова: {e}", log_type="NormalWord") return word \ No newline at end of file diff --git a/BotLibrary/validators/url_valid.py b/BotLibrary/validators/url_valid.py index 609e2e2..ceaeb1a 100644 --- a/BotLibrary/validators/url_valid.py +++ b/BotLibrary/validators/url_valid.py @@ -1,13 +1,9 @@ # BotLibrary/validators/url_valid.py # Валидатор ссылок на регулярных выражениях -import re - # Настройка экспорта из этого модуля __all__ = ("valid_url", "url_to_text") - -# Функция определения является ли строка ссылкой def valid_url(url: str) -> bool: """ Проверяет, является ли строка валидной ссылкой (URL). @@ -15,7 +11,8 @@ def valid_url(url: str) -> bool: :param url: Строка для проверки. :return: True, если строка является валидным URL, иначе False. """ - url_pattern = re.compile( + from re import compile + url_pattern = compile( r'^(https?://)?' # Протокол (http или https, необязателен) r'([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}' # Домен r'(:\d+)?' # Порт (необязателен) @@ -24,7 +21,6 @@ def valid_url(url: str) -> bool: return bool(url_pattern.match(url)) -# Функция, что дает тексту ссылку на HTML def url_to_text(text: str, url: str) -> str: """ Преобразует текст в HTML ссылку с указанным URL. @@ -45,7 +41,7 @@ def url_to_text(text: str, url: str) -> str: except ValueError as e: # Импортируем Logs внутри функции, чтобы избежать циклического импорта - from ..loggers.custom_loggers import Logs + from ..loggers.logs import Logs # Логируем ошибку с использованием Logs.error, как указано Logs.error(text=f"Ошибка при создании ссылки: {e}", log_type="InvalidURL") raise e # Перебрасываем ошибку выше для дальнейшей обработки или уведомления \ No newline at end of file diff --git a/BotLibrary/validators/username.py b/BotLibrary/validators/username.py index 6f1cfc0..2dab866 100644 --- a/BotLibrary/validators/username.py +++ b/BotLibrary/validators/username.py @@ -7,7 +7,6 @@ from aiogram.types import Message __all__ = ("username", "username_to_text") -# Функция получения юзера или ID пользователя def username(message: Message) -> str: """ Возвращает юзернейм пользователя из сообщения, или ID, если юзернейм не указан. @@ -26,7 +25,6 @@ def username(message: Message) -> str: raise e # Перебрасываем ошибку выше для дальнейшей обработки -# Функция получение имени пользователя + ссылка на него def username_to_text(message: Message) -> str: """ Преобразует информацию о пользователе в строку с HTML-ссылкой. diff --git a/Documentation/docs.md b/Documentation/docs.md index cad3735..ddbd64d 100644 --- a/Documentation/docs.md +++ b/Documentation/docs.md @@ -14,15 +14,6 @@ -... - - - - - - - - - Второй модуль loggers: logs.py - создает логгеры, три кастомных уровня и 4 обычных. Он может создавать -NEW_USER diff --git a/GUI/__init__.py b/GUI/__init__.py deleted file mode 100644 index 732d5ec..0000000 --- a/GUI/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# GUI/__init__.py -# Инициализация пакета GUI, для работы с графическим интерфейсом - -from .console import * diff --git a/GUI/console.py b/GUI/console.py deleted file mode 100644 index f06444b..0000000 --- a/GUI/console.py +++ /dev/null @@ -1,167 +0,0 @@ -from tkinter import PhotoImage -import customtkinter as ctk # модуль для создания стилизованных интерфейсов -from PIL import Image, ImageTk -from BotLibrary import * # импорт настроек бота (например, имени, фамилии, и имени пользователя) - -# Создание роутера и настройка экспорта -__all__ = ("App", ) -command_text = "GUI" - - -# Настройка внешнего вида интерфейса -ctk.set_appearance_mode("System") # Установка режима внешнего вида: "System", "Dark" или "Light" -ctk.set_default_color_theme("blue") # Установка цветовой темы: "blue", "green", "dark-blue" - - -# Класс приложения, наследующий от customtkinter.CTk -class App(ctk.CTk): - def __init__(self): - super().__init__() # Инициализация базового класса - - # Настройка главного окна - self.title(f"{BotInfo.first_name} {BotInfo.last_name} - @{BotInfo.username}") # Заголовок окна - self.geometry(f"{700}x{580}") # Размер окна (ширина x высота) - - # Настройка сетки (разметка окна на ячейки) - self.grid_columnconfigure(0, weight=1) # Установка растяжения для 1-го столбца (боковая панель) - self.grid_columnconfigure(1, weight=9) # Установка растяжения для 2-го столбца (содержимое вкладок) - self.grid_rowconfigure((0, 1), weight=1) # Установка растяжения для первых трех строк - - # Создание боковой панели - self.sidebar_frame = ctk.CTkFrame(self, width=100, corner_radius=0) # Рамка боковой панели с уменьшенной шириной - self.sidebar_frame.grid(row=0, column=0, rowspan=4, sticky="nsew") # Расположение в сетке - self.sidebar_frame.grid_rowconfigure(4, weight=1) # Установка растяжения для 4-й строки - - # Логотип на боковой панели - self.logo_label = ctk.CTkLabel(self.sidebar_frame, text=BotInfo.username, - font=ctk.CTkFont(size=16, weight="bold")) # Метка с текстом - self.logo_label.grid(row=0, column=0, padx=10, pady=(20, 10)) # Расположение метки в сетке - - # Кнопки на боковой панели - self.sidebar_button_1 = ctk.CTkButton(self.sidebar_frame, text="Консоль", - command=self.switch_tab_console, - font=ctk.CTkFont(size=14)) - self.sidebar_button_1.grid(row=1, column=0, padx=10, pady=10) # Первая кнопка - self.sidebar_button_2 = ctk.CTkButton(self.sidebar_frame, text="База данных", - command=self.switch_tab_database, - font=ctk.CTkFont(size=14)) - self.sidebar_button_2.grid(row=2, column=0, padx=10, pady=10) # Вторая кнопка - self.sidebar_button_3 = ctk.CTkButton(self.sidebar_frame, text="Иное", - command=self.switch_tab_other, - font=ctk.CTkFont(size=14)) - self.sidebar_button_3.grid(row=3, column=0, padx=10, pady=10) # Третья кнопка - - # Новые кнопки "Запуск" и "Выключение" - self.start_button = ctk.CTkButton(self.sidebar_frame, text="Запуск", command=self.start_button_click, - font=ctk.CTkFont(size=14)) - self.start_button.grid(row=5, column=0, padx=10, pady=10) # Кнопка "Запуск" - - self.stop_button = ctk.CTkButton(self.sidebar_frame, text="Выключение", - command=self.stop_button_click, - font=ctk.CTkFont(size=14)) - self.stop_button.grid(row=6, column=0, padx=10, pady=10) # Кнопка "Выключение" - - # Элементы управления режимами и масштабированием интерфейса - self.appearance_mode_label = ctk.CTkLabel(self.sidebar_frame, text="Тема UI", anchor="w", - font=ctk.CTkFont(size=12)) - self.appearance_mode_label.grid(row=7, column=0, padx=10, pady=(10, 0)) # Метка для выбора темы - self.appearance_mode_optionemenu = ctk.CTkOptionMenu( - self.sidebar_frame, values=["Светлая", "Темная", "Система"], command=self.change_appearance_mode_event - ) # Меню выбора темы - self.appearance_mode_optionemenu.grid(row=8, column=0, padx=10, pady=(10, 10)) # Расположение меню - - self.scaling_label = ctk.CTkLabel(self.sidebar_frame, text="Масштабирование", - anchor="w", font=ctk.CTkFont(size=12)) # Метка для масштабирования - self.scaling_label.grid(row=9, column=0, padx=10, pady=(10, 0)) # Расположение метки - self.scaling_optionemenu = ctk.CTkOptionMenu( - self.sidebar_frame, values=["80%", "90%", "100%", "110%", "120%"], command=self.change_scaling_event - ) # Меню выбора масштаба - self.scaling_optionemenu.grid(row=10, column=0, padx=10, pady=(10, 20)) # Расположение меню - - # Создание вкладок - self.tabview = ctk.CTkTabview(self, width=250) # Виджет вкладок - self.tabview.grid(row=0, column=1, rowspan=3, columnspan=2, padx=(20, 0), pady=(20, 0), - sticky="nsew") # Растягиваем на все пустое пространство - self.tabview.add("Консоль") # Первая вкладка - self.tabview.add("База данных") # Вторая вкладка - self.tabview.add("Иное") # Третья вкладка - - # Вкладка "Консоль" с текстовым полем - self.textbox_tab_1 = ctk.CTkTextbox(self.tabview.tab("Консоль")) - self.textbox_tab_1.grid(row=0, column=0, padx=0, pady=0, sticky="nsew") - self.textbox_tab_1.insert("0.0", "Текст в Textbox на вкладке Консоль\n\n") - - # Вкладка "База данных" с текстовым полем - self.textbox_tab_2 = ctk.CTkTextbox(self.tabview.tab("База данных")) - self.textbox_tab_2.grid(row=0, column=0, padx=0, pady=0, sticky="nsew") - self.textbox_tab_2.insert("0.0", "Текст в Textbox на вкладке База данных\n\n") - - # Вкладка "Иное" с текстовым полем - self.textbox_tab_3 = ctk.CTkTextbox(self.tabview.tab("Иное")) - self.textbox_tab_3.grid(row=0, column=0, padx=0, pady=0, sticky="nsew") - self.textbox_tab_3.insert("0.0", "Текст в Textbox на вкладке Иное\n\n") - - # Растягиваем строки и столбцы вкладок - self.tabview.grid_rowconfigure(0, weight=1) # Растягиваем строку, где находится текстовое поле - self.tabview.grid_columnconfigure(0, weight=1) # Растягиваем столбец, где находится текстовое поле - - # Убедитесь, что в каждой из вкладок разрешено растяжение: - self.tabview.tab("Консоль").grid_rowconfigure(0, weight=1) - self.tabview.tab("База данных").grid_rowconfigure(0, weight=1) - self.tabview.tab("Иное").grid_rowconfigure(0, weight=1) - - self.tabview.tab("Консоль").grid_columnconfigure(0, weight=1) - self.tabview.tab("База данных").grid_columnconfigure(0, weight=1) - self.tabview.tab("Иное").grid_columnconfigure(0, weight=1) - - # Настройка элементов - self.appearance_mode_optionemenu.set("Темная") # Режим отображения установлен на "Dark" - self.scaling_optionemenu.set("100%") # Масштаб интерфейса установлен на 100% - - self.update() # Принудительное обновление окна после создания - - def change_appearance_mode_event(self, new_appearance_mode: str): - # Метод для изменения режима отображения интерфейса - if new_appearance_mode == "Светлая": - ctk.set_appearance_mode("Light") - elif new_appearance_mode == "Темная": - ctk.set_appearance_mode("Dark") - elif new_appearance_mode == "Система": - ctk.set_appearance_mode("System") - - def change_scaling_event(self, new_scaling: str): - # Метод для изменения масштаба интерфейса - new_scaling_float = int(new_scaling.replace("%", "")) / 100 - # Преобразование процента масштаба в дробное число - ctk.set_widget_scaling(new_scaling_float) - # Применение нового масштаба ко всем виджетам - - def switch_tab_console(self): - # Переключение на вкладку "Консоль" - self.tabview.grid(row=0, column=1, rowspan=3, columnspan=2, padx=(20, 0), pady=(20, 0), sticky="nsew") - self.tabview.set("Консоль") - - def switch_tab_database(self): - # Переключение на вкладку "База данных" - # Для возвращения вкладок - self.tabview.grid(row=0, column=1, rowspan=3, columnspan=2, padx=(20, 0), pady=(20, 0), sticky="nsew") - self.tabview.set("База данных") - - def switch_tab_other(self): - # Переключение на вкладку "Иное" - self.tabview.grid(row=0, column=1, rowspan=3, columnspan=2, padx=(20, 0), pady=(20, 0), sticky="nsew") - self.tabview.set("Иное") - - def start_button_click(self): - # Обработчик для кнопки "Запуск" - print("Запуск") - - def stop_button_click(self): - # Обработчик для кнопки "Выключение" - print("Выключение") - - -# Начало кода и запуск GUI -if __name__ == "__main__": - app = App() - app.mainloop() diff --git a/GUI/console_0.py b/GUI/console_0.py deleted file mode 100644 index ce73298..0000000 --- a/GUI/console_0.py +++ /dev/null @@ -1,74 +0,0 @@ -import sys -import customtkinter as ctk -import asyncio -import threading - -# Ваши настройки и модули -from BotLibrary import * -from BotCode.routers import router as main_router -from BotCode.routers import set_commands - - -# Класс для перенаправления стандартного вывода в текстовое поле -class Logger: - def __init__(self, text_widget): - self.text_widget = text_widget - - def write(self, message): - self.text_widget.insert("end", message) - self.text_widget.see("end") # Прокрутка к последнему сообщению - - def flush(self): - pass - - -# Основная функция для запуска бота -async def main_bot(): - just_fix_windows_console() # Подключение ANSI в Windows CMD - dp.include_router(main_router) # Подключение главного роутера - - await set_all() # Установка настроек бота - await set_commands() # Установка команд бота - await bot_get_info() # Получение информации о боте - - # Логирование в консоль и текстовое поле - logger.add(sys.stderr, colorize=True, format=logs_text, level="INFO") - logger.info(f"Начало запуска бота @{BotInfo.username}...") - - bot_info_out() # Включение опроса бота - await dp.start_polling(bot) # Запуск бота - - -# Класс графического интерфейса -class BotConsoleWindow(ctk.CTk): - def __init__(self): - super().__init__() - - # Настройка окна - self.title("Bot Console") - self.geometry("800x600") - - # Создание текстового поля для логов - self.log_text = ctk.CTkTextbox(self, wrap="word", width=780, height=500) - self.log_text.pack(padx=10, pady=10) - - # Перенаправление вывода в текстовое поле - sys.stdout = Logger(self.log_text) - sys.stderr = Logger(self.log_text) - - # Кнопка запуска бота - self.start_button = ctk.CTkButton(self, text="Запустить бота", command=self.start_bot) - self.start_button.pack(pady=10) - - def start_bot(self): - self.start_button.configure(state="disabled") # Отключить кнопку - threading.Thread(target=self.run_bot, daemon=True).start() - - def run_bot(self): - asyncio.run(main_bot()) - self.start_button.configure(state="normal") # Включить кнопку после завершения - - -if __name__ == "__main__": - app = BotConsoleWindow() - app.mainloop() diff --git a/ProjectsFiles/configs/.env b/ProjectsFiles/configs/.env index 62d5ed9..5cc6816 100644 --- a/ProjectsFiles/configs/.env +++ b/ProjectsFiles/configs/.env @@ -2,7 +2,7 @@ # Файл-хранилище всех секретных токенов и ключей # Токены от ботов телеграмма -BOT_TOKEN=7162745909:AAF9023WMRMm6uZYldtUcgqB_al4VmZpH1c +BOT_TOKEN=6393974380:AAFM0F3opRVzCvnhfmNaaNeW5-gJKXwSicU BOT1_TOKEN=7193685715:AAHFEnFreZGLQcHj8_wdWYJ2FLPrB-A-hzY BOT2_TOKEN=8076305634:AAGNoo4N-WVP9mbeD76G7SLClSsySw23nGw diff --git a/ProjectsFiles/configs/config.py b/ProjectsFiles/configs/config.py index ae9ee80..3a235f5 100644 --- a/ProjectsFiles/configs/config.py +++ b/ProjectsFiles/configs/config.py @@ -30,11 +30,11 @@ class BotEdit: Класс для хранения данных о боте: имя, описание, разрешения и настройки. """ # Разрешение на ведение логов - project_name: str = "Свалка Флуд" + project_name: str = "PRIMO" permission: bool = Permissions.bot_edit - name: str = "Стартовый бот" - description: str = "Описание бота" - short_description: str = "Описание виджета" + name: str = "Первозданная Жемчужина" + description: str = "Ваш помощник в удивительные миры! Prod. by:『@verdise』" + short_description: str = "Тех.поддержка: @verdise" is_anonymous: bool = False manage_chat: bool = True @@ -61,7 +61,8 @@ class BotVar: encod: str = "utf-8" language: str = "Python3-Aiogram" time_format: str = "%Y-%m-%d %H:%M:%S" - bd_names: str = 'SQLite3/bd.db' + bd_names: str = 'ProjectsFiles/database/users.db' + bd_path: str = 'ProjectsFiles/database' prefix: Tuple[str, ...] = ('$', '!', '.', '%', '&', ':', '|', '+', '-', '/', '~', '?') parse_mode: str = "HTML" # Устанавливаем формат HTML для всех сообщений diff --git a/SQLite3/bd.db b/ProjectsFiles/database/users.db similarity index 56% rename from SQLite3/bd.db rename to ProjectsFiles/database/users.db index 58b17b37fc71e79c3214b3d07e2b3b8fc2a025f7..3489a3a8d2887fdf99866370b84479b800fe48d3 100644 GIT binary patch delta 342 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 None: - tg_id = message.from_user.id - usernames = username(message) - first_name = message.from_user.first_name - last_name = message.from_user.last_name - - if Permissions.sql_user: - await add_user(tg_id, usernames, first_name, last_name, role="", status="active", user="user") - await update_user(tg_id=tg_id, first_name=first_name, last_name=last_name) - await update_user_messages(message=message) diff --git a/SQLite3/bd_func/bd_add_user.py b/SQLite3/bd_func/bd_add_user.py deleted file mode 100644 index 939f3a7..0000000 --- a/SQLite3/bd_func/bd_add_user.py +++ /dev/null @@ -1,39 +0,0 @@ -# SQLite3/bd_func/bd_add_user.py -# Добавление пользователя в базу данных bd|user - -import sqlite3 -from ProjectsFiles import BotVar - -# Настройка экспорта в модули -__all__ = ("add_user",) - - -# Функция добавления пользователя с последовательным user_id -async def add_user(tg_id: int, username: str, first_name: str, last_name: str, - role: str, status: str, user: str, bd_name: str = BotVar.bd_names) -> None: - with sqlite3.connect(bd_name) as db: - cursor = db.cursor() - - # Проверяем, существует ли пользователь с таким tg_id - cursor.execute("SELECT user_id FROM users WHERE tg_id = ?", (tg_id,)) - if cursor.fetchone(): - return # Пользователь уже существует, ничего не добавляем - - # Находим максимальный user_id - cursor.execute("SELECT MAX(user_id) FROM users") - max_id = cursor.fetchone()[0] - new_user_id = 1 if max_id is None else max_id + 1 - - # Добавляем нового пользователя - cursor.execute(''' - INSERT INTO users (user_id, tg_id, username, first_name, last_name, role, status, user) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) - ''', (new_user_id, tg_id, username, first_name, last_name, role, status, user)) - - # Добавляем запись в user_messages - cursor.execute(''' - INSERT INTO user_messages (user_id) - VALUES (?) - ''', (new_user_id,)) - - db.commit() diff --git a/SQLite3/bd_func/bd_get_user.py b/SQLite3/bd_func/bd_get_user.py deleted file mode 100644 index 16542ac..0000000 --- a/SQLite3/bd_func/bd_get_user.py +++ /dev/null @@ -1,16 +0,0 @@ -# SQLite3/bd_func/bd_get_user.py -# Получение информации о пользователе из базы данных bd|user - -import sqlite3 -from ProjectsFiles import BotVar - -# Настройка экспорта в модули -__all__ = ("get_user",) - - -# Функция для получения данных о пользователе -async def get_user(tg_id: int, bd_name: str = BotVar.bd_names) -> None: - with sqlite3.connect(bd_name) as db: - cursor = db.cursor() - cursor.execute("SELECT * FROM users WHERE tg_id = ?", (tg_id,)) - return cursor.fetchone() diff --git a/SQLite3/bd_func/bd_update_user.py b/SQLite3/bd_func/bd_update_user.py deleted file mode 100644 index 86d99b0..0000000 --- a/SQLite3/bd_func/bd_update_user.py +++ /dev/null @@ -1,39 +0,0 @@ -# SQLite3/bd_func/bd_update_user.py -# Обновление данных о пользователях в базу данных bd|user - -import sqlite3 -from ProjectsFiles import BotVar - -# Настройка экспорта в модули -__all__ = ("update_user",) - - -# Функция обновления пользователя -async def update_user(tg_id: int, username: str = None, first_name: str = None, last_name: str = None, - bd_name: str = BotVar.bd_names, role: str = None, user: str = None) -> None: - updates = [] - params = [] - - if username: - updates.append("username = ?") - params.append(username) - if first_name: - updates.append("first_name = ?") - params.append(first_name) - if last_name: - updates.append("last_name = ?") - params.append(last_name) - if role: - updates.append("role = ?") - params.append(role) - if user: - updates.append("user = ?") - params.append(user) - - if updates: - query = f"UPDATE users SET {', '.join(updates)} WHERE tg_id = ?" - params.append(tg_id) - with sqlite3.connect(bd_name) as db: - cursor = db.cursor() - cursor.execute(query, params) - db.commit() diff --git a/SQLite3/bd_func/bd_update_user_msg.py b/SQLite3/bd_func/bd_update_user_msg.py deleted file mode 100644 index b993587..0000000 --- a/SQLite3/bd_func/bd_update_user_msg.py +++ /dev/null @@ -1,78 +0,0 @@ -# SQLite3/bd_func/bd_update_user_msg.py -# Обновление данных о сообщениях пользователя в базу данных bd|message - -import sqlite3 -from aiogram import types -from datetime import timezone, datetime, timedelta - -from BotLibrary import type_msg -from ProjectsFiles import BotVar - -# Настройка экспорта в модули -__all__ = ("update_user_messages",) - - -# Функция обновления статистики сообщений пользователя -async def update_user_messages(message: types.Message, bd_name: str = BotVar.bd_names) -> None: - with sqlite3.connect(bd_name) as db: - cursor = db.cursor() - user_id = message.from_user.id # Используем user_id напрямую - - # Проверяем, существует ли запись в user_messages - cursor.execute( - "SELECT last_message_time, messages_per_day, messages_per_week, messages_per_month, total_messages FROM user_messages WHERE user_id = ?", - (user_id,)) - result = cursor.fetchone() - - # Время сообщения в московском времени - now = message.date.astimezone(timezone(timedelta(hours=3))) - today = now.date() - start_of_week = today - timedelta(days=today.weekday()) # Понедельник текущей недели - current_month = now.month - current_year = now.year - - last_message = message.text or type_msg(message) - last_message_id = message.message_id - - if result: - last_message_time, messages_per_day, messages_per_week, messages_per_month, total_messages = result - if last_message_time: - last_message_time = datetime.fromisoformat(last_message_time).astimezone(timezone(timedelta(hours=3))) - last_date = last_message_time.date() - last_week = last_date - timedelta(days=last_date.weekday()) - last_month = last_message_time.month - last_year = last_message_time.year - - # Обнуляем счетчики, если наступил новый день, неделя или месяц - if last_date != today: - messages_per_day = 0 - if last_week != start_of_week: - messages_per_week = 0 - if last_month != current_month or last_year != current_year: - messages_per_month = 0 - else: - messages_per_day, messages_per_week, messages_per_month = 0, 0, 0 - - # Увеличиваем счетчики - messages_per_day += 1 - messages_per_week += 1 - messages_per_month += 1 - total_messages += 1 - else: - # Если записи нет, создаем новую - messages_per_day, messages_per_week, messages_per_month, total_messages = 1, 1, 1, 1 - cursor.execute('INSERT INTO user_messages (user_id, last_message, last_message_id, last_message_time, messages_per_day, messages_per_week, messages_per_month, total_messages) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', - (user_id, last_message, last_message_id, now.isoformat(), messages_per_day, messages_per_week, messages_per_month, total_messages)) - db.commit() - return - - # Обновляем существующую запись - cursor.execute(''' - UPDATE user_messages - SET last_message = ?, last_message_id = ?, last_message_time = ?, - messages_per_day = ?, messages_per_week = ?, messages_per_month = ?, - total_messages = ? - WHERE user_id = ? - ''', (last_message, last_message_id, now.isoformat(), messages_per_day, - messages_per_week, messages_per_month, total_messages, user_id)) - db.commit() diff --git a/SQLite3/bd_func/bd_user_create.py b/SQLite3/bd_func/bd_user_create.py deleted file mode 100644 index 61aafaf..0000000 --- a/SQLite3/bd_func/bd_user_create.py +++ /dev/null @@ -1,283 +0,0 @@ -# SQLite3/bd_func/bd_user_create.py -# Создание базы данных - -import sqlite3 -from ProjectsFiles import BotVar - -# Настройка экспорта в модули -__all__ = ("create_user_db",) - - -# Функция создания базы данных -async def create_user_db(bd_name: str = BotVar.bd_names) -> None: - with sqlite3.connect(bd_name) as db: - cursor = db.cursor() - - # Таблица пользователей - cursor.execute(''' - CREATE TABLE IF NOT EXISTS users ( - user_id INTEGER PRIMARY KEY, - tg_id INTEGER NOT NULL UNIQUE, - username TEXT, - first_name TEXT, - last_name TEXT, - role TEXT DEFAULT NULL, - status TEXT DEFAULT 'active', - user TEXT DEFAULT 'user' - );''') - - # Таблица сообщений пользователей - cursor.execute(''' - CREATE TABLE IF NOT EXISTS user_messages ( - user_id INTEGER PRIMARY KEY, -- Уникальный ключ - last_message TEXT, - last_message_id INTEGER, - last_message_time TEXT, - messages_per_day INTEGER DEFAULT 0, - messages_per_week INTEGER DEFAULT 0, - messages_per_month INTEGER DEFAULT 0, - total_messages INTEGER DEFAULT 0, - FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE CASCADE - );''') - - # Таблица персонажей - cursor.execute(''' - CREATE TABLE IF NOT EXISTS characters ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - region TEXT NOT NULL, - name TEXT NOT NULL, - status TEXT DEFAULT 'Свободно', - user_id INTEGER DEFAULT NULL, - comment TEXT DEFAULT '', - FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE SET NULL - );''') - - # Таблица персонажей Хонкай - cursor.execute(''' - CREATE TABLE IF NOT EXISTS characters_hsr ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - region TEXT NOT NULL, - name TEXT NOT NULL, - status TEXT DEFAULT 'Свободно', - user_id INTEGER DEFAULT NULL, - comment TEXT DEFAULT '', - FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE SET NULL - );''') - - characters_genshin = [ - # Мондштадт - ("Мондштадт", "Венти", "Свободно", ""), - ("Мондштадт", "Кэйа", "Свободно", ""), - ("Мондштадт", "Альбедо", "Свободно", ""), - ("Мондштадт", "Дилюк", "Свободно", ""), - ("Мондштадт", "Мика", "Свободно", ""), - ("Мондштадт", "Беннет", "Свободно", ""), - ("Мондштадт", "Рэйзор", "Свободно", ""), - ("Мондштадт", "Эола", "Свободно", ""), - ("Мондштадт", "Мона", "Свободно", ""), - ("Мондштадт", "Джинн", "Свободно", ""), - ("Мондштадт", "Диона", "Свободно", ""), - ("Мондштадт", "Лиза", "Свободно", ""), - ("Мондштадт", "Ноэлль", "Свободно", ""), - ("Мондштадт", "Сахароза", "Свободно", ""), - ("Мондштадт", "Розария", "Свободно", ""), - ("Мондштадт", "Эмбер", "Свободно", ""), - ("Мондштадт", "Фишль", "Свободно", ""), - ("Мондштадт", "Барбара", "Свободно", ""), - - # Ли Юэ - ("Ли Юэ", "Чжун Ли", "Свободно", ""), - ("Ли Юэ", "Сяо", "Свободно", ""), - ("Ли Юэ", "Син Цю", "Свободно", ""), - ("Ли Юэ", "Чун Юнь", "Свободно", ""), - ("Ли Юэ", "Бай Чжу", "Свободно", ""), - ("Ли Юэ", "Е Лань", "Свободно", ""), - ("Ли Юэ", "Шень Хэ", "Свободно", ""), - ("Ли Юэ", "Гань Юй", "Свободно", ""), - ("Ли Юэ", "Ци Ци", "Свободно", ""), - ("Ли Юэ", "Кэ Цин", "Свободно", ""), - ("Ли Юэ", "Янь Фэй", "Свободно", ""), - ("Ли Юэ", "Нин Гуан", "Свободно", ""), - ("Ли Юэ", "Бэй Доу", "Свободно", ""), - ("Ли Юэ", "Яо Яо", "Свободно", ""), - ("Ли Юэ", "Ка Мин", "Свободно", ""), - ("Ли Юэ", "Сянь Юнь", "Свободно", ""), - ("Ли Юэ", "Юнь Цзинь", "Свободно", ""), - ("Ли Юэ", "Ху Тао", "Свободно", ""), - ("Ли Юэ", "Лань Янь", "Свободно", ""), - - # Инадзума - ("Инадзума", "Итто", "Свободно", ""), - ("Инадзума", "Горо", "Свободно", ""), - ("Инадзума", "Аято", "Свободно", ""), - ("Инадзума", "Хэйдзо", "Свободно", ""), - ("Инадзума", "Тома", "Свободно", ""), - ("Инадзума", "Кадзуха", "Свободно", ""), - ("Инадзума", "Кирара", "Свободно", ""), - ("Инадзума", "Кудзё Сара", "Свободно", ""), - ("Инадзума", "Ёимия", "Свободно", ""), - ("Инадзума", "Аяка", "Свободно", ""), - ("Инадзума", "Сангономия Кокоми", "Свободно", ""), - ("Инадзума", "Яэ Мико", "Свободно", ""), - ("Инадзума", "Райдэн Эи", "Свободно", ""), - ("Инадзума", "Саю", "Свободно", ""), - ("Инадзума", "Куки Синобу", "Свободно", ""), - ("Инадзума", "Мидзуки", "Свободно", ""), - - # Сумеру - ("Сумеру", "Аль-Хайтам", "Свободно", ""), - ("Сумеру", "Кавех", "Свободно", ""), - ("Сумеру", "Сайно", "Свободно", ""), - ("Сумеру", "Тигнари", "Свободно", ""), - ("Сумеру", "Сетос", "Свободно", ""), - ("Сумеру", "Нилу", "Свободно", ""), - ("Сумеру", "Нахида", "Свободно", ""), - ("Сумеру", "Лайла", "Свободно", ""), - ("Сумеру", "Кандакия", "Свободно", ""), - ("Сумеру", "Дори", "Свободно", ""), - ("Сумеру", "Дэхья", "Свободно", ""), - ("Сумеру", "Коллеи", "Свободно", ""), - ("Сумеру", "Фарузан", "Свободно", ""), - - # Фонтейн - ("Фонтейн", "Лини", "Свободно", ""), - ("Фонтейн", "Ризли", "Свободно", ""), - ("Фонтейн", "Невиллет", "Свободно", ""), - ("Фонтейн", "Фремине", "Свободно", ""), - ("Фонтейн", "Линетт", "Свободно", ""), - ("Фонтейн", "Эмилия", "Свободно", ""), - ("Фонтейн", "Клоринда", "Свободно", ""), - ("Фонтейн", "Навия", "Свободно", ""), - ("Фонтейн", "Шарлотта", "Свободно", ""), - ("Фонтейн", "Фурина", "Свободно", ""), - ("Фонтейн", "Тиори", "Свободно", ""), - ("Фонтейн", "Сиджвин", "Свободно", ""), - - # Натлан - ("Натлан", "Кинич", "Свободно", ""), - ("Натлан", "Оророн", "Свободно", ""), - ("Натлан", "Муалани", "Свободно", ""), - ("Натлан", "Ситлали", "Свободно", ""), - ("Натлан", "Шилонен", "Свободно", ""), - ("Натлан", "Иансан", "Свободно", ""), - ("Натлан", "Мавуика", "Свободно", ""), - ("Натлан", "Часка", "Свободно", ""), - - # Фатуи - ("Фатуи", "Тарталья", "Свободно", ""), - ("Фатуи", "Панталоне", "Свободно", ""), - ("Фатуи", "Дотторе", "Свободно", ""), - ("Фатуи", "Капитано", "Свободно", ""), - ("Фатуи", "Пьеро", "Свободно", ""), - ("Фатуи", "Пульничелла", "Свободно", ""), - ("Фатуи", "Синьора", "Свободно", ""), - ("Фатуи", "Арлекино", "Свободно", ""), - ("Фатуи", "Коломбина", "Свободно", ""), - ("Фатуи", "Царица", "Свободно", ""), - ("Фатуи", "Странник", "Свободно", ""), - - # Иные персонажи - ("Иные персонажи", "Итэр", "Свободно", ""), - ("Иные персонажи", "Люмин", "Свободно", ""), - ("Иные персонажи", "Элой", "Свободно", ""), - ("Иные персонажи", "Паймон", "Свободно", ""), - ("Иные персонажи", "Дайнслейф", "Свободно", ""), - ] - - characters_hsr = [ - # Ярило-6 - ("Ярило-6", "Броня", "Свободно", ""), - ("Ярило-6", "Гепард", "Свободно", ""), - ("Ярило-6", "Зеле", "Свободно", ""), - ("Ярило-6", "Клара", "Свободно", ""), - ("Ярило-6", "Лука", "Свободно", ""), - ("Ярило-6", "Наташа", "Свободно", ""), - ("Ярило-6", "Пела", "Свободно", ""), - ("Ярило-6", "Рысь", "Свободно", ""), - ("Ярило-6", "Сампо", "Свободно", ""), - ("Ярило-6", "Сервал", "Свободно", ""), - ("Ярило-6", "Хук", "Свободно", ""), - - # Станция «Герта» - ("Станция «Герта»", "Арлан", "Свободно", ""), - ("Станция «Герта»", "Аста", "Свободно", ""), - ("Станция «Герта»", "Великая Герта", "Свободно", ""), - ("Станция «Герта»", "Кукла «Герта»", "Свободно", ""), - ("Станция «Герта»", "Жуань Мэй", "Свободно", ""), - - # Пенакония - ("Пенакония", "Авантюрин", "Свободно", ""), - ("Пенакония", "Ахерон", "Свободно", ""), - ("Пенакония", "Галлахер", "Свободно", ""), - ("Пенакония", "Зарянка", "Свободно", ""), - ("Пенакония", "Миша", "Свободно", ""), - ("Пенакония", "Мистер Река", "Свободно", ""), - ("Пенакония", "Раппа", "Свободно", ""), - ("Пенакония", "Чёрный Лебедь", "Свободно", ""), - ("Пенакония", "Яшма", "Свободно", ""), - ("Пенакония", "Воскресенье", "Свободно", ""), - - # Звёздный Экспресс - ("Звёздный Экспресс", "Вельт", "Свободно", ""), - ("Звёздный Экспресс", "Келус", "Свободно", ""), - ("Звёздный Экспресс", "Стелла", "Свободно", ""), - ("Звёздный Экспресс", "Дань Хэн", "Свободно", ""), - ("Звёздный Экспресс", "Март 7", "Свободно", ""), - ("Звёздный Экспресс", "Химеко", "Свободно", ""), - ("Звёздный Экспресс", "Пом Пом", "Свободно", ""), - - # Галактика - ("Галактика", "Аргенти", "Свободно", ""), - ("Галактика", "Блэйд", "Свободно", ""), - ("Галактика", "Бутхилл", "Свободно", ""), - ("Галактика", "Доктор Рацио", "Свободно", ""), - ("Галактика", "Кафка", "Свободно", ""), - ("Галактика", "Светлячок", "Свободно", ""), - ("Галактика", "Искорка", "Свободно", ""), - ("Галактика", "Серебряный Волк", "Свободно", ""), - ("Галактика", "Топаз", "Свободно", ""), - - # Амфореус - ("Амфореус", "Аглая", "Свободно", ""), - ("Амфореус", "Мидей", "Свободно", ""), - ("Амфореус", "Трибби", "Свободно", ""), - - # Альянс Сяньчжоу - ("Альянс Сяньчжоу", "Байлу", "Свободно", ""), - ("Альянс Сяньчжоу", "Гуйнайфей", "Свободно", ""), - ("Альянс Сяньчжоу", "Линша", "Свободно", ""), - ("Альянс Сяньчжоу", "Лоча", "Свободно", ""), - ("Альянс Сяньчжоу", "Моцзэ", "Свободно", ""), - ("Альянс Сяньчжоу", "Пожиратель Луны", "Свободно", ""), - ("Альянс Сяньчжоу", "Сушан", "Свободно", ""), - ("Альянс Сяньчжоу", "Сюзи", "Свободно", ""), - ("Альянс Сяньчжоу", "Фуга", "Свободно", ""), - ("Альянс Сяньчжоу", "Фэйсяо", "Свободно", ""), - ("Альянс Сяньчжоу", "Ханья", "Свободно", ""), - ("Альянс Сяньчжоу", "Хохо", "Свободно", ""), - ("Альянс Сяньчжоу", "Цзин Юань", "Свободно", ""), - ("Альянс Сяньчжоу", "Цзиннлю", "Свободно", ""), - ("Альянс Сяньчжоу", "Цзяоцю", "Свободно", ""), - ("Альянс Сяньчжоу", "Цинцюэ", "Свободно", ""), - ("Альянс Сяньчжоу", "Юйкун", "Свободно", ""), - ("Альянс Сяньчжоу", "Юньли", "Свободно", ""), - ("Альянс Сяньчжоу", "Яньцин", "Свободно", ""), - ] - - # Проверяем, пуста ли таблица characters - cursor.execute("SELECT COUNT(*) FROM characters;") - if cursor.fetchone()[0] == 0: - cursor.executemany(''' - INSERT INTO characters (region, name, status, comment) - VALUES (?, ?, ?, ?); - ''', characters_genshin) - - # Проверяем, пуста ли таблица characters_hsr - cursor.execute("SELECT COUNT(*) FROM characters_hsr;") - if cursor.fetchone()[0] == 0: - cursor.executemany(''' - INSERT INTO characters_hsr (region, name, status, comment) - VALUES (?, ?, ?, ?); - ''', characters_hsr) - - db.commit() diff --git a/SQLite3/bd_func/status_user.py b/SQLite3/bd_func/status_user.py deleted file mode 100644 index dab1f4b..0000000 --- a/SQLite3/bd_func/status_user.py +++ /dev/null @@ -1,41 +0,0 @@ -# SQLite3/bd_func/status_user.py -# Проверка статусов пользователя с БД - -import sqlite3 -from aiogram import types -from ProjectsFiles import BotVar - -# Функция проверки статуса пользователя -async def status_user(message: types.Message, bd_path: str = BotVar.bd_names) -> str: - # Подключение к базе данных - bd = sqlite3.connect(bd_path) - tg_id = message.from_user.id - cursor = bd.cursor() - - # Запрос к базе данных для получения значения из столбца 'user' для конкретного tg_id - cursor.execute("SELECT user FROM users WHERE tg_id = ?", (tg_id,)) - - # Получаем результат - row = cursor.fetchone() - - # Словарь для сопоставления статусов - status_map = { - "ban": "Забаннен", - "user": "Пользователь", - "moderator": "Модератор", - "admin": "Администратор", - "so-owner": "Совладелец", - "owner": "Владелец", - } - - if row: - user_type = row[0] # предполагаем, что в столбце 'user' находится только одно значение - status: str = status_map.get(user_type, "Ошибка!") # Получаем статус или "Ошибка!" - else: - status: str = "Пользователь не найден" - - # Закрываем соединение с базой данных - bd.close() - - # Выводим статус - return status diff --git a/SQLite3/bd_func/username_to_id.py b/SQLite3/bd_func/username_to_id.py deleted file mode 100644 index cfbe136..0000000 --- a/SQLite3/bd_func/username_to_id.py +++ /dev/null @@ -1,3 +0,0 @@ -# SQLite3/bd_func/username_to_id.py -# - diff --git a/SQLite3/list_ids.json b/SQLite3/list_ids.json deleted file mode 100644 index 67a513a..0000000 --- a/SQLite3/list_ids.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "ban_list_ids": { - "6666666666666": "Забанненый" - }, - "important_adm_ids": { - "6751720805": "Лейн", - "7051557370": "Рикси", - "1570652377": "Риша", - "1398573474": "Финаки", - "1851081467": "Финик", - "929782381": "Хиде", - "6714237814": "Слешик", - "1686743480": "Полина", - "1184519857": "Катаз" - }, - "important_groups_ids": { - "1087968824": "GroupAnonymousBot", - "-1002442589033": "Любовники Сергея", - "-1002124483077": "Труба_Сквад", - "-1002123850090": "Тест_Чат", - "-1001552311087": "Все_Будет_Хорошо" - }, - "important_users_list_ids": { - "7145369362": "Артур", - "1219440132": "Данил", - "1443833264": "Виктор", - "5424384921": "Олег", - "556943853": "Ваня", - "1295708467": "Степан" - }, - "important_channel_ids": { - "10000000000000": "Канал1", - "20000000000000": "Канал2" - } -} diff --git a/Test/commands/ban_cmd.py b/Test/commands/ban_cmd.py deleted file mode 100644 index c022f72..0000000 --- a/Test/commands/ban_cmd.py +++ /dev/null @@ -1,63 +0,0 @@ -# BotCode/routers/commands/admin_cmd/ban_cmd.py -# Работа с админ-командой /ban, для блокировки пользователей (в разработке) -# Проверка на наличие блокировки пользователя в боте - - -from aiogram import Router, types -from aiogram.filters import Command -from BotLibrary import * - -# Создание роутера и настройка экспорта -__all__ = ("router", "banned_user", "ban_user_by_username",) -router = Router(name="ban_router") -command_text = "BAN" - - -# Функция проверки блокировки пользователя в боте -@router.message(lambda message: message.from_user.id in ListId.ban_list_id) -async def banned_user(message: types_msg.Message): - try: - # Вывод сообщения пользователю - chat_id = await find_chat_id(message) - text = (f"{TextDecorator.RED}Получено сообщение от забанненго пользователя" - f" из ({chat_id}) : {message.text}{TextDecorator.RESET_DECORATOR}") - await message.answer(f"Вы были забаннены в боте @{BotInfo.username}!") - - # Активация логгера - await cmd_logginger(message, command_text, text) - - return text - - # Проверка на ошибку и ее логирование - except Exception as e: - text_error = await error_cmd_logginger(message, command_text, e) - return text_error - - -# Обработчик команды /ban -@router.message(Command("ban", "ифт", "бан", ",fy", prefix=BotEdit.prefixs, ignore_case=True)) -async def ban_user_by_username(message: types_msg.Message): - try: - text = f"использовал(а) команду /{command_text.lower()}" - - # Получаем аргументы команды - args = message.get_args() # Вернет все, что идет после /ban - - # Проверка на наличие аргумента - if not args: - text = f"Пожалуйста, укажите ID или имя пользователя для бана. Пример: /ban 123456" - await message.reply(text) - return text - - # Вывод сообщения пользователю - await message.reply(text=f"Вы попытались забанить, обидно да?") - - # Активация логгера - await cmd_logginger(message, command_text, text) - - return text - - # Проверка на ошибку и ее логирование - except Exception as e: - text_error = await error_cmd_logginger(message, command_text, e) - return text_error diff --git a/Test/commands/find_username.py b/Test/commands/find_username.py deleted file mode 100644 index 7733ca5..0000000 --- a/Test/commands/find_username.py +++ /dev/null @@ -1,25 +0,0 @@ -# BotLibrary/analitics/find_username.py -# Нахождение юзернейма пользователя по id (в разработке) - -from loguru import logger -from BotLibrary.library.bots import bot - -# Настройка экспорта -__all__ = ("get_user_id_by_username",) -type_messages = "ID_USERNAME" - - -# Получение ID пользователя по юзернейму (в разработке) -async def get_user_id_by_username(chat_id, username): - try: - user = await bot.get_chat_member_by_username(chat_id, username) - if user: - return user.user.id - else: - return None - - # Проверка на ошибку и ее логирование (в разработке) - except Exception as e: - text_error = f"Ошибка при получении ID пользователя: {e}" - logger.bind(custom_variable="IDS", user_var=type_messages).error(text_error) - return text_error diff --git a/Test/old_files/__init__.py b/Test/old_files/__init__.py deleted file mode 100644 index 492dcdc..0000000 --- a/Test/old_files/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# BotCode/routers/old_files/__init__.py -# Инициализация старого пакета old_files, для хранения старых функций - -from aiogram import Router -from .media_func import router as media_old_router -from .regular_handlers import router as regular_router - - -# Объявление роутера и настройка экспорта -__all__ = ("router",) -router = Router(name="old_files_router") - - -# Список подключаемых роутеров сверху-вниз -router.include_routers( - regular_router, - media_old_router, -) - - -# Список подключаемых роутеров сверху-вниз -router.include_routers() diff --git a/Test/old_files/media_func.py b/Test/old_files/media_func.py deleted file mode 100644 index ac76469..0000000 --- a/Test/old_files/media_func.py +++ /dev/null @@ -1,55 +0,0 @@ -# BotCode/routers/old_files/media_func.py -# Некоторые функции для работы с медиа-сообщениями - -from re import Match -from aiogram import Router, F, types -from magic_filter import RegexpMode -from BotLibrary import * - -# Настройка экспорта модулей и роутера -__all__ = ("router",) -router = Router(name="media_func") - - -# @router.message(F.photo, ~F.caption) -async def handle_photo_wo_caption(message: types_msg.Message): - caption = f"Простите, я не могу это увидеть. Вы можете описать что это?" - await message.reply_photo( - photo=message.photo[-1].file_id, - caption=caption, - ) - return caption - - -# @router.message(F.photo, F.caption.contains("please")) -async def handle_photo_with_please_caption(message: types_msg.Message): - text = f"Простите, я не могу это увидеть." - await message.reply(text) - return text - - -# @router.message(any_media_filter, ~F.caption) -async def handle_any_media_wo_caption(message: types_msg.Message): - if message.document: - await message.reply_document( - document=message.document.file_id, - ) - return f"Перессылка документа" - - elif message.video: - await message.reply_video( - video=message.video.file_id, - ) - return f"Перессылка видео" - - else: - text = f"Я не могу это увидеть." - await message.reply(text) - return text - - -# @router.message(any_media_filter, F.caption) -async def handle_any_media_w_caption(message: types_msg.Message): - text = f"Что-то на медиа. Твой текст: {message.caption!r}" - await message.reply(text) - return text diff --git a/main.py b/main.py index 2219858..99e7f7a 100644 --- a/main.py +++ b/main.py @@ -1,21 +1,39 @@ # main.py # Основной код проекта, который и соединяет в себе все его возможности -import asyncio from BotLibrary import * -from BotCode import router as main_router +from ProjectsFiles import Permissions +from BotCode import router as main_routers # Запуск основного кода -async def main(): - # Функция установки - await setup() +async def main() -> None: + # Запуск логеров + Logs.setup() + + # Получение информации о боте + await BotInfo.info() + + # Вывод сообщение о запуске + Logs.start(text=f"Начало запуска бота @{BotInfo.username}...") + + # Создание пустых директорий + await Directory.setup() + + # Нужно ли удалить веб-хук + if Permissions.delete_webhook: + await bot.delete_webhook() + + # Установка необходимых прав + await BotRights.all(bot) + Logs.console() # Подключение главного маршрутизатора - dp.include_router(main_router) + dp.include_router(main_routers) # Включение опроса бота await dp.start_polling(bot) # Вечная загрузка бота if __name__ == "__main__": - asyncio.run(main()) + from asyncio import run + run(main()) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..e984e1f0f521ebc16dca8915c2bec76033ca5efb GIT binary patch literal 1466 zcmZ9MQEuWu5JdZXr9Da_h}rDsV?Xu=k#Yb;Y%pkzv5ZaP96m|CZYBefH8!|ws;j$e z{PDiBogHkWdt+-m+FxCFwy;~9Yt8JZ{jk8)*6Y{a5rxP9d_!y%?{rOKVm<4BqbsrA zI`91j=hg;Y-32FE=TeaK9;L0e*11pzg8T{7Ds~XdN^5PE-RL=qGgUJa_P18l^3NXa z$x7QgcJ!+IZ~0TzXzRb!{}M;;NvZw4;ydqMbZsV02~`M74W+a-TG6|eXF?Gjj!t9* z>cQ6MoO5Rbow!Ct1|jyk|5xr-Pjrx<%y+)XTd5ztFqPt9O5H)hmgiQ9;ph_^)RBY! zMJ1OVtMnd*)=pmx4`O-n`qt8dKN?#q&b)V?8?IV&y6^!k$hAZ z-iaPW#*N+{?9CBm1C45CLl0tlTN_Tlg|nlp(`??3f6EZrx`CL%u+b!7TIIAbR}t=Jhsvv zX2T2%=S3saS$OwgO^&+}_S#{&e>gd?Z)bJ+j1lA8@ahrv%rDM)d;?HW%bB