smart-bot-factory 0.3.4__py3-none-any.whl → 0.3.5__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.

@@ -1,7 +1,6 @@
1
1
  import asyncio
2
2
  import json
3
3
  import logging
4
- import re
5
4
  from datetime import datetime
6
5
  from aiogram import Router
7
6
  from aiogram.filters import Command
@@ -170,8 +169,13 @@ def parse_ai_response_method2(ai_response: str) -> tuple[str, dict]:
170
169
  logger.warning(f"Ошибка резервного метода: {e}")
171
170
  return ai_response, {}
172
171
 
173
- async def process_events(session_id: str, events: list, user_id: int):
174
- """Обрабатывает события из ответа ИИ"""
172
+ async def process_events(session_id: str, events: list, user_id: int) -> bool:
173
+ """
174
+ Обрабатывает события из ответа ИИ
175
+
176
+ Returns:
177
+ bool: True если нужно отправить сообщение от ИИ, False если не нужно
178
+ """
175
179
 
176
180
  # Проверяем кастомный процессор
177
181
  custom_processor = get_global_var('custom_event_processor')
@@ -180,11 +184,14 @@ async def process_events(session_id: str, events: list, user_id: int):
180
184
  # Используем кастомную функцию обработки событий
181
185
  logger.info(f"🔄 Используется кастомная обработка событий: {custom_processor.__name__}")
182
186
  await custom_processor(session_id, events, user_id)
183
- return
187
+ return True # По умолчанию отправляем сообщение
184
188
 
185
189
  # Стандартная обработка
186
190
  supabase_client = get_global_var('supabase_client')
187
191
 
192
+ # Флаг для отслеживания, нужно ли отправлять сообщение от ИИ
193
+ should_send_ai_response = True
194
+
188
195
  for event in events:
189
196
  try:
190
197
  event_type = event.get('тип', '')
@@ -200,7 +207,6 @@ async def process_events(session_id: str, events: list, user_id: int):
200
207
 
201
208
  # Определяем категорию события и сохраняем в БД
202
209
  event_id = None
203
- should_execute_immediately = False
204
210
  should_notify = False
205
211
 
206
212
  try:
@@ -228,8 +234,14 @@ async def process_events(session_id: str, events: list, user_id: int):
228
234
 
229
235
  event_handler_info = event_handlers.get(event_type, {})
230
236
  once_only = event_handler_info.get('once_only', True)
237
+ send_ai_response_flag = event_handler_info.get('send_ai_response', True)
231
238
 
232
- logger.info(f" 🔍 Обработчик '{event_type}': once_only={once_only}")
239
+ logger.info(f" 🔍 Обработчик '{event_type}': once_only={once_only}, send_ai_response={send_ai_response_flag}")
240
+
241
+ # Проверяем флаг send_ai_response ИЗ ДЕКОРАТОРА
242
+ if not send_ai_response_flag:
243
+ should_send_ai_response = False
244
+ logger.warning(f" 🔇🔇🔇 ОБРАБОТЧИК '{event_type}' ЗАПРЕТИЛ ОТПРАВКУ СООБЩЕНИЯ ОТ ИИ (send_ai_response=False) 🔇🔇🔇")
233
245
 
234
246
  # Если once_only=True - проверяем в БД наличие выполненных событий
235
247
  if once_only:
@@ -256,7 +268,7 @@ async def process_events(session_id: str, events: list, user_id: int):
256
268
  logger.info(f" 🎯 Немедленно выполняем user_event: '{event_type}'")
257
269
 
258
270
  try:
259
- # Выполняем СНАЧАЛА
271
+ # Выполняем событие
260
272
  result = await execute_event_handler(event_type, user_id, event_info)
261
273
 
262
274
  # Сохраняем в БД УЖЕ со статусом completed (избегаем дублирования)
@@ -275,7 +287,6 @@ async def process_events(session_id: str, events: list, user_id: int):
275
287
  event_id = response.data[0]['id']
276
288
 
277
289
  should_notify = event_handler_info.get('notify', False)
278
- should_execute_immediately = True
279
290
 
280
291
  logger.info(f" ✅ Событие {event_id} выполнено и сохранено как completed")
281
292
 
@@ -298,8 +309,18 @@ async def process_events(session_id: str, events: list, user_id: int):
298
309
  # Если не user_event, пробуем как запланированную задачу
299
310
  elif event_type in scheduled_tasks:
300
311
  try:
