smart-bot-factory 0.3.7__py3-none-any.whl → 0.3.8__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 +1129 -749
- 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 +928 -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.8.dist-info}/METADATA +3 -1
- smart_bot_factory-0.3.8.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.8.dist-info}/WHEEL +0 -0
- {smart_bot_factory-0.3.7.dist-info → smart_bot_factory-0.3.8.dist-info}/entry_points.txt +0 -0
- {smart_bot_factory-0.3.7.dist-info → smart_bot_factory-0.3.8.dist-info}/licenses/LICENSE +0 -0
smart_bot_factory/core/router.py
CHANGED
|
@@ -2,20 +2,21 @@
|
|
|
2
2
|
EventRouter для Smart Bot Factory - роутер для событий, задач и глобальных обработчиков
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from typing import Dict, Any, Callable, Union
|
|
6
5
|
import logging
|
|
6
|
+
from typing import Any, Callable, Dict, Union
|
|
7
7
|
|
|
8
8
|
logger = logging.getLogger(__name__)
|
|
9
9
|
|
|
10
|
+
|
|
10
11
|
class EventRouter:
|
|
11
12
|
"""
|
|
12
13
|
Роутер для организации обработчиков событий, задач и глобальных обработчиков
|
|
13
14
|
"""
|
|
14
|
-
|
|
15
|
+
|
|
15
16
|
def __init__(self, name: str = None):
|
|
16
17
|
"""
|
|
17
18
|
Инициализация роутера
|
|
18
|
-
|
|
19
|
+
|
|
19
20
|
Args:
|
|
20
21
|
name: Имя роутера для логирования
|
|
21
22
|
"""
|
|
@@ -23,61 +24,84 @@ class EventRouter:
|
|
|
23
24
|
self._event_handlers: Dict[str, Dict[str, Any]] = {}
|
|
24
25
|
self._scheduled_tasks: Dict[str, Dict[str, Any]] = {}
|
|
25
26
|
self._global_handlers: Dict[str, Dict[str, Any]] = {}
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
logger.info(f"🔄 Создан роутер: {self.name}")
|
|
28
|
-
|
|
29
|
-
def event_handler(
|
|
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
|
+
):
|
|
30
37
|
"""
|
|
31
38
|
Декоратор для регистрации обработчика события в роутере
|
|
32
|
-
|
|
39
|
+
|
|
33
40
|
Args:
|
|
34
41
|
event_type: Тип события
|
|
35
42
|
notify: Уведомлять ли админов
|
|
36
43
|
once_only: Выполнять ли только один раз
|
|
37
44
|
send_ai_response: Отправлять ли сообщение от ИИ после обработки события (по умолчанию True)
|
|
38
45
|
"""
|
|
46
|
+
|
|
39
47
|
def decorator(func: Callable) -> Callable:
|
|
40
48
|
self._event_handlers[event_type] = {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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,
|
|
47
55
|
}
|
|
48
|
-
|
|
49
|
-
logger.info(
|
|
50
|
-
|
|
56
|
+
|
|
57
|
+
logger.info(
|
|
58
|
+
f"📝 Роутер {self.name}: зарегистрирован обработчик события '{event_type}': {func.__name__}"
|
|
59
|
+
)
|
|
60
|
+
|
|
51
61
|
from functools import wraps
|
|
62
|
+
|
|
52
63
|
@wraps(func)
|
|
53
64
|
async def wrapper(*args, **kwargs):
|
|
54
65
|
try:
|
|
55
66
|
result = await func(*args, **kwargs)
|
|
56
|
-
|
|
67
|
+
|
|
57
68
|
# Автоматически добавляем флаги notify и send_ai_response к результату
|
|
58
69
|
if isinstance(result, dict):
|
|
59
|
-
result[
|
|
60
|
-
result[
|
|
70
|
+
result["notify"] = notify
|
|
71
|
+
result["send_ai_response"] = send_ai_response
|
|
61
72
|
else:
|
|
62
73
|
# Если результат не словарь, создаем словарь
|
|
63
74
|
result = {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
"status": "success",
|
|
76
|
+
"result": result,
|
|
77
|
+
"notify": notify,
|
|
78
|
+
"send_ai_response": send_ai_response,
|
|
68
79
|
}
|
|
69
|
-
|
|
80
|
+
|
|
70
81
|
return result
|
|
71
82
|
except Exception as e:
|
|
72
|
-
logger.error(
|
|
83
|
+
logger.error(
|
|
84
|
+
f"Ошибка выполнения обработчика '{event_type}' в роутере {self.name}: {e}"
|
|
85
|
+
)
|
|
73
86
|
raise
|
|
87
|
+
|
|
74
88
|
return wrapper
|
|
89
|
+
|
|
75
90
|
return decorator
|
|
76
|
-
|
|
77
|
-
def schedule_task(
|
|
91
|
+
|
|
92
|
+
def schedule_task(
|
|
93
|
+
self,
|
|
94
|
+
task_name: str,
|
|
95
|
+
notify: bool = False,
|
|
96
|
+
smart_check: bool = True,
|
|
97
|
+
once_only: bool = True,
|
|
98
|
+
delay: Union[str, int] = None,
|
|
99
|
+
event_type: Union[str, Callable] = None,
|
|
100
|
+
send_ai_response: bool = True,
|
|
101
|
+
):
|
|
78
102
|
"""
|
|
79
103
|
Декоратор для регистрации запланированной задачи в роутере
|
|
80
|
-
|
|
104
|
+
|
|
81
105
|
Args:
|
|
82
106
|
task_name: Название задачи
|
|
83
107
|
notify: Уведомлять ли админов
|
|
@@ -89,72 +113,98 @@ class EventRouter:
|
|
|
89
113
|
- Callable: Функция async def(user_id, user_data) -> datetime
|
|
90
114
|
send_ai_response: Отправлять ли сообщение от ИИ после выполнения задачи (по умолчанию True)
|
|
91
115
|
"""
|
|
116
|
+
|
|
92
117
|
def decorator(func: Callable) -> Callable:
|
|
93
118
|
# Время ОБЯЗАТЕЛЬНО должно быть указано
|
|
94
119
|
if delay is None:
|
|
95
|
-
raise ValueError(
|
|
96
|
-
|
|
120
|
+
raise ValueError(
|
|
121
|
+
f"Для задачи '{task_name}' в роутере {self.name} ОБЯЗАТЕЛЬНО нужно указать параметр delay"
|
|
122
|
+
)
|
|
123
|
+
|
|
97
124
|
# Импортируем функцию парсинга времени
|
|
98
125
|
from .decorators import parse_time_string
|
|
99
|
-
|
|
126
|
+
|
|
100
127
|
# Парсим время
|
|
101
128
|
try:
|
|
102
129
|
default_delay_seconds = parse_time_string(delay)
|
|
103
130
|
if event_type:
|
|
104
|
-
logger.info(
|
|
131
|
+
logger.info(
|
|
132
|
+
f"⏰ Роутер {self.name}: задача '{task_name}' настроена как напоминание о событии '{event_type}' за {delay} ({default_delay_seconds}с)"
|
|
133
|
+
)
|
|
105
134
|
else:
|
|
106
|
-
logger.info(
|
|
135
|
+
logger.info(
|
|
136
|
+
f"⏰ Роутер {self.name}: задача '{task_name}' настроена с задержкой: {delay} ({default_delay_seconds}с)"
|
|
137
|
+
)
|
|
107
138
|
except ValueError as e:
|
|
108
|
-
logger.error(
|
|
139
|
+
logger.error(
|
|
140
|
+
f"❌ Ошибка парсинга времени для задачи '{task_name}' в роутере {self.name}: {e}"
|
|
141
|
+
)
|
|
109
142
|
raise
|
|
110
|
-
|
|
143
|
+
|
|
111
144
|
self._scheduled_tasks[task_name] = {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
145
|
+
"handler": func,
|
|
146
|
+
"name": func.__name__,
|
|
147
|
+
"notify": notify,
|
|
148
|
+
"smart_check": smart_check,
|
|
149
|
+
"once_only": once_only,
|
|
150
|
+
"router": self.name,
|
|
151
|
+
"default_delay": default_delay_seconds,
|
|
152
|
+
"event_type": event_type, # Новое поле для типа события
|
|
153
|
+
"send_ai_response": send_ai_response,
|
|
121
154
|
}
|
|
122
|
-
|
|
155
|
+
|
|
123
156
|
if event_type:
|
|
124
|
-
logger.info(
|
|
157
|
+
logger.info(
|
|
158
|
+
f"⏰ Роутер {self.name}: зарегистрирована задача-напоминание '{task_name}' для события '{event_type}': {func.__name__}"
|
|
159
|
+
)
|
|
125
160
|
else:
|
|
126
|
-
logger.info(
|
|
127
|
-
|
|
161
|
+
logger.info(
|
|
162
|
+
f"⏰ Роутер {self.name}: зарегистрирована задача '{task_name}': {func.__name__}"
|
|
163
|
+
)
|
|
164
|
+
|
|
128
165
|
from functools import wraps
|
|
166
|
+
|
|
129
167
|
@wraps(func)
|
|
130
168
|
async def wrapper(*args, **kwargs):
|
|
131
169
|
try:
|
|
132
170
|
result = await func(*args, **kwargs)
|
|
133
|
-
|
|
171
|
+
|
|
134
172
|
# Автоматически добавляем флаги notify и send_ai_response к результату
|
|
135
173
|
if isinstance(result, dict):
|
|
136
|
-
result[
|
|
137
|
-
result[
|
|
174
|
+
result["notify"] = notify
|
|
175
|
+
result["send_ai_response"] = send_ai_response
|
|
138
176
|
else:
|
|
139
177
|
# Если результат не словарь, создаем словарь
|
|
140
178
|
result = {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
179
|
+
"status": "success",
|
|
180
|
+
"result": result,
|
|
181
|
+
"notify": notify,
|
|
182
|
+
"send_ai_response": send_ai_response,
|
|
145
183
|
}
|
|
146
|
-
|
|
184
|
+
|
|
147
185
|
return result
|
|
148
186
|
except Exception as e:
|
|
149
|
-
logger.error(
|
|
187
|
+
logger.error(
|
|
188
|
+
f"Ошибка выполнения задачи '{task_name}' в роутере {self.name}: {e}"
|
|
189
|
+
)
|
|
150
190
|
raise
|
|
191
|
+
|
|
151
192
|
return wrapper
|
|
193
|
+
|
|
152
194
|
return decorator
|
|
153
|
-
|
|
154
|
-
def global_handler(
|
|
195
|
+
|
|
196
|
+
def global_handler(
|
|
197
|
+
self,
|
|
198
|
+
handler_type: str,
|
|
199
|
+
notify: bool = False,
|
|
200
|
+
once_only: bool = True,
|
|
201
|
+
delay: Union[str, int] = None,
|
|
202
|
+
event_type: Union[str, Callable] = None,
|
|
203
|
+
send_ai_response: bool = True,
|
|
204
|
+
):
|
|
155
205
|
"""
|
|
156
206
|
Декоратор для регистрации глобального обработчика в роутере
|
|
157
|
-
|
|
207
|
+
|
|
158
208
|
Args:
|
|
159
209
|
handler_type: Тип глобального обработчика
|
|
160
210
|
notify: Уведомлять ли админов
|
|
@@ -162,73 +212,87 @@ class EventRouter:
|
|
|
162
212
|
delay: Время задержки в удобном формате (например, "1h 30m", "45m", 3600) - ОБЯЗАТЕЛЬНО
|
|
163
213
|
send_ai_response: Отправлять ли сообщение от ИИ после выполнения обработчика (по умолчанию True)
|
|
164
214
|
"""
|
|
215
|
+
|
|
165
216
|
def decorator(func: Callable) -> Callable:
|
|
166
217
|
# Время ОБЯЗАТЕЛЬНО должно быть указано
|
|
167
218
|
if delay is None:
|
|
168
|
-
raise ValueError(
|
|
169
|
-
|
|
219
|
+
raise ValueError(
|
|
220
|
+
f"Для глобального обработчика '{handler_type}' в роутере {self.name} ОБЯЗАТЕЛЬНО нужно указать параметр delay"
|
|
221
|
+
)
|
|
222
|
+
|
|
170
223
|
# Импортируем функцию парсинга времени
|
|
171
224
|
from .decorators import parse_time_string
|
|
172
|
-
|
|
225
|
+
|
|
173
226
|
# Парсим время
|
|
174
227
|
try:
|
|
175
228
|
default_delay_seconds = parse_time_string(delay)
|
|
176
|
-
logger.info(
|
|
229
|
+
logger.info(
|
|
230
|
+
f"🌍 Роутер {self.name}: глобальный обработчик '{handler_type}' настроен с задержкой: {delay} ({default_delay_seconds}с)"
|
|
231
|
+
)
|
|
177
232
|
except ValueError as e:
|
|
178
|
-
logger.error(
|
|
233
|
+
logger.error(
|
|
234
|
+
f"❌ Ошибка парсинга времени для глобального обработчика '{handler_type}' в роутере {self.name}: {e}"
|
|
235
|
+
)
|
|
179
236
|
raise
|
|
180
|
-
|
|
237
|
+
|
|
181
238
|
self._global_handlers[handler_type] = {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
239
|
+
"handler": func,
|
|
240
|
+
"name": func.__name__,
|
|
241
|
+
"notify": notify,
|
|
242
|
+
"once_only": once_only,
|
|
243
|
+
"router": self.name,
|
|
244
|
+
"default_delay": default_delay_seconds,
|
|
245
|
+
"event_type": event_type, # Добавляем event_type для глобальных обработчиков
|
|
246
|
+
"send_ai_response": send_ai_response,
|
|
190
247
|
}
|
|
191
|
-
|
|
192
|
-
logger.info(
|
|
193
|
-
|
|
248
|
+
|
|
249
|
+
logger.info(
|
|
250
|
+
f"🌍 Роутер {self.name}: зарегистрирован глобальный обработчик '{handler_type}': {func.__name__}"
|
|
251
|
+
)
|
|
252
|
+
|
|
194
253
|
from functools import wraps
|
|
254
|
+
|
|
195
255
|
@wraps(func)
|
|
196
256
|
async def wrapper(*args, **kwargs):
|
|
197
257
|
try:
|
|
198
258
|
result = await func(*args, **kwargs)
|
|
199
|
-
|
|
259
|
+
|
|
200
260
|
# Автоматически добавляем флаги notify и send_ai_response к результату
|
|
201
261
|
if isinstance(result, dict):
|
|
202
|
-
result[
|
|
203
|
-
result[
|
|
262
|
+
result["notify"] = notify
|
|
263
|
+
result["send_ai_response"] = send_ai_response
|
|
204
264
|
else:
|
|
205
265
|
# Если результат не словарь, создаем словарь
|
|
206
266
|
result = {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
267
|
+
"status": "success",
|
|
268
|
+
"result": result,
|
|
269
|
+
"notify": notify,
|
|
270
|
+
"send_ai_response": send_ai_response,
|
|
211
271
|
}
|
|
212
|
-
|
|
272
|
+
|
|
213
273
|
return result
|
|
214
274
|
except Exception as e:
|
|
215
|
-
logger.error(
|
|
275
|
+
logger.error(
|
|
276
|
+
f"Ошибка выполнения глобального обработчика '{handler_type}' в роутере {self.name}: {e}"
|
|
277
|
+
)
|
|
216
278
|
raise
|
|
279
|
+
|
|
217
280
|
return wrapper
|
|
281
|
+
|
|
218
282
|
return decorator
|
|
219
283
|
|
|
220
284
|
def get_event_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
221
285
|
"""Получает все обработчики событий роутера"""
|
|
222
286
|
return self._event_handlers.copy()
|
|
223
|
-
|
|
287
|
+
|
|
224
288
|
def get_scheduled_tasks(self) -> Dict[str, Dict[str, Any]]:
|
|
225
289
|
"""Получает все запланированные задачи роутера"""
|
|
226
290
|
return self._scheduled_tasks.copy()
|
|
227
|
-
|
|
291
|
+
|
|
228
292
|
def get_global_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
229
293
|
"""Получает все глобальные обработчики роутера"""
|
|
230
294
|
return self._global_handlers.copy()
|
|
231
|
-
|
|
295
|
+
|
|
232
296
|
def get_all_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
233
297
|
"""Получает все обработчики роутера"""
|
|
234
298
|
all_handlers = {}
|
|
@@ -236,33 +300,39 @@ class EventRouter:
|
|
|
236
300
|
all_handlers.update(self._scheduled_tasks)
|
|
237
301
|
all_handlers.update(self._global_handlers)
|
|
238
302
|
return all_handlers
|
|
239
|
-
|
|
240
|
-
def include_router(self, router:
|
|
303
|
+
|
|
304
|
+
def include_router(self, router: "EventRouter"):
|
|
241
305
|
"""
|
|
242
306
|
Включает другой роутер в текущий
|
|
243
|
-
|
|
307
|
+
|
|
244
308
|
Args:
|
|
245
309
|
router: EventRouter для включения
|
|
246
310
|
"""
|
|
247
311
|
# Добавляем обработчики событий
|
|
248
312
|
for event_type, handler_info in router.get_event_handlers().items():
|
|
249
313
|
if event_type in self._event_handlers:
|
|
250
|
-
logger.warning(
|
|
314
|
+
logger.warning(
|
|
315
|
+
f"⚠️ Конфликт обработчиков событий '{event_type}' между роутерами {self.name} и {router.name}"
|
|
316
|
+
)
|
|
251
317
|
self._event_handlers[event_type] = handler_info
|
|
252
|
-
|
|
318
|
+
|
|
253
319
|
# Добавляем запланированные задачи
|
|
254
320
|
for task_name, task_info in router.get_scheduled_tasks().items():
|
|
255
321
|
if task_name in self._scheduled_tasks:
|
|
256
|
-
logger.warning(
|
|
322
|
+
logger.warning(
|
|
323
|
+
f"⚠️ Конфликт задач '{task_name}' между роутерами {self.name} и {router.name}"
|
|
324
|
+
)
|
|
257
325
|
self._scheduled_tasks[task_name] = task_info
|
|
258
|
-
|
|
326
|
+
|
|
259
327
|
# Добавляем глобальные обработчики
|
|
260
328
|
for handler_type, handler_info in router.get_global_handlers().items():
|
|
261
329
|
if handler_type in self._global_handlers:
|
|
262
|
-
logger.warning(
|
|
330
|
+
logger.warning(
|
|
331
|
+
f"⚠️ Конфликт глобальных обработчиков '{handler_type}' между роутерами {self.name} и {router.name}"
|
|
332
|
+
)
|
|
263
333
|
self._global_handlers[handler_type] = handler_info
|
|
264
|
-
|
|
334
|
+
|
|
265
335
|
logger.info(f"🔗 Роутер {self.name}: включен роутер {router.name}")
|
|
266
|
-
|
|
336
|
+
|
|
267
337
|
def __repr__(self):
|
|
268
|
-
return f"EventRouter(name='{self.name}', events={len(self._event_handlers)}, tasks={len(self._scheduled_tasks)}, globals={len(self._global_handlers)})"
|
|
338
|
+
return f"EventRouter(name='{self.name}', events={len(self._event_handlers)}, tasks={len(self._scheduled_tasks)}, globals={len(self._global_handlers)})"
|