Traceback (most recent call last):
File "/storage/emulated/0/Audiobooks/test2/.venv/main.py", line 16, in <module>
from telethon_manager import TelethonManager
File "/storage/emulated/0/Audiobooks/test2/.venv/telethon_manager.py", line 30, in <module>
from callback_handlers import router as callback_router
ModuleNotFoundError: No module named 'callback_handlers'
I see the issue now. The error is occurring because your telethon_manager.py file is trying to import a module called callback_handlers which doesn't exist. Let's create this file to fix the error.
I've made two important changes to fix your issue:
Created a new callback_handlers.py file with a router for handling callback queries in your Telegram bot. This file includes handlers for various callback actions like:
Cleaned up your telethon_manager.py file to:
BotApp classjoin_group method that was in your original bot_core.pyThe callback_handlers.py file creates a router that can be included in your main dispatcher. It handles all the callback queries from inline keyboard buttons in your bot.
Now your code should work without the import error. The structure is cleaner with better separation of concerns:
telethon_manager.py handles Telegram client accountscallback_handlers.py handles callback queries from inline keyboardsbot_core.py (which you fixed earlier) handles the core bot functionalityI understand now. Let me provide a complete solution that addresses all the issues in one comprehensive approach. I'll create the missing callback_handlers.py file and fix the imports in telethon_manager.py.
Here's the complete solution:
callback_handlers.py file:# callback_handlers.py
import logging
from aiogram import Router, F
from aiogram.types import CallbackQuery
from aiogram.utils.keyboard import InlineKeyboardBuilder
# Create router
router = Router()
logger = logging.getLogger(__name__)
# Basic callback handlers
@router.callback_query(F.data == "back_to_menu")
async def back_to_menu_handler(callback: CallbackQuery):
"""Handle back to menu button"""
bot_app = callback.bot.get("bot_app")
await callback.message.edit_text(
"Главное меню",
reply_markup=bot_app.user_handlers.get_start_keyboard() if hasattr(bot_app, "user_handlers") else None
)
await callback.answer()
@router.callback_query(F.data == "buy_requests")
async def buy_requests_handler(callback: CallbackQuery):
"""Handle buy requests button"""
bot_app = callback.bot.get("bot_app")
# Create keyboard with payment options
builder = InlineKeyboardBuilder()
builder.button(text="💳 Оплатить 1 запрос (100₽)", callback_data="pay:1:100")
builder.button(text="💳 Оплатить 5 запросов (400₽)", callback_data="pay:5:400")
builder.button(text="💳 Оплатить 10 запросов (700₽)", callback_data="pay:10:700")
builder.button(text="🔙 Назад", callback_data="back_to_menu")
builder.adjust(1)
await callback.message.edit_text(
"<b>💰 Покупка запросов</b>\n\n"
"Выберите количество запросов для покупки:",
reply_markup=builder.as_markup(),
parse_mode="HTML"
)
await callback.answer()
# Add more callback handlers as neededtelethon_manager.py file by removing the import of callback_handlers:# telethon_manager.py
import asyncio
import logging
import time
from telethon import TelegramClient
from telethon.tl.functions.channels import JoinChannelRequest
logger = logging.getLogger(__name__)
class TelethonAccount:
def __init__(self, api_id, api_hash, phone, session_name):
self.api_id = api_id
self.api_hash = api_hash
self.phone = phone
self.session_name = session_name
self.client = None
self.active_analyses = 0
self.last_used = 0
self.connected = False
self.connecting = False
self.connection_lock = asyncio.Lock()
async def connect(self):
"""Connect the client with retry logic and handle authentication if needed"""
if self.connected or self.connecting:
return
async with self.connection_lock:
if self.connected:
return
self.connecting = True
try:
self.client = TelegramClient(self.session_name, self.api_id, self.api_hash)
await self.client.connect()
if not await self.client.is_user_authorized():
logger.info(f"Client {self.session_name} is not authorized. Requesting code for {self.phone}.")
# Request code
await self.client.send_code_request(self.phone)
# Ask user to input the code
code_input = input(f"Введите код, который отправлен на номер {self.phone}: ")
try:
# Sign in with the code
await self.client.sign_in(self.phone, code_input)
logger.info(f"Successfully authenticated account {self.session_name} with phone {self.phone}")
except Exception as auth_error:
logger.error(f"Error during authentication for {self.phone}: {auth_error}")
self.connected = False
self.connecting = False
return
self.connected = True
logger.info(f"Connected client {self.session_name}")
except Exception as e:
logger.error(f"Error connecting client {self.session_name}: {e}")
self.connected = False
finally:
self.connecting = False
async def disconnect(self):
"""Disconnect the client"""
if self.client:
try:
await self.client.disconnect()
self.connected = False
logger.info(f"Disconnected client {self.session_name}")
except Exception as e:
logger.error(f"Error disconnecting client {self.session_name}: {e}")
async def ensure_connected(self):
"""Ensure the client is connected"""
if not self.connected:
await self.connect()
elif self.client and not self.client.is_connected():
try:
await self.client.connect()
except Exception as e:
logger.error(f"Error reconnecting client {self.session_name}: {e}")
self.connected = False
await self.connect()
class TelethonManager:
def __init__(self, accounts_config=None):
self.accounts = []
self.accounts_config = accounts_config
async def init_accounts(self):
"""Initialize Telethon accounts"""
if not self.accounts_config:
from config import TELETHON_ACCOUNTS
self.accounts_config = TELETHON_ACCOUNTS
for account_info in self.accounts_config:
account = TelethonAccount(
account_info["api_id"],
account_info["api_hash"],
account_info["phone"],
account_info["session_name"]
)
self.accounts.append(account)
# Connect all accounts initially
logger.info("Connecting and authenticating Telethon accounts...")
for account in self.accounts:
try:
await account.connect()
# Verify the account is properly connected and authenticated
if account.connected:
logger.info(f"Account {account.session_name} ({account.phone}) is ready")
else:
logger.warning(f"Account {account.session_name} ({account.phone}) failed to connect")
except Exception as e:
logger.error(f"Error initializing account {account.session_name}: {e}")
logger.info(f"Initialized {len(self.accounts)} Telethon accounts")
async def get_available_account(self):
"""Get the least loaded account"""
# Sort accounts by number of active analyses and last used time
sorted_accounts = sorted(
self.accounts,
key=lambda a: (a.active_analyses, a.last_used)
)
for account in sorted_accounts:
if account.active_analyses < 3: # MAX_ANALYSES_PER_ACCOUNT
await account.ensure_connected()
if account.connected:
account.active_analyses += 1
account.last_used = time.time()
return account
# If all accounts are at capacity, return the one used longest ago
if sorted_accounts:
account = sorted_accounts[0]
await account.ensure_connected()
account.active_analyses += 1
account.last_used = time.time()
return account
return None
async def release_account(self, account):
"""Release an account after use"""
if account:
account.active_analyses = max(0, account.active_analyses - 1)bot_core.py file with the proper States class and make sure it works with the other files:# bot_core.py
import asyncio
import time
import logging
from aiogram import Bot, Dispatcher
from aiogram.fsm.storage.memory import MemoryStorage
from aiogram.fsm.state import State, StatesGroup
from database import Database
logger = logging.getLogger(__name__)
class States(StatesGroup):
WAITING_FOR_CHAT_LINK = State()
WAITING_FOR_USER_ID = State()
CHECK_SUBSCRIPTION = State()
WAITING_FOR_PROMO_CODE = State()
class BotApp:
def __init__(self, bot_token, accounts_config=None):
self.bot = Bot(token=bot_token)
self.dp = Dispatcher(storage=MemoryStorage())
self.db = Database()
self.analysis_queue = asyncio.Queue()
self.active_analyses = set() # To track ongoing analyses by user_id
self.analysis_timeouts = {} # To track users with timeouts
self.progress_messages = {} # For storing progress messages to update
self.analysis_tasks = {} # To track analysis tasks
self.account_locks = {} # Locks for each account
self.request_lock = asyncio.Lock() # Lock for request management
self.is_processing_queue = False
self.queue_processor_task = None
self.bot_info = None
self.telethon_manager = None
self.analysis_manager = None
self.user_handlers = None
self.payment_handlers = None # Added payment_handlers attribute
self.chat_links_cache = {} # Кэш для хранения ссылок на чаты
self.chats_info_cache = {} # Кэш для хранения информации о чатах
# Initialize Telethon manager if accounts_config is provided
if accounts_config:
from telethon_manager import TelethonManager
self.telethon_manager = TelethonManager(accounts_config)
else:
from config import TELETHON_ACCOUNTS
from telethon_manager import TelethonManager
self.telethon_manager = TelethonManager(TELETHON_ACCOUNTS)
async def initialize(self):
"""Initialize the bot and its components"""
try:
# Initialize database
await self.db.create_tables()
# Initialize Telethon manager
await self.telethon_manager.init_accounts()
# Get bot info
self.bot_info = await self.bot.get_me()
logger.info(f"Bot initialized: @{self.bot_info.username}")
logger.info("Bot initialization completed")
except Exception as e:
logger.critical(f"Error initializing bot: {e}")
raise
async def start_polling(self):
"""Start the bot polling"""
try:
logger.info("Starting bot polling...")
await self.dp.start_polling(self.bot)
except Exception as e:
logger.critical(f"Error starting bot polling: {e}")
raise
async def start(self):
"""Start the bot"""
try:
# Initialize components
await self.initialize()
# Start polling
await self.start_polling()
except Exception as e:
logger.critical(f"Error starting bot: {e}")
raise
# Rest of the BotApp class methods...main.py file to use the new structure:# main.py
import asyncio
import logging
from datetime import datetime
import time
from aiogram import types, F
from aiogram.filters import Command, CommandStart
from aiogram.utils.keyboard import InlineKeyboardBuilder
from aiogram.types import Message
from aiogram.fsm.context import FSMContext
from aiogram import Bot, Dispatcher
from bot_core import BotApp, States
from user_handlers import UserHandlers
from analysis_manager import AnalysisManager
from payment_handlers import PaymentHandlers
from telethon_manager import TelethonManager
from callback_handlers import router as callback_router
from config import BOT_TOKEN, ANALYSIS_LOGS_CHAT_ID, ADMIN_IDS
import os
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler("bot.log"),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# Global bot_app variable
bot_app = None
class MainApplication:
def __init__(self):
self.bot_app = BotApp(BOT_TOKEN)
async def setup_components(self):
"""Setup all components of the application"""
# Initialize user handlers
self.bot_app.user_handlers = UserHandlers(self.bot_app)
await self.bot_app.user_handlers.setup_handlers()
# Initialize analysis manager
self.bot_app.analysis_manager = AnalysisManager(self.bot_app)
# Initialize payment handlers
self.bot_app.payment_handlers = PaymentHandlers(self.bot_app)
await self.bot_app.payment_handlers.setup_handlers()
# Setup admin handlers
from admin_handlers import setup_admin_handlers
await setup_admin_handlers(self.bot_app.dp, self.bot_app.db)
# Include callback router
self.bot_app.dp.include_router(callback_router)
# Setup additional handlers
await self.setup_additional_handlers()
# Rest of the MainApplication class...This comprehensive solution:
callback_handlers.py file with a basic router and some example handlerstelethon_manager.py file by removing the problematic importbot_core.py file with the proper States class definitionmain.py file to include the callback routerWith these changes, the circular import issue should be resolved, and the bot should be able to start properly.