312
+ # Достаем метаданные задачи
313
+ task_info = scheduled_tasks.get(event_type, {})
314
+ send_ai_response_flag = task_info.get('send_ai_response', True)
315
+
316
+ logger.info(f" ⏰ Планируем scheduled_task: '{event_type}', send_ai_response={send_ai_response_flag}")
317
+
318
+ # Проверяем флаг send_ai_response ИЗ ДЕКОРАТОРА
319
+ if not send_ai_response_flag:
320
+ should_send_ai_response = False
321
+ logger.warning(f" 🔇🔇🔇 ЗАДАЧА '{event_type}' ЗАПРЕТИЛА ОТПРАВКУ СООБЩЕНИЯ ОТ ИИ (send_ai_response=False) 🔇🔇🔇")
322
+
301
323
  # Используем новую логику - время берется из декоратора
302
- logger.info(f" ⏰ Планируем scheduled_task: '{event_type}' с данными: '{event_info}'")
303
324
  result = await execute_scheduled_task_from_event(user_id, event_type, event_info, session_id)
304
325
  event_id = result.get('event_id', 'unknown')
305
326
  should_notify = result.get('notify', False)
@@ -354,6 +375,10 @@ async def process_events(session_id: str, events: list, user_id: int):
354
375
  except Exception as e:
355
376
  logger.error(f"❌ Ошибка обработки события {event}: {e}")
356
377
  logger.exception("Стек ошибки:")
378
+
379
+ # Возвращаем флаг, нужно ли отправлять сообщение от ИИ
380
+ logger.warning(f"🔊🔊🔊 ИТОГОВЫЙ ФЛАГ send_ai_response: {should_send_ai_response} 🔊🔊🔊")
381
+ return should_send_ai_response
357
382
 
358
383
  async def notify_admins_about_event(user_id: int, event: dict):
359
384
  """Отправляем уведомление админам о событии с явным указанием ID пользователя"""
@@ -377,7 +377,7 @@ _global_handlers: Dict[str, Dict[str, Any]] = {}
377
377
  # Глобальный менеджер роутеров
378
378
  _router_manager = None
379
379
 
380
- def event_handler(event_type: str, notify: bool = False, once_only: bool = True):
380
+ def event_handler(event_type: str, notify: bool = False, once_only: bool = True, send_ai_response: bool = True):
381
381
  """
382
382
  Декоратор для регистрации обработчика события
383
383
 
@@ -385,18 +385,19 @@ def event_handler(event_type: str, notify: bool = False, once_only: bool = True)
385
385
  event_type: Тип события (например, 'appointment_booking', 'phone_collection')
386
386
  notify: Уведомлять ли админов о выполнении события (по умолчанию False)
387
387
  once_only: Обрабатывать ли событие только один раз (по умолчанию True)
388
+ send_ai_response: Отправлять ли сообщение от ИИ после обработки события (по умолчанию True)
388
389
 
389
390
  Example:
390
- # Обработчик только один раз (по умолчанию)
391
+ # Обработчик с отправкой сообщения от ИИ
391
392
  @event_handler("appointment_booking", notify=True)
392
393
  async def book_appointment(user_id: int, appointment_data: dict):
393
394
  # Логика записи на прием
394
395
  return {"status": "success", "appointment_id": "123"}
395
396
 
396
- # Обработчик может выполняться многократно
397
- @event_handler("phone_collection", once_only=False)
397
+ # Обработчик БЕЗ отправки сообщения от ИИ
398
+ @event_handler("phone_collection", once_only=False, send_ai_response=False)
398
399
  async def collect_phone(user_id: int, phone_data: dict):
399
- # Логика сбора телефона
400
+ # Логика сбора телефона - ИИ не отправит сообщение
400
401
  return {"status": "phone_collected"}
401
402
  """
402
403
  def decorator(func: Callable) -> Callable:
@@ -404,7 +405,8 @@ def event_handler(event_type: str, notify: bool = False, once_only: bool = True)
404
405
  'handler': func,
405
406
  'name': func.__name__,
406
407
  'notify': notify,
407
- 'once_only': once_only
408
+ 'once_only': once_only,
409
+ 'send_ai_response': send_ai_response
408
410
  }
409
411
 
410
412
  logger.info(f"📝 Зарегистрирован обработчик события '{event_type}': {func.__name__}")
@@ -416,15 +418,17 @@ def event_handler(event_type: str, notify: bool = False, once_only: bool = True)
416
418
  result = await func(*args, **kwargs)
417
419
  logger.info(f"✅ Обработчик '{event_type}' выполнен успешно")
418
420
 
419
- # Автоматически добавляем флаг notify к результату
421
+ # Автоматически добавляем флаги notify и send_ai_response к результату
420
422
  if isinstance(result, dict):
421
423
  result['notify'] = notify
424
+ result['send_ai_response'] = send_ai_response
422
425
  else:
423
426
  # Если результат не словарь, создаем словарь
424
427
  result = {
425
428
  'status': 'success',
426
429
  'result': result,
427
- 'notify': notify
430
+ 'notify': notify,
431
+ 'send_ai_response': send_ai_response
428
432
  }
