📁 Мультифайловость
Введение
По мере роста вашего модуля хранение всего кода в одном файле может стать обременительным. PBModular предоставляет класс ModuleExtension
(base.mod_ext.ModuleExtension
), чтобы помочь вам организовать ваш код в нескольких файлах, сохраняя при этом доступ к контексту основного модуля и беспрепятственно регистрируя обработчики.
INFO
Это отличается от BaseExtension
(base.base_ext.BaseExtension
), которое влияет на все модули при загрузке. ModuleExtension
специфично для модуля, определяющего его.
Шаг 1: Создайте файл расширения
Создайте новый Python-файл в каталоге вашего модуля (подкаталог extensions/ является обычной практикой, но не обязателен). Определите в этом файле класс, который наследуется от ModuleExtension
.
Пример (extensions/extra_handlers.py):
# extensions/extra_handlers.py
import logging
from base.mod_ext import ModuleExtension
from base.module import command, message, HelpPage, ModuleInfo # Импортируйте необходимые типы
from pyrogram import Client, filters
from pyrogram.types import Message, InlineKeyboardButton
from pyrogram.handlers import MessageHandler # Для пользовательских обработчиков
# При желании получите логгер, специфичный для этого файла расширения.
# Примечание: self.logger в методах будет ссылаться на логгер *главного* модуля
ext_logger = logging.getLogger(__name__)
class ExtraHandlersExtension(ModuleExtension):
def on_init(self):
"""Пользовательская инициализация для этого расширения."""
# Это выполняется *после* stage2 основного модуля и его on_init.
# Доступ к унаследованным атрибутам:
ext_logger.info(f"ExtraHandlersExtension initialized for module: {self.__base_mod.module_info.name}")
ext_logger.info(f"Current language in main module: {self.cur_lang}")
# self.bot, self.S, self.rawS, self.loader, self.db (если есть) доступны.
# self.logger указывает на логгер главного модуля.
@command("extra")
async def extra_cmd(self, _: Client, message: Message):
"""Дополнительная команда, определенная в расширении."""
# Используйте переводы из основного модуля
reply_text = self.S["extra_command_reply"]
await message.reply(reply_text.format(user=message.from_user.first_name))
@message(filters.photo & filters.private)
async def photo_handler(self, client: Client, message: Message):
"""Работа с фотографиями в приватном чате."""
if self.db: # Проверьте, доступна ли БД
# Пример взаимодействия с БД (требует настройки БД в главном модуле)
# async with self.db.session_maker() as session:
# # ... сделать что-нибудь с базой данных ...
# pass
await message.reply(self.S["photo_received_db_notice"])
else:
await message.reply(self.S["photo_received"])
ext_logger.info(f"Получил фотографию от {message.from_user.id}")
# Пример добавления пользовательского обработчика Pyrogram
@property
def custom_handlers(self) -> list[MessageHandler]:
"""Определите обработчики, которые не вписываются в стандартные декораторы."""
# return [
# MessageHandler(self.raw_update_handler, filters.chat(config.log_chat))
# ]
return [] # Возвращаем пустой список, если пользовательские обработчики не нужны
# async def raw_update_handler(self, client: Client, update):
# """Обработка специфических необработанных обновлений."""
# # Обработка необработанных обновлений
# ext_logger.debug(f"Raw update handler caught: {update}")
# pass
# Пример доступа к состоянию основного модуля или методам, если это необходимо (используйте self.__base_mod)
def get_main_module_version(self) -> str:
return self.__base_mod.module_info.version
Шаг 2: Зарегистрируйте расширение в классе Главного Модуля
В классе вашего главного модуля (например, в main.py
) переопределите свойство module_extensions
, чтобы оно возвращало список, содержащий класс(ы) расширения.
# main.py (или там, где находится ваш основной класс BaseModule)
from base.module import BaseModule # Другие импорты...
from typing import Type # Тип импорта для подсказки
# Импортируйте класс расширения
from .extensions.extra_handlers import ExtraHandlersExtension
class MyModule(BaseModule):
# ... другие свойства и методы ...
@property
def module_extensions(self) -> list[Type[ModuleExtension]]:
"""Register module extensions."""
return [
ExtraHandlersExtension,
# Добавьте сюда другие классы расширения, если у вас их больше
]
# ... остальная часть класса модуля ...
Как это работает
- Когда выполняется метод
stage2
главного модуля, он перебирает классы, перечисленные вmodule_extensions
. - Для каждого класса расширения создается экземпляр, передавая экземпляр главного модуля (
self
) в__init__
расширения. - Модуль расширения
ModuleExtension.__init
__ автоматически:- Наследует ключевые атрибуты (
bot
,S
,rawS
,cur_lang
,loader
,logger
,state_machine
,get_sm
) от основного экземпляра модуля. - Сохраняет ссылку на экземпляр основного модуля как
self.__base_mod
. - Вызывает
self.__base_mod.register_all(ext=self)
, который сканирует экземпляр расширения на наличие методов@command
,@callback_query
,@message
и регистрирует их в Pyrogram, как и обработчики в основном классе. - Вызывает метод расширения
on_init
.
- Наследует ключевые атрибуты (
- Обработчики, определенные в расширении, теперь функционируют так, как если бы они были частью основного модуля, с доступом к его общим ресурсам (
S
, db и т.д.).
Это позволяет аккуратно разделить задачи, например, поместить все административные команды в один файл расширения, пользовательские команды - в другой, а логику фоновых задач - в третий, при этом разделяя настройку и состояние основного модуля.