smart-bot-factory 0.3.6__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.

Files changed (45) hide show
  1. smart_bot_factory/admin/__init__.py +7 -7
  2. smart_bot_factory/admin/admin_events.py +483 -383
  3. smart_bot_factory/admin/admin_logic.py +234 -158
  4. smart_bot_factory/admin/admin_manager.py +68 -53
  5. smart_bot_factory/admin/admin_tester.py +46 -40
  6. smart_bot_factory/admin/timeout_checker.py +201 -153
  7. smart_bot_factory/aiogram_calendar/__init__.py +11 -3
  8. smart_bot_factory/aiogram_calendar/common.py +12 -18
  9. smart_bot_factory/aiogram_calendar/dialog_calendar.py +126 -64
  10. smart_bot_factory/aiogram_calendar/schemas.py +49 -28
  11. smart_bot_factory/aiogram_calendar/simple_calendar.py +94 -50
  12. smart_bot_factory/analytics/analytics_manager.py +414 -392
  13. smart_bot_factory/cli.py +204 -148
  14. smart_bot_factory/config.py +123 -102
  15. smart_bot_factory/core/bot_utils.py +480 -324
  16. smart_bot_factory/core/conversation_manager.py +287 -200
  17. smart_bot_factory/core/decorators.py +1145 -739
  18. smart_bot_factory/core/message_sender.py +287 -266
  19. smart_bot_factory/core/router.py +170 -100
  20. smart_bot_factory/core/router_manager.py +121 -83
  21. smart_bot_factory/core/states.py +4 -3
  22. smart_bot_factory/creation/__init__.py +1 -1
  23. smart_bot_factory/creation/bot_builder.py +320 -242
  24. smart_bot_factory/creation/bot_testing.py +440 -365
  25. smart_bot_factory/dashboard/__init__.py +1 -3
  26. smart_bot_factory/event/__init__.py +2 -7
  27. smart_bot_factory/handlers/handlers.py +682 -466
  28. smart_bot_factory/integrations/openai_client.py +218 -168
  29. smart_bot_factory/integrations/supabase_client.py +928 -637
  30. smart_bot_factory/message/__init__.py +18 -22
  31. smart_bot_factory/router/__init__.py +2 -2
  32. smart_bot_factory/setup_checker.py +162 -126
  33. smart_bot_factory/supabase/__init__.py +1 -1
  34. smart_bot_factory/supabase/client.py +631 -515
  35. smart_bot_factory/utils/__init__.py +2 -3
  36. smart_bot_factory/utils/debug_routing.py +38 -27
  37. smart_bot_factory/utils/prompt_loader.py +153 -120
  38. smart_bot_factory/utils/user_prompt_loader.py +55 -56
  39. smart_bot_factory/utm_link_generator.py +123 -116
  40. {smart_bot_factory-0.3.6.dist-info → smart_bot_factory-0.3.8.dist-info}/METADATA +3 -1
  41. smart_bot_factory-0.3.8.dist-info/RECORD +59 -0
  42. smart_bot_factory-0.3.6.dist-info/RECORD +0 -59
  43. {smart_bot_factory-0.3.6.dist-info → smart_bot_factory-0.3.8.dist-info}/WHEEL +0 -0
  44. {smart_bot_factory-0.3.6.dist-info → smart_bot_factory-0.3.8.dist-info}/entry_points.txt +0 -0
  45. {smart_bot_factory-0.3.6.dist-info → smart_bot_factory-0.3.8.dist-info}/licenses/LICENSE +0 -0
@@ -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(self, event_type: str, notify: bool = False, once_only: bool = True, send_ai_response: bool = True):
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
- 'handler': func,
42
- 'name': func.__name__,
43
- 'notify': notify,
44
- 'once_only': once_only,
45
- 'send_ai_response': send_ai_response,
46
- 'router': self.name
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(f"📝 Роутер {self.name}: зарегистрирован обработчик события '{event_type}': {func.__name__}")
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['notify'] = notify
60
- result['send_ai_response'] = send_ai_response
70
+ result["notify"] = notify
71
+ result["send_ai_response"] = send_ai_response
61
72
  else:
62
73
  # Если результат не словарь, создаем словарь
