1.2 Вроде все готово

This commit is contained in:
Verum
2025-02-24 00:54:00 +07:00
parent 410b8eff59
commit 61241a2cf4
17 changed files with 385 additions and 171 deletions

View File

@@ -0,0 +1,5 @@
# BotCode/routers/commands/cmd_class/__init__.py
# Инициализация модуля cmd_class, для пользовательских классов команд
# Экспортирование модулей во внешние слои проекта
from .user_cmd_class import *

View File

@@ -9,9 +9,9 @@ from BotLibrary import *
class CommandHandler: class CommandHandler:
def __init__(self, name: str, keywords : list, def __init__(self, name: str, keywords : list,
description: str = "Описание команды", text_msg : str = "Сообщение", description: str = "Описание команды", text_msg : str = "Сообщение",
keyboard = None, prefix = BotVar.prefix, keyboard = None, prefix = BotVar.prefix, callbackdata = None,
ignore_case : bool = True, activate_keywoards : bool = True, ignore_case : bool = True, activate_keywoards : bool = True,
activate_commands : bool = True, activate_commands : bool = True, activate_callback : bool = True,
): ):
""" """
Универсальный обработчик команд для бота. Универсальный обработчик команд для бота.
@@ -27,6 +27,7 @@ class CommandHandler:
self.log_type = name.capitalize() self.log_type = name.capitalize()
self.description = description self.description = description
self.keywords = keywords self.keywords = keywords
self.callbackdata = callbackdata
self.text_msg = text_msg self.text_msg = text_msg
self.keyboard = keyboard self.keyboard = keyboard
@@ -35,13 +36,14 @@ class CommandHandler:
self.router.message(Command(*keywords, prefix=prefix, ignore_case=ignore_case))(self.handler) self.router.message(Command(*keywords, prefix=prefix, ignore_case=ignore_case))(self.handler)
if activate_keywoards: if activate_keywoards:
self.router.message(F.text.lower().in_(keywords))(self.handler) self.router.message(F.text.lower().in_(keywords))(self.handler)
if activate_callback:
self.router.message(F.text.lower().in_(callbackdata))(self.handler)
async def handler(self, message: types.Message): async def handler(self, message: types.Message):
"""Основной хэндлер команды.""" """Основной хэндлер команды."""
user = f"@{message.from_user.username or message.from_user.id}"
try: try:
logger.bind(log_type=self.name.capitalize(), user=user).info(f"использовал(а) команду /{self.name}") Logs.info(log_type=self.name.capitalize(), text=f"использовал(а) команду /{self.name}")
await message.reply( await message.reply(
text=self.text_msg, text=self.text_msg,
reply_markup=self.keyboard() if self.keyboard else None, reply_markup=self.keyboard() if self.keyboard else None,
@@ -49,4 +51,4 @@ class CommandHandler:
# Проверка на ошибку # Проверка на ошибку
except Exception as e: except Exception as e:
logger.bind(log_type=self.name.capitalize(), user=user).error(f"Ошибка команды: {e}") Logs.error(log_type=self.name.capitalize(), text=f"Ошибка команды: {e}")

View File

@@ -1,7 +1,7 @@
# BotCode/routers/commands/user_cmd/help_cmd.py # BotCode/routers/commands/user_cmd/help_cmd.py
# Работа с командой /help, для вывода помощи пользователю # Работа с командой /help, для вывода помощи пользователю
from .user_cmd_class import CommandHandler from ..cmd_class.user_cmd_class import CommandHandler
# Создание команды /help с нужными параметрами # Создание команды /help с нужными параметрами
help_cmd = CommandHandler( help_cmd = CommandHandler(

View File

@@ -1,7 +1,7 @@
# BotCode/routers/commands/user_cmd/start_cmd.py # BotCode/routers/commands/user_cmd/start_cmd.py
# # Работа с командой /start, для запуска бота # # Работа с командой /start, для запуска бота
from .user_cmd_class import CommandHandler from ..cmd_class.user_cmd_class import CommandHandler
from BotCode.keyboards import get_start_kb from BotCode.keyboards import get_start_kb
# Создание команды /start с нужными параметрами # Создание команды /start с нужными параметрами

View File

@@ -2,15 +2,22 @@
# Определение типа сообщения # Определение типа сообщения
from aiogram.types import ContentType from aiogram.types import ContentType
from aiogram.types import Message
# Настройка экспорта из модуля # Настройка экспорта из модуля
__all__ = ("types_message",) __all__ = ("types_message",)
# Функция определения типа сообщения # Функция определения типа сообщения
def types_message(message): def types_message(message: Message) -> str:
"""
Функция для определения типа сообщения на основе его содержимого.
:param message: Сообщение от пользователя.
:return: Описание типа сообщения.
"""
# Словарь для соответствия типов сообщений # Словарь для соответствия типов сообщений
content_types = { content_types: dict = {
ContentType.TEXT: "Текст", ContentType.TEXT: "Текст",
ContentType.PHOTO: "Фото", ContentType.PHOTO: "Фото",
ContentType.STICKER: "Стикер", ContentType.STICKER: "Стикер",
@@ -61,7 +68,7 @@ def types_message(message):
# Проверка для обычных сообщений # Проверка для обычных сообщений
for content_type, description in content_types.items(): for content_type, description in content_types.items():
if getattr(message, str(content_type.value)): if getattr(message, str(content_type.value)): # Если тип содержимого найден
return description return description
# Если сообщение не соответствует ни одному из типов # Если сообщение не соответствует ни одному из типов

View File

@@ -4,4 +4,5 @@
# Экспортирование модулей во внешние слои проекта # Экспортирование модулей во внешние слои проекта
from .logs import * from .logs import *
from .msg_logger import * from .msg_logger import *
from .custom_loggers import *
from .start_info_out import * from .start_info_out import *

View File

@@ -0,0 +1,69 @@
# BotLibrary/loggers/custom_loggers.py
# Кастомные логгеры для проекта, с более стандартизированным использованием
from loguru import logger
from ..validators import username
from aiogram.types import Message
# Настройка экспорта из модуля
__all__ = ("Logs",)
class Logs:
"""Класс для логирования с разными уровнями через loguru."""
@staticmethod
def debug(text: str = "Логирование!", log_type: str = "Logs", user: str = "Console", message: Message = None) -> None:
"""
Логирует сообщение на уровне DEBUG.
:param text: Сообщение для логирования.
:param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя.
"""
if message:
user = username(message)
logger.bind(log_type=log_type, user=user).debug(text)
@staticmethod
def info(text: str = "Логирование!", log_type: str = "Logs", user: str = "Console", message: Message = None) -> None:
"""
Логирует сообщение на уровне INFO.
:param text: Сообщение для логирования.
:param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя.
"""
if message:
user = username(message)
logger.bind(log_type=log_type, user=user).info(text)
@staticmethod
def warning(text: str = "Логирование!", log_type: str = "Logs", user: str = "Console", message: Message = None) -> None:
"""
Логирует сообщение на уровне WARNING.
:param text: Сообщение для логирования.
:param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя.
"""
if message:
user = username(message)
logger.bind(log_type=log_type, user=user).warning(text)
@staticmethod
def error(text: str = "Логирование!", log_type: str = "Logs", user: str = "Console", message: Message = None) -> None:
"""
Логирует сообщение на уровне ERROR.
:param text: Сообщение для логирования.
:param log_type: Тип лога (например, "Logs").
:param user: Имя пользователя или источник вызова лога.
:param message: Сообщение от пользователя, если необходимо извлечь имя.
"""
if message:
user = username(message)
logger.bind(log_type=log_type, user=user).error(text)

View File

@@ -5,34 +5,55 @@ import sys
from loguru import logger from loguru import logger
from ProjectsFiles import BotLogs from ProjectsFiles import BotLogs
# Создание обычного логгера + логгер в файл # Создание обычного логгера + логгер в файл
async def setup_logger(): async def setup_logger() -> None:
"""
Настройка логгеров для проекта, выводящих логи в консоль.
Логгеры конфигурируются в зависимости от настроек в BotLogs.
Если разрешено логирование, добавляются логи для уровней DEBUG, INFO, WARNING, ERROR.
"""
logger.remove() # Удаляем все логгеры logger.remove() # Удаляем все логгеры
# Настройка логирования в консоль для каждого уровня
if BotLogs.permission: if BotLogs.permission:
logger.add(sys.stderr,
colorize=True,
format=BotLogs.debug_text,
level="DEBUG",
filter=lambda record: record["level"].name == "DEBUG")
logger.add(sys.stderr, logger.add(sys.stderr,
colorize=True, colorize=True,
format=BotLogs.info_text, format=BotLogs.info_text,
level="INFO", level="INFO",
filter=lambda record: record["level"].name == "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, logger.add(sys.stderr,
colorize=True, colorize=True,
format=BotLogs.error_text, format=BotLogs.error_text,
level="ERROR", level="ERROR",
filter=lambda record: record["level"].name == "ERROR") filter=lambda record: record["level"].name == "ERROR")
if BotLogs.permission: # Добавление логгера для записи в файл (закомментированное, по необходимости активируется)
"""logger.add(ProjectPath.log_file, if BotLogs.permission_to_file:
rotation=BotLogs.max_size, # Uncomment and adjust if file logging is required
format=BotLogs.info_text, # logger.add(ProjectPath.log_file,
backtrace=True, # rotation=BotLogs.max_size,
diagnose=True, # format=BotLogs.info_text,
level="INFO", # backtrace=True,
filter=lambda record: record["level"].name == "INFO") # diagnose=True,
logger.add(ProjectPath.log_error_file, # level="INFO",
rotation=BotLogs.max_size, # filter=lambda record: record["level"].name == "INFO")
format=BotLogs.error_text, # logger.add(ProjectPath.log_error_file,
backtrace=True, # rotation=BotLogs.max_size,
diagnose=True, # format=BotLogs.error_text,
level="ERROR", # backtrace=True,
filter=lambda record: record["level"].name == "ERROR")""" # diagnose=True,
# level="ERROR",
# filter=lambda record: record["level"].name == "ERROR")
return

View File

@@ -1,19 +1,29 @@
# BotLibrary/loggers/msg_logger.py # BotLibrary/loggers/msg_logger.py
# Логгер для всех не обработанных сообщений # Логгер для всех не обработанных сообщений
from .logs import logger
from ProjectsFiles import BotLogs from ProjectsFiles import BotLogs
from .custom_loggers import Logs
from ..analytics.type_msg import types_message from ..analytics.type_msg import types_message
from aiogram.types import Message
# Настройка экспорта из модуля # Настройка экспорта из модуля
__all__ = ("logger_msg",) __all__ = ("logger_msg",)
# Создание функции логирования на обычные сообщения # Создание функции логирования на обычные сообщения
async def logger_msg(message, log_type : str = "Message"): async def logger_msg(message: Message, log_type: str = "Message") -> None:
user = f"@{message.from_user.username or message.from_user.id}" """
Логирует сообщение, если оно не обработано.
:param message: Сообщение от пользователя.
:param log_type: Тип лога (по умолчанию "Message").
"""
# Получаем username или id пользователя
user: str = f"@{message.from_user.username or message.from_user.id}"
# Логирование только если разрешено
if BotLogs.permission: if BotLogs.permission:
# Проверка на наличие текста и его типа # Проверка на наличие текста и его типа
if message.text is None: if message.text is None:
logger.bind(log_type=log_type, user=user).info(f"Получено сообщение из ({message.chat.id}) : {types_message(message)}") Logs.info(log_type=log_type, user=user, text=f"Получено сообщение из ({message.chat.id}) : {types_message(message)}")
else: else:
logger.bind(log_type=log_type, user=user).info(f"Получено сообщение из ({message.chat.id}) : {message.text}") Logs.info(log_type=log_type, user=user, text=f"Получено сообщение из ({message.chat.id}) : {message.text}")

View File

@@ -3,30 +3,34 @@
from time import sleep from time import sleep
from colorama import Fore from colorama import Fore
from ProjectsFiles import Permissions from ProjectsFiles import Permissions
from .logs import logger from .custom_loggers import Logs
from ..system import BotInfo from ..system import BotInfo
# Функция для получения информации о боте и выводе ее в консоль и файл # Функция для получения информации о боте и выводе ее в консоль и файл
def bot_info_out(): def bot_info_out() -> str:
"""
Собирает информацию о боте и выводит её в консоль, а также возвращает как строку.
:return: Информация о боте в виде строки.
"""
try: try:
bot_name = f"Основное имя: {BotInfo.first_name}\n" # Собираем данные о боте
bot_post_name = f" Доп. имя: {BotInfo.last_name}\n" bot_name: str = f"Основное имя: {BotInfo.first_name}\n"
bot_username = f" Юзернейм: @{BotInfo.username}\n" bot_post_name: str = f"Доп. имя: {BotInfo.last_name}\n"
bot_id = f" ID: {BotInfo.id}\n" bot_username: str = f"Юзернейм: @{BotInfo.username}\n"
bot_language = f" Языковой код: {BotInfo.language_code}\n" bot_id: str = f"ID: {BotInfo.id}\n"
bot_can_join_groups = f" Может ли вступать в группы: {BotInfo.can_join_groups}\n" bot_language: str = f"Языковой код: {BotInfo.language_code}\n"
bot_can_read_all_group_messages = f" Чтение всех сообщений: {BotInfo.can_read_all_group_messages}\n" bot_can_join_groups: str = f"Может ли вступать в группы: {BotInfo.can_join_groups}\n"
bot_is_premium = f" Является премиум-ботом: {BotInfo.is_premium}\n" bot_can_read_all_group_messages: str = f"Чтение всех сообщений: {BotInfo.can_read_all_group_messages}\n"
bot_added_to_attachment_menu = f" Добавлен в меню вложений: {BotInfo.added_to_attachment_menu}\n" bot_is_premium: str = f"Является премиум-ботом: {BotInfo.is_premium}\n"
bot_supports_inline_queries = f" Поддерживает инлайн-запросы: {BotInfo.supports_inline_queries}\n" bot_added_to_attachment_menu: str = f"Добавлен в меню вложений: {BotInfo.added_to_attachment_menu}\n"
bot_can_connect_to_business = f" Подключение к бизнес-аккаунтам: {BotInfo.can_connect_to_business}\n" bot_supports_inline_queries: str = f"Поддерживает инлайн-запросы: {BotInfo.supports_inline_queries}\n"
bot_has_main_web_app = f" Основное веб-приложение: {BotInfo.has_main_web_app}\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 = (f"{bot_name} {bot_post_name} {bot_username} {bot_id} {bot_language} " bot_all_info: str = (f"{bot_name} {bot_post_name} {bot_username} {bot_id} {bot_language} "
f"{bot_can_join_groups} {bot_can_read_all_group_messages} {bot_is_premium} " f"{bot_can_join_groups} {bot_can_read_all_group_messages} {bot_is_premium} "
f"{bot_added_to_attachment_menu} {bot_supports_inline_queries} {bot_can_connect_to_business} " f"{bot_added_to_attachment_menu} {bot_supports_inline_queries} {bot_can_connect_to_business} "
f"{bot_has_main_web_app}") f"{bot_has_main_web_app}")
@@ -38,6 +42,5 @@ def bot_info_out():
return bot_all_info return bot_all_info
# Проверка на ошибку и ее логирование
except Exception as e: except Exception as e:
logger.bind(log_type="INFO", user="Start_INFO").error(f"Ошибка при получении ID пользователя: {e}") Logs.error(log_type="INFO", user="Start_INFO", text=f"Ошибка при получении ID пользователя: {e}")

View File

@@ -4,13 +4,18 @@
from aiogram.types import ChatAdministratorRights from aiogram.types import ChatAdministratorRights
from ProjectsFiles import BotEdit from ProjectsFiles import BotEdit
from .bots import bot from .bots import bot
from ..loggers import logger from ..loggers import Logs
# Настройка логирования # Настройка логирования
log_type = "Edit" log_type = "Edit"
# Функция для выполнения всех настроек, если они не совпадают # Функция для выполнения всех настроек, если они не совпадают
async def set_all(): async def set_all() -> None:
"""
Выполняет все необходимые настройки бота, если они не совпадают с текущими значениями.
:return: None
"""
await set_adm_rights() await set_adm_rights()
await set_bot_name() await set_bot_name()
await set_bot_description() await set_bot_description()
@@ -18,8 +23,12 @@ async def set_all():
# Функция установки прав администратора # Функция установки прав администратора
async def set_adm_rights(): async def set_adm_rights() -> None:
# Применить права администратора для бота """
Устанавливает права администратора для бота, если они отличаются от текущих.
:return: None
"""
rights = ChatAdministratorRights( rights = ChatAdministratorRights(
is_anonymous=BotEdit.is_anonymous, is_anonymous=BotEdit.is_anonymous,
can_manage_chat=BotEdit.manage_chat, can_manage_chat=BotEdit.manage_chat,
@@ -45,15 +54,18 @@ async def set_adm_rights():
# Функция установки имени бота с проверкой на ограничения # Функция установки имени бота с проверкой на ограничения
async def set_bot_name(): async def set_bot_name() -> None:
"""
Устанавливает имя бота, если оно отличается от текущего и соответствует ограничениям.
:return: None
"""
# Получаем текущее имя бота # Получаем текущее имя бота
current_name = (await bot.get_me()).first_name current_name = (await bot.get_me()).first_name
# Проверка длины имени # Проверка длины имени
if len(BotEdit.name) < 1 or len(BotEdit.name) > 32: if len(BotEdit.name) < 1 or len(BotEdit.name) > 32:
# Логируем ошибку, если имя не соответствует ограничению Logs.error(log_type=log_type, user="NAME_BOT", text="Имя бота должно быть от 1 до 32 символов.")
(logger.bind(log_type=log_type, user="NAME_BOT")
.error("Имя бота должно быть от 1 до 32 символов."))
# Проверяем, совпадает ли текущее имя с тем, которое мы хотим установить # Проверяем, совпадает ли текущее имя с тем, которое мы хотим установить
if current_name != BotEdit.name: if current_name != BotEdit.name:
@@ -61,14 +73,18 @@ async def set_bot_name():
# Функция установки описания бота с проверкой на ограничения # Функция установки описания бота с проверкой на ограничения
async def set_bot_description(): async def set_bot_description() -> None:
"""
Устанавливает описание бота, если оно отличается от текущего и соответствует ограничениям.
:return: None
"""
# Получаем текущее описание бота # Получаем текущее описание бота
current_description = await bot.get_my_description() current_description = await bot.get_my_description()
# Проверка длины описания # Проверка длины описания
if len(BotEdit.description) > 255: if len(BotEdit.description) > 255:
(logger.bind(log_type=log_type, user="DISCRIPT") Logs.error(log_type=log_type, user="DISCRIPT", text="Короткое описание бота не может превышать 255 символов.")
.error("Короткое описание бота не может превышать 255 символов."))
# Проверяем, совпадает ли текущее описание с тем, которое мы хотим установить # Проверяем, совпадает ли текущее описание с тем, которое мы хотим установить
if current_description != BotEdit.description: if current_description != BotEdit.description:
@@ -76,14 +92,18 @@ async def set_bot_description():
# Функция установки короткого описания бота с проверкой на ограничения # Функция установки короткого описания бота с проверкой на ограничения
async def set_bot_short_description(): async def set_bot_short_description() -> None:
"""
Устанавливает короткое описание бота, если оно отличается от текущего и соответствует ограничениям.
:return: None
"""
# Получаем текущее короткое описание бота # Получаем текущее короткое описание бота
current_short_description = await bot.get_my_short_description() current_short_description = await bot.get_my_short_description()
# Проверка длины короткого описания # Проверка длины короткого описания
if len(BotEdit.short_description) > 512: if len(BotEdit.short_description) > 512:
(logger.bind(log_type=log_type, user="SHORT_DISCRIPT") Logs.error(log_type=log_type, user="SHORT_DISCRIPT", text="Описание виджета не может превышать 512 символов.")
.error("Описание виджета не может превышать 512 символов."))
# Проверяем, совпадает ли текущее короткое описание с тем, которое мы хотим установить # Проверяем, совпадает ли текущее короткое описание с тем, которое мы хотим установить
if current_short_description != BotEdit.short_description: if current_short_description != BotEdit.short_description:

View File

@@ -5,17 +5,18 @@ from aiogram import Dispatcher, Bot, F
from aiogram.enums import ParseMode from aiogram.enums import ParseMode
from aiogram.client.default import DefaultBotProperties from aiogram.client.default import DefaultBotProperties
from aiogram.utils.keyboard import InlineKeyboardBuilder, ReplyKeyboardBuilder from aiogram.utils.keyboard import InlineKeyboardBuilder, ReplyKeyboardBuilder
from aiogram.types import Bot as BotType
from ..timer import * from ..timer import get_host_time, get_moscow_time
from ProjectsFiles import * from ProjectsFiles import bot_token, BotVar
# Создание экземпляра диспатчера, строителей кнопок
dp = Dispatcher() # Создание строителей кнопок
rkb = ReplyKeyboardBuilder() rkb = ReplyKeyboardBuilder()
ikb = InlineKeyboardBuilder() ikb = InlineKeyboardBuilder()
# Настройка параметров диспатчера # Настройка параметров диспатчера
dp = Dispatcher()
dp["started_at"] = get_host_time() dp["started_at"] = get_host_time()
dp["started_at_msk"] = get_moscow_time() dp["started_at_msk"] = get_moscow_time()
dp["is_active"] = True # Флаг активности бота dp["is_active"] = True # Флаг активности бота
@@ -32,8 +33,7 @@ dp["handlers"] = {"on_message": [], "on_error": []}
dp["storage"] = {} dp["storage"] = {}
dp["database"] = None dp["database"] = None
# Настройки для бота
# Объявление экземпляров и переменных
bot_properties = DefaultBotProperties( bot_properties = DefaultBotProperties(
parse_mode=ParseMode.HTML, # Устанавливаем формат HTML для всех сообщений parse_mode=ParseMode.HTML, # Устанавливаем формат HTML для всех сообщений
disable_notification=True, # Отключаем уведомления при отправке сообщений disable_notification=True, # Отключаем уведомления при отправке сообщений
@@ -42,39 +42,49 @@ bot_properties = DefaultBotProperties(
link_preview_is_disabled=True, # Отключаем предварительный просмотр ссылок link_preview_is_disabled=True, # Отключаем предварительный просмотр ссылок
show_caption_above_media=False, # Показываем подпись выше медиа show_caption_above_media=False, # Показываем подпись выше медиа
) )
# Создание экземпляра бота
bot = Bot(token=bot_token, default=bot_properties) # Объявление бота bot = Bot(token=bot_token, default=bot_properties) # Объявление бота
F_Media = F.photo | F.files | F.video | F.animation | F.voice | F.video_note # Фильтр-медиа
# Фильтры для различных типов сообщений
F_Media = F.photo | F.files | F.video | F.animation | F.voice | F.video_note # Фильтр для медиа
F_All = F.text | F.photo | F.files | F.video | F.animation | F.voice | F.video_note # Фильтр на все F_All = F.text | F.photo | F.files | F.video | F.animation | F.voice | F.video_note # Фильтр на все
# Класс для хранения данных о боте
# Класс для хранения данных о боте (некоторые переменные даны как шаблон)
class BotInfo: class BotInfo:
"""
Класс для хранения данных о боте и их обновления.
"""
# Статические переменные для хранения данных # Статические переменные для хранения данных
id = None id: int = None
first_name = None first_name: str = None
last_name = None last_name: str = None
username = None username: str = None
description = None description: str = None
short_description = None short_description: str = None
can_join_groups = None can_join_groups: bool = None
can_read_all_group_messages = None can_read_all_group_messages: bool = None
language_code = BotVar.language language_code: str = BotVar.language
prefix = BotVar.prefix prefix: str = BotVar.prefix
is_premium = None is_premium: bool = None
added_to_attachment_menu = None added_to_attachment_menu: bool = None
supports_inline_queries = None supports_inline_queries: bool = None
can_connect_to_business = None can_connect_to_business: bool = None
has_main_web_app = None has_main_web_app: bool = None
# Метод для обновления данных
@classmethod @classmethod
def update(cls, bot_info): def update(cls, bot_info) -> None:
"""
Обновляет данные о боте.
:param bot_info: Объект с данными о боте, полученные через API Telegram.
"""
cls.id = bot_info.id cls.id = bot_info.id
cls.first_name = bot_info.first_name cls.first_name = bot_info.first_name
cls.last_name = bot_info.last_name cls.last_name = bot_info.last_name
cls.username = bot_info.username cls.username = bot_info.username
cls.description = getattr(bot_info, 'description', '') cls.description = getattr(bot_info, 'description', '')
cls.short_description = getattr(bot_info, 'description', '') cls.short_description = getattr(bot_info, 'short_description', '')
cls.language_code = bot_info.language_code cls.language_code = bot_info.language_code
cls.is_premium = bot_info.is_premium cls.is_premium = bot_info.is_premium
cls.added_to_attachment_menu = bot_info.added_to_attachment_menu cls.added_to_attachment_menu = bot_info.added_to_attachment_menu
@@ -85,15 +95,19 @@ class BotInfo:
cls.can_read_all_group_messages = getattr(bot_info, 'can_read_all_group_messages', None) cls.can_read_all_group_messages = getattr(bot_info, 'can_read_all_group_messages', None)
# Функция получения данных о боте async def bot_get_info() -> dict:
async def bot_get_info(): """
# Получение информации о боте Получает информацию о боте и обновляет данные в классе BotInfo.
:return: Словарь с данными о боте.
"""
# Получение информации о боте через API
bot_info_data = await bot.get_me() bot_info_data = await bot.get_me()
# Обновляем данные о боте в BotInfo # Обновляем данные о боте в BotInfo
BotInfo.update(bot_info_data) BotInfo.update(bot_info_data)
# Возвращаем обновленные данные # Возвращаем обновленные данные о боте
return { return {
'bot_info': bot_info_data, 'bot_info': bot_info_data,
'id': bot_info_data.id, 'id': bot_info_data.id,
@@ -101,7 +115,7 @@ async def bot_get_info():
'last_name': bot_info_data.last_name, 'last_name': bot_info_data.last_name,
'username': bot_info_data.username, 'username': bot_info_data.username,
'description': getattr(bot_info_data, 'description', ''), 'description': getattr(bot_info_data, 'description', ''),
'short_description': getattr(bot_info_data, 'description', ''), 'short_description': getattr(bot_info_data, 'short_description', ''),
'language_code': bot_info_data.language_code, 'language_code': bot_info_data.language_code,
'prefix': BotVar.prefix, 'prefix': BotVar.prefix,
'is_premium': bot_info_data.is_premium, 'is_premium': bot_info_data.is_premium,

View File

@@ -3,12 +3,19 @@
import os import os
from ProjectsFiles import ProjectPath, TypeDirectory from ProjectsFiles import ProjectPath, TypeDirectory
from typing import List
# Настройка экспорта из модуля # Настройка экспорта из модуля
__all__ = ("create_directories", "setup_directories") __all__ = ("create_directories", "setup_directories")
# Функция создания пустых директорий
async def create_directories(base_directory, subdirectories): async def create_directories(base_directory: str, subdirectories: List[str]) -> None:
"""
Создает указанные поддиректории в указанной базовой директории, если они еще не существуют.
:param base_directory: Путь к базовой директории.
:param subdirectories: Список поддиректорий, которые необходимо создать.
"""
# Создание директорий и файлов в каждой из них # Создание директорий и файлов в каждой из них
for subdirectory in subdirectories: for subdirectory in subdirectories:
directory_path = os.path.join(base_directory, subdirectory) directory_path = os.path.join(base_directory, subdirectory)
@@ -16,11 +23,17 @@ async def create_directories(base_directory, subdirectories):
# Проверка, существует ли директория, если нет - создаём # Проверка, существует ли директория, если нет - создаём
if not os.path.exists(directory_path): if not os.path.exists(directory_path):
os.makedirs(directory_path) os.makedirs(directory_path)
print(f"Создана директория: {directory_path}")
# Начальная установка пустых директорий async def setup_directories() -> None:
async def setup_directories(): """
Настройка начальных пустых директорий для проекта.
"""
# Создание директорий для медиа файлов
await create_directories(ProjectPath.personal_media, TypeDirectory.media_directories) await create_directories(ProjectPath.personal_media, TypeDirectory.media_directories)
# Раскомментируйте следующие строки, если необходимо создать другие директории
# await create_directories(ProjectPath.received_media, TypeDirectory.media_directories) # await create_directories(ProjectPath.received_media, TypeDirectory.media_directories)
# await create_directories(ProjectPath.bot_files, TypeDirectory.avatar_directories) # await create_directories(ProjectPath.bot_files, TypeDirectory.avatar_directories)
# await create_directories(ProjectPath.msg, TypeDirectory.msg_directories) # await create_directories(ProjectPath.msg, TypeDirectory.msg_directories)

View File

@@ -1,5 +1,5 @@
# BotLibrary/timer/start_time.py # BotLibrary/timer/start_time.py
# Получение времени по # Получение времени по разным часовым поясам
import pytz import pytz
from datetime import datetime from datetime import datetime
@@ -7,18 +7,31 @@ from tzlocal import get_localzone
from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.schedulers.asyncio import AsyncIOScheduler
from ProjectsFiles import BotVar from ProjectsFiles import BotVar
# Создание планировщика # Создание планировщика для работы с задачами по времени
scheduler = AsyncIOScheduler(timezone=get_localzone().key) scheduler = AsyncIOScheduler(timezone=get_localzone().key)
# Функция получение времени по Московскому времени def get_moscow_time() -> str:
def get_moscow_time(): """
Получение текущего времени по московскому времени.
:return: Строка, представляющая время в формате, заданном в BotVar.time_format.
"""
# Устанавливаем временную зону для Москвы
moscow_tz = pytz.timezone('Europe/Moscow') moscow_tz = pytz.timezone('Europe/Moscow')
# Получаем текущее время по московскому времени
moscow_time = datetime.now(moscow_tz) moscow_time = datetime.now(moscow_tz)
# Возвращаем строку с форматом времени
return moscow_time.strftime(BotVar.time_format) return moscow_time.strftime(BotVar.time_format)
# Функция получение времени хоста def get_host_time() -> str:
def get_host_time(): """
Получение текущего времени хоста (локального времени).
:return: Строка, представляющая локальное время в формате, заданном в BotVar.time_format.
"""
# Получаем текущее время на хосте
host_time = datetime.now() host_time = datetime.now()
# Возвращаем строку с форматом времени
return host_time.strftime(BotVar.time_format) return host_time.strftime(BotVar.time_format)

View File

@@ -2,15 +2,23 @@
# Создание валидации почты для проекта # Создание валидации почты для проекта
from email_validator import validate_email, EmailNotValidError from email_validator import validate_email, EmailNotValidError
from typing import Optional
# Настройка экспорта из этого модуля # Настройка экспорта из этого модуля
__all__ = ("valid_email",) __all__ = ("valid_email",)
# Функция проверки почты на корректность # Функция проверки почты на корректность
def valid_email(text: str) -> str | None: def valid_email(text: str) -> Optional[str]:
"""
Проверяет корректность почтового адреса.
:param text: Почтовый адрес в виде строки.
:return: Нормализованный почтовый адрес, если он валиден, иначе None.
"""
try: try:
# Проверка и нормализация email
email = validate_email(text) email = validate_email(text)
except EmailNotValidError:
return None
return email.normalized return email.normalized
except EmailNotValidError:
# Если email невалиден, можно добавить логирование или обработку ошибок
return None

View File

@@ -1,11 +1,19 @@
# BotLibrary/validators/username.py # BotLibrary/validators/username.py
# Получение юзера пользователя # Получение юзера или ID пользователя
from aiogram.types import message from aiogram.types import Message
# Настройка экспорта из модуля # Настройка экспорта из модуля
__all__ = ("username",) __all__ = ("username",)
# Функция получения юзера или id пользователя # Функция получения юзера или ID пользователя
def username(message: message.Message): def username(message: Message) -> str:
return f"@{message.from_user.username or message.from_user.id}" """
Возвращает юзернейм пользователя из сообщения, или ID, если юзернейм не указан.
:param message: Объект сообщения из aiogram.
:return: Строка с юзернеймом пользователя или его ID.
"""
if message.from_user:
return f"@{message.from_user.username}" if message.from_user.username else str(message.from_user.id)
return "@Unknown_User" # Если from_user отсутствует

View File

@@ -1,109 +1,129 @@
# ProjectsFiles/config.py # ProjectsFiles/config.py
# Файл-хранилище всех конфигов и настроек для бота # Файл-хранилище всех конфигов и настроек для бота
from typing import List, Tuple
# Список разрешений для бота # Список разрешений для бота
class Permissions: class Permissions:
bot_edit = False # Изменение имени, описания и виджета (True) """
delete_webhook = True # Удаление веб-хука (True) Класс для хранения настроек разрешений бота.
"""
bot_edit : bool = False # Разрешение на изменение имени, описания и виджета (True/False)
delete_webhook : bool = True # Разрешение на удаление веб-хука (True/False)
logging = True # Вывод логов в консоль (True) logging : bool = True # Разрешение на вывод логов в консоль (True/False)
logging_to_file = False # Вывод логов в файл (True) logging_to_file : bool = False # Разрешение на вывод логов в файл (True/False)
msg_logging = False # Логирование сообщений в консоль (В разработке) msg_logging : bool = False # Логирование сообщений в консоль (В разработке)
start_info_console = True # Вывод информации о боте в начале (True) start_info_console : bool = True # Вывод информации о боте в начале (True/False)
sql_user = True # Регистрирование в базу данных (True) sql_user : bool = True # Разрешение на регистрацию в базе данных (True/False)
# Имя, описание и виджет бота(при наличии баннера виджета) # Имя, описание и виджет бота(при наличии баннера виджета)
class BotEdit: class BotEdit:
"""
Класс для хранения данных о боте: имя, описание, разрешения и настройки.
"""
# Разрешение на ведение логов # Разрешение на ведение логов
permission = Permissions.bot_edit permission : bool = Permissions.bot_edit
name = "Стартовый бот" name : str = "Стартовый бот"
description = "Описание бота" description : str = "Описание бота"
short_description = "Описание виджета" short_description : str = "Описание виджета"
is_anonymous=False is_anonymous : bool = False
manage_chat=True manage_chat : bool = True
delete_messages=True delete_messages : bool = True
manage_video_chats=True manage_video_chats : bool = True
restrict_members=True restrict_members : bool = True
promote_members=True promote_members : bool = True
change_info=True change_info : bool = True
invite_users=True invite_users : bool = True
post_stories=True post_stories : bool = True
edit_stories=True edit_stories : bool = True
delete_stories=True delete_stories : bool = True
post_messages=True post_messages : bool = True
edit_messages=True edit_messages : bool = True
pin_messages=True pin_messages : bool = True
manage_topics=True manage_topics : bool = True
# Хранение параметров проекта # Хранение параметров проекта
class BotVar: class BotVar:
encod = "utf-8" """
language = "Python3-Aiogram" Класс для хранения глобальных параметров проекта.
time_format = "%Y-%m-%d %H:%M:%S" """
prefix = ('$', '!', '.', '%', '&', ':', '|', '+', '-', '/', '~', '?') encod : str = "utf-8"
language : str = "Python3-Aiogram"
time_format : str = "%Y-%m-%d %H:%M:%S"
prefix : Tuple[str, ...] = ('$', '!', '.', '%', '&', ':', '|', '+', '-', '/', '~', '?')
# Класс для хранения типов директорий # Класс для хранения типов директорий
class TypeDirectory: class TypeDirectory:
"""
Класс для хранения типов сообщений и директорий, которые нужно создать.
"""
# Типы сообщений и список директорий для создания # Типы сообщений и список директорий для создания
private_msg = "Личные" private_msg : str = "Личные"
group_msg = "Группы" group_msg : str = "Группы"
# Названия директорий-хранилищ # Названия директорий-хранилищ
avatar = "Avatar" avatar : str = "Avatar"
photo = "Photo" photo : str = "Photo"
video = "Video" video : str = "Video"
videonote = "VideoNote" videonote : str = "VideoNote"
gif = "GIF" gif : str = "GIF"
files = "Document" files : str = "Document"
voice = "Voice" voice : str = "Voice"
media_directories = [avatar, photo, video, videonote, gif, files, voice] media_directories : List[str] = [avatar, photo, video, videonote, gif, files, voice]
# Класс создания директорий проекта # Класс создания директорий проекта
class ProjectPath: class ProjectPath:
BotLogs = "BotLogs" """
Класс для хранения путей к проектам и логам.
personal_media = "ProjectsFiles/media" """
BotLogs : str = "BotLogs"
personal_media : str = "ProjectsFiles/media"
# Настройки логирования бота # Настройки логирования бота
class BotLogs: class BotLogs:
"""
Класс для хранения параметров логирования: шаблоны логов, разрешения, размеры файлов и т. д.
"""
# Разрешение на ведение логов # Разрешение на ведение логов
permission = Permissions.logging permission : bool = Permissions.logging
permission_to_file = Permissions.logging_to_file permission_to_file : bool = Permissions.logging_to_file
permission_msg = Permissions.msg_logging permission_msg : bool = Permissions.msg_logging
# Максимальный размер лог-файла # Максимальный размер лог-файла
max_size = "500 MB" max_size : str = "500 MB"
# Шаблон логов для отладки # Шаблон логов для отладки
debug_text = ( debug_text : str = (
"<cyan>{time:YYYY-MM-DD HH:mm:ss}</cyan> <red>|</red> " "<cyan>{time:YYYY-MM-DD HH:mm:ss}</cyan> <red>|</red> "
"<magenta>DEBUG-{extra[log_type]}</magenta> <red>|</red> " "<magenta>DEBUG-{extra[log_type]}</magenta> <red>|</red> "
"<yellow>{extra[user]} |</yellow> <level>{message}</level>" "<yellow>{extra[user]} |</yellow> <level>{message}</level>"
) )
# Шаблон логов для информации # Шаблон логов для информации
info_text = ( info_text : str = (
"<green>{time:YYYY-MM-DD HH:mm:ss}</green> <red>|</red> " "<green>{time:YYYY-MM-DD HH:mm:ss}</green> <red>|</red> "
"<blue>PRIMO-{extra[log_type]}</blue> <red>|</red> " "<blue>PRIMO-{extra[log_type]}</blue> <red>|</red> "
"<red>{extra[user]} |</red> <level>{message}</level>" "<red>{extra[user]} |</red> <level>{message}</level>"
) )
# Шаблон логов для предупреждений # Шаблон логов для предупреждений
warning_text = ( warning_text : str = (
"<yellow>{time:YYYY-MM-DD HH:mm:ss}</yellow> <red>|</red> " "<yellow>{time:YYYY-MM-DD HH:mm:ss}</yellow> <red>|</red> "
"<orange>WARNING-{extra[log_type]}</orange> <red>|</red> " "<orange>WARNING-{extra[log_type]}</orange> <red>|</red> "
"<red>{extra[user]} |</red> <level>{message}</level>" "<red>{extra[user]} |</red> <level>{message}</level>"
) )
# Шаблон логов для ошибок # Шаблон логов для ошибок
error_text = ( error_text : str = (
"<level>{time:YYYY-MM-DD HH:mm:ss} | " "<level>{time:YYYY-MM-DD HH:mm:ss} | "
"<bold>ERROR-{extra[log_type]}</bold> | " "<bold>ERROR-{extra[log_type]}</bold> | "
"{extra[user]} | {message}</level>" "{extra[user]} | {message}</level>"