📁 Multifiling
Introduction
As your module grows, keeping all the code in a single file can become cumbersome. PBModular provides the ModuleExtension
class (base.mod_ext.ModuleExtension
) to help you organize your code into multiple files while maintaining access to the main module's context and seamlessly registering handlers.
INFO
This is distinct from BaseExtension
(base.base_ext.BaseExtension
), which affects all modules during loading. ModuleExtension
is specific to the module defining it.
Step 1: Create an Extension File
Create a new Python file within your module's directory (a sub-directory like extensions/ is common practice but not mandatory). Define a class in this file that inherits from ModuleExtension
.
Example (extensions/extra_handlers.py):
# extensions/extra_handlers.py
import logging
from base.mod_ext import ModuleExtension
from base.module import command, message, HelpPage, ModuleInfo # Import necessary types
from pyrogram import Client, filters
from pyrogram.types import Message, InlineKeyboardButton
from pyrogram.handlers import MessageHandler # For custom handlers
# Get a logger specific to this extension file if desired
# Note: self.logger within methods will refer to the *main* module's logger
ext_logger = logging.getLogger(__name__)
class ExtraHandlersExtension(ModuleExtension):
def on_init(self):
"""Custom initialization for this extension."""
# This runs *after* the main module's stage2 and its on_init.
# Access inherited attributes:
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 (if available) are accessible.
# self.logger points to the main module's logger.
@command("extra")
async def extra_cmd(self, _: Client, message: Message):
"""An extra command defined in the extension."""
# Use translations from the main module
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):
"""Handles photos in private chat."""
if self.db: # Check if DB is available
# Example DB interaction (requires db setup in main module)
# async with self.db.session_maker() as session:
# # ... do something with the db ...
# pass
await message.reply(self.S["photo_received_db_notice"])
else:
await message.reply(self.S["photo_received"])
ext_logger.info(f"Received photo from {message.from_user.id}")
# Example of adding a custom Pyrogram handler
@property
def custom_handlers(self) -> list[MessageHandler]:
"""Define handlers that don't fit standard decorators."""
# return [
# MessageHandler(self.raw_update_handler, filters.chat(config.log_chat))
# ]
return [] # Return empty list if no custom handlers needed
# async def raw_update_handler(self, client: Client, update):
# """Handles specific raw updates."""
# # Process the raw update
# ext_logger.debug(f"Raw update handler caught: {update}")
# pass
# Example accessing main module state or methods if needed (use self.__base_mod)
def get_main_module_version(self) -> str:
return self.__base_mod.module_info.version
Step 2: Register the Extension in the Main Module Class
In your main module class (e.g., in main.py
), override the module_extensions
property to return a list containing your extension class(es).
# main.py (or wherever your main BaseModule class is)
from base.module import BaseModule # Other imports...
from typing import Type # Import Type for hinting
# Import your extension class
from .extensions.extra_handlers import ExtraHandlersExtension
class MyModule(BaseModule):
# ... other properties and methods ...
@property
def module_extensions(self) -> list[Type[ModuleExtension]]:
"""Register module extensions."""
return [
ExtraHandlersExtension,
# Add other extension classes here if you have more
]
# ... rest of the module class ...
How it Works
- When the main module's
stage2
method runs, it iterates through the classes listed inmodule_extensions
. - For each extension class, it creates an instance, passing the main module instance (
self
) to the extension's__init__
. - The
ModuleExtension.__init
__ automatically:- Inherits key attributes (
bot
,S
,rawS
,cur_lang
,loader
,logger,
state_machine
,get_sm
) from the main module instance. - Stores a reference to the main module instance as
self.__base_mod
. - Crucially, calls
self.__base_mod.register_all(ext=self)
, which scans the extension instance for methods decorated with@command
,@callback_query
,@message
and registers them with Pyrogram, just like handlers in the main class. - Calls the extension's
on_init
method.
- Inherits key attributes (
- Handlers defined in the extension now function as if they were part of the main module, with access to its shared resources (
S
, db, etc.).
This allows you to neatly separate concerns, like putting all administrative commands in one extension file, user commands in another, and background task logic elsewhere, all while sharing the core module's setup and state.