429
433
 
430
434
  return result
@@ -435,7 +439,7 @@ def event_handler(event_type: str, notify: bool = False, once_only: bool = True)
435
439
  return wrapper
436
440
  return decorator
437
441
 
438
- def schedule_task(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):
442
+ def schedule_task(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):
439
443
  """
440
444
  Декоратор для регистрации задачи, которую можно запланировать на время
441
445
 
@@ -448,6 +452,7 @@ def schedule_task(task_name: str, notify: bool = False, smart_check: bool = True
448
452
  event_type: Источник времени события - ОПЦИОНАЛЬНО:
449
453
  - str: Тип события для поиска в БД (например, 'appointment_booking')
450
454
  - Callable: Функция для получения datetime (например, async def(user_id, user_data) -> datetime)
455
+ send_ai_response: Отправлять ли сообщение от ИИ после выполнения задачи (по умолчанию True)
451
456
 
452
457
  Example:
453
458
  # Обычная задача с фиксированным временем
@@ -509,7 +514,8 @@ def schedule_task(task_name: str, notify: bool = False, smart_check: bool = True
509
514
  'smart_check': smart_check,
510
515
  'once_only': once_only,
511
516
  'default_delay': default_delay_seconds,
512
- 'event_type': event_type # Новое поле для типа события
517
+ 'event_type': event_type, # Новое поле для типа события
518
+ 'send_ai_response': send_ai_response
513
519
  }
514
520
 
515
521
  if event_type:
@@ -524,15 +530,17 @@ def schedule_task(task_name: str, notify: bool = False, smart_check: bool = True
524
530
  result = await func(*args, **kwargs)
525
531
  logger.info(f"✅ Задача '{task_name}' выполнена успешно")
526
532
 
527
- # Автоматически добавляем флаг notify к результату
533
+ # Автоматически добавляем флаги notify и send_ai_response к результату
528
534
  if isinstance(result, dict):
529
535
  result['notify'] = notify
536
+ result['send_ai_response'] = send_ai_response
530
537
  else:
531
538
  # Если результат не словарь, создаем словарь
532
539
  result = {
533
540
  'status': 'success',
534
541
  'result': result,
535
- 'notify': notify
542
+ 'notify': notify,
543
+ 'send_ai_response': send_ai_response
536
544
  }
537
545
 
538
546
  return result
@@ -543,7 +551,7 @@ def schedule_task(task_name: str, notify: bool = False, smart_check: bool = True
543
551
  return wrapper
544
552
  return decorator
545
553
 
546
- def global_handler(handler_type: str, notify: bool = False, once_only: bool = True, delay: Union[str, int] = None, event_type: Union[str, Callable] = None):
554
+ def global_handler(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):
547
555
  """
548
556
  Декоратор для регистрации глобального обработчика (для всех пользователей)
549
557
 
