smart-bot-factory 1.1.1__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.
- smart_bot_factory/__init__.py +3 -0
- smart_bot_factory/admin/__init__.py +18 -0
- smart_bot_factory/admin/admin_events.py +1223 -0
- smart_bot_factory/admin/admin_logic.py +553 -0
- smart_bot_factory/admin/admin_manager.py +156 -0
- smart_bot_factory/admin/admin_tester.py +157 -0
- smart_bot_factory/admin/timeout_checker.py +547 -0
- smart_bot_factory/aiogram_calendar/__init__.py +14 -0
- smart_bot_factory/aiogram_calendar/common.py +64 -0
- smart_bot_factory/aiogram_calendar/dialog_calendar.py +259 -0
- smart_bot_factory/aiogram_calendar/schemas.py +99 -0
- smart_bot_factory/aiogram_calendar/simple_calendar.py +224 -0
- smart_bot_factory/analytics/analytics_manager.py +414 -0
- smart_bot_factory/cli.py +806 -0
- smart_bot_factory/config.py +258 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/1sales_context.txt +16 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/2product_info.txt +582 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/3objection_handling.txt +66 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/final_instructions.txt +212 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/help_message.txt +28 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/welcome_message.txt +8 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064229.txt +818 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064335.txt +32 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064638.txt +35 -0
- smart_bot_factory/configs/growthmed-october-24/tests/quick_scenarios.yaml +133 -0
- smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml +108 -0
- smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml +46 -0
- smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt +16 -0
- smart_bot_factory/configs/growthmed-october-24/welcome_file//342/225/250/320/267/342/225/250/342/225/241/342/225/250/342/225/221 /342/225/250/342/225/227/342/225/250/342/225/225/342/225/244/320/221/342/225/244/320/222 /342/225/250/342/224/220/342/225/250/342/225/233 152/342/225/250/320/264/342/225/250/320/247 /342/225/250/342/225/225 323/342/225/250/320/264/342/225/250/320/247 /342/225/250/342/224/244/342/225/250/342/225/227/342/225/244/320/237 /342/225/250/342/225/235/342/225/250/342/225/241/342/225/250/342/224/244/342/225/250/342/225/225/342/225/244/320/226/342/225/250/342/225/225/342/225/250/342/225/234/342/225/244/320/233.pdf +0 -0
- smart_bot_factory/core/bot_utils.py +1108 -0
- smart_bot_factory/core/conversation_manager.py +653 -0
- smart_bot_factory/core/decorators.py +2464 -0
- smart_bot_factory/core/message_sender.py +729 -0
- smart_bot_factory/core/router.py +347 -0
- smart_bot_factory/core/router_manager.py +218 -0
- smart_bot_factory/core/states.py +27 -0
- smart_bot_factory/creation/__init__.py +7 -0
- smart_bot_factory/creation/bot_builder.py +1093 -0
- smart_bot_factory/creation/bot_testing.py +1122 -0
- smart_bot_factory/dashboard/__init__.py +3 -0
- smart_bot_factory/event/__init__.py +7 -0
- smart_bot_factory/handlers/handlers.py +2013 -0
- smart_bot_factory/integrations/langchain_openai.py +542 -0
- smart_bot_factory/integrations/openai_client.py +513 -0
- smart_bot_factory/integrations/supabase_client.py +1678 -0
- smart_bot_factory/memory/__init__.py +8 -0
- smart_bot_factory/memory/memory_manager.py +299 -0
- smart_bot_factory/memory/static_memory.py +214 -0
- smart_bot_factory/message/__init__.py +56 -0
- smart_bot_factory/rag/__init__.py +5 -0
- smart_bot_factory/rag/decorators.py +29 -0
- smart_bot_factory/rag/router.py +54 -0
- smart_bot_factory/rag/templates/__init__.py +3 -0
- smart_bot_factory/rag/templates/create_table.sql +7 -0
- smart_bot_factory/rag/templates/create_table_and_function_template.py +94 -0
- smart_bot_factory/rag/templates/match_function.sql +61 -0
- smart_bot_factory/rag/templates/match_services_template.py +82 -0
- smart_bot_factory/rag/vectorstore.py +449 -0
- smart_bot_factory/router/__init__.py +10 -0
- smart_bot_factory/setup_checker.py +512 -0
- smart_bot_factory/supabase/__init__.py +7 -0
- smart_bot_factory/supabase/client.py +631 -0
- smart_bot_factory/utils/__init__.py +11 -0
- smart_bot_factory/utils/debug_routing.py +114 -0
- smart_bot_factory/utils/prompt_loader.py +529 -0
- smart_bot_factory/utils/tool_router.py +68 -0
- smart_bot_factory/utils/user_prompt_loader.py +55 -0
- smart_bot_factory/utm_link_generator.py +123 -0
- smart_bot_factory-1.1.1.dist-info/METADATA +1135 -0
- smart_bot_factory-1.1.1.dist-info/RECORD +73 -0
- smart_bot_factory-1.1.1.dist-info/WHEEL +4 -0
- smart_bot_factory-1.1.1.dist-info/entry_points.txt +2 -0
- smart_bot_factory-1.1.1.dist-info/licenses/LICENSE +24 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"""
|
|
2
|
+
EventRouter для Smart Bot Factory - роутер для событий, задач и глобальных обработчиков
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Any, Callable, Dict, Union
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EventRouter:
|
|
12
|
+
"""
|
|
13
|
+
Роутер для организации обработчиков событий, задач и глобальных обработчиков
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, name: str = None):
|
|
17
|
+
"""
|
|
18
|
+
Инициализация роутера
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
name: Имя роутера для логирования
|
|
22
|
+
"""
|
|
23
|
+
self.name = name or f"EventRouter_{id(self)}"
|
|
24
|
+
self._event_handlers: Dict[str, Dict[str, Any]] = {}
|
|
25
|
+
self._scheduled_tasks: Dict[str, Dict[str, Any]] = {}
|
|
26
|
+
self._global_handlers: Dict[str, Dict[str, Any]] = {}
|
|
27
|
+
|
|
28
|
+
logger.info(f"🔄 Создан роутер: {self.name}")
|
|
29
|
+
|
|
30
|
+
def event_handler(
|
|
31
|
+
self,
|
|
32
|
+
event_type: str,
|
|
33
|
+
notify: bool = False,
|
|
34
|
+
once_only: bool = True,
|
|
35
|
+
send_ai_response: bool = True,
|
|
36
|
+
):
|
|
37
|
+
"""
|
|
38
|
+
Декоратор для регистрации обработчика события в роутере
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
event_type: Тип события
|
|
42
|
+
notify: Уведомлять ли админов
|
|
43
|
+
once_only: Выполнять ли только один раз
|
|
44
|
+
send_ai_response: Отправлять ли сообщение от ИИ после обработки события (по умолчанию True)
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def decorator(func: Callable) -> Callable:
|
|
48
|
+
self._event_handlers[event_type] = {
|
|
49
|
+
"handler": func,
|
|
50
|
+
"name": func.__name__,
|
|
51
|
+
"notify": notify,
|
|
52
|
+
"once_only": once_only,
|
|
53
|
+
"send_ai_response": send_ai_response,
|
|
54
|
+
"router": self.name,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
logger.info(
|
|
58
|
+
f"📝 Роутер {self.name}: зарегистрирован обработчик события '{event_type}': {func.__name__}"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
from functools import wraps
|
|
62
|
+
|
|
63
|
+
@wraps(func)
|
|
64
|
+
async def wrapper(*args, **kwargs):
|
|
65
|
+
try:
|
|
66
|
+
result = await func(*args, **kwargs)
|
|
67
|
+
|
|
68
|
+
# Автоматически добавляем флаги notify и send_ai_response к результату
|
|
69
|
+
if isinstance(result, dict):
|
|
70
|
+
result["notify"] = notify
|
|
71
|
+
result["send_ai_response"] = send_ai_response
|
|
72
|
+
else:
|
|
73
|
+
# Если результат не словарь, создаем словарь
|
|
74
|
+
result = {
|
|
75
|
+
"status": "success",
|
|
76
|
+
"result": result,
|
|
77
|
+
"notify": notify,
|
|
78
|
+
"send_ai_response": send_ai_response,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return result
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.error(
|
|
84
|
+
f"Ошибка выполнения обработчика '{event_type}' в роутере {self.name}: {e}"
|
|
85
|
+
)
|
|
86
|
+
raise
|
|
87
|
+
|
|
88
|
+
return wrapper
|
|
89
|
+
|
|
90
|
+
return decorator
|
|
91
|
+
|
|
92
|
+
def schedule_task(
|
|
93
|
+
self,
|
|
94
|
+
task_name: str,
|
|
95
|
+
notify: bool = False,
|
|
96
|
+
notify_time: str = "after", # 'after' или 'before'
|
|
97
|
+
smart_check: bool = True,
|
|
98
|
+
once_only: bool = True,
|
|
99
|
+
delay: Union[str, int] = None,
|
|
100
|
+
event_type: Union[str, Callable] = None,
|
|
101
|
+
send_ai_response: bool = True,
|
|
102
|
+
):
|
|
103
|
+
"""
|
|
104
|
+
Декоратор для регистрации запланированной задачи в роутере
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
task_name: Название задачи
|
|
108
|
+
notify: Уведомлять ли админов
|
|
109
|
+
notify_time: Когда отправлять уведомление админам:
|
|
110
|
+
- 'before': при создании задачи
|
|
111
|
+
- 'after': после успешного выполнения (по умолчанию)
|
|
112
|
+
smart_check: Использовать ли умную проверку
|
|
113
|
+
once_only: Выполнять ли только один раз
|
|
114
|
+
delay: Время задержки в удобном формате (например, "1h 30m", "45m", 3600) - ОБЯЗАТЕЛЬНО
|
|
115
|
+
event_type: Источник времени события - ОПЦИОНАЛЬНО:
|
|
116
|
+
- str: Тип события для поиска в БД (например, 'appointment_booking')
|
|
117
|
+
- Callable: Функция async def(user_id, user_data) -> datetime
|
|
118
|
+
send_ai_response: Отправлять ли сообщение от ИИ после выполнения задачи (по умолчанию True)
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
def decorator(func: Callable) -> Callable:
|
|
122
|
+
# Время ОБЯЗАТЕЛЬНО должно быть указано
|
|
123
|
+
if delay is None:
|
|
124
|
+
raise ValueError(
|
|
125
|
+
f"Для задачи '{task_name}' в роутере {self.name} ОБЯЗАТЕЛЬНО нужно указать параметр delay"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Импортируем функцию парсинга времени
|
|
129
|
+
from .decorators import parse_time_string
|
|
130
|
+
|
|
131
|
+
# Парсим время
|
|
132
|
+
try:
|
|
133
|
+
default_delay_seconds = parse_time_string(delay)
|
|
134
|
+
if event_type:
|
|
135
|
+
logger.info(
|
|
136
|
+
f"⏰ Роутер {self.name}: задача '{task_name}' настроена как напоминание о событии '{event_type}' за {delay} ({default_delay_seconds}с)"
|
|
137
|
+
)
|
|
138
|
+
else:
|
|
139
|
+
logger.info(
|
|
140
|
+
f"⏰ Роутер {self.name}: задача '{task_name}' настроена с задержкой: {delay} ({default_delay_seconds}с)"
|
|
141
|
+
)
|
|
142
|
+
except ValueError as e:
|
|
143
|
+
logger.error(
|
|
144
|
+
f"❌ Ошибка парсинга времени для задачи '{task_name}' в роутере {self.name}: {e}"
|
|
145
|
+
)
|
|
146
|
+
raise
|
|
147
|
+
|
|
148
|
+
# Проверяем корректность notify_time
|
|
149
|
+
if notify_time not in ["before", "after"]:
|
|
150
|
+
raise ValueError(f"notify_time должен быть 'before' или 'after', получено: {notify_time}")
|
|
151
|
+
|
|
152
|
+
self._scheduled_tasks[task_name] = {
|
|
153
|
+
"handler": func,
|
|
154
|
+
"name": func.__name__,
|
|
155
|
+
"notify": notify,
|
|
156
|
+
"notify_time": notify_time, # Когда отправлять уведомление
|
|
157
|
+
"smart_check": smart_check,
|
|
158
|
+
"once_only": once_only,
|
|
159
|
+
"router": self.name,
|
|
160
|
+
"default_delay": default_delay_seconds,
|
|
161
|
+
"event_type": event_type, # Новое поле для типа события
|
|
162
|
+
"send_ai_response": send_ai_response,
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if event_type:
|
|
166
|
+
logger.info(
|
|
167
|
+
f"⏰ Роутер {self.name}: зарегистрирована задача-напоминание '{task_name}' для события '{event_type}': {func.__name__}"
|
|
168
|
+
)
|
|
169
|
+
else:
|
|
170
|
+
logger.info(
|
|
171
|
+
f"⏰ Роутер {self.name}: зарегистрирована задача '{task_name}': {func.__name__}"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
from functools import wraps
|
|
175
|
+
|
|
176
|
+
@wraps(func)
|
|
177
|
+
async def wrapper(*args, **kwargs):
|
|
178
|
+
try:
|
|
179
|
+
result = await func(*args, **kwargs)
|
|
180
|
+
|
|
181
|
+
# Автоматически добавляем флаги notify и send_ai_response к результату
|
|
182
|
+
if isinstance(result, dict):
|
|
183
|
+
result["notify"] = notify
|
|
184
|
+
result["send_ai_response"] = send_ai_response
|
|
185
|
+
else:
|
|
186
|
+
# Если результат не словарь, создаем словарь
|
|
187
|
+
result = {
|
|
188
|
+
"status": "success",
|
|
189
|
+
"result": result,
|
|
190
|
+
"notify": notify,
|
|
191
|
+
"send_ai_response": send_ai_response,
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return result
|
|
195
|
+
except Exception as e:
|
|
196
|
+
logger.error(
|
|
197
|
+
f"Ошибка выполнения задачи '{task_name}' в роутере {self.name}: {e}"
|
|
198
|
+
)
|
|
199
|
+
raise
|
|
200
|
+
|
|
201
|
+
return wrapper
|
|
202
|
+
|
|
203
|
+
return decorator
|
|
204
|
+
|
|
205
|
+
def global_handler(
|
|
206
|
+
self,
|
|
207
|
+
handler_type: str,
|
|
208
|
+
notify: bool = False,
|
|
209
|
+
once_only: bool = True,
|
|
210
|
+
delay: Union[str, int] = None,
|
|
211
|
+
event_type: Union[str, Callable] = None,
|
|
212
|
+
send_ai_response: bool = True,
|
|
213
|
+
):
|
|
214
|
+
"""
|
|
215
|
+
Декоратор для регистрации глобального обработчика в роутере
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
handler_type: Тип глобального обработчика
|
|
219
|
+
notify: Уведомлять ли админов
|
|
220
|
+
once_only: Выполнять ли только один раз
|
|
221
|
+
delay: Время задержки в удобном формате (например, "1h 30m", "45m", 3600) - ОБЯЗАТЕЛЬНО
|
|
222
|
+
send_ai_response: Отправлять ли сообщение от ИИ после выполнения обработчика (по умолчанию True)
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
def decorator(func: Callable) -> Callable:
|
|
226
|
+
# Время ОБЯЗАТЕЛЬНО должно быть указано
|
|
227
|
+
if delay is None:
|
|
228
|
+
raise ValueError(
|
|
229
|
+
f"Для глобального обработчика '{handler_type}' в роутере {self.name} ОБЯЗАТЕЛЬНО нужно указать параметр delay"
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# Импортируем функцию парсинга времени
|
|
233
|
+
from .decorators import parse_time_string
|
|
234
|
+
|
|
235
|
+
# Парсим время
|
|
236
|
+
try:
|
|
237
|
+
default_delay_seconds = parse_time_string(delay)
|
|
238
|
+
logger.info(
|
|
239
|
+
f"🌍 Роутер {self.name}: глобальный обработчик '{handler_type}' настроен с задержкой: {delay} ({default_delay_seconds}с)"
|
|
240
|
+
)
|
|
241
|
+
except ValueError as e:
|
|
242
|
+
logger.error(
|
|
243
|
+
f"❌ Ошибка парсинга времени для глобального обработчика '{handler_type}' в роутере {self.name}: {e}"
|
|
244
|
+
)
|
|
245
|
+
raise
|
|
246
|
+
|
|
247
|
+
self._global_handlers[handler_type] = {
|
|
248
|
+
"handler": func,
|
|
249
|
+
"name": func.__name__,
|
|
250
|
+
"notify": notify,
|
|
251
|
+
"once_only": once_only,
|
|
252
|
+
"router": self.name,
|
|
253
|
+
"default_delay": default_delay_seconds,
|
|
254
|
+
"event_type": event_type, # Добавляем event_type для глобальных обработчиков
|
|
255
|
+
"send_ai_response": send_ai_response,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
logger.info(
|
|
259
|
+
f"🌍 Роутер {self.name}: зарегистрирован глобальный обработчик '{handler_type}': {func.__name__}"
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
from functools import wraps
|
|
263
|
+
|
|
264
|
+
@wraps(func)
|
|
265
|
+
async def wrapper(*args, **kwargs):
|
|
266
|
+
try:
|
|
267
|
+
result = await func(*args, **kwargs)
|
|
268
|
+
|
|
269
|
+
# Автоматически добавляем флаги notify и send_ai_response к результату
|
|
270
|
+
if isinstance(result, dict):
|
|
271
|
+
result["notify"] = notify
|
|
272
|
+
result["send_ai_response"] = send_ai_response
|
|
273
|
+
else:
|
|
274
|
+
# Если результат не словарь, создаем словарь
|
|
275
|
+
result = {
|
|
276
|
+
"status": "success",
|
|
277
|
+
"result": result,
|
|
278
|
+
"notify": notify,
|
|
279
|
+
"send_ai_response": send_ai_response,
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return result
|
|
283
|
+
except Exception as e:
|
|
284
|
+
logger.error(
|
|
285
|
+
f"Ошибка выполнения глобального обработчика '{handler_type}' в роутере {self.name}: {e}"
|
|
286
|
+
)
|
|
287
|
+
raise
|
|
288
|
+
|
|
289
|
+
return wrapper
|
|
290
|
+
|
|
291
|
+
return decorator
|
|
292
|
+
|
|
293
|
+
def get_event_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
294
|
+
"""Получает все обработчики событий роутера"""
|
|
295
|
+
return self._event_handlers.copy()
|
|
296
|
+
|
|
297
|
+
def get_scheduled_tasks(self) -> Dict[str, Dict[str, Any]]:
|
|
298
|
+
"""Получает все запланированные задачи роутера"""
|
|
299
|
+
return self._scheduled_tasks.copy()
|
|
300
|
+
|
|
301
|
+
def get_global_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
302
|
+
"""Получает все глобальные обработчики роутера"""
|
|
303
|
+
return self._global_handlers.copy()
|
|
304
|
+
|
|
305
|
+
def get_all_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
306
|
+
"""Получает все обработчики роутера"""
|
|
307
|
+
all_handlers = {}
|
|
308
|
+
all_handlers.update(self._event_handlers)
|
|
309
|
+
all_handlers.update(self._scheduled_tasks)
|
|
310
|
+
all_handlers.update(self._global_handlers)
|
|
311
|
+
return all_handlers
|
|
312
|
+
|
|
313
|
+
def include_router(self, router: "EventRouter"):
|
|
314
|
+
"""
|
|
315
|
+
Включает другой роутер в текущий
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
router: EventRouter для включения
|
|
319
|
+
"""
|
|
320
|
+
# Добавляем обработчики событий
|
|
321
|
+
for event_type, handler_info in router.get_event_handlers().items():
|
|
322
|
+
if event_type in self._event_handlers:
|
|
323
|
+
logger.warning(
|
|
324
|
+
f"⚠️ Конфликт обработчиков событий '{event_type}' между роутерами {self.name} и {router.name}"
|
|
325
|
+
)
|
|
326
|
+
self._event_handlers[event_type] = handler_info
|
|
327
|
+
|
|
328
|
+
# Добавляем запланированные задачи
|
|
329
|
+
for task_name, task_info in router.get_scheduled_tasks().items():
|
|
330
|
+
if task_name in self._scheduled_tasks:
|
|
331
|
+
logger.warning(
|
|
332
|
+
f"⚠️ Конфликт задач '{task_name}' между роутерами {self.name} и {router.name}"
|
|
333
|
+
)
|
|
334
|
+
self._scheduled_tasks[task_name] = task_info
|
|
335
|
+
|
|
336
|
+
# Добавляем глобальные обработчики
|
|
337
|
+
for handler_type, handler_info in router.get_global_handlers().items():
|
|
338
|
+
if handler_type in self._global_handlers:
|
|
339
|
+
logger.warning(
|
|
340
|
+
f"⚠️ Конфликт глобальных обработчиков '{handler_type}' между роутерами {self.name} и {router.name}"
|
|
341
|
+
)
|
|
342
|
+
self._global_handlers[handler_type] = handler_info
|
|
343
|
+
|
|
344
|
+
logger.info(f"🔗 Роутер {self.name}: включен роутер {router.name}")
|
|
345
|
+
|
|
346
|
+
def __repr__(self):
|
|
347
|
+
return f"EventRouter(name='{self.name}', events={len(self._event_handlers)}, tasks={len(self._scheduled_tasks)}, globals={len(self._global_handlers)})"
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Менеджер роутеров для Smart Bot Factory
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
from .router import EventRouter
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RouterManager:
|
|
14
|
+
"""
|
|
15
|
+
Менеджер для управления роутерами событий и их обработчиками
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
self._routers: List[EventRouter] = []
|
|
20
|
+
self._combined_handlers: Dict[str, Dict[str, Any]] = {
|
|
21
|
+
"event_handlers": {},
|
|
22
|
+
"scheduled_tasks": {},
|
|
23
|
+
"global_handlers": {},
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
logger.info("🔄 Создан менеджер роутеров")
|
|
27
|
+
|
|
28
|
+
def register_router(self, router: EventRouter):
|
|
29
|
+
"""
|
|
30
|
+
Регистрирует роутер событий в менеджере
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
router: EventRouter для регистрации
|
|
34
|
+
"""
|
|
35
|
+
if router not in self._routers:
|
|
36
|
+
self._routers.append(router)
|
|
37
|
+
self._update_combined_handlers()
|
|
38
|
+
logger.info(f"✅ Зарегистрирован роутер: {router.name}")
|
|
39
|
+
else:
|
|
40
|
+
logger.warning(f"⚠️ Роутер {router.name} уже зарегистрирован")
|
|
41
|
+
|
|
42
|
+
def unregister_router(self, router: EventRouter):
|
|
43
|
+
"""
|
|
44
|
+
Отменяет регистрацию роутера событий
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
router: EventRouter для отмены регистрации
|
|
48
|
+
"""
|
|
49
|
+
if router in self._routers:
|
|
50
|
+
self._routers.remove(router)
|
|
51
|
+
self._update_combined_handlers()
|
|
52
|
+
logger.info(f"❌ Отменена регистрация роутера: {router.name}")
|
|
53
|
+
else:
|
|
54
|
+
logger.warning(f"⚠️ Роутер {router.name} не найден в зарегистрированных")
|
|
55
|
+
|
|
56
|
+
def _update_combined_handlers(self):
|
|
57
|
+
"""Обновляет объединенные обработчики всех роутеров"""
|
|
58
|
+
# Очищаем текущие обработчики
|
|
59
|
+
self._combined_handlers = {
|
|
60
|
+
"event_handlers": {},
|
|
61
|
+
"scheduled_tasks": {},
|
|
62
|
+
"global_handlers": {},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
logger.debug(
|
|
66
|
+
f"🔍 RouterManager._update_combined_handlers(): обновляем обработчики для {len(self._routers)} роутеров"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Собираем обработчики из всех роутеров
|
|
70
|
+
for router in self._routers:
|
|
71
|
+
logger.debug(f"🔍 Обрабатываем роутер: {router.name}")
|
|
72
|
+
|
|
73
|
+
# Обработчики событий
|
|
74
|
+
event_handlers = router.get_event_handlers()
|
|
75
|
+
logger.debug(
|
|
76
|
+
f"🔍 Роутер {router.name}: {len(event_handlers)} обработчиков событий"
|
|
77
|
+
)
|
|
78
|
+
for event_type, handler_info in event_handlers.items():
|
|
79
|
+
if event_type in self._combined_handlers["event_handlers"]:
|
|
80
|
+
existing_router = self._combined_handlers["event_handlers"][
|
|
81
|
+
event_type
|
|
82
|
+
]["router"]
|
|
83
|
+
logger.warning(
|
|
84
|
+
f"⚠️ Конфликт обработчиков событий '{event_type}' между роутерами {existing_router} и {router.name}"
|
|
85
|
+
)
|
|
86
|
+
self._combined_handlers["event_handlers"][event_type] = handler_info
|
|
87
|
+
|
|
88
|
+
# Запланированные задачи
|
|
89
|
+
scheduled_tasks = router.get_scheduled_tasks()
|
|
90
|
+
logger.debug(
|
|
91
|
+
f"🔍 Роутер {router.name}: {len(scheduled_tasks)} запланированных задач: {list(scheduled_tasks.keys())}"
|
|
92
|
+
)
|
|
93
|
+
for task_name, task_info in scheduled_tasks.items():
|
|
94
|
+
if task_name in self._combined_handlers["scheduled_tasks"]:
|
|
95
|
+
existing_router = self._combined_handlers["scheduled_tasks"][
|
|
96
|
+
task_name
|
|
97
|
+
]["router"]
|
|
98
|
+
logger.warning(
|
|
99
|
+
f"⚠️ Конфликт задач '{task_name}' между роутерами {existing_router} и {router.name}"
|
|
100
|
+
)
|
|
101
|
+
self._combined_handlers["scheduled_tasks"][task_name] = task_info
|
|
102
|
+
|
|
103
|
+
# Глобальные обработчики
|
|
104
|
+
global_handlers = router.get_global_handlers()
|
|
105
|
+
logger.debug(
|
|
106
|
+
f"🔍 Роутер {router.name}: {len(global_handlers)} глобальных обработчиков"
|
|
107
|
+
)
|
|
108
|
+
for handler_type, handler_info in global_handlers.items():
|
|
109
|
+
if handler_type in self._combined_handlers["global_handlers"]:
|
|
110
|
+
existing_router = self._combined_handlers["global_handlers"][
|
|
111
|
+
handler_type
|
|
112
|
+
]["router"]
|
|
113
|
+
logger.warning(
|
|
114
|
+
f"⚠️ Конфликт глобальных обработчиков '{handler_type}' между роутерами {existing_router} и {router.name}"
|
|
115
|
+
)
|
|
116
|
+
self._combined_handlers["global_handlers"][handler_type] = handler_info
|
|
117
|
+
|
|
118
|
+
logger.debug(
|
|
119
|
+
f"🔍 RouterManager._update_combined_handlers(): итого - {len(self._combined_handlers['scheduled_tasks'])} задач: {list(self._combined_handlers['scheduled_tasks'].keys())}"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
total_handlers = (
|
|
123
|
+
len(self._combined_handlers["event_handlers"])
|
|
124
|
+
+ len(self._combined_handlers["scheduled_tasks"])
|
|
125
|
+
+ len(self._combined_handlers["global_handlers"])
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
logger.info(
|
|
129
|
+
f"📊 Обновлены объединенные обработчики: {total_handlers} обработчиков из {len(self._routers)} роутеров"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def get_event_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
133
|
+
"""Получает все обработчики событий"""
|
|
134
|
+
return self._combined_handlers["event_handlers"].copy()
|
|
135
|
+
|
|
136
|
+
def get_scheduled_tasks(self) -> Dict[str, Dict[str, Any]]:
|
|
137
|
+
"""Получает все запланированные задачи"""
|
|
138
|
+
tasks = self._combined_handlers["scheduled_tasks"].copy()
|
|
139
|
+
logger.debug(
|
|
140
|
+
f"🔍 RouterManager.get_scheduled_tasks(): возвращаем {len(tasks)} задач: {list(tasks.keys())}"
|
|
141
|
+
)
|
|
142
|
+
return tasks
|
|
143
|
+
|
|
144
|
+
def get_global_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
145
|
+
"""Получает все глобальные обработчики"""
|
|
146
|
+
return self._combined_handlers["global_handlers"].copy()
|
|
147
|
+
|
|
148
|
+
def get_all_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
149
|
+
"""Получает все обработчики всех типов"""
|
|
150
|
+
all_handlers = {}
|
|
151
|
+
all_handlers.update(self._combined_handlers["event_handlers"])
|
|
152
|
+
all_handlers.update(self._combined_handlers["scheduled_tasks"])
|
|
153
|
+
all_handlers.update(self._combined_handlers["global_handlers"])
|
|
154
|
+
return all_handlers
|
|
155
|
+
|
|
156
|
+
def get_handlers_for_prompt(self) -> str:
|
|
157
|
+
"""Возвращает описание всех обработчиков для добавления в промпт ИИ"""
|
|
158
|
+
prompt_parts: List[str] = []
|
|
159
|
+
|
|
160
|
+
if not any(self._combined_handlers.values()):
|
|
161
|
+
return ""
|
|
162
|
+
|
|
163
|
+
if self._combined_handlers["event_handlers"]:
|
|
164
|
+
prompt_parts.append("ДОСТУПНЫЕ ОБРАБОТЧИКИ СОБЫТИЙ:")
|
|
165
|
+
for event_type, handler_info in self._combined_handlers[
|
|
166
|
+
"event_handlers"
|
|
167
|
+
].items():
|
|
168
|
+
router_name = handler_info.get("router", "unknown")
|
|
169
|
+
prompt_parts.append(
|
|
170
|
+
f"- {event_type}: {handler_info['name']} (роутер: {router_name})"
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
if self._combined_handlers["scheduled_tasks"]:
|
|
174
|
+
prompt_parts.append("\nДОСТУПНЫЕ ЗАДАЧИ ДЛЯ ПЛАНИРОВАНИЯ:")
|
|
175
|
+
for task_name, task_info in self._combined_handlers[
|
|
176
|
+
"scheduled_tasks"
|
|
177
|
+
].items():
|
|
178
|
+
router_name = task_info.get("router", "unknown")
|
|
179
|
+
prompt_parts.append(
|
|
180
|
+
f"- {task_name}: {task_info['name']} (роутер: {router_name})"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if self._combined_handlers["global_handlers"]:
|
|
184
|
+
prompt_parts.append("\nДОСТУПНЫЕ ГЛОБАЛЬНЫЕ ОБРАБОТЧИКИ:")
|
|
185
|
+
for handler_type, handler_info in self._combined_handlers[
|
|
186
|
+
"global_handlers"
|
|
187
|
+
].items():
|
|
188
|
+
router_name = handler_info.get("router", "unknown")
|
|
189
|
+
prompt_parts.append(
|
|
190
|
+
f"- {handler_type}: {handler_info['name']} (роутер: {router_name})"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return "\n".join(prompt_parts)
|
|
194
|
+
|
|
195
|
+
def get_router_by_name(self, name: str) -> Optional[EventRouter]:
|
|
196
|
+
"""Получает роутер событий по имени"""
|
|
197
|
+
for router in self._routers:
|
|
198
|
+
if router.name == name:
|
|
199
|
+
return router
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
def get_router_stats(self) -> Dict[str, Any]:
|
|
203
|
+
"""Получает статистику по роутерам"""
|
|
204
|
+
stats = {"total_routers": len(self._routers), "routers": []}
|
|
205
|
+
|
|
206
|
+
for router in self._routers:
|
|
207
|
+
router_stats = {
|
|
208
|
+
"name": router.name,
|
|
209
|
+
"event_handlers": len(router.get_event_handlers()),
|
|
210
|
+
"scheduled_tasks": len(router.get_scheduled_tasks()),
|
|
211
|
+
"global_handlers": len(router.get_global_handlers()),
|
|
212
|
+
}
|
|
213
|
+
stats["routers"].append(router_stats)
|
|
214
|
+
|
|
215
|
+
return stats
|
|
216
|
+
|
|
217
|
+
def __repr__(self):
|
|
218
|
+
return f"RouterManager(routers={len(self._routers)}, handlers={len(self.get_all_handlers())})"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Состояния FSM для бота
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from aiogram.fsm.state import State, StatesGroup
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class UserStates(StatesGroup):
|
|
9
|
+
waiting_for_message = State()
|
|
10
|
+
admin_chat = State() # пользователь в диалоге с админом
|
|
11
|
+
|
|
12
|
+
voice_confirmation = State() # ожидание подтверждения распознанного текста
|
|
13
|
+
voice_editing = State() # редактирование распознанного текста
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AdminStates(StatesGroup):
|
|
17
|
+
admin_mode = State()
|
|
18
|
+
in_conversation = State()
|
|
19
|
+
|
|
20
|
+
# Состояния для создания события
|
|
21
|
+
create_event_name = State()
|
|
22
|
+
create_event_date = State()
|
|
23
|
+
create_event_time = State()
|
|
24
|
+
create_event_segment = State()
|
|
25
|
+
create_event_message = State()
|
|
26
|
+
create_event_files = State()
|
|
27
|
+
create_event_confirm = State()
|