smart-bot-factory 0.2.0__py3-none-any.whl → 0.2.2__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/creation/bot_builder.py +169 -0
- smart_bot_factory/handlers/handlers.py +151 -71
- {smart_bot_factory-0.2.0.dist-info → smart_bot_factory-0.2.2.dist-info}/METADATA +1 -1
- {smart_bot_factory-0.2.0.dist-info → smart_bot_factory-0.2.2.dist-info}/RECORD +7 -7
- {smart_bot_factory-0.2.0.dist-info → smart_bot_factory-0.2.2.dist-info}/WHEEL +0 -0
- {smart_bot_factory-0.2.0.dist-info → smart_bot_factory-0.2.2.dist-info}/entry_points.txt +0 -0
- {smart_bot_factory-0.2.0.dist-info → smart_bot_factory-0.2.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -46,6 +46,14 @@ class BotBuilder:
|
|
|
46
46
|
self.prompt_loader: Optional[PromptLoader] = None
|
|
47
47
|
self.router_manager: Optional[RouterManager] = None
|
|
48
48
|
self._telegram_routers: List = [] # Список Telegram роутеров
|
|
49
|
+
self._start_handlers: List = [] # Список обработчиков on_start
|
|
50
|
+
|
|
51
|
+
# Хуки для кастомизации process_user_message
|
|
52
|
+
self._message_validators: List = [] # Валидация ДО обработки
|
|
53
|
+
self._prompt_enrichers: List = [] # Обогащение системного промпта
|
|
54
|
+
self._context_enrichers: List = [] # Обогащение контекста для AI
|
|
55
|
+
self._response_processors: List = [] # Обработка ответа AI
|
|
56
|
+
self._send_filters: List = [] # Фильтры перед отправкой пользователю
|
|
49
57
|
|
|
50
58
|
# Флаги инициализации
|
|
51
59
|
self._initialized = False
|
|
@@ -326,6 +334,165 @@ class BotBuilder:
|
|
|
326
334
|
|
|
327
335
|
logger.info(f"✅ Зарегистрировано {len(telegram_routers)} Telegram роутеров")
|
|
328
336
|
|
|
337
|
+
def on_start(self, handler):
|
|
338
|
+
"""
|
|
339
|
+
Регистрирует обработчик, который вызывается после стандартной логики /start
|
|
340
|
+
|
|
341
|
+
Обработчик получает доступ к:
|
|
342
|
+
- user_id: int - ID пользователя Telegram
|
|
343
|
+
- session_id: str - ID созданной сессии
|
|
344
|
+
- message: Message - Объект сообщения от aiogram
|
|
345
|
+
- state: FSMContext - Контекст состояния
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
handler: Async функция с сигнатурой:
|
|
349
|
+
async def handler(user_id: int, session_id: str, message: Message, state: FSMContext)
|
|
350
|
+
|
|
351
|
+
Example:
|
|
352
|
+
@bot_builder.on_start
|
|
353
|
+
async def my_start_handler(user_id, session_id, message, state):
|
|
354
|
+
keyboard = InlineKeyboardMarkup(...)
|
|
355
|
+
await message.answer("Выберите действие:", reply_markup=keyboard)
|
|
356
|
+
"""
|
|
357
|
+
if not callable(handler):
|
|
358
|
+
raise TypeError(f"Обработчик должен быть callable, получен {type(handler)}")
|
|
359
|
+
|
|
360
|
+
self._start_handlers.append(handler)
|
|
361
|
+
logger.info(f"✅ Зарегистрирован обработчик on_start: {handler.__name__}")
|
|
362
|
+
return handler # Возвращаем handler для использования как декоратор
|
|
363
|
+
|
|
364
|
+
def get_start_handlers(self) -> List:
|
|
365
|
+
"""Получает список обработчиков on_start"""
|
|
366
|
+
return self._start_handlers.copy()
|
|
367
|
+
|
|
368
|
+
# ========== ХУКИ ДЛЯ КАСТОМИЗАЦИИ ОБРАБОТКИ СООБЩЕНИЙ ==========
|
|
369
|
+
|
|
370
|
+
def validate_message(self, handler):
|
|
371
|
+
"""
|
|
372
|
+
Регистрирует валидатор сообщений (вызывается ДО обработки AI)
|
|
373
|
+
|
|
374
|
+
Если валидатор возвращает False, обработка прерывается
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
handler: async def(message: Message, supabase_client) -> bool
|
|
378
|
+
|
|
379
|
+
Example:
|
|
380
|
+
@bot_builder.validate_message
|
|
381
|
+
async def check_service_names(message, supabase_client):
|
|
382
|
+
if "неправильное название" in message.text:
|
|
383
|
+
await message.answer("Пожалуйста, уточните название услуги")
|
|
384
|
+
return False # Прерываем обработку
|
|
385
|
+
return True # Продолжаем
|
|
386
|
+
"""
|
|
387
|
+
if not callable(handler):
|
|
388
|
+
raise TypeError(f"Обработчик должен быть callable, получен {type(handler)}")
|
|
389
|
+
|
|
390
|
+
self._message_validators.append(handler)
|
|
391
|
+
logger.info(f"✅ Зарегистрирован валидатор сообщений: {handler.__name__}")
|
|
392
|
+
return handler
|
|
393
|
+
|
|
394
|
+
def enrich_prompt(self, handler):
|
|
395
|
+
"""
|
|
396
|
+
Регистрирует обогатитель системного промпта
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
handler: async def(system_prompt: str, user_id: int, session_id: str, supabase_client) -> str
|
|
400
|
+
|
|
401
|
+
Example:
|
|
402
|
+
@bot_builder.enrich_prompt
|
|
403
|
+
async def add_client_info(system_prompt, user_id, session_id, supabase_client):
|
|
404
|
+
session = await supabase_client.get_active_session(user_id)
|
|
405
|
+
phone = session.get('metadata', {}).get('phone')
|
|
406
|
+
if phone:
|
|
407
|
+
return f"{system_prompt}\\n\\nТелефон клиента: {phone}"
|
|
408
|
+
return system_prompt
|
|
409
|
+
"""
|
|
410
|
+
if not callable(handler):
|
|
411
|
+
raise TypeError(f"Обработчик должен быть callable, получен {type(handler)}")
|
|
412
|
+
|
|
413
|
+
self._prompt_enrichers.append(handler)
|
|
414
|
+
logger.info(f"✅ Зарегистрирован обогатитель промпта: {handler.__name__}")
|
|
415
|
+
return handler
|
|
416
|
+
|
|
417
|
+
def enrich_context(self, handler):
|
|
418
|
+
"""
|
|
419
|
+
Регистрирует обогатитель контекста для AI (messages array)
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
handler: async def(messages: List[dict], user_id: int, session_id: str) -> List[dict]
|
|
423
|
+
|
|
424
|
+
Example:
|
|
425
|
+
@bot_builder.enrich_context
|
|
426
|
+
async def add_external_data(messages, user_id, session_id):
|
|
427
|
+
# Добавляем данные из внешнего API
|
|
428
|
+
messages.append({
|
|
429
|
+
"role": "system",
|
|
430
|
+
"content": "Дополнительная информация..."
|
|
431
|
+
})
|
|
432
|
+
return messages
|
|
433
|
+
"""
|
|
434
|
+
if not callable(handler):
|
|
435
|
+
raise TypeError(f"Обработчик должен быть callable, получен {type(handler)}")
|
|
436
|
+
|
|
437
|
+
self._context_enrichers.append(handler)
|
|
438
|
+
logger.info(f"✅ Зарегистрирован обогатитель контекста: {handler.__name__}")
|
|
439
|
+
return handler
|
|
440
|
+
|
|
441
|
+
def process_response(self, handler):
|
|
442
|
+
"""
|
|
443
|
+
Регистрирует обработчик ответа AI (ПОСЛЕ получения ответа)
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
handler: async def(response_text: str, ai_metadata: dict, user_id: int) -> tuple[str, dict]
|
|
447
|
+
|
|
448
|
+
Example:
|
|
449
|
+
@bot_builder.process_response
|
|
450
|
+
async def modify_response(response_text, ai_metadata, user_id):
|
|
451
|
+
# Модифицируем ответ
|
|
452
|
+
if "цена" in response_text.lower():
|
|
453
|
+
response_text += "\\n\\n💰 Актуальные цены на сайте"
|
|
454
|
+
return response_text, ai_metadata
|
|
455
|
+
"""
|
|
456
|
+
if not callable(handler):
|
|
457
|
+
raise TypeError(f"Обработчик должен быть callable, получен {type(handler)}")
|
|
458
|
+
|
|
459
|
+
self._response_processors.append(handler)
|
|
460
|
+
logger.info(f"✅ Зарегистрирован обработчик ответа: {handler.__name__}")
|
|
461
|
+
return handler
|
|
462
|
+
|
|
463
|
+
def filter_send(self, handler):
|
|
464
|
+
"""
|
|
465
|
+
Регистрирует фильтр отправки (может блокировать отправку пользователю)
|
|
466
|
+
|
|
467
|
+
Если фильтр возвращает False, сообщение НЕ отправляется
|
|
468
|
+
|
|
469
|
+
Args:
|
|
470
|
+
handler: async def(user_id: int, response_text: str) -> bool
|
|
471
|
+
|
|
472
|
+
Example:
|
|
473
|
+
@bot_builder.filter_send
|
|
474
|
+
async def block_during_process(user_id, response_text):
|
|
475
|
+
if is_processing(user_id):
|
|
476
|
+
return False # Не отправляем
|
|
477
|
+
return True # Отправляем
|
|
478
|
+
"""
|
|
479
|
+
if not callable(handler):
|
|
480
|
+
raise TypeError(f"Обработчик должен быть callable, получен {type(handler)}")
|
|
481
|
+
|
|
482
|
+
self._send_filters.append(handler)
|
|
483
|
+
logger.info(f"✅ Зарегистрирован фильтр отправки: {handler.__name__}")
|
|
484
|
+
return handler
|
|
485
|
+
|
|
486
|
+
def get_message_hooks(self) -> Dict[str, List]:
|
|
487
|
+
"""Получает все хуки для обработки сообщений"""
|
|
488
|
+
return {
|
|
489
|
+
'validators': self._message_validators.copy(),
|
|
490
|
+
'prompt_enrichers': self._prompt_enrichers.copy(),
|
|
491
|
+
'context_enrichers': self._context_enrichers.copy(),
|
|
492
|
+
'response_processors': self._response_processors.copy(),
|
|
493
|
+
'send_filters': self._send_filters.copy()
|
|
494
|
+
}
|
|
495
|
+
|
|
329
496
|
def get_router_manager(self) -> RouterManager:
|
|
330
497
|
"""Получает менеджер роутеров событий"""
|
|
331
498
|
return self.router_manager
|
|
@@ -374,6 +541,8 @@ class BotBuilder:
|
|
|
374
541
|
handlers_module.admin_manager = self.admin_manager
|
|
375
542
|
handlers_module.analytics_manager = self.analytics_manager
|
|
376
543
|
handlers_module.conversation_manager = self.conversation_manager
|
|
544
|
+
handlers_module.start_handlers = self._start_handlers # Передаем обработчики on_start
|
|
545
|
+
handlers_module.message_hooks = self.get_message_hooks() # Передаем хуки для обработки сообщений
|
|
377
546
|
logger.info("✅ Глобальные переменные установлены в handlers")
|
|
378
547
|
except Exception as e:
|
|
379
548
|
logger.warning(f"⚠️ Не удалось установить глобальные переменные в handlers: {e}")
|
|
@@ -277,6 +277,23 @@ async def user_start_handler(message: Message, state: FSMContext):
|
|
|
277
277
|
|
|
278
278
|
logging.info(f"✅ Приветственное сообщение успешно сохранено в БД для сессии {session_id}")
|
|
279
279
|
|
|
280
|
+
# ВЫЗЫВАЕМ ПОЛЬЗОВАТЕЛЬСКИЕ ОБРАБОТЧИКИ on_start
|
|
281
|
+
start_handlers = get_global_var('start_handlers')
|
|
282
|
+
if start_handlers:
|
|
283
|
+
logger.info(f"🔔 Вызов {len(start_handlers)} пользовательских обработчиков on_start")
|
|
284
|
+
for handler in start_handlers:
|
|
285
|
+
try:
|
|
286
|
+
await handler(
|
|
287
|
+
user_id=message.from_user.id,
|
|
288
|
+
session_id=session_id,
|
|
289
|
+
message=message,
|
|
290
|
+
state=state
|
|
291
|
+
)
|
|
292
|
+
logger.info(f"✅ Обработчик on_start '{handler.__name__}' выполнен успешно")
|
|
293
|
+
except Exception as handler_error:
|
|
294
|
+
logger.error(f"❌ Ошибка в обработчике on_start '{handler.__name__}': {handler_error}")
|
|
295
|
+
# Продолжаем выполнение остальных обработчиков
|
|
296
|
+
|
|
280
297
|
except Exception as e:
|
|
281
298
|
logger.error(f"Ошибка при обработке user /start: {e}")
|
|
282
299
|
await send_message(message, "Произошла ошибка при инициализации. Попробуйте позже.")
|
|
@@ -475,10 +492,22 @@ async def process_user_message(message: Message, state: FSMContext, session_id:
|
|
|
475
492
|
config = get_global_var('config')
|
|
476
493
|
bot = get_global_var('bot')
|
|
477
494
|
prompt_loader = get_global_var('prompt_loader')
|
|
495
|
+
message_hooks = get_global_var('message_hooks') or {}
|
|
478
496
|
from datetime import datetime
|
|
479
497
|
import pytz # Добавляем импорт для работы с временными зонами
|
|
480
498
|
|
|
481
499
|
try:
|
|
500
|
+
# ============ ХУК 1: ВАЛИДАЦИЯ СООБЩЕНИЯ ============
|
|
501
|
+
validators = message_hooks.get('validators', [])
|
|
502
|
+
for validator in validators:
|
|
503
|
+
try:
|
|
504
|
+
should_continue = await validator(message, supabase_client)
|
|
505
|
+
if not should_continue:
|
|
506
|
+
logger.info(f"⛔ Валидатор '{validator.__name__}' прервал обработку")
|
|
507
|
+
return # Прерываем обработку
|
|
508
|
+
except Exception as e:
|
|
509
|
+
logger.error(f"❌ Ошибка в валидаторе '{validator.__name__}': {e}")
|
|
510
|
+
|
|
482
511
|
# Сохраняем сообщение пользователя
|
|
483
512
|
await supabase_client.add_message(
|
|
484
513
|
session_id=session_id,
|
|
@@ -497,13 +526,27 @@ async def process_user_message(message: Message, state: FSMContext, session_id:
|
|
|
497
526
|
current_time = datetime.now(moscow_tz)
|
|
498
527
|
time_info = current_time.strftime('%H:%M, %d.%m.%Y, %A')
|
|
499
528
|
|
|
500
|
-
#
|
|
529
|
+
# Базовый системный промпт с временем
|
|
501
530
|
system_prompt_with_time = f"""
|
|
502
531
|
{system_prompt}
|
|
503
532
|
|
|
504
533
|
ТЕКУЩЕЕ ВРЕМЯ: {time_info} (московское время)
|
|
505
534
|
"""
|
|
506
535
|
|
|
536
|
+
# ============ ХУК 2: ОБОГАЩЕНИЕ ПРОМПТА ============
|
|
537
|
+
prompt_enrichers = message_hooks.get('prompt_enrichers', [])
|
|
538
|
+
for enricher in prompt_enrichers:
|
|
539
|
+
try:
|
|
540
|
+
system_prompt_with_time = await enricher(
|
|
541
|
+
system_prompt_with_time,
|
|
542
|
+
message.from_user.id,
|
|
543
|
+
session_id,
|
|
544
|
+
supabase_client
|
|
545
|
+
)
|
|
546
|
+
logger.info(f"✅ Промпт обогащен '{enricher.__name__}'")
|
|
547
|
+
except Exception as e:
|
|
548
|
+
logger.error(f"❌ Ошибка в обогатителе промпта '{enricher.__name__}': {e}")
|
|
549
|
+
|
|
507
550
|
# Формируем контекст для OpenAI с обновленным системным промптом
|
|
508
551
|
messages = [{"role": "system", "content": system_prompt_with_time}]
|
|
509
552
|
|
|
@@ -519,6 +562,19 @@ async def process_user_message(message: Message, state: FSMContext, session_id:
|
|
|
519
562
|
messages.append({"role": "system", "content": final_instructions})
|
|
520
563
|
logger.info(f"🎯 Добавлены финальные инструкции ({len(final_instructions)} символов)")
|
|
521
564
|
|
|
565
|
+
# ============ ХУК 3: ОБОГАЩЕНИЕ КОНТЕКСТА ============
|
|
566
|
+
context_enrichers = message_hooks.get('context_enrichers', [])
|
|
567
|
+
for enricher in context_enrichers:
|
|
568
|
+
try:
|
|
569
|
+
messages = await enricher(
|
|
570
|
+
messages,
|
|
571
|
+
message.from_user.id,
|
|
572
|
+
session_id
|
|
573
|
+
)
|
|
574
|
+
logger.info(f"✅ Контекст обогащен '{enricher.__name__}'")
|
|
575
|
+
except Exception as e:
|
|
576
|
+
logger.error(f"❌ Ошибка в обогатителе контекста '{enricher.__name__}': {e}")
|
|
577
|
+
|
|
522
578
|
logger.info(f"📝 Контекст сформирован: {len(messages)} сообщений (включая время: {time_info})")
|
|
523
579
|
|
|
524
580
|
await bot.send_chat_action(message.chat.id, "typing")
|
|
@@ -571,82 +627,95 @@ async def process_user_message(message: Message, state: FSMContext, session_id:
|
|
|
571
627
|
response_text = ai_response
|
|
572
628
|
|
|
573
629
|
logger.info(f"✅ Финальный текст для отправки: {len(response_text)} символов")
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
if stage or quality is not None:
|
|
592
|
-
await supabase_client.update_session_stage(session_id, stage, quality)
|
|
593
|
-
logger.info(f" ✅ Этап и качество обновлены в БД")
|
|
594
|
-
|
|
595
|
-
# Обрабатываем события
|
|
596
|
-
events = ai_metadata.get('события', [])
|
|
597
|
-
if events:
|
|
598
|
-
logger.info(f"\n🔔 События в диалоге ({len(events)}):")
|
|
599
|
-
for idx, event in enumerate(events, 1):
|
|
600
|
-
event_type = event.get('тип', 'неизвестно')
|
|
601
|
-
event_info = event.get('инфо', 'нет информации')
|
|
602
|
-
|
|
603
|
-
# Подбираем эмодзи для разных типов событий
|
|
604
|
-
event_emoji = {
|
|
605
|
-
'телефон': '📱',
|
|
606
|
-
'email': '📧',
|
|
607
|
-
'встреча': '📅',
|
|
608
|
-
'заказ': '🛍️',
|
|
609
|
-
'вопрос': '❓',
|
|
610
|
-
'консультация': '💬',
|
|
611
|
-
'жалоба': '⚠️',
|
|
612
|
-
'отзыв': '💭'
|
|
613
|
-
}.get(event_type.lower(), '📌')
|
|
614
|
-
|
|
615
|
-
logger.info(f" {idx}. {event_emoji} {event_type}: {event_info}")
|
|
616
|
-
|
|
617
|
-
# Обрабатываем события в системе
|
|
618
|
-
await process_events(session_id, events, message.from_user.id)
|
|
619
|
-
logger.info(" ✅ События обработаны")
|
|
630
|
+
|
|
631
|
+
# ============ ХУК 4: ОБРАБОТКА ОТВЕТА ============
|
|
632
|
+
response_processors = message_hooks.get('response_processors', [])
|
|
633
|
+
for processor in response_processors:
|
|
634
|
+
try:
|
|
635
|
+
response_text, ai_metadata = await processor(
|
|
636
|
+
response_text,
|
|
637
|
+
ai_metadata,
|
|
638
|
+
message.from_user.id
|
|
639
|
+
)
|
|
640
|
+
logger.info(f"✅ Ответ обработан '{processor.__name__}'")
|
|
641
|
+
except Exception as e:
|
|
642
|
+
logger.error(f"❌ Ошибка в обработчике ответа '{processor.__name__}': {e}")
|
|
643
|
+
|
|
644
|
+
# Обновляем этап сессии и качество лида
|
|
645
|
+
if ai_metadata:
|
|
646
|
+
logger.info("🔍 Анализ метаданных от ИИ:")
|
|
620
647
|
|
|
621
|
-
#
|
|
622
|
-
|
|
623
|
-
|
|
648
|
+
# Вывод информации об этапе
|
|
649
|
+
stage = ai_metadata.get('этап')
|
|
650
|
+
if stage:
|
|
651
|
+
logger.info(f" 📈 Этап диалога: {stage}")
|
|
624
652
|
|
|
625
|
-
#
|
|
653
|
+
# Вывод информации о качестве лида
|
|
654
|
+
quality = ai_metadata.get('качество')
|
|
655
|
+
if quality is not None:
|
|
656
|
+
quality_emoji = "⭐" * min(quality, 5) # Максимум 5 звезд
|
|
657
|
+
logger.info(f" {quality_emoji} Качество лида: {quality}/10")
|
|
658
|
+
|
|
659
|
+
# Обновляем в базе данных
|
|
660
|
+
if stage or quality is not None:
|
|
661
|
+
await supabase_client.update_session_stage(session_id, stage, quality)
|
|
662
|
+
logger.info(f" ✅ Этап и качество обновлены в БД")
|
|
663
|
+
|
|
664
|
+
# Обрабатываем события
|
|
665
|
+
events = ai_metadata.get('события', [])
|
|
666
|
+
if events:
|
|
667
|
+
logger.info(f"\n🔔 События в диалоге ({len(events)}):")
|
|
668
|
+
for idx, event in enumerate(events, 1):
|
|
669
|
+
event_type = event.get('тип', 'неизвестно')
|
|
670
|
+
event_info = event.get('инфо', 'нет информации')
|
|
671
|
+
|
|
672
|
+
# Подбираем эмодзи для разных типов событий
|
|
673
|
+
event_emoji = {
|
|
674
|
+
'телефон': '📱',
|
|
675
|
+
'email': '📧',
|
|
676
|
+
'встреча': '📅',
|
|
677
|
+
'заказ': '🛍️',
|
|
678
|
+
'вопрос': '❓',
|
|
679
|
+
'консультация': '💬',
|
|
680
|
+
'жалоба': '⚠️',
|
|
681
|
+
'отзыв': '💭'
|
|
682
|
+
}.get(event_type.lower(), '📌')
|
|
683
|
+
|
|
684
|
+
logger.info(f" {idx}. {event_emoji} {event_type}: {event_info}")
|
|
685
|
+
|
|
686
|
+
# Обрабатываем события в системе
|
|
687
|
+
await process_events(session_id, events, message.from_user.id)
|
|
688
|
+
logger.info(" ✅ События обработаны")
|
|
689
|
+
|
|
690
|
+
# Обрабатываем файлы и каталоги
|
|
691
|
+
files_list = ai_metadata.get('файлы', [])
|
|
692
|
+
directories_list = ai_metadata.get('каталоги', [])
|
|
693
|
+
|
|
694
|
+
# Форматируем информацию о файлах
|
|
695
|
+
if files_list:
|
|
696
|
+
logger.info("📎 Найденные файлы:")
|
|
697
|
+
for idx, file in enumerate(files_list, 1):
|
|
698
|
+
logger.info(f" {idx}. 📄 {file}")
|
|
699
|
+
|
|
700
|
+
# Форматируем информацию о каталогах
|
|
701
|
+
if directories_list:
|
|
702
|
+
logger.info("📂 Найденные каталоги:")
|
|
703
|
+
for idx, directory in enumerate(directories_list, 1):
|
|
704
|
+
logger.info(f" {idx}. 📁 {directory}")
|
|
705
|
+
|
|
706
|
+
# Добавляем информацию в текст ответа
|
|
707
|
+
if files_list or directories_list:
|
|
708
|
+
files_info = []
|
|
626
709
|
if files_list:
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
logger.info(f" {idx}. 📄 {file}")
|
|
710
|
+
files_str = "\n".join(f"• {file}" for file in files_list)
|
|
711
|
+
files_info.append(f"\n\n📎 Доступные файлы:\n{files_str}")
|
|
630
712
|
|
|
631
|
-
# Форматируем информацию о каталогах
|
|
632
713
|
if directories_list:
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
logger.info(f" {idx}. 📁 {directory}")
|
|
714
|
+
dirs_str = "\n".join(f"• {directory}" for directory in directories_list)
|
|
715
|
+
files_info.append(f"\n\n📂 Доступные каталоги:\n{dirs_str}")
|
|
636
716
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
files_info = []
|
|
640
|
-
if files_list:
|
|
641
|
-
files_str = "\n".join(f"• {file}" for file in files_list)
|
|
642
|
-
files_info.append(f"\n\n📎 Доступные файлы:\n{files_str}")
|
|
643
|
-
|
|
644
|
-
if directories_list:
|
|
645
|
-
dirs_str = "\n".join(f"• {directory}" for directory in directories_list)
|
|
646
|
-
files_info.append(f"\n\n📂 Доступные каталоги:\n{dirs_str}")
|
|
647
|
-
|
|
648
|
-
else:
|
|
649
|
-
logger.info("📎 Файлы и каталоги не указаны")
|
|
717
|
+
else:
|
|
718
|
+
logger.info("📎 Файлы и каталоги не указаны")
|
|
650
719
|
|
|
651
720
|
# Сохраняем ответ ассистента с метаданными
|
|
652
721
|
try:
|
|
@@ -680,6 +749,17 @@ async def process_user_message(message: Message, state: FSMContext, session_id:
|
|
|
680
749
|
|
|
681
750
|
logger.info(f"📱 Отправляем пользователю: {len(final_response)} символов")
|
|
682
751
|
|
|
752
|
+
# ============ ХУК 5: ФИЛЬТРЫ ОТПРАВКИ ============
|
|
753
|
+
send_filters = message_hooks.get('send_filters', [])
|
|
754
|
+
for filter_func in send_filters:
|
|
755
|
+
try:
|
|
756
|
+
should_send = await filter_func(message.from_user.id, final_response)
|
|
757
|
+
if not should_send:
|
|
758
|
+
logger.info(f"⛔ Фильтр '{filter_func.__name__}' заблокировал отправку")
|
|
759
|
+
return # Не отправляем
|
|
760
|
+
except Exception as e:
|
|
761
|
+
logger.error(f"❌ Ошибка в фильтре отправки '{filter_func.__name__}': {e}")
|
|
762
|
+
|
|
683
763
|
# Отправляем ответ пользователю
|
|
684
764
|
try:
|
|
685
765
|
await send_message(message, final_response, files_list=files_list, directories_list=directories_list)
|
|
@@ -33,12 +33,12 @@ smart_bot_factory/core/router_manager.py,sha256=dUwesog-oHk1U2EDdS8p0e4MTSkwtx5_
|
|
|
33
33
|
smart_bot_factory/core/states.py,sha256=AOc19_yAsDW_8md-2oiowjBhuW3bwcsmMxszCXWZZTQ,355
|
|
34
34
|
smart_bot_factory/core/telegram_router.py,sha256=LcPLOd87Y4Y4YN6TBXVAtmpSp8gAK2otgMI4YS5f5tk,2091
|
|
35
35
|
smart_bot_factory/creation/__init__.py,sha256=IgDk8GDS3pg7Pw_Et41J33ZmeZIU5dRwQdTmYKXfJfE,128
|
|
36
|
-
smart_bot_factory/creation/bot_builder.py,sha256=
|
|
36
|
+
smart_bot_factory/creation/bot_builder.py,sha256=VjRPlar6xgul7QpG57od--uS-juWrlW9-YYNnFN_Yx4,33828
|
|
37
37
|
smart_bot_factory/creation/bot_testing.py,sha256=JDWXyJfZmbgo-DLdAPk8Sd9FiehtHHa4sLD17lBrTOc,55669
|
|
38
38
|
smart_bot_factory/database/database_structure.sql,sha256=26gFtMC2jdQGQF7Zb_F4Br56rMd4hUDTk9FkNZYneLo,2789
|
|
39
39
|
smart_bot_factory/database/schema.sql,sha256=-6kOmA9QnSkUtmGI2iQRbTvbdiqOhEOQcuz1lJn79mU,28059
|
|
40
40
|
smart_bot_factory/event/__init__.py,sha256=hPL449RULIOB-OXv1ZbGNiHctAYaOMUqhSWGPrDHYBM,212
|
|
41
|
-
smart_bot_factory/handlers/handlers.py,sha256=
|
|
41
|
+
smart_bot_factory/handlers/handlers.py,sha256=kfzveBtye0KIrRVWhegNVQxfj3PyL9M3hvQBE-lseaY,41601
|
|
42
42
|
smart_bot_factory/integrations/openai_client.py,sha256=aMcDrKO0GEx3ZSVEOGDeDtFCDWSXs6biUfgrbRK8yTU,23180
|
|
43
43
|
smart_bot_factory/integrations/supabase_client.py,sha256=AfALLZdDYeMWHLJw6POTGiBd-sH3i03oT6tT7m9C28I,44644
|
|
44
44
|
smart_bot_factory/message/__init__.py,sha256=6QvjdfF99venyDB9udZv9WDNjIHJLNuaVhYdTK3a44A,282
|
|
@@ -48,8 +48,8 @@ smart_bot_factory/supabase/client.py,sha256=8_-I3kxZQlKQElI4cTUjNGYcqlyIyEkSrUZa
|
|
|
48
48
|
smart_bot_factory/utils/__init__.py,sha256=5zNjw491bj5VkOhoEAwh2hjmv8nldyDOTrG7pbGpz6A,285
|
|
49
49
|
smart_bot_factory/utils/debug_routing.py,sha256=BOoDhKBg7UXe5uHQxRk3TSfPfLPOFqt0N7lAo6kjCOo,4719
|
|
50
50
|
smart_bot_factory/utils/prompt_loader.py,sha256=JSn7CsWnToSbHYtURdeuZn7ectyDqQGrPGHN2ixIGkw,19930
|
|
51
|
-
smart_bot_factory-0.2.
|
|
52
|
-
smart_bot_factory-0.2.
|
|
53
|
-
smart_bot_factory-0.2.
|
|
54
|
-
smart_bot_factory-0.2.
|
|
55
|
-
smart_bot_factory-0.2.
|
|
51
|
+
smart_bot_factory-0.2.2.dist-info/METADATA,sha256=XB5NPdlGpOpGPQtsbskUmJ0SGI3hp74IfAE5OHHAmq4,28224
|
|
52
|
+
smart_bot_factory-0.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
53
|
+
smart_bot_factory-0.2.2.dist-info/entry_points.txt,sha256=ybKEAI0WSb7WoRiey7QE-HHfn88UGV7nxLDxXq7b7SU,50
|
|
54
|
+
smart_bot_factory-0.2.2.dist-info/licenses/LICENSE,sha256=OrK3cwdUTzNzIhJvSPtJaVMoYIyC_sSx5EFE_FDMvGs,1092
|
|
55
|
+
smart_bot_factory-0.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|