@@ -555,6 +563,7 @@ def global_handler(handler_type: str, notify: bool = False, once_only: bool = Tr
555
563
  event_type: Источник времени события - ОПЦИОНАЛЬНО:
556
564
  - str: Тип события для поиска в БД
557
565
  - Callable: Функция для получения datetime (например, async def(handler_data: str) -> datetime)
566
+ send_ai_response: Отправлять ли сообщение от ИИ после выполнения обработчика (по умолчанию True)
558
567
 
559
568
  Example:
560
569
  # Глобальный обработчик с задержкой
@@ -610,7 +619,8 @@ def global_handler(handler_type: str, notify: bool = False, once_only: bool = Tr
610
619
  'notify': notify,
611
620
  'once_only': once_only,
612
621
  'default_delay': default_delay_seconds,
613
- 'event_type': event_type # Добавляем event_type для глобальных обработчиков
622
+ 'event_type': event_type, # Добавляем event_type для глобальных обработчиков
623
+ 'send_ai_response': send_ai_response
614
624
  }
615
625
 
616
626
  logger.info(f"🌍 Зарегистрирован глобальный обработчик '{handler_type}': {func.__name__}")
@@ -622,15 +632,17 @@ def global_handler(handler_type: str, notify: bool = False, once_only: bool = Tr
622
632
  result = await func(*args, **kwargs)
623
633
  logger.info(f"✅ Глобальный обработчик '{handler_type}' выполнен успешно")
624
634
 
625
- # Автоматически добавляем флаг notify к результату
635
+ # Автоматически добавляем флаги notify и send_ai_response к результату
626
636
  if isinstance(result, dict):
627
637
  result['notify'] = notify
638
+ result['send_ai_response'] = send_ai_response
628
639
  else:
629
640
  # Если результат не словарь, создаем словарь
630
641
  result = {
631
642
  'status': 'success',
632
643
  'result': result,
633
- 'notify': notify
644
+ 'notify': notify,
645
+ 'send_ai_response': send_ai_response
634
646
  }
635
647
 
636
648
  return result
@@ -143,10 +143,10 @@ async def send_message_by_ai(
143
143
  logger.info(f"✅ Этап и качество обновлены в БД")
144
144
 
145
145
  # Обрабатываем события
146
- events = ai_metadata.get('события', [])
146
+ events = ai_metadata.get('събития', [])
147
147
  if events:
148
148
  logger.info(f"🔔 Обрабатываем {len(events)} событий")
149
- await process_events(session_id, events, user_id)
149
+ should_send_response = await process_events(session_id, events, user_id)
150
150
 
151
151
  # Сохраняем ответ ассистента
152
152
  await supabase_client.add_message(
@@ -165,6 +165,15 @@ async def send_message_by_ai(
165
165
  else:
166
166
  final_response = response_text
167
167
 
168
+ # Проверяем, нужно ли отправлять сообщение от ИИ
169
+ if 'should_send_response' in locals() and not should_send_response:
170
+ logger.info("🔇 События запретили отправку сообщения от ИИ (message_sender), пропускаем отправку")
171
+ return {
172
+ "status": "skipped",
173
+ "reason": "send_ai_response=False",
174
+ "user_id": user_id
175
+ }
176
+
168
177
  # Отправляем ответ пользователю напрямую через бота
169
178
  await bot.send_message(
170
179
  chat_id=user_id,
@@ -26,7 +26,7 @@ class EventRouter:
26
26
 
27
27
  logger.info(f"🔄 Создан роутер: {self.name}")
28
28
 
29
- def event_handler(self, event_type: str, notify: bool = False, once_only: bool = True):
29
+ def event_handler(self, event_type: str, notify: bool = False, once_only: bool = True, send_ai_response: bool = True):
30
30
  """
31
31
  Декоратор для регистрации обработчика события в роутере
32
32
 
@@ -34,6 +34,7 @@ class EventRouter:
34
34
  event_type: Тип события
35
35
  notify: Уведомлять ли админов
36
36
  once_only: Выполнять ли только один раз
37
+ send_ai_response: Отправлять ли сообщение от ИИ после обработки события (по умолчанию True)
37
38
  """
38
39
  def decorator(func: Callable) -> Callable:
39
40
  self._event_handlers[event_type] = {
@@ -41,6 +42,7 @@ class EventRouter:
41
42
  'name': func.__name__,
42
43
  'notify': notify,
43
44
  'once_only': once_only,
45
+ 'send_ai_response': send_ai_response,
44
46
  'router': self.name
45
47
  }
46
48
 
@@ -50,14 +52,29 @@ class EventRouter:
50
52
  @wraps(func)
51
53
  async def wrapper(*args, **kwargs):
52
54
  try:
53
- return await func(*args, **kwargs)
55
+ result = await func(*args, **kwargs)
56
+
57
+ # Автоматически добавляем флаги notify и send_ai_response к результату
58
+ if isinstance(result, dict):
59
+ result['notify'] = notify
60
+ result['send_ai_response'] = send_ai_response
61
+ else:
62
+ # Если результат не словарь, создаем словарь
63
+ result = {
64
+ 'status': 'success',
65
+ 'result': result,
66
+ 'notify': notify,
67
+ 'send_ai_response': send_ai_response
68
+ }
69
+
70
+ return result
54
71
  except Exception as e:
55
72
  logger.error(f"Ошибка выполнения обработчика '{event_type}' в роутере {self.name}: {e}")
56
73
  raise
57
74
  return wrapper
58
75
  return decorator
59
76
 
60
- 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):
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):
61
78
  """
62
79
  Декоратор для регистрации запланированной задачи в роутере
63
80
 
@@ -70,6 +87,7 @@ class EventRouter:
70
87
  event_type: Источник времени события - ОПЦИОНАЛЬНО:
71
88
  - str: Тип события для поиска в БД (например, 'appointment_booking')
72
89
  - Callable: Функция async def(user_id, user_data) -> datetime
90
+ send_ai_response: Отправлять ли сообщение от ИИ после выполнения задачи (по умолчанию True)
73
91
  """
74
92
  def decorator(func: Callable) -> Callable:
75
93
  # Время ОБЯЗАТЕЛЬНО должно быть указано
@@ -98,7 +116,8 @@ class EventRouter:
98
116
  'once_only': once_only,
99
117
  'router': self.name,
100
118
  'default_delay': default_delay_seconds,
101
- 'event_type': event_type # Новое поле для типа события
119
+ 'event_type': event_type, # Новое поле для типа события
120
+ 'send_ai_response': send_ai_response
102
121
  }
103
122
 
104
123
  if event_type:
@@ -110,14 +129,29 @@ class EventRouter:
110
129
  @wraps(func)
111
130
  async def wrapper(*args, **kwargs):
112
131
  try:
113
- return await func(*args, **kwargs)
132
+ result = await func(*args, **kwargs)
133
+
134
+ # Автоматически добавляем флаги notify и send_ai_response к результату
135
+ if isinstance(result, dict):
136
+ result['notify'] = notify
137
+ result['send_ai_response'] = send_ai_response
138
+ else:
139
+ # Если результат не словарь, создаем словарь
140
+ result = {
141
+ 'status': 'success',
142
+ 'result': result,
143
+ 'notify': notify,
144
+ 'send_ai_response': send_ai_response
145
+ }
146
+
147
+ return result
114
148
  except Exception as e:
115
149
  logger.error(f"Ошибка выполнения задачи '{task_name}' в роутере {self.name}: {e}")
116
150
  raise
117
151
  return wrapper
118
152
  return decorator
119
153
 
120
- 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):
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):
121
155
  """
122
156
  Декоратор для регистрации глобального обработчика в роутере
123
157
 
@@ -126,6 +160,7 @@ class EventRouter:
126
160
  notify: Уведомлять ли админов
127
161
  once_only: Выполнять ли только один раз
128
162
  delay: Время задержки в удобном формате (например, "1h 30m", "45m", 3600) - ОБЯЗАТЕЛЬНО
163
+ send_ai_response: Отправлять ли сообщение от ИИ после выполнения обработчика (по умолчанию True)
129
164
  """
130
165
  def decorator(func: Callable) -> Callable:
131
166
  # Время ОБЯЗАТЕЛЬНО должно быть указано
@@ -150,7 +185,8 @@ class EventRouter:
150
185
  'once_only': once_only,
151
186
  'router': self.name,
152
187
  'default_delay': default_delay_seconds,
153
- 'event_type': event_type # Добавляем event_type для глобальных обработчиков
188
+ 'event_type': event_type, # Добавляем event_type для глобальных обработчиков
189
+ 'send_ai_response': send_ai_response
154
190
  }
155
191
 
156
192
  logger.info(f"🌍 Роутер {self.name}: зарегистрирован глобальный обработчик '{handler_type}': {func.__name__}")
@@ -159,7 +195,22 @@ class EventRouter:
159
195
  @wraps(func)
160
196
  async def wrapper(*args, **kwargs):
161
197
  try:
162
- return await func(*args, **kwargs)
198
+ result = await func(*args, **kwargs)
199
+
200
+ # Автоматически добавляем флаги notify и send_ai_response к результату
201
+ if isinstance(result, dict):
202
+ result['notify'] = notify
203
+ result['send_ai_response'] = send_ai_response
204
+ else:
205
+ # Если результат не словарь, создаем словарь
206
+ result = {
207
+ 'status': 'success',
208
+ 'result': result,
209
+ 'notify': notify,
210
+ 'send_ai_response': send_ai_response
211
+ }
212
+
213
+ return result
163
214
  except Exception as e:
164
215
  logger.error(f"Ошибка выполнения глобального обработчика '{handler_type}' в роутере {self.name}: {e}")
165
216
  raise
@@ -942,8 +942,8 @@ async def process_user_message(message: Message, state: FSMContext, session_id:
942
942
  logger.info(f" {idx}. {event_emoji} {event_type}: {event_info}")
943
943
 
944
944
  # Обрабатываем события в системе
945
- await process_events(session_id, events, message.from_user.id)
946
- logger.info(" ✅ События обработаны")
945
+ should_send_response = await process_events(session_id, events, message.from_user.id)
946
+ logger.warning(f" ✅ События обработаны, should_send_response = {should_send_response}")
947
947
 
948
948
  # Обрабатываем файлы и каталоги
949
949
  files_list = ai_metadata.get('файлы', [])
@@ -1007,6 +1007,14 @@ async def process_user_message(message: Message, state: FSMContext, session_id:
1007
1007
 
1008
1008
  logger.info(f"📱 Отправляем пользователю: {len(final_response)} символов")
1009
1009
 
1010
+ # ============ ПРОВЕРКА: НУЖНО ЛИ ОТПРАВЛЯТЬ СООБЩЕНИЕ ОТ ИИ ============
1011
+ # Проверяем флаг из событий (если события запретили отправку)
1012
+ logger.warning(f"🔍 Проверка should_send_response: exists={('should_send_response' in locals())}, value={locals().get('should_send_response', 'NOT_SET')}")
1013
+
1014
+ if 'should_send_response' in locals() and not should_send_response:
1015
+ logger.warning("🔇🔇🔇 СОБЫТИЯ ЗАПРЕТИЛИ ОТПРАВКУ СООБЩЕНИЯ ОТ ИИ, ПРОПУСКАЕМ ОТПРАВКУ 🔇🔇🔇")
1016
+ return
1017
+
1010
1018
  # ============ ХУК 5: ФИЛЬТРЫ ОТПРАВКИ ============
1011
1019
  send_filters = message_hooks.get('send_filters', [])
1012
1020
  for filter_func in send_filters:
@@ -1133,9 +1141,9 @@ async def process_voice_message(message: Message, state: FSMContext, session_id:
1133
1141
  await supabase_client.update_session_stage(session_id, stage, quality)
1134
1142
 
1135
1143
  # Обрабатываем события
1136
- events = ai_metadata.get('события', [])
1144
+ events = ai_metadata.get('події', [])
1137
1145
  if events:
1138
- await process_events(session_id, events, message.from_user.id)
1146
+ should_send_response = await process_events(session_id, events, message.from_user.id)
1139
1147
 
1140
1148
  # Сохраняем ответ ассистента
1141
1149
  await supabase_client.add_message(
@@ -1158,6 +1166,11 @@ async def process_voice_message(message: Message, state: FSMContext, session_id:
1158
1166
  files_list = ai_metadata.get('файлы', [])
1159
1167
  directories_list = ai_metadata.get('каталоги', [])
1160
1168
 
1169
+ # Проверяем, нужно ли отправлять сообщение от ИИ
1170
+ if 'should_send_response' in locals() and not should_send_response:
1171
+ logger.info("🔇 События запретили отправку сообщения от ИИ (voice), пропускаем отправку")
1172
+ return
1173
+
1161
1174
  # Отправляем ответ пользователю
1162
1175
  await send_message(message, final_response, files_list=files_list, directories_list=directories_list)
1163
1176
  logger.info(f"✅ Ответ отправлен пользователю {message.from_user.id}")
@@ -107,6 +107,7 @@ class PromptLoader:
107
107
  {
108
108
  "этап": "introduction|consult|offer|contacts",
109
109
  "качество": 1-10,
110
+ "ссылка": 1,
110
111
  "события": [
111
112
  {
112
113
  "тип": "телефон|консультация|покупка|отказ",
@@ -126,9 +127,10 @@ class PromptLoader:
126
127
  СИСТЕМА ОЦЕНКИ КАЧЕСТВА (1-10):
127
128
  1-3: низкий интерес, много возражений, скептически настроен
128
129
  4-6: средний интерес, есть вопросы, обдумывает
129
- 7-8: высокий интерес, готов к покупке, активно интересуется
130
+ 7-8: высокий интерес, готов к покупке, активно интересуется
130
131
  9-10: горячий лид, предоставил контакты или готов к действию
131
132
 
133
+
132
134
  СОБЫТИЯ - добавляй ТОЛЬКО когда происходит что-то из этого:
133
135
  - "телефон": пользователь предоставил номер телефона
134
136
  - "консультация": пользователь просит живую консультацию по телефону
@@ -140,7 +142,7 @@ class PromptLoader:
140
142
 
141
143
  ПРИМЕРЫ ПРАВИЛЬНОГО ИСПОЛЬЗОВАНИЯ:
142
144
 
143
- Пример 1 - обычный диалог:
145
+ Пример 1 - обычный диалог (без ссылки):
144
146
  "Расскажу подробнее о конференции GrowthMED. Она пройдет 24-25 октября..."
145
147
 
146
148
  {
@@ -151,11 +153,11 @@ class PromptLoader:
151
153
  "каталоги": []
152
154
  }
153
155
 
154
- Пример 2 - получен телефон:
156
+ Пример 2 - получен телефон (без ссылки):
155
157
  "Отлично! Записал ваш номер. Мы перезвоним в течение 10 минут!"
156
158
 
157
159
  {
158
- "этап": "contacts",
160
+ "этап": "contacts",
159
161
  "качество": 9,
160
162
  "события": [
161
163
  {
@@ -167,7 +169,7 @@ class PromptLoader:
167
169
  "каталоги": []
168
170
  }
169
171
 
170
- Пример 3 - отправка презентации:
172
+ Пример 3 - отправка презентации (без ссылки):
171
173
  "Отправляю вам презентацию о нашей компании и прайс-лист с актуальными ценами."
172
174
 
173
175
  {
@@ -175,7 +177,7 @@ class PromptLoader:
175
177
  "качество": 7,
176
178
  "события": [
177
179
  {
178
- "тип": "консультация",
180
+ "тип": "консультация",
179
181
  "инфо": "Запросил материалы"
180
182
  }
181
183
  ],
@@ -183,7 +185,7 @@ class PromptLoader:
183
185
  "каталоги": []
184
186
  }
185
187
 
186
- Пример 4 - отправка файлов из каталога:
188
+ Пример 4 - отправка файлов из каталога (без ссылки):
187
189
  "В каталоге 'примеры_работ' вы можете посмотреть наши последние проекты."
188
190
 
189
191
  {
@@ -194,7 +196,7 @@ class PromptLoader:
194
196
  "каталоги": ["примеры_работ"]
195
197
  }
196
198
 
197
- Пример 5 - комбинированная отправка:
199
+ Пример 5 - комбинированная отправка (без ссылки):
198
200
  "Отправляю вам коммерческое предложение и примеры похожих проектов из нашего портфолио."
199
201
 
200
202
  {
@@ -210,6 +212,7 @@ class PromptLoader:
210
212
  "каталоги": ["портфолио_2023"]
211
213
  }
212
214
 
215
+
213
216
  ТРЕБОВАНИЯ К JSON:
214
217
  - JSON должен быть валидным и находиться в самом конце ответа
215
218
  - Всегда используй кавычки для строк
@@ -217,6 +220,7 @@ class PromptLoader:
217
220
  - Если событий нет - не добавляй их в массив
218
221
  - Качество должно быть числом от 1 до 10
219
222
 
223
+
220
224
  ПОМНИ: Этот JSON критически важен для работы системы администрирования и аналитики!
221
225
  """
222
226
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: smart-bot-factory
3
- Version: 0.3.4
3
+ Version: 0.3.5
4
4
  Summary: Библиотека для создания умных чат-ботов
5
5
  Author-email: Kopatych <eserov73@gmail.com>
6
6
  License: MIT
@@ -21,12 +21,10 @@ Classifier: Topic :: Communications :: Chat
21
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
22
  Requires-Python: >=3.9
23
23
  Requires-Dist: aiofiles>=23.0.0
24
- Requires-Dist: aiogram-media-group>=0.5.1
25
24
  Requires-Dist: aiogram>=3.4.1
26
25
  Requires-Dist: click>=8.0.0
27
26
  Requires-Dist: openai>=1.12.0
28
27
  Requires-Dist: project-root-finder>=1.9
29
- Requires-Dist: python-dateutil>=2.9.0.post0
30
28
  Requires-Dist: python-dotenv>=1.0.1
31
29
  Requires-Dist: pytz>=2023.3
32
30
  Requires-Dist: pyyaml>=6.0.2
@@ -30,18 +30,18 @@ smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml,sh
30
30
  smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml,sha256=bzDulOU4a2LyWlcHzlQU8GYhOky2WTfyizGfjX4ioMY,2436
31
31
  smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt,sha256=Db21Mm0r8SBWFdX9EeIF2FZtLQ2cvuwVlSRJd2KEYCg,922
32
32
  smart_bot_factory/configs/growthmed-october-24/welcome_file/Чек лист по 152ФЗ и 323ФЗ для медицины.pdf,sha256=BiAiQHNnQXJPMsks9AeL6s0beEjRFkRMJLMlAn4WorA,5284954
33
- smart_bot_factory/core/bot_utils.py,sha256=Be334BPspBgHNO9OpEiPlrCc0Gmy7_8sox5WpV8XHic,45174
33
+ smart_bot_factory/core/bot_utils.py,sha256=4c98tVPThXW4ET6XJ0FYMePrx4gYrw_HNhx3YLep9hY,46976
34
34
  smart_bot_factory/core/conversation_manager.py,sha256=eoHL7MCEz68DRvTVwRwZgf2PWwGv4T6J9D-I-thETi8,28289
35
- smart_bot_factory/core/decorators.py,sha256=oSsX1yCULoUfVf2aNynBmvap7zDEh5-cEcW4yoRv3dc,93903
36
- smart_bot_factory/core/message_sender.py,sha256=J4b6n8nXVjqf-qzL6URRSvc-FVnQfShwujVSM6qv26w,32232
37
- smart_bot_factory/core/router.py,sha256=03fbysaj0LR96p4-8iiml8dTmEHCkR-AaTposSv8q8o,11898
35
+ smart_bot_factory/core/decorators.py,sha256=0fDgYNaXU3ZLhFJBxAognLWfy_Fz1F63wdUNaVziDQU,95141
36
+ smart_bot_factory/core/message_sender.py,sha256=0-SQcK4W1x__VgvyaeVRuFlXcxV56TsR_nNK07Nr4b4,32763
37
+ smart_bot_factory/core/router.py,sha256=ji7rzpuKaO8yKaxFW58WhlgG5ExXlbCgqCTONxAyqL4,15022
38
38
  smart_bot_factory/core/router_manager.py,sha256=dUwesog-oHk1U2EDdS8p0e4MTSkwtx5_qXn6nrJ9l9I,9700
39
39
  smart_bot_factory/core/states.py,sha256=L8qp1UmYFuxTN5U9tY076rDuKgxtFbpSGqBpva2eWbo,895
40
40
  smart_bot_factory/creation/__init__.py,sha256=IgDk8GDS3pg7Pw_Et41J33ZmeZIU5dRwQdTmYKXfJfE,128
41
41
  smart_bot_factory/creation/bot_builder.py,sha256=yGRmOPD7qCMbhcBiltHWISoKxWx8eqjDSnZXpwhqnUs,43115
42
42
  smart_bot_factory/creation/bot_testing.py,sha256=JDWXyJfZmbgo-DLdAPk8Sd9FiehtHHa4sLD17lBrTOc,55669
43
43
  smart_bot_factory/event/__init__.py,sha256=hPL449RULIOB-OXv1ZbGNiHctAYaOMUqhSWGPrDHYBM,212
44
- smart_bot_factory/handlers/handlers.py,sha256=ymPCQPjA0uQc9deiwiVrMhxpBsqdKsAfeajHzK74seA,60739
44
+ smart_bot_factory/handlers/handlers.py,sha256=v3ubszkN7ML-CXChveTdp68EdMjHl02NTJ3hMT2zXKA,61893
45
45
  smart_bot_factory/integrations/openai_client.py,sha256=fwaJpwojFdLBWChcFWpFGOHK9upG-nCIwDochkCRRlY,24291
46
46
  smart_bot_factory/integrations/supabase_client.py,sha256=Rv0sZHXGSfm3UWodmaR1N-X5-2JbmynPWJKY0a0k_Tk,63557
47
47
  smart_bot_factory/message/__init__.py,sha256=-ehDZweUc3uKgmLLxFVsD-KWrDtnHpHms7pCrDelWo0,1950
@@ -50,10 +50,10 @@ smart_bot_factory/supabase/__init__.py,sha256=XmZP6yM9ffERM5ddAWyJnrNzEhCYtMu3Ac
50
50
  smart_bot_factory/supabase/client.py,sha256=lWIzfOgoSvU7xPhYLoJtM5GnbWdoWsvHcRFC22sFBMU,25637
51
51
  smart_bot_factory/utils/__init__.py,sha256=UhsJXEHfrIK8h1AHsroHSwAriijk-LvnqLyvgzi2VYs,273
52
52
  smart_bot_factory/utils/debug_routing.py,sha256=BOoDhKBg7UXe5uHQxRk3TSfPfLPOFqt0N7lAo6kjCOo,4719
53
- smart_bot_factory/utils/prompt_loader.py,sha256=JSn7CsWnToSbHYtURdeuZn7ectyDqQGrPGHN2ixIGkw,19930
53
+ smart_bot_factory/utils/prompt_loader.py,sha256=HS_6Vf-qvRBkhvyzu-HNVS1swFgmqWOKNNv0F6We_AQ,20060
54
54
  smart_bot_factory/utils/user_prompt_loader.py,sha256=dk6P0X_3UcNqxjRtuIvb0LcPrp03zIIsstZwdmeCPaE,2519
55
- smart_bot_factory-0.3.4.dist-info/METADATA,sha256=lO_csJRkPwV6c8SUmBUEN4WBl9X8LTx_lSt_FALl6J0,31991
56
- smart_bot_factory-0.3.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
57
- smart_bot_factory-0.3.4.dist-info/entry_points.txt,sha256=ybKEAI0WSb7WoRiey7QE-HHfn88UGV7nxLDxXq7b7SU,50
58
- smart_bot_factory-0.3.4.dist-info/licenses/LICENSE,sha256=OrK3cwdUTzNzIhJvSPtJaVMoYIyC_sSx5EFE_FDMvGs,1092
59
- smart_bot_factory-0.3.4.dist-info/RECORD,,
55
+ smart_bot_factory-0.3.5.dist-info/METADATA,sha256=xOG5tl-e-w8yfvxuZfiyVRnQGdEvUxYLMPbKVGTcbmY,31905
56
+ smart_bot_factory-0.3.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
57
+ smart_bot_factory-0.3.5.dist-info/entry_points.txt,sha256=ybKEAI0WSb7WoRiey7QE-HHfn88UGV7nxLDxXq7b7SU,50
58
+ smart_bot_factory-0.3.5.dist-info/licenses/LICENSE,sha256=OrK3cwdUTzNzIhJvSPtJaVMoYIyC_sSx5EFE_FDMvGs,1092
59
+ smart_bot_factory-0.3.5.dist-info/RECORD,,