smart-bot-factory 0.3.7__py3-none-any.whl → 0.3.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of smart-bot-factory might be problematic. Click here for more details.
- smart_bot_factory/admin/__init__.py +7 -7
- smart_bot_factory/admin/admin_events.py +483 -383
- smart_bot_factory/admin/admin_logic.py +234 -158
- smart_bot_factory/admin/admin_manager.py +68 -53
- smart_bot_factory/admin/admin_tester.py +46 -40
- smart_bot_factory/admin/timeout_checker.py +201 -153
- smart_bot_factory/aiogram_calendar/__init__.py +11 -3
- smart_bot_factory/aiogram_calendar/common.py +12 -18
- smart_bot_factory/aiogram_calendar/dialog_calendar.py +126 -64
- smart_bot_factory/aiogram_calendar/schemas.py +49 -28
- smart_bot_factory/aiogram_calendar/simple_calendar.py +94 -50
- smart_bot_factory/analytics/analytics_manager.py +414 -392
- smart_bot_factory/cli.py +204 -148
- smart_bot_factory/config.py +123 -102
- smart_bot_factory/core/bot_utils.py +474 -332
- smart_bot_factory/core/conversation_manager.py +287 -200
- smart_bot_factory/core/decorators.py +1200 -755
- smart_bot_factory/core/message_sender.py +287 -266
- smart_bot_factory/core/router.py +170 -100
- smart_bot_factory/core/router_manager.py +121 -83
- smart_bot_factory/core/states.py +4 -3
- smart_bot_factory/creation/__init__.py +1 -1
- smart_bot_factory/creation/bot_builder.py +320 -242
- smart_bot_factory/creation/bot_testing.py +440 -365
- smart_bot_factory/dashboard/__init__.py +1 -3
- smart_bot_factory/event/__init__.py +2 -7
- smart_bot_factory/handlers/handlers.py +676 -472
- smart_bot_factory/integrations/openai_client.py +218 -168
- smart_bot_factory/integrations/supabase_client.py +948 -637
- smart_bot_factory/message/__init__.py +18 -22
- smart_bot_factory/router/__init__.py +2 -2
- smart_bot_factory/setup_checker.py +162 -126
- smart_bot_factory/supabase/__init__.py +1 -1
- smart_bot_factory/supabase/client.py +631 -515
- smart_bot_factory/utils/__init__.py +2 -3
- smart_bot_factory/utils/debug_routing.py +38 -27
- smart_bot_factory/utils/prompt_loader.py +153 -120
- smart_bot_factory/utils/user_prompt_loader.py +55 -56
- smart_bot_factory/utm_link_generator.py +123 -116
- {smart_bot_factory-0.3.7.dist-info → smart_bot_factory-0.3.9.dist-info}/METADATA +3 -1
- smart_bot_factory-0.3.9.dist-info/RECORD +59 -0
- smart_bot_factory-0.3.7.dist-info/RECORD +0 -59
- {smart_bot_factory-0.3.7.dist-info → smart_bot_factory-0.3.9.dist-info}/WHEEL +0 -0
- {smart_bot_factory-0.3.7.dist-info → smart_bot_factory-0.3.9.dist-info}/entry_points.txt +0 -0
- {smart_bot_factory-0.3.7.dist-info → smart_bot_factory-0.3.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,40 +2,41 @@
|
|
|
2
2
|
Строитель ботов для Smart Bot Factory
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
import os
|
|
6
5
|
import logging
|
|
6
|
+
import os
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
9
|
|
|
10
|
-
from ..config import Config
|
|
11
|
-
from ..integrations.openai_client import OpenAIClient
|
|
12
|
-
from ..integrations.supabase_client import SupabaseClient
|
|
13
|
-
from ..core.conversation_manager import ConversationManager
|
|
14
10
|
from ..admin.admin_manager import AdminManager
|
|
15
11
|
from ..analytics.analytics_manager import AnalyticsManager
|
|
16
|
-
from ..
|
|
12
|
+
from ..config import Config
|
|
13
|
+
from ..core.conversation_manager import ConversationManager
|
|
17
14
|
from ..core.decorators import get_handlers_for_prompt
|
|
18
15
|
from ..core.router_manager import RouterManager
|
|
16
|
+
from ..integrations.openai_client import OpenAIClient
|
|
17
|
+
from ..integrations.supabase_client import SupabaseClient
|
|
18
|
+
from ..utils.prompt_loader import PromptLoader
|
|
19
19
|
|
|
20
20
|
logger = logging.getLogger(__name__)
|
|
21
21
|
|
|
22
|
+
|
|
22
23
|
class BotBuilder:
|
|
23
24
|
"""
|
|
24
25
|
Строитель ботов, который использует существующие файлы проекта
|
|
25
26
|
и добавляет новые возможности через декораторы
|
|
26
27
|
"""
|
|
27
|
-
|
|
28
|
+
|
|
28
29
|
def __init__(self, bot_id: str, config_dir: Optional[Path] = None):
|
|
29
30
|
"""
|
|
30
31
|
Инициализация строителя бота
|
|
31
|
-
|
|
32
|
+
|
|
32
33
|
Args:
|
|
33
34
|
bot_id: Идентификатор бота
|
|
34
35
|
config_dir: Путь к директории конфигурации (по умолчанию configs/bot_id)
|
|
35
36
|
"""
|
|
36
37
|
self.bot_id = bot_id
|
|
37
38
|
self.config_dir = config_dir or Path("bots") / bot_id
|
|
38
|
-
|
|
39
|
+
|
|
39
40
|
# Компоненты бота
|
|
40
41
|
self.config: Optional[Config] = None
|
|
41
42
|
self.openai_client: Optional[OpenAIClient] = None
|
|
@@ -47,180 +48,190 @@ class BotBuilder:
|
|
|
47
48
|
self.router_manager: Optional[RouterManager] = None
|
|
48
49
|
self._telegram_routers: List = [] # Список Telegram роутеров
|
|
49
50
|
self._start_handlers: List = [] # Список обработчиков on_start
|
|
50
|
-
|
|
51
|
+
|
|
51
52
|
# Хуки для кастомизации process_user_message
|
|
52
53
|
self._message_validators: List = [] # Валидация ДО обработки
|
|
53
|
-
self._prompt_enrichers: List = []
|
|
54
|
-
self._context_enrichers: List = []
|
|
55
|
-
self._response_processors: List = []
|
|
56
|
-
self._send_filters: List = []
|
|
57
|
-
|
|
54
|
+
self._prompt_enrichers: List = [] # Обогащение системного промпта
|
|
55
|
+
self._context_enrichers: List = [] # Обогащение контекста для AI
|
|
56
|
+
self._response_processors: List = [] # Обработка ответа AI
|
|
57
|
+
self._send_filters: List = [] # Фильтры перед отправкой пользователю
|
|
58
|
+
|
|
58
59
|
# Кастомный PromptLoader
|
|
59
60
|
self._custom_prompt_loader = None
|
|
60
|
-
|
|
61
|
+
|
|
61
62
|
# Кастомный процессор событий
|
|
62
63
|
self._custom_event_processor = None
|
|
63
|
-
|
|
64
|
+
|
|
64
65
|
# Флаги инициализации
|
|
65
66
|
self._initialized = False
|
|
66
|
-
|
|
67
|
+
|
|
67
68
|
logger.info(f"🏗️ Создан BotBuilder для бота: {bot_id}")
|
|
68
|
-
|
|
69
|
-
async def build(self) ->
|
|
69
|
+
|
|
70
|
+
async def build(self) -> "BotBuilder":
|
|
70
71
|
"""
|
|
71
72
|
Строит и инициализирует все компоненты бота
|
|
72
|
-
|
|
73
|
+
|
|
73
74
|
Returns:
|
|
74
75
|
BotBuilder: Возвращает self для цепочки вызовов
|
|
75
76
|
"""
|
|
76
77
|
if self._initialized:
|
|
77
78
|
logger.warning(f"⚠️ Бот {self.bot_id} уже инициализирован")
|
|
78
79
|
return self
|
|
79
|
-
|
|
80
|
+
|
|
80
81
|
try:
|
|
81
82
|
logger.info(f"🚀 Начинаем сборку бота {self.bot_id}")
|
|
82
|
-
|
|
83
|
+
|
|
83
84
|
# 1. Инициализируем конфигурацию
|
|
84
85
|
await self._init_config()
|
|
85
|
-
|
|
86
|
+
|
|
86
87
|
# 2. Инициализируем клиенты
|
|
87
88
|
await self._init_clients()
|
|
88
|
-
|
|
89
|
+
|
|
89
90
|
# 3. Инициализируем менеджеры
|
|
90
91
|
await self._init_managers()
|
|
91
|
-
|
|
92
|
+
|
|
92
93
|
# 4. Обновляем промпты с информацией о доступных инструментах
|
|
93
94
|
await self._update_prompts_with_tools()
|
|
94
|
-
|
|
95
|
+
|
|
95
96
|
self._initialized = True
|
|
96
97
|
logger.info(f"✅ Бот {self.bot_id} успешно собран и готов к работе")
|
|
97
|
-
|
|
98
|
+
|
|
98
99
|
return self
|
|
99
|
-
|
|
100
|
+
|
|
100
101
|
except Exception as e:
|
|
101
102
|
logger.error(f"❌ Ошибка при сборке бота {self.bot_id}: {e}")
|
|
102
103
|
raise
|
|
103
|
-
|
|
104
|
+
|
|
104
105
|
async def _init_config(self):
|
|
105
106
|
"""Инициализация конфигурации"""
|
|
106
107
|
logger.info(f"⚙️ Инициализация конфигурации для {self.bot_id}")
|
|
107
|
-
|
|
108
|
+
|
|
108
109
|
# Устанавливаем BOT_ID в переменные окружения
|
|
109
|
-
os.environ[
|
|
110
|
-
|
|
110
|
+
os.environ["BOT_ID"] = self.bot_id
|
|
111
|
+
|
|
111
112
|
# Загружаем .env файл если существует
|
|
112
113
|
env_file = self.config_dir / ".env"
|
|
113
114
|
if env_file.exists():
|
|
114
115
|
from dotenv import load_dotenv
|
|
116
|
+
|
|
115
117
|
load_dotenv(env_file)
|
|
116
118
|
logger.info(f"📄 Загружен .env файл: {env_file}")
|
|
117
|
-
|
|
119
|
+
|
|
118
120
|
# Устанавливаем путь к промптам относительно папки бота
|
|
119
121
|
prompts_subdir = os.environ.get("PROMT_FILES_DIR", "prompts")
|
|
120
122
|
logger.info(f"🔍 PROMT_FILES_DIR из .env: {prompts_subdir}")
|
|
121
|
-
|
|
123
|
+
|
|
122
124
|
prompts_dir = self.config_dir / prompts_subdir
|
|
123
125
|
logger.info(f"🔍 Путь к промптам: {prompts_dir}")
|
|
124
126
|
logger.info(f"🔍 Существует ли папка: {prompts_dir.exists()}")
|
|
125
|
-
|
|
127
|
+
|
|
126
128
|
# ВАЖНО: Устанавливаем правильный путь ДО создания Config
|
|
127
129
|
os.environ["PROMT_FILES_DIR"] = str(prompts_dir)
|
|
128
130
|
logger.info(f"📁 Установлен путь к промптам: {prompts_dir}")
|
|
129
|
-
|
|
131
|
+
|
|
130
132
|
# Создаем конфигурацию
|
|
131
|
-
logger.info(
|
|
133
|
+
logger.info(
|
|
134
|
+
f"🔍 PROMT_FILES_DIR перед созданием Config: {os.environ.get('PROMT_FILES_DIR')}"
|
|
135
|
+
)
|
|
132
136
|
self.config = Config()
|
|
133
|
-
logger.info(
|
|
134
|
-
|
|
137
|
+
logger.info("✅ Конфигурация инициализирована")
|
|
138
|
+
|
|
135
139
|
async def _init_clients(self):
|
|
136
140
|
"""Инициализация клиентов"""
|
|
137
141
|
logger.info(f"🔌 Инициализация клиентов для {self.bot_id}")
|
|
138
|
-
|
|
142
|
+
|
|
139
143
|
# OpenAI клиент
|
|
140
144
|
self.openai_client = OpenAIClient(
|
|
141
145
|
api_key=self.config.OPENAI_API_KEY,
|
|
142
146
|
model=self.config.OPENAI_MODEL,
|
|
143
147
|
max_tokens=self.config.OPENAI_MAX_TOKENS,
|
|
144
|
-
temperature=self.config.OPENAI_TEMPERATURE
|
|
148
|
+
temperature=self.config.OPENAI_TEMPERATURE,
|
|
145
149
|
)
|
|
146
|
-
logger.info(
|
|
147
|
-
|
|
150
|
+
logger.info("✅ OpenAI клиент инициализирован")
|
|
151
|
+
|
|
148
152
|
# Supabase клиент
|
|
149
153
|
self.supabase_client = SupabaseClient(
|
|
150
154
|
url=self.config.SUPABASE_URL,
|
|
151
155
|
key=self.config.SUPABASE_KEY,
|
|
152
|
-
bot_id=self.bot_id
|
|
156
|
+
bot_id=self.bot_id,
|
|
153
157
|
)
|
|
154
158
|
await self.supabase_client.initialize()
|
|
155
|
-
logger.info(
|
|
156
|
-
|
|
159
|
+
logger.info("✅ Supabase клиент инициализирован")
|
|
160
|
+
|
|
157
161
|
async def _init_managers(self):
|
|
158
162
|
"""Инициализация менеджеров"""
|
|
159
163
|
logger.info(f"👥 Инициализация менеджеров для {self.bot_id}")
|
|
160
|
-
|
|
164
|
+
|
|
161
165
|
# Admin Manager
|
|
162
166
|
self.admin_manager = AdminManager(self.config, self.supabase_client)
|
|
163
167
|
await self.admin_manager.sync_admins_from_config()
|
|
164
|
-
logger.info(
|
|
165
|
-
|
|
168
|
+
logger.info("✅ Admin Manager инициализирован")
|
|
169
|
+
|
|
166
170
|
# Analytics Manager
|
|
167
171
|
self.analytics_manager = AnalyticsManager(self.supabase_client)
|
|
168
|
-
logger.info(
|
|
169
|
-
|
|
172
|
+
logger.info("✅ Analytics Manager инициализирован")
|
|
173
|
+
|
|
170
174
|
# Conversation Manager
|
|
171
|
-
parse_mode = os.environ.get(
|
|
172
|
-
admin_session_timeout_minutes = int(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
parse_mode = os.environ.get("MESSAGE_PARSE_MODE", "Markdown")
|
|
176
|
+
admin_session_timeout_minutes = int(
|
|
177
|
+
os.environ.get("ADMIN_SESSION_TIMEOUT_MINUTES", "30")
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
self.conversation_manager = ConversationManager(
|
|
181
|
+
self.supabase_client,
|
|
182
|
+
self.admin_manager,
|
|
183
|
+
parse_mode,
|
|
184
|
+
admin_session_timeout_minutes,
|
|
185
|
+
)
|
|
186
|
+
logger.info("✅ Conversation Manager инициализирован")
|
|
187
|
+
|
|
177
188
|
# Router Manager (создаем только если еще не создан)
|
|
178
189
|
if not self.router_manager:
|
|
179
190
|
self.router_manager = RouterManager()
|
|
180
|
-
logger.info(
|
|
191
|
+
logger.info("✅ Router Manager инициализирован")
|
|
181
192
|
else:
|
|
182
|
-
logger.info(
|
|
183
|
-
|
|
193
|
+
logger.info("✅ Router Manager уже был создан ранее")
|
|
194
|
+
|
|
184
195
|
# Prompt Loader (используем кастомный если установлен)
|
|
185
196
|
if self._custom_prompt_loader:
|
|
186
197
|
self.prompt_loader = self._custom_prompt_loader
|
|
187
|
-
logger.info(
|
|
188
|
-
|
|
189
|
-
self.prompt_loader = PromptLoader(
|
|
190
|
-
prompts_dir=self.config.PROMT_FILES_DIR
|
|
198
|
+
logger.info(
|
|
199
|
+
f"✅ Используется кастомный Prompt Loader: {type(self.prompt_loader).__name__}"
|
|
191
200
|
)
|
|
192
|
-
|
|
193
|
-
|
|
201
|
+
else:
|
|
202
|
+
self.prompt_loader = PromptLoader(prompts_dir=self.config.PROMT_FILES_DIR)
|
|
203
|
+
logger.info("✅ Используется стандартный Prompt Loader")
|
|
204
|
+
|
|
194
205
|
await self.prompt_loader.validate_prompts()
|
|
195
|
-
logger.info(
|
|
196
|
-
|
|
206
|
+
logger.info("✅ Prompt Loader инициализирован")
|
|
207
|
+
|
|
197
208
|
async def _update_prompts_with_tools(self):
|
|
198
209
|
"""
|
|
199
210
|
Обновляет промпты информацией о доступных обработчиках событий
|
|
200
211
|
"""
|
|
201
|
-
logger.info(
|
|
202
|
-
|
|
212
|
+
logger.info("🔧 Обновление промптов с информацией об обработчиках")
|
|
213
|
+
|
|
203
214
|
# Получаем информацию о доступных обработчиках
|
|
204
215
|
# Сначала пробуем получить из роутеров, если нет - из старых декораторов
|
|
205
216
|
if self.router_manager:
|
|
206
217
|
event_handlers_info = self.router_manager.get_handlers_for_prompt()
|
|
207
218
|
else:
|
|
208
219
|
event_handlers_info = get_handlers_for_prompt()
|
|
209
|
-
|
|
220
|
+
|
|
210
221
|
# Если есть обработчики, добавляем их в системный промпт
|
|
211
222
|
if event_handlers_info:
|
|
212
223
|
# Сохраняем информацию о обработчиках для использования в handlers.py
|
|
213
224
|
self._tools_prompt = event_handlers_info
|
|
214
|
-
|
|
215
|
-
logger.info(
|
|
225
|
+
|
|
226
|
+
logger.info("✅ Промпты обновлены с информацией об обработчиках")
|
|
216
227
|
else:
|
|
217
228
|
self._tools_prompt = ""
|
|
218
|
-
logger.info(
|
|
219
|
-
|
|
229
|
+
logger.info("ℹ️ Нет зарегистрированных обработчиков")
|
|
230
|
+
|
|
220
231
|
def get_tools_prompt(self) -> str:
|
|
221
232
|
"""Возвращает промпт с информацией об инструментах"""
|
|
222
|
-
return getattr(self,
|
|
223
|
-
|
|
233
|
+
return getattr(self, "_tools_prompt", "")
|
|
234
|
+
|
|
224
235
|
def get_status(self) -> Dict[str, Any]:
|
|
225
236
|
"""Возвращает статус бота"""
|
|
226
237
|
return {
|
|
@@ -234,35 +245,43 @@ class BotBuilder:
|
|
|
234
245
|
"conversation_manager": self.conversation_manager is not None,
|
|
235
246
|
"admin_manager": self.admin_manager is not None,
|
|
236
247
|
"analytics_manager": self.analytics_manager is not None,
|
|
237
|
-
"prompt_loader": self.prompt_loader is not None
|
|
248
|
+
"prompt_loader": self.prompt_loader is not None,
|
|
238
249
|
},
|
|
239
250
|
"tools": {
|
|
240
|
-
"event_handlers":
|
|
241
|
-
|
|
251
|
+
"event_handlers": (
|
|
252
|
+
len(get_handlers_for_prompt().split("\n"))
|
|
253
|
+
if get_handlers_for_prompt()
|
|
254
|
+
else 0
|
|
255
|
+
)
|
|
256
|
+
},
|
|
242
257
|
}
|
|
243
|
-
|
|
258
|
+
|
|
244
259
|
def set_global_vars_in_module(self, module_name: str):
|
|
245
260
|
"""
|
|
246
261
|
Устанавливает глобальные переменные в указанном модуле для удобного доступа
|
|
247
|
-
|
|
262
|
+
|
|
248
263
|
Args:
|
|
249
264
|
module_name: Имя модуля (например, 'valera', 'my_bot')
|
|
250
265
|
"""
|
|
251
266
|
try:
|
|
252
|
-
import sys
|
|
253
267
|
import importlib
|
|
254
|
-
|
|
268
|
+
import sys
|
|
269
|
+
|
|
255
270
|
# Получаем модуль бота
|
|
256
271
|
bot_module = sys.modules.get(module_name)
|
|
257
272
|
if not bot_module:
|
|
258
273
|
# Пытаемся импортировать модуль, если он не загружен
|
|
259
274
|
try:
|
|
260
275
|
bot_module = importlib.import_module(module_name)
|
|
261
|
-
logger.info(
|
|
276
|
+
logger.info(
|
|
277
|
+
f"📦 Модуль '{module_name}' импортирован для установки глобальных переменных"
|
|
278
|
+
)
|
|
262
279
|
except ImportError as ie:
|
|
263
|
-
logger.warning(
|
|
280
|
+
logger.warning(
|
|
281
|
+
f"⚠️ Не удалось импортировать модуль '{module_name}': {ie}"
|
|
282
|
+
)
|
|
264
283
|
return
|
|
265
|
-
|
|
284
|
+
|
|
266
285
|
# Устанавливаем глобальные переменные
|
|
267
286
|
bot_module.supabase_client = self.supabase_client
|
|
268
287
|
bot_module.openai_client = self.openai_client
|
|
@@ -271,115 +290,128 @@ class BotBuilder:
|
|
|
271
290
|
bot_module.analytics_manager = self.analytics_manager
|
|
272
291
|
bot_module.conversation_manager = self.conversation_manager
|
|
273
292
|
bot_module.prompt_loader = self.prompt_loader
|
|
274
|
-
|
|
275
|
-
logger.info(
|
|
276
|
-
|
|
293
|
+
|
|
294
|
+
logger.info(
|
|
295
|
+
f"✅ Глобальные переменные установлены в модуле '{module_name}'"
|
|
296
|
+
)
|
|
297
|
+
|
|
277
298
|
except Exception as e:
|
|
278
|
-
logger.warning(
|
|
279
|
-
|
|
299
|
+
logger.warning(
|
|
300
|
+
f"⚠️ Не удалось установить глобальные переменные в модуле '{module_name}': {e}"
|
|
301
|
+
)
|
|
302
|
+
|
|
280
303
|
def register_router(self, router):
|
|
281
304
|
"""
|
|
282
305
|
Регистрирует роутер событий в менеджере роутеров
|
|
283
|
-
|
|
306
|
+
|
|
284
307
|
Args:
|
|
285
308
|
router: EventRouter для регистрации
|
|
286
309
|
"""
|
|
287
310
|
# Если RouterManager еще не инициализирован, создаем его
|
|
288
311
|
if not self.router_manager:
|
|
289
312
|
from ..core.router_manager import RouterManager
|
|
313
|
+
|
|
290
314
|
self.router_manager = RouterManager()
|
|
291
|
-
logger.info(
|
|
292
|
-
|
|
315
|
+
logger.info(
|
|
316
|
+
f"✅ Router Manager создан для регистрации роутера '{router.name}'"
|
|
317
|
+
)
|
|
318
|
+
|
|
293
319
|
self.router_manager.register_router(router)
|
|
294
|
-
logger.info(
|
|
295
|
-
|
|
320
|
+
logger.info(
|
|
321
|
+
f"✅ Роутер событий '{router.name}' зарегистрирован в боте {self.bot_id}"
|
|
322
|
+
)
|
|
323
|
+
|
|
296
324
|
def register_routers(self, *event_routers):
|
|
297
325
|
"""
|
|
298
326
|
Регистрирует несколько роутеров событий одновременно
|
|
299
|
-
|
|
327
|
+
|
|
300
328
|
Args:
|
|
301
329
|
*event_routers: Произвольное количество EventRouter
|
|
302
|
-
|
|
330
|
+
|
|
303
331
|
Example:
|
|
304
332
|
bot_builder.register_routers(event_router1, event_router2, event_router3)
|
|
305
333
|
"""
|
|
306
334
|
if not event_routers:
|
|
307
335
|
logger.warning("⚠️ register_routers вызван без аргументов")
|
|
308
336
|
return
|
|
309
|
-
|
|
337
|
+
|
|
310
338
|
for router in event_routers:
|
|
311
339
|
self.register_router(router)
|
|
312
|
-
|
|
340
|
+
|
|
313
341
|
logger.info(f"✅ Зарегистрировано {len(event_routers)} роутеров событий")
|
|
314
|
-
|
|
342
|
+
|
|
315
343
|
def register_telegram_router(self, telegram_router):
|
|
316
344
|
"""
|
|
317
345
|
Регистрирует Telegram роутер для обработки команд и сообщений
|
|
318
|
-
|
|
346
|
+
|
|
319
347
|
Args:
|
|
320
348
|
telegram_router: aiogram.Router для регистрации
|
|
321
|
-
|
|
349
|
+
|
|
322
350
|
Example:
|
|
323
351
|
from aiogram import Router
|
|
324
352
|
from aiogram.filters import Command
|
|
325
|
-
|
|
353
|
+
|
|
326
354
|
# Создаем обычный aiogram Router
|
|
327
355
|
my_router = Router(name="my_commands")
|
|
328
|
-
|
|
356
|
+
|
|
329
357
|
@my_router.message(Command("price"))
|
|
330
358
|
async def price_handler(message: Message):
|
|
331
359
|
await message.answer("Наши цены...")
|
|
332
|
-
|
|
360
|
+
|
|
333
361
|
# Регистрируем в боте
|
|
334
362
|
bot_builder.register_telegram_router(my_router)
|
|
335
363
|
"""
|
|
336
364
|
from aiogram import Router as AiogramRouter
|
|
337
|
-
|
|
365
|
+
|
|
338
366
|
if not isinstance(telegram_router, AiogramRouter):
|
|
339
|
-
raise TypeError(
|
|
340
|
-
|
|
367
|
+
raise TypeError(
|
|
368
|
+
f"Ожидается aiogram.Router, получен {type(telegram_router)}"
|
|
369
|
+
)
|
|
370
|
+
|
|
341
371
|
self._telegram_routers.append(telegram_router)
|
|
342
|
-
router_name = getattr(telegram_router,
|
|
343
|
-
logger.info(
|
|
344
|
-
|
|
372
|
+
router_name = getattr(telegram_router, "name", "unnamed")
|
|
373
|
+
logger.info(
|
|
374
|
+
f"✅ Telegram роутер '{router_name}' зарегистрирован в боте {self.bot_id}"
|
|
375
|
+
)
|
|
376
|
+
|
|
345
377
|
def register_telegram_routers(self, *telegram_routers):
|
|
346
378
|
"""
|
|
347
379
|
Регистрирует несколько Telegram роутеров одновременно
|
|
348
|
-
|
|
380
|
+
|
|
349
381
|
Args:
|
|
350
382
|
*telegram_routers: Произвольное количество aiogram.Router
|
|
351
|
-
|
|
383
|
+
|
|
352
384
|
Example:
|
|
353
385
|
from aiogram import Router
|
|
354
|
-
|
|
386
|
+
|
|
355
387
|
router1 = Router(name="commands")
|
|
356
388
|
router2 = Router(name="callbacks")
|
|
357
|
-
|
|
389
|
+
|
|
358
390
|
bot_builder.register_telegram_routers(router1, router2)
|
|
359
391
|
"""
|
|
360
392
|
if not telegram_routers:
|
|
361
393
|
logger.warning("⚠️ register_telegram_routers вызван без аргументов")
|
|
362
394
|
return
|
|
363
|
-
|
|
395
|
+
|
|
364
396
|
for router in telegram_routers:
|
|
365
397
|
self.register_telegram_router(router)
|
|
366
|
-
|
|
398
|
+
|
|
367
399
|
logger.info(f"✅ Зарегистрировано {len(telegram_routers)} Telegram роутеров")
|
|
368
|
-
|
|
400
|
+
|
|
369
401
|
def on_start(self, handler):
|
|
370
402
|
"""
|
|
371
403
|
Регистрирует обработчик, который вызывается после стандартной логики /start
|
|
372
|
-
|
|
404
|
+
|
|
373
405
|
Обработчик получает доступ к:
|
|
374
406
|
- user_id: int - ID пользователя Telegram
|
|
375
407
|
- session_id: str - ID созданной сессии
|
|
376
408
|
- message: Message - Объект сообщения от aiogram
|
|
377
409
|
- state: FSMContext - Контекст состояния
|
|
378
|
-
|
|
410
|
+
|
|
379
411
|
Args:
|
|
380
412
|
handler: Async функция с сигнатурой:
|
|
381
413
|
async def handler(user_id: int, session_id: str, message: Message, state: FSMContext)
|
|
382
|
-
|
|
414
|
+
|
|
383
415
|
Example:
|
|
384
416
|
@bot_builder.on_start
|
|
385
417
|
async def my_start_handler(user_id, session_id, message, state):
|
|
@@ -388,64 +420,66 @@ class BotBuilder:
|
|
|
388
420
|
"""
|
|
389
421
|
if not callable(handler):
|
|
390
422
|
raise TypeError(f"Обработчик должен быть callable, получен {type(handler)}")
|
|
391
|
-
|
|
423
|
+
|
|
392
424
|
self._start_handlers.append(handler)
|
|
393
425
|
logger.info(f"✅ Зарегистрирован обработчик on_start: {handler.__name__}")
|
|
394
426
|
return handler # Возвращаем handler для использования как декоратор
|
|
395
|
-
|
|
427
|
+
|
|
396
428
|
def get_start_handlers(self) -> List:
|
|
397
429
|
"""Получает список обработчиков on_start"""
|
|
398
430
|
return self._start_handlers.copy()
|
|
399
|
-
|
|
431
|
+
|
|
400
432
|
def set_prompt_loader(self, prompt_loader):
|
|
401
433
|
"""
|
|
402
434
|
Устанавливает кастомный PromptLoader
|
|
403
|
-
|
|
435
|
+
|
|
404
436
|
Должен быть вызван ДО build()
|
|
405
|
-
|
|
437
|
+
|
|
406
438
|
Args:
|
|
407
439
|
prompt_loader: Экземпляр PromptLoader или его наследника (например UserPromptLoader)
|
|
408
|
-
|
|
440
|
+
|
|
409
441
|
Example:
|
|
410
442
|
from smart_bot_factory.utils import UserPromptLoader
|
|
411
|
-
|
|
443
|
+
|
|
412
444
|
# Использовать UserPromptLoader с автопоиском prompts_dir
|
|
413
445
|
custom_loader = UserPromptLoader("my-bot")
|
|
414
446
|
bot_builder.set_prompt_loader(custom_loader)
|
|
415
|
-
|
|
447
|
+
|
|
416
448
|
# Или кастомный наследник
|
|
417
449
|
class MyPromptLoader(UserPromptLoader):
|
|
418
450
|
def __init__(self, bot_id):
|
|
419
451
|
super().__init__(bot_id)
|
|
420
452
|
self.extra_file = self.prompts_dir / 'extra.txt'
|
|
421
|
-
|
|
453
|
+
|
|
422
454
|
my_loader = MyPromptLoader("my-bot")
|
|
423
455
|
bot_builder.set_prompt_loader(my_loader)
|
|
424
456
|
"""
|
|
425
457
|
self._custom_prompt_loader = prompt_loader
|
|
426
|
-
logger.info(
|
|
427
|
-
|
|
458
|
+
logger.info(
|
|
459
|
+
f"✅ Установлен кастомный PromptLoader: {type(prompt_loader).__name__}"
|
|
460
|
+
)
|
|
461
|
+
|
|
428
462
|
def set_event_processor(self, custom_processor):
|
|
429
463
|
"""
|
|
430
464
|
Устанавливает кастомную функцию для обработки событий
|
|
431
|
-
|
|
465
|
+
|
|
432
466
|
Полностью заменяет стандартную process_events из bot_utils
|
|
433
|
-
|
|
467
|
+
|
|
434
468
|
Args:
|
|
435
469
|
custom_processor: async def(session_id: str, events: list, user_id: int)
|
|
436
|
-
|
|
470
|
+
|
|
437
471
|
Example:
|
|
438
472
|
from smart_bot_factory.message import get_bot
|
|
439
473
|
from smart_bot_factory.core.decorators import execute_event_handler
|
|
440
|
-
|
|
474
|
+
|
|
441
475
|
async def my_process_events(session_id, events, user_id):
|
|
442
476
|
'''Моя кастомная обработка событий'''
|
|
443
477
|
bot = get_bot()
|
|
444
|
-
|
|
478
|
+
|
|
445
479
|
for event in events:
|
|
446
480
|
event_type = event.get('тип')
|
|
447
481
|
event_info = event.get('инфо')
|
|
448
|
-
|
|
482
|
+
|
|
449
483
|
if event_type == 'запись':
|
|
450
484
|
# Кастомная логика для бронирования
|
|
451
485
|
telegram_user = await bot.get_chat(user_id)
|
|
@@ -454,26 +488,30 @@ class BotBuilder:
|
|
|
454
488
|
else:
|
|
455
489
|
# Для остальных - стандартная обработка
|
|
456
490
|
await execute_event_handler(event_type, user_id, event_info)
|
|
457
|
-
|
|
491
|
+
|
|
458
492
|
bot_builder.set_event_processor(my_process_events)
|
|
459
493
|
"""
|
|
460
494
|
if not callable(custom_processor):
|
|
461
|
-
raise TypeError(
|
|
462
|
-
|
|
495
|
+
raise TypeError(
|
|
496
|
+
f"Процессор должен быть callable, получен {type(custom_processor)}"
|
|
497
|
+
)
|
|
498
|
+
|
|
463
499
|
self._custom_event_processor = custom_processor
|
|
464
|
-
logger.info(
|
|
465
|
-
|
|
500
|
+
logger.info(
|
|
501
|
+
f"✅ Установлена кастомная функция обработки событий: {custom_processor.__name__}"
|
|
502
|
+
)
|
|
503
|
+
|
|
466
504
|
# ========== ХУКИ ДЛЯ КАСТОМИЗАЦИИ ОБРАБОТКИ СООБЩЕНИЙ ==========
|
|
467
|
-
|
|
505
|
+
|
|
468
506
|
def validate_message(self, handler):
|
|
469
507
|
"""
|
|
470
508
|
Регистрирует валидатор сообщений (вызывается ДО обработки AI)
|
|
471
|
-
|
|
509
|
+
|
|
472
510
|
Если валидатор возвращает False, обработка прерывается
|
|
473
|
-
|
|
511
|
+
|
|
474
512
|
Args:
|
|
475
513
|
handler: async def(message: Message, supabase_client) -> bool
|
|
476
|
-
|
|
514
|
+
|
|
477
515
|
Example:
|
|
478
516
|
@bot_builder.validate_message
|
|
479
517
|
async def check_service_names(message, supabase_client):
|
|
@@ -484,18 +522,18 @@ class BotBuilder:
|
|
|
484
522
|
"""
|
|
485
523
|
if not callable(handler):
|
|
486
524
|
raise TypeError(f"Обработчик должен быть callable, получен {type(handler)}")
|
|
487
|
-
|
|
525
|
+
|
|
488
526
|
self._message_validators.append(handler)
|
|
489
527
|
logger.info(f"✅ Зарегистрирован валидатор сообщений: {handler.__name__}")
|
|
490
528
|
return handler
|
|
491
|
-
|
|
529
|
+
|
|
492
530
|
def enrich_prompt(self, handler):
|
|
493
531
|
"""
|
|
494
532
|
Регистрирует обогатитель системного промпта
|
|
495
|
-
|
|
533
|
+
|
|
496
534
|
Args:
|
|
497
535
|
handler: async def(system_prompt: str, user_id: int, session_id: str, supabase_client) -> str
|
|
498
|
-
|
|
536
|
+
|
|
499
537
|
Example:
|
|
500
538
|
@bot_builder.enrich_prompt
|
|
501
539
|
async def add_client_info(system_prompt, user_id, session_id, supabase_client):
|
|
@@ -507,42 +545,42 @@ class BotBuilder:
|
|
|
507
545
|
"""
|
|
508
546
|
if not callable(handler):
|
|
509
547
|
raise TypeError(f"Обработчик должен быть callable, получен {type(handler)}")
|
|
510
|
-
|
|
548
|
+
|
|
511
549
|
self._prompt_enrichers.append(handler)
|
|
512
550
|
logger.info(f"✅ Зарегистрирован обогатитель промпта: {handler.__name__}")
|
|
513
551
|
return handler
|
|
514
|
-
|
|
552
|
+
|
|
515
553
|
def enrich_context(self, handler):
|
|
516
554
|
"""
|
|
517
555
|
Регистрирует обогатитель контекста для AI (messages array)
|
|
518
|
-
|
|
556
|
+
|
|
519
557
|
Args:
|
|
520
558
|
handler: async def(messages: List[dict], user_id: int, session_id: str) -> List[dict]
|
|
521
|
-
|
|
559
|
+
|
|
522
560
|
Example:
|
|
523
561
|
@bot_builder.enrich_context
|
|
524
562
|
async def add_external_data(messages, user_id, session_id):
|
|
525
563
|
# Добавляем данные из внешнего API
|
|
526
564
|
messages.append({
|
|
527
|
-
"role": "system",
|
|
565
|
+
"role": "system",
|
|
528
566
|
"content": "Дополнительная информация..."
|
|
529
567
|
})
|
|
530
568
|
return messages
|
|
531
569
|
"""
|
|
532
570
|
if not callable(handler):
|
|
533
571
|
raise TypeError(f"Обработчик должен быть callable, получен {type(handler)}")
|
|
534
|
-
|
|
572
|
+
|
|
535
573
|
self._context_enrichers.append(handler)
|
|
536
574
|
logger.info(f"✅ Зарегистрирован обогатитель контекста: {handler.__name__}")
|
|
537
575
|
return handler
|
|
538
|
-
|
|
576
|
+
|
|
539
577
|
def process_response(self, handler):
|
|
540
578
|
"""
|
|
541
579
|
Регистрирует обработчик ответа AI (ПОСЛЕ получения ответа)
|
|
542
|
-
|
|
580
|
+
|
|
543
581
|
Args:
|
|
544
582
|
handler: async def(response_text: str, ai_metadata: dict, user_id: int) -> tuple[str, dict]
|
|
545
|
-
|
|
583
|
+
|
|
546
584
|
Example:
|
|
547
585
|
@bot_builder.process_response
|
|
548
586
|
async def modify_response(response_text, ai_metadata, user_id):
|
|
@@ -553,27 +591,27 @@ class BotBuilder:
|
|
|
553
591
|
"""
|
|
554
592
|
if not callable(handler):
|
|
555
593
|
raise TypeError(f"Обработчик должен быть callable, получен {type(handler)}")
|
|
556
|
-
|
|
594
|
+
|
|
557
595
|
self._response_processors.append(handler)
|
|
558
596
|
logger.info(f"✅ Зарегистрирован обработчик ответа: {handler.__name__}")
|
|
559
597
|
return handler
|
|
560
|
-
|
|
598
|
+
|
|
561
599
|
def filter_send(self, handler):
|
|
562
600
|
"""
|
|
563
601
|
Регистрирует фильтр отправки (может блокировать отправку пользователю)
|
|
564
|
-
|
|
602
|
+
|
|
565
603
|
Если фильтр возвращает True, сообщение НЕ отправляется
|
|
566
|
-
|
|
604
|
+
|
|
567
605
|
Args:
|
|
568
606
|
handler: async def(user_id: int) -> bool
|
|
569
|
-
|
|
607
|
+
|
|
570
608
|
Example:
|
|
571
609
|
@bot_builder.filter_send
|
|
572
610
|
async def block_during_process(user_id):
|
|
573
611
|
if is_processing(user_id):
|
|
574
612
|
return True # Блокируем отправку
|
|
575
613
|
return False # Разрешаем отправку
|
|
576
|
-
|
|
614
|
+
|
|
577
615
|
# Или совместимый с should_block_ai_response
|
|
578
616
|
@bot_builder.filter_send
|
|
579
617
|
async def should_block_ai_response(user_id):
|
|
@@ -582,45 +620,48 @@ class BotBuilder:
|
|
|
582
620
|
"""
|
|
583
621
|
if not callable(handler):
|
|
584
622
|
raise TypeError(f"Обработчик должен быть callable, получен {type(handler)}")
|
|
585
|
-
|
|
623
|
+
|
|
586
624
|
self._send_filters.append(handler)
|
|
587
625
|
logger.info(f"✅ Зарегистрирован фильтр отправки: {handler.__name__}")
|
|
588
626
|
return handler
|
|
589
|
-
|
|
627
|
+
|
|
590
628
|
def get_message_hooks(self) -> Dict[str, List]:
|
|
591
629
|
"""Получает все хуки для обработки сообщений"""
|
|
592
630
|
return {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
631
|
+
"validators": self._message_validators.copy(),
|
|
632
|
+
"prompt_enrichers": self._prompt_enrichers.copy(),
|
|
633
|
+
"context_enrichers": self._context_enrichers.copy(),
|
|
634
|
+
"response_processors": self._response_processors.copy(),
|
|
635
|
+
"send_filters": self._send_filters.copy(),
|
|
598
636
|
}
|
|
599
|
-
|
|
637
|
+
|
|
600
638
|
def get_router_manager(self) -> RouterManager:
|
|
601
639
|
"""Получает менеджер роутеров событий"""
|
|
602
640
|
return self.router_manager
|
|
603
|
-
|
|
641
|
+
|
|
604
642
|
async def _setup_bot_commands(self, bot):
|
|
605
643
|
"""Устанавливает меню команд для бота (разные для админов и пользователей)"""
|
|
606
|
-
from aiogram.types import BotCommand,
|
|
607
|
-
|
|
644
|
+
from aiogram.types import (BotCommand, BotCommandScopeChat,
|
|
645
|
+
BotCommandScopeDefault)
|
|
646
|
+
|
|
608
647
|
try:
|
|
609
648
|
# Команды для обычных пользователей
|
|
610
649
|
user_commands = [
|
|
611
650
|
BotCommand(command="start", description="🚀 Начать/перезапустить бота"),
|
|
612
651
|
BotCommand(command="help", description="❓ Помощь"),
|
|
613
652
|
]
|
|
614
|
-
|
|
653
|
+
|
|
615
654
|
# Устанавливаем для всех пользователей по умолчанию
|
|
616
655
|
await bot.set_my_commands(user_commands, scope=BotCommandScopeDefault())
|
|
617
656
|
logger.info("✅ Установлены команды для обычных пользователей")
|
|
618
|
-
|
|
657
|
+
|
|
619
658
|
# Команды для админов (включая команды пользователей + админские)
|
|
620
659
|
admin_commands = [
|
|
621
660
|
BotCommand(command="start", description="🚀 Начать/перезапустить бота"),
|
|
622
661
|
BotCommand(command="help", description="❓ Помощь"),
|
|
623
|
-
BotCommand(
|
|
662
|
+
BotCommand(
|
|
663
|
+
command="cancel", description="❌ Отменить текущее действие"
|
|
664
|
+
),
|
|
624
665
|
BotCommand(command="admin", description="👑 Админ панель"),
|
|
625
666
|
BotCommand(command="stats", description="📊 Статистика"),
|
|
626
667
|
BotCommand(command="chat", description="💬 Начать чат с пользователем"),
|
|
@@ -631,61 +672,67 @@ class BotBuilder:
|
|
|
631
672
|
BotCommand(command="list_events", description="📋 Список событий"),
|
|
632
673
|
BotCommand(command="delete_event", description="🗑️ Удалить событие"),
|
|
633
674
|
]
|
|
634
|
-
|
|
675
|
+
|
|
635
676
|
# Устанавливаем для каждого админа персональные команды
|
|
636
677
|
for admin_id in self.config.ADMIN_TELEGRAM_IDS:
|
|
637
678
|
try:
|
|
638
679
|
await bot.set_my_commands(
|
|
639
|
-
admin_commands,
|
|
640
|
-
scope=BotCommandScopeChat(chat_id=admin_id)
|
|
680
|
+
admin_commands, scope=BotCommandScopeChat(chat_id=admin_id)
|
|
641
681
|
)
|
|
642
682
|
logger.info(f"✅ Установлены админские команды для {admin_id}")
|
|
643
683
|
except Exception as e:
|
|
644
|
-
logger.warning(
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
684
|
+
logger.warning(
|
|
685
|
+
f"⚠️ Не удалось установить команды для админа {admin_id}: {e}"
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
logger.info(
|
|
689
|
+
f"✅ Меню команд настроено ({len(self.config.ADMIN_TELEGRAM_IDS)} админов)"
|
|
690
|
+
)
|
|
691
|
+
|
|
648
692
|
except Exception as e:
|
|
649
693
|
logger.error(f"❌ Ошибка установки команд бота: {e}")
|
|
650
|
-
|
|
694
|
+
|
|
651
695
|
async def start(self):
|
|
652
696
|
"""
|
|
653
697
|
Запускает бота (аналог main.py)
|
|
654
698
|
"""
|
|
655
699
|
if not self._initialized:
|
|
656
|
-
raise RuntimeError(
|
|
657
|
-
|
|
700
|
+
raise RuntimeError(
|
|
701
|
+
f"Бот {self.bot_id} не инициализирован. Вызовите build() сначала"
|
|
702
|
+
)
|
|
703
|
+
|
|
658
704
|
logger.info(f"🚀 Запускаем бота {self.bot_id}")
|
|
659
|
-
|
|
705
|
+
|
|
660
706
|
try:
|
|
661
707
|
# Импортируем необходимые компоненты
|
|
662
708
|
from aiogram import Bot, Dispatcher
|
|
663
709
|
from aiogram.fsm.storage.memory import MemoryStorage
|
|
664
|
-
|
|
710
|
+
|
|
665
711
|
# Создаем бота и диспетчер
|
|
666
712
|
bot = Bot(token=self.config.TELEGRAM_BOT_TOKEN)
|
|
667
713
|
storage = MemoryStorage()
|
|
668
714
|
dp = Dispatcher(storage=storage)
|
|
669
|
-
|
|
715
|
+
|
|
670
716
|
# Устанавливаем меню команд для бота
|
|
671
717
|
await self._setup_bot_commands(bot)
|
|
672
|
-
|
|
718
|
+
|
|
673
719
|
# Инициализируем базу данных
|
|
674
720
|
await self.supabase_client.initialize()
|
|
675
|
-
|
|
721
|
+
|
|
676
722
|
# Синхронизируем админов из конфигурации
|
|
677
723
|
await self.admin_manager.sync_admins_from_config()
|
|
678
|
-
|
|
724
|
+
|
|
679
725
|
# Проверяем доступность промптов
|
|
680
726
|
prompts_status = await self.prompt_loader.validate_prompts()
|
|
681
727
|
logger.info(f"Статус промптов: {prompts_status}")
|
|
682
|
-
|
|
683
|
-
|
|
728
|
+
|
|
684
729
|
import importlib
|
|
685
|
-
|
|
730
|
+
|
|
686
731
|
# Устанавливаем глобальные переменные в модулях handlers и admin_logic
|
|
687
732
|
try:
|
|
688
|
-
handlers_module = importlib.import_module(
|
|
733
|
+
handlers_module = importlib.import_module(
|
|
734
|
+
"smart_bot_factory.handlers.handlers"
|
|
735
|
+
)
|
|
689
736
|
handlers_module.config = self.config
|
|
690
737
|
handlers_module.bot = bot
|
|
691
738
|
handlers_module.dp = dp
|
|
@@ -695,15 +742,25 @@ class BotBuilder:
|
|
|
695
742
|
handlers_module.admin_manager = self.admin_manager
|
|
696
743
|
handlers_module.analytics_manager = self.analytics_manager
|
|
697
744
|
handlers_module.conversation_manager = self.conversation_manager
|
|
698
|
-
handlers_module.start_handlers =
|
|
699
|
-
|
|
700
|
-
|
|
745
|
+
handlers_module.start_handlers = (
|
|
746
|
+
self._start_handlers
|
|
747
|
+
) # Передаем обработчики on_start
|
|
748
|
+
handlers_module.message_hooks = (
|
|
749
|
+
self.get_message_hooks()
|
|
750
|
+
) # Передаем хуки для обработки сообщений
|
|
751
|
+
handlers_module.custom_event_processor = (
|
|
752
|
+
self._custom_event_processor
|
|
753
|
+
) # Передаем кастомный процессор событий
|
|
701
754
|
logger.info("✅ Глобальные переменные установлены в handlers")
|
|
702
755
|
except Exception as e:
|
|
703
|
-
logger.warning(
|
|
704
|
-
|
|
756
|
+
logger.warning(
|
|
757
|
+
f"⚠️ Не удалось установить глобальные переменные в handlers: {e}"
|
|
758
|
+
)
|
|
759
|
+
|
|
705
760
|
try:
|
|
706
|
-
admin_logic_module = importlib.import_module(
|
|
761
|
+
admin_logic_module = importlib.import_module(
|
|
762
|
+
"smart_bot_factory.admin.admin_logic"
|
|
763
|
+
)
|
|
707
764
|
admin_logic_module.config = self.config
|
|
708
765
|
admin_logic_module.bot = bot
|
|
709
766
|
admin_logic_module.dp = dp
|
|
@@ -715,11 +772,15 @@ class BotBuilder:
|
|
|
715
772
|
admin_logic_module.conversation_manager = self.conversation_manager
|
|
716
773
|
logger.info("✅ Глобальные переменные установлены в admin_logic")
|
|
717
774
|
except Exception as e:
|
|
718
|
-
logger.warning(
|
|
719
|
-
|
|
775
|
+
logger.warning(
|
|
776
|
+
f"⚠️ Не удалось установить глобальные переменные в admin_logic: {e}"
|
|
777
|
+
)
|
|
778
|
+
|
|
720
779
|
# Также устанавливаем в bot_utils
|
|
721
780
|
try:
|
|
722
|
-
bot_utils_module = importlib.import_module(
|
|
781
|
+
bot_utils_module = importlib.import_module(
|
|
782
|
+
"smart_bot_factory.core.bot_utils"
|
|
783
|
+
)
|
|
723
784
|
bot_utils_module.config = self.config
|
|
724
785
|
bot_utils_module.bot = bot
|
|
725
786
|
bot_utils_module.dp = dp
|
|
@@ -729,14 +790,19 @@ class BotBuilder:
|
|
|
729
790
|
bot_utils_module.admin_manager = self.admin_manager
|
|
730
791
|
bot_utils_module.analytics_manager = self.analytics_manager
|
|
731
792
|
bot_utils_module.conversation_manager = self.conversation_manager
|
|
732
|
-
bot_utils_module.custom_event_processor =
|
|
793
|
+
bot_utils_module.custom_event_processor = (
|
|
794
|
+
self._custom_event_processor
|
|
795
|
+
) # Передаем кастомный процессор событий
|
|
733
796
|
logger.info("✅ Глобальные переменные установлены в bot_utils")
|
|
734
797
|
except Exception as e:
|
|
735
|
-
logger.warning(
|
|
736
|
-
|
|
798
|
+
logger.warning(
|
|
799
|
+
f"⚠️ Не удалось установить глобальные переменные в bot_utils: {e}"
|
|
800
|
+
)
|
|
801
|
+
|
|
737
802
|
# Также устанавливаем в debug_routing
|
|
738
803
|
try:
|
|
739
804
|
from ..utils import debug_routing
|
|
805
|
+
|
|
740
806
|
debug_routing.config = self.config
|
|
741
807
|
debug_routing.bot = bot
|
|
742
808
|
debug_routing.dp = dp
|
|
@@ -747,73 +813,85 @@ class BotBuilder:
|
|
|
747
813
|
debug_routing.conversation_manager = self.conversation_manager
|
|
748
814
|
logger.info("✅ Глобальные переменные установлены в debug_routing")
|
|
749
815
|
except Exception as e:
|
|
750
|
-
logger.warning(
|
|
751
|
-
|
|
816
|
+
logger.warning(
|
|
817
|
+
f"⚠️ Не удалось установить глобальные переменные в debug_routing: {e}"
|
|
818
|
+
)
|
|
819
|
+
|
|
752
820
|
# Теперь импортируем и настраиваем обработчики
|
|
753
|
-
from ..handlers.handlers import setup_handlers
|
|
754
|
-
from ..admin.admin_logic import setup_admin_handlers
|
|
755
821
|
from ..admin.admin_events import setup_admin_events_handlers
|
|
822
|
+
from ..admin.admin_logic import setup_admin_handlers
|
|
756
823
|
from ..core.bot_utils import setup_utils_handlers
|
|
757
|
-
|
|
824
|
+
from ..handlers.handlers import setup_handlers
|
|
825
|
+
|
|
758
826
|
# Подключаем пользовательские Telegram роутеры ПЕРВЫМИ (высший приоритет)
|
|
759
827
|
if self._telegram_routers:
|
|
760
|
-
logger.info(
|
|
828
|
+
logger.info(
|
|
829
|
+
f"🔗 Подключаем {len(self._telegram_routers)} пользовательских Telegram роутеров"
|
|
830
|
+
)
|
|
761
831
|
for telegram_router in self._telegram_routers:
|
|
762
832
|
dp.include_router(telegram_router)
|
|
763
|
-
router_name = getattr(telegram_router,
|
|
833
|
+
router_name = getattr(telegram_router, "name", "unnamed")
|
|
764
834
|
logger.info(f"✅ Подключен Telegram роутер: {router_name}")
|
|
765
|
-
|
|
835
|
+
|
|
766
836
|
# Настраиваем стандартные обработчики (меньший приоритет)
|
|
767
|
-
setup_utils_handlers(dp)
|
|
768
|
-
setup_admin_handlers(dp)
|
|
837
|
+
setup_utils_handlers(dp) # Утилитарные команды (/status, /help)
|
|
838
|
+
setup_admin_handlers(dp) # Админские команды (/админ, /стат, /чат)
|
|
769
839
|
setup_admin_events_handlers(dp) # Админские события (/создать_событие)
|
|
770
|
-
setup_handlers(dp)
|
|
771
|
-
|
|
840
|
+
setup_handlers(dp) # Основные пользовательские обработчики
|
|
841
|
+
|
|
772
842
|
# Устанавливаем глобальные переменные в модуле бота для удобного доступа
|
|
773
843
|
self.set_global_vars_in_module(self.bot_id)
|
|
774
|
-
|
|
844
|
+
|
|
775
845
|
# Устанавливаем роутер-менеджер в декораторы ПЕРЕД настройкой обработчиков
|
|
776
846
|
if self.router_manager:
|
|
777
847
|
from ..core.decorators import set_router_manager
|
|
848
|
+
|
|
778
849
|
set_router_manager(self.router_manager)
|
|
779
850
|
logger.info("✅ RouterManager установлен в decorators")
|
|
780
|
-
|
|
851
|
+
|
|
781
852
|
# Обновляем обработчики после установки RouterManager
|
|
782
853
|
# (на случай если декораторы выполнялись после добавления роутера)
|
|
783
854
|
self.router_manager._update_combined_handlers()
|
|
784
855
|
logger.info("✅ RouterManager обработчики обновлены")
|
|
785
|
-
|
|
856
|
+
|
|
786
857
|
# Фоновые задачи выполняются через asyncio.create_task в decorators.py
|
|
787
|
-
|
|
858
|
+
|
|
788
859
|
# Логируем информацию о запуске
|
|
789
860
|
logger.info(f"✅ Бот {self.bot_id} запущен и готов к работе!")
|
|
790
861
|
logger.info(f" 📊 Изоляция данных: bot_id = {self.config.BOT_ID}")
|
|
791
|
-
logger.info(
|
|
862
|
+
logger.info(
|
|
863
|
+
f" 👑 Админов настроено: {len(self.config.ADMIN_TELEGRAM_IDS)}"
|
|
864
|
+
)
|
|
792
865
|
logger.info(f" 📝 Загружено промптов: {len(self.config.PROMPT_FILES)}")
|
|
793
|
-
|
|
866
|
+
|
|
794
867
|
# Запускаем единый фоновый процессор для всех событий
|
|
795
|
-
from ..core.decorators import background_event_processor
|
|
796
868
|
import asyncio
|
|
869
|
+
|
|
870
|
+
from ..core.decorators import background_event_processor
|
|
871
|
+
|
|
797
872
|
asyncio.create_task(background_event_processor())
|
|
798
|
-
logger.info(
|
|
799
|
-
|
|
873
|
+
logger.info(
|
|
874
|
+
"✅ Фоновый процессор событий запущен (user_event, scheduled_task, global_handler, admin_event)"
|
|
875
|
+
)
|
|
876
|
+
|
|
800
877
|
# Четкое сообщение о запуске
|
|
801
878
|
print(f"\n🤖 БОТ {self.bot_id.upper()} УСПЕШНО ЗАПУЩЕН!")
|
|
802
879
|
print(f"📱 Telegram Bot ID: {self.config.BOT_ID}")
|
|
803
880
|
print(f"👑 Админов: {len(self.config.ADMIN_TELEGRAM_IDS)}")
|
|
804
881
|
print(f"📝 Промптов: {len(self.config.PROMPT_FILES)}")
|
|
805
|
-
print(
|
|
806
|
-
print(
|
|
807
|
-
|
|
882
|
+
print("⏳ Ожидание сообщений...")
|
|
883
|
+
print("⏹️ Для остановки нажмите Ctrl+C\n")
|
|
884
|
+
|
|
808
885
|
# Запуск polling (бесконечная обработка сообщений)
|
|
809
886
|
await dp.start_polling(bot)
|
|
810
|
-
|
|
887
|
+
|
|
811
888
|
except Exception as e:
|
|
812
889
|
logger.error(f"❌ Ошибка при запуске бота {self.bot_id}: {e}")
|
|
813
890
|
import traceback
|
|
891
|
+
|
|
814
892
|
logger.error(f"Стек ошибки: {traceback.format_exc()}")
|
|
815
893
|
raise
|
|
816
894
|
finally:
|
|
817
895
|
# Очистка ресурсов при завершении
|
|
818
|
-
if
|
|
896
|
+
if "bot" in locals():
|
|
819
897
|
await bot.session.close()
|