63
74
  result = {
64
- 'status': 'success',
65
- 'result': result,
66
- 'notify': notify,
67
- 'send_ai_response': send_ai_response
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(f"Ошибка выполнения обработчика '{event_type}' в роутере {self.name}: {e}")
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(self, task_name: str, notify: bool = False, smart_check: bool = True, once_only: bool = True, delay: Union[str, int] = None, event_type: Union[str, Callable] = None, send_ai_response: bool = True):
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(f"Для задачи '{task_name}' в роутере {self.name} ОБЯЗАТЕЛЬНО нужно указать параметр delay")
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(f"⏰ Роутер {self.name}: задача '{task_name}' настроена как напоминание о событии '{event_type}' за {delay} ({default_delay_seconds}с)")
131
+ logger.info(
132
+ f"⏰ Роутер {self.name}: задача '{task_name}' настроена как напоминание о событии '{event_type}' за {delay} ({default_delay_seconds}с)"
133
+ )
105
134
  else:
106
- logger.info(f"⏰ Роутер {self.name}: задача '{task_name}' настроена с задержкой: {delay} ({default_delay_seconds}с)")
135
+ logger.info(
136
+ f"⏰ Роутер {self.name}: задача '{task_name}' настроена с задержкой: {delay} ({default_delay_seconds}с)"
137
+ )
107
138
  except ValueError as e:
108
- logger.error(f"❌ Ошибка парсинга времени для задачи '{task_name}' в роутере {self.name}: {e}")
139
+ logger.error(
140
+ f"❌ Ошибка парсинга времени для задачи '{task_name}' в роутере {self.name}: {e}"
141
+ )
109
142
  raise
110
-
143
+
111
144
  self._scheduled_tasks[task_name] = {
112
- 'handler': func,
113
- 'name': func.__name__,
114
- 'notify': notify,
115
- 'smart_check': smart_check,
116
- 'once_only': once_only,
117
- 'router': self.name,
118
- 'default_delay': default_delay_seconds,
119
- 'event_type': event_type, # Новое поле для типа события
120
- 'send_ai_response': send_ai_response
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(f"⏰ Роутер {self.name}: зарегистрирована задача-напоминание '{task_name}' для события '{event_type}': {func.__name__}")
157
+ logger.info(
158
+ f"⏰ Роутер {self.name}: зарегистрирована задача-напоминание '{task_name}' для события '{event_type}': {func.__name__}"
159
+ )
125
160
  else:
126
- logger.info(f"⏰ Роутер {self.name}: зарегистрирована задача '{task_name}': {func.__name__}")
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['notify'] = notify
137
- result['send_ai_response'] = send_ai_response
174
+ result["notify"] = notify
175
+ result["send_ai_response"] = send_ai_response
138
176
  else:
139
177
  # Если результат не словарь, создаем словарь
140
178
  result = {
141
- 'status': 'success',
142
- 'result': result,
143
- 'notify': notify,
144
- 'send_ai_response': send_ai_response
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(f"Ошибка выполнения задачи '{task_name}' в роутере {self.name}: {e}")
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(self, handler_type: str, notify: bool = False, once_only: bool = True, delay: Union[str, int] = None, event_type: Union[str, Callable] = None, send_ai_response: bool = True):
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(f"Для глобального обработчика '{handler_type}' в роутере {self.name} ОБЯЗАТЕЛЬНО нужно указать параметр delay")
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(f"🌍 Роутер {self.name}: глобальный обработчик '{handler_type}' настроен с задержкой: {delay} ({default_delay_seconds}с)")
229
+ logger.info(
230
+ f"🌍 Роутер {self.name}: глобальный обработчик '{handler_type}' настроен с задержкой: {delay} ({default_delay_seconds}с)"
231
+ )
177
232
  except ValueError as e:
178
- logger.error(f"❌ Ошибка парсинга времени для глобального обработчика '{handler_type}' в роутере {self.name}: {e}")
233
+ logger.error(
234
+ f"❌ Ошибка парсинга времени для глобального обработчика '{handler_type}' в роутере {self.name}: {e}"
235
+ )
179
236
  raise
180
-
237
+
181
238
  self._global_handlers[handler_type] = {
182
- 'handler': func,
183
- 'name': func.__name__,
184
- 'notify': notify,
185
- 'once_only': once_only,
186
- 'router': self.name,
187
- 'default_delay': default_delay_seconds,
188
- 'event_type': event_type, # Добавляем event_type для глобальных обработчиков
189
- 'send_ai_response': send_ai_response
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(f"🌍 Роутер {self.name}: зарегистрирован глобальный обработчик '{handler_type}': {func.__name__}")
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['notify'] = notify
203
- result['send_ai_response'] = send_ai_response
262
+ result["notify"] = notify
263
+ result["send_ai_response"] = send_ai_response
204
264
  else:
205
265
  # Если результат не словарь, создаем словарь
206
266
  result = {
207
- 'status': 'success',
208
- 'result': result,
209
- 'notify': notify,
210
- 'send_ai_response': send_ai_response
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(f"Ошибка выполнения глобального обработчика '{handler_type}' в роутере {self.name}: {e}")
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: 'EventRouter'):
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(f"⚠️ Конфликт обработчиков событий '{event_type}' между роутерами {self.name} и {router.name}")
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(f"⚠️ Конфликт задач '{task_name}' между роутерами {self.name} и {router.name}")
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(f"⚠️ Конфликт глобальных обработчиков '{handler_type}' между роутерами {self.name} и {router.name}")
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)})"