smart-bot-factory 0.2.2__tar.gz → 0.2.3__tar.gz

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 (102) hide show
  1. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/PKG-INFO +1 -1
  2. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/best-valera.py +11 -12
  3. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/pyproject.toml +1 -1
  4. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/core/message_sender.py +241 -0
  5. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/creation/bot_builder.py +0 -24
  6. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/handlers/handlers.py +3 -20
  7. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/message/__init__.py +2 -0
  8. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/uv.lock +1 -1
  9. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/.claude/settings.local.json +0 -0
  10. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/.env.example +0 -0
  11. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/.github/ISSUE_TEMPLATE//342/234/250-/320/267/320/260/320/277/321/200/320/276/321/201-/321/204/321/203/320/275/320/272/321/206/320/270/320/270.md" +0 -0
  12. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/.github/ISSUE_TEMPLATE//360/237/220/233-/320/261/320/260/320/263-/321/200/320/265/320/277/320/276/321/200/321/202.md" +0 -0
  13. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/.github/workflows/ci.yml +0 -0
  14. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/.github/workflows/publish-private.yml +0 -0
  15. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/.github/workflows/publish.yml +0 -0
  16. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/.gitignore +0 -0
  17. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/.python-version +0 -0
  18. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/CLIENTS_USAGE.md +0 -0
  19. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/Dockerfile +0 -0
  20. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/LICENSE +0 -0
  21. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/README.md +0 -0
  22. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/best-valera/prompts/1sales_context.txt +0 -0
  23. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/best-valera/prompts/2product_info.txt +0 -0
  24. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/best-valera/prompts/3objection_handling.txt +0 -0
  25. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/best-valera/prompts/final_instructions.txt +0 -0
  26. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/best-valera/prompts/help_message.txt +0 -0
  27. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/best-valera/prompts/welcome_message.txt +0 -0
  28. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/best-valera/tests/quick_scenarios.yaml +0 -0
  29. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/best-valera/tests/realistic_scenarios.yaml +0 -0
  30. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/best-valera/tests/scenario_examples.yaml +0 -0
  31. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/best-valera/welcome_files/welcome_file_msg.txt +0 -0
  32. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/best-valera/welcome_files//320/247/320/265/320/272 /320/273/320/270/321/201/321/202 /320/277/320/276 152/320/244/320/227 /320/270 323/320/244/320/227 /320/264/320/273/321/217 /320/274/320/265/320/264/320/270/321/206/320/270/320/275/321/213.pdf" +0 -0
  33. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/valera/prompts/2product_info.txt +0 -0
  34. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/valera/prompts/3objection_handling.txt +0 -0
  35. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/valera/prompts/final_instructions.txt +0 -0
  36. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/valera/prompts/help_message.txt +0 -0
  37. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/valera/prompts/welcome_message.txt +0 -0
  38. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/valera/tests/quick_scenarios.yaml +0 -0
  39. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/valera/tests/realistic_scenarios.yaml +0 -0
  40. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/valera/tests/scenario_examples.yaml +0 -0
  41. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/valera/welcome_files/welcome_file_msg.txt +0 -0
  42. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/bots/valera/welcome_files//320/247/320/265/320/272 /320/273/320/270/321/201/321/202 /320/277/320/276 152/320/244/320/227 /320/270 323/320/244/320/227 /320/264/320/273/321/217 /320/274/320/265/320/264/320/270/321/206/320/270/320/275/321/213.pdf" +0 -0
  43. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/configs/valera/cats//320/224/320/276/320/263/320/276/320/262/320/276/321/200.pdf" +0 -0
  44. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/configs/valera/cats//320/272/320/276/320/275/320/270.jpg" +0 -0
  45. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/configs/valera/cats//320/272/320/276/321/202.jpg" +0 -0
  46. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/configs/valera/cats//320/273/320/265/321/201.jpg" +0 -0
  47. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/configs/valera/tests/fixes_elina.yaml +0 -0
  48. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/configs/valera/welcome_file//320/247/320/265/320/272 /320/273/320/270/321/201/321/202 /320/277/320/276 152/320/244/320/227 /320/270 323/320/244/320/227 /320/264/320/273/321/217 /320/274/320/265/320/264/320/270/321/206/320/270/320/275/321/213.pdf" +0 -0
  49. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/create_tag.sh +0 -0
  50. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/docker-compose.yml +0 -0
  51. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/env.example +0 -0
  52. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/requirements.txt +0 -0
  53. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/save_backup.sh +0 -0
  54. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/save_fixes.sh +0 -0
  55. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/__init__.py +0 -0
  56. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/admin/__init__.py +0 -0
  57. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/admin/admin_logic.py +0 -0
  58. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/admin/admin_manager.py +0 -0
  59. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/admin/admin_migration.sql +0 -0
  60. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/admin/admin_tester.py +0 -0
  61. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/admin/timeout_checker.py +0 -0
  62. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/analytics/analytics_manager.py +0 -0
  63. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/cli.py +0 -0
  64. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/config.py +0 -0
  65. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/prompts/1sales_context.txt +0 -0
  66. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/prompts/2product_info.txt +0 -0
  67. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/prompts/3objection_handling.txt +0 -0
  68. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/prompts/final_instructions.txt +0 -0
  69. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/prompts/help_message.txt +0 -0
  70. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/prompts/welcome_message.txt +0 -0
  71. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064229.txt +0 -0
  72. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064335.txt +0 -0
  73. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064638.txt +0 -0
  74. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/tests/quick_scenarios.yaml +0 -0
  75. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml +0 -0
  76. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml +0 -0
  77. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt +0 -0
  78. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/configs/growthmed-october-24/welcome_file//320/247/320/265/320/272 /320/273/320/270/321/201/321/202 /320/277/320/276 152/320/244/320/227 /320/270 323/320/244/320/227 /320/264/320/273/321/217 /320/274/320/265/320/264/320/270/321/206/320/270/320/275/321/213.pdf" +0 -0
  79. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/core/bot_utils.py +0 -0
  80. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/core/conversation_manager.py +0 -0
  81. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/core/decorators.py +0 -0
  82. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/core/router.py +0 -0
  83. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/core/router_manager.py +0 -0
  84. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/core/states.py +0 -0
  85. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/core/telegram_router.py +0 -0
  86. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/creation/__init__.py +0 -0
  87. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/creation/bot_testing.py +0 -0
  88. {smart_bot_factory-0.2.2/smart_bot_factory/supabase → smart_bot_factory-0.2.3/smart_bot_factory/database}/__init__.py +0 -0
  89. {smart_bot_factory-0.2.2/smart_bot_factory/supabase → smart_bot_factory-0.2.3/smart_bot_factory/database}/client.py +0 -0
  90. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/event/__init__.py +0 -0
  91. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/integrations/openai_client.py +0 -0
  92. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/integrations/supabase_client.py +0 -0
  93. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/router/__init__.py +0 -0
  94. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/setup_checker.py +0 -0
  95. {smart_bot_factory-0.2.2/smart_bot_factory/database → smart_bot_factory-0.2.3/smart_bot_factory/table}/database_structure.sql +0 -0
  96. {smart_bot_factory-0.2.2/smart_bot_factory/database → smart_bot_factory-0.2.3/smart_bot_factory/table}/schema.sql +0 -0
  97. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/utils/__init__.py +0 -0
  98. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/utils/debug_routing.py +0 -0
  99. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/utils/prompt_loader.py +0 -0
  100. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/smart_bot_factory/utm_link_generator.py +0 -0
  101. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/system_prompt_example.txt +0 -0
  102. {smart_bot_factory-0.2.2 → smart_bot_factory-0.2.3}/valera.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: smart-bot-factory
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Библиотека для создания умных чат-ботов
5
5
  Author-email: Kopatych <kopatych@example.com>
6
6
  License: MIT
@@ -2,7 +2,7 @@ import asyncio
2
2
  import logging
3
3
 
4
4
  from smart_bot_factory.router import EventRouter, TelegramRouter
5
- from smart_bot_factory.message import send_message_by_human, send_message_to_users_by_stage
5
+ from smart_bot_factory.message import send_message_by_human, send_message_to_users_by_stage, send_message
6
6
  from smart_bot_factory.supabase import SupabaseClient
7
7
  from smart_bot_factory.creation import BotBuilder
8
8
 
@@ -114,16 +114,7 @@ async def add_external_api_data(messages, user_id, session_id):
114
114
  # })
115
115
  return messages
116
116
 
117
- # ХУК 4: Обработка ответа AI
118
- @bot_builder.process_response
119
- async def add_promo_to_price_response(response_text, ai_metadata, user_id):
120
- """Добавляем промо-код к ответам о ценах"""
121
- if "цен" in response_text.lower() or "стоимост" in response_text.lower():
122
- response_text += "\n\n🎁 Промокод FIRST10 для скидки 10% на первый визит!"
123
-
124
- return response_text, ai_metadata
125
-
126
- # ХУК 5: Фильтр отправки
117
+ # ХУК 4: Фильтр отправки
127
118
  @bot_builder.filter_send
128
119
  async def allow_all_messages(user_id, response_text):
129
120
  """Разрешаем все сообщения (можно добавить блокировку по условию)"""
@@ -152,7 +143,15 @@ async def handle_price_question(message: Message, state: FSMContext):
152
143
  async def handle_catalog(callback: CallbackQuery, state: FSMContext):
153
144
  """Обработка кнопки Каталог"""
154
145
  await callback.answer()
155
- await callback.message.answer("📖 Вот наш каталог товаров:\n\n1. Товар 1\n2. Товар 2\n3. Товар 3")
146
+
147
+ # Пример: используем send_message с файлами
148
+ await send_message(
149
+ message=callback.message,
150
+ text="📖 Вот наш каталог товаров:\n\n1. Товар 1\n2. Товар 2\n3. Товар 3",
151
+ supabase_client=supabase_client,
152
+ files_list=[], # Можно добавить файлы: ["catalog.pdf"]
153
+ parse_mode="Markdown"
154
+ )
156
155
 
157
156
  @telegram_router_1.router.callback_query(F.data == "prices")
158
157
  async def handle_prices(callback: CallbackQuery, state: FSMContext):
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "smart-bot-factory"
3
- version = "0.2.2"
3
+ version = "0.2.3"
4
4
  description = "Библиотека для создания умных чат-ботов"
5
5
  authors = [
6
6
  {name = "Kopatych", email = "kopatych@example.com"}
@@ -449,3 +449,244 @@ async def get_users_by_stage_stats(
449
449
  "error": str(e),
450
450
  "bot_id": bot_id
451
451
  }
452
+
453
+ async def send_message(
454
+ message,
455
+ text: str,
456
+ supabase_client,
457
+ files_list: list = [],
458
+ directories_list: list = [],
459
+ parse_mode: str = "Markdown",
460
+ **kwargs
461
+ ):
462
+ """
463
+ Пользовательская функция для отправки сообщений с файлами и кнопками
464
+
465
+ Args:
466
+ message: Message объект от aiogram
467
+ text: Текст сообщения
468
+ supabase_client: SupabaseClient для работы с БД
469
+ files_list: Список файлов для отправки
470
+ directories_list: Список каталогов (отправятся все файлы)
471
+ parse_mode: Режим парсинга ('Markdown', 'HTML' или None)
472
+ **kwargs: Дополнительные параметры (reply_markup и т.д.)
473
+
474
+ Returns:
475
+ Message объект отправленного сообщения или None
476
+
477
+ Example:
478
+ from smart_bot_factory.message import send_message
479
+ from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
480
+
481
+ keyboard = InlineKeyboardMarkup(inline_keyboard=[
482
+ [InlineKeyboardButton(text="Кнопка", callback_data="action")]
483
+ ])
484
+
485
+ await send_message(
486
+ message=message,
487
+ text="Привет!",
488
+ supabase_client=supabase_client,
489
+ files_list=["file.pdf"],
490
+ parse_mode="Markdown",
491
+ reply_markup=keyboard
492
+ )
493
+ """
494
+ from pathlib import Path
495
+ from aiogram.types import FSInputFile
496
+ from aiogram.utils.media_group import MediaGroupBuilder
497
+
498
+ logger.info(f"📤 send_message вызвана:")
499
+ logger.info(f" 👤 Пользователь: {message.from_user.id}")
500
+ logger.info(f" 📝 Длина текста: {len(text)} символов")
501
+ logger.info(f" 🔧 Parse mode: {parse_mode}")
502
+
503
+ try:
504
+ user_id = message.from_user.id
505
+
506
+ # Устанавливаем parse_mode (None если передана строка 'None')
507
+ actual_parse_mode = None if parse_mode == 'None' else parse_mode
508
+
509
+ # Текст уже готов, используем как есть
510
+ final_text = text
511
+
512
+ # Работаем с переданными файлами и каталогами
513
+ logger.info(f" 📦 Передано файлов: {files_list}")
514
+ logger.info(f" 📂 Передано каталогов: {directories_list}")
515
+
516
+ # Получаем список уже отправленных файлов и каталогов
517
+ sent_files = await supabase_client.get_sent_files(user_id)
518
+ sent_directories = await supabase_client.get_sent_directories(user_id)
519
+
520
+ logger.info(f" 📋 Уже отправлено файлов: {sent_files}")
521
+ logger.info(f" 📋 Уже отправлено каталогов: {sent_directories}")
522
+
523
+ # Фильтруем файлы и каталоги, которые уже отправлялись
524
+ actual_files_list = [f for f in files_list if f not in sent_files]
525
+ actual_directories_list = [d for d in directories_list if str(d) not in sent_directories]
526
+
527
+ logger.info(f" 🆕 После фильтрации файлов: {actual_files_list}")
528
+ logger.info(f" 🆕 После фильтрации каталогов: {actual_directories_list}")
529
+
530
+ # Проверяем, что есть что отправлять
531
+ if not final_text or not final_text.strip():
532
+ logger.error(f"❌ КРИТИЧЕСКАЯ ОШИБКА: final_text пуст после обработки!")
533
+ logger.error(f" Исходный text: '{text[:200]}...'")
534
+ final_text = "Ошибка формирования ответа. Попробуйте еще раз."
535
+
536
+ logger.info(f"📱 Подготовка сообщения: {len(final_text)} символов")
537
+ logger.info(f" 📦 Файлов для обработки: {actual_files_list}")
538
+ logger.info(f" 📂 Каталогов для обработки: {actual_directories_list}")
539
+
540
+ # Проверяем наличие файлов для отправки
541
+ if actual_files_list or actual_directories_list:
542
+ # Функция определения типа медиа по расширению
543
+ def get_media_type(file_path: str) -> str:
544
+ ext = Path(file_path).suffix.lower()
545
+ if ext in {'.jpg', '.jpeg', '.png'}:
546
+ return 'photo'
547
+ elif ext in {'.mp4', '.mov'}:
548
+ return 'video'
549
+ else:
550
+ return 'document'
551
+
552
+ # Создаем списки для разных типов файлов
553
+ video_files = []
554
+ photo_files = []
555
+ document_files = []
556
+
557
+ # Функция обработки файла
558
+ def process_file(file_path: Path, source: str = ""):
559
+ if file_path.is_file():
560
+ media_type = get_media_type(str(file_path))
561
+ if media_type == 'video':
562
+ video_files.append(file_path)
563
+ logger.info(f" 🎥 Добавлено видео{f' из {source}' if source else ''}: {file_path.name}")
564
+ elif media_type == 'photo':
565
+ photo_files.append(file_path)
566
+ logger.info(f" 📸 Добавлено фото{f' из {source}' if source else ''}: {file_path.name}")
567
+ else:
568
+ document_files.append(file_path)
569
+ logger.info(f" 📄 Добавлен документ{f' из {source}' if source else ''}: {file_path.name}")
570
+ else:
571
+ logger.warning(f" ⚠️ Файл не найден: {file_path}")
572
+
573
+ # Обрабатываем прямые файлы
574
+ for file_name in actual_files_list:
575
+ try:
576
+ process_file(Path(f"files/{file_name}"))
577
+ except Exception as e:
578
+ logger.error(f" ❌ Ошибка обработки файла {file_name}: {e}")
579
+
580
+ # Обрабатываем файлы из каталогов
581
+ for dir_name in actual_directories_list:
582
+ dir_name = Path(dir_name)
583
+ try:
584
+ if dir_name.is_dir():
585
+ for file_path in dir_name.iterdir():
586
+ try:
587
+ process_file(file_path, dir_name)
588
+ except Exception as e:
589
+ logger.error(f" ❌ Ошибка обработки файла {file_path}: {e}")
590
+ else:
591
+ logger.warning(f" ⚠️ Каталог не найден: {dir_name}")
592
+ except Exception as e:
593
+ logger.error(f" ❌ Ошибка обработки каталога {dir_name}: {e}")
594
+
595
+ # Списки для отслеживания реально отправленных файлов
596
+ sent_files_to_save = []
597
+ sent_dirs_to_save = []
598
+
599
+ # 1. Отправляем видео (если есть)
600
+ if video_files:
601
+ video_group = MediaGroupBuilder()
602
+ for file_path in video_files:
603
+ video_group.add_video(media=FSInputFile(str(file_path)))
604
+
605
+ videos = video_group.build()
606
+ if videos:
607
+ await message.answer_media_group(media=videos)
608
+ logger.info(f" ✅ Отправлено {len(videos)} видео")
609
+
610
+ # 2. Отправляем фото (если есть)
611
+ if photo_files:
612
+ photo_group = MediaGroupBuilder()
613
+ for file_path in photo_files:
614
+ photo_group.add_photo(media=FSInputFile(str(file_path)))
615
+
616
+ photos = photo_group.build()
617
+ if photos:
618
+ await message.answer_media_group(media=photos)
619
+ logger.info(f" ✅ Отправлено {len(photos)} фото")
620
+
621
+ # 3. Отправляем текст
622
+ result = await message.answer(final_text, parse_mode=actual_parse_mode, **kwargs)
623
+ logger.info(f" ✅ Отправлен текст сообщения")
624
+
625
+ # 4. Отправляем документы (если есть)
626
+ if document_files:
627
+ doc_group = MediaGroupBuilder()
628
+ for file_path in document_files:
629
+ doc_group.add_document(media=FSInputFile(str(file_path)))
630
+
631
+ docs = doc_group.build()
632
+ if docs:
633
+ await message.answer_media_group(media=docs)
634
+ logger.info(f" ✅ Отправлено {len(docs)} документов")
635
+
636
+ # 5. Собираем список реально отправленных файлов и каталогов
637
+ if video_files or photo_files or document_files:
638
+ sent_files_to_save.extend(actual_files_list)
639
+ logger.info(f" 📝 Добавляем в список для сохранения файлы: {actual_files_list}")
640
+ sent_dirs_to_save.extend([str(d) for d in actual_directories_list])
641
+ logger.info(f" 📝 Добавляем в список для сохранения каталоги: {actual_directories_list}")
642
+
643
+ # 6. Обновляем информацию в БД
644
+ if sent_files_to_save or sent_dirs_to_save:
645
+ try:
646
+ if sent_files_to_save:
647
+ logger.info(f" 💾 Сохраняем файлы в БД: {sent_files_to_save}")
648
+ await supabase_client.add_sent_files(user_id, sent_files_to_save)
649
+ if sent_dirs_to_save:
650
+ logger.info(f" 💾 Сохраняем каталоги в БД: {sent_dirs_to_save}")
651
+ await supabase_client.add_sent_directories(user_id, sent_dirs_to_save)
652
+ logger.info(f" ✅ Обновлена информация о отправленных файлах в БД")
653
+ except Exception as e:
654
+ logger.error(f" ❌ Ошибка обновления информации о файлах в БД: {e}")
655
+ else:
656
+ logger.info(f" ℹ️ Нет новых файлов для сохранения в БД")
657
+
658
+ return result
659
+ else:
660
+ # Если нет файлов, отправляем просто текст
661
+ logger.info(" ⚠️ Нет файлов для отправки, отправляем как текст")
662
+ result = await message.answer(final_text, parse_mode=actual_parse_mode, **kwargs)
663
+ return result
664
+
665
+ except Exception as e:
666
+ # Проверяем, является ли ошибка блокировкой бота
667
+ if "Forbidden: bot was blocked by the user" in str(e):
668
+ logger.warning(f"🚫 Бот заблокирован пользователем {user_id}")
669
+ return None
670
+ elif "TelegramForbiddenError" in str(type(e).__name__):
671
+ logger.warning(f"🚫 Бот заблокирован пользователем {user_id}")
672
+ return None
673
+
674
+ logger.error(f"❌ ОШИБКА в send_message: {e}")
675
+ logger.exception("Полный стек ошибки send_message:")
676
+
677
+ # Пытаемся отправить простое сообщение без форматирования
678
+ try:
679
+ fallback_text = "Произошла ошибка при отправке ответа. Попробуйте еще раз."
680
+ result = await message.answer(fallback_text)
681
+ logger.info(f"✅ Запасное сообщение отправлено")
682
+ return result
683
+ except Exception as e2:
684
+ if "Forbidden: bot was blocked by the user" in str(e2):
685
+ logger.warning(f"🚫 Бот заблокирован пользователем {user_id} (fallback)")
686
+ return None
687
+ elif "TelegramForbiddenError" in str(type(e2).__name__):
688
+ logger.warning(f"🚫 Бот заблокирован пользователем {user_id} (fallback)")
689
+ return None
690
+
691
+ logger.error(f"❌ Даже запасное сообщение не отправилось: {e2}")
692
+ raise
@@ -52,7 +52,6 @@ class BotBuilder:
52
52
  self._message_validators: List = [] # Валидация ДО обработки
53
53
  self._prompt_enrichers: List = [] # Обогащение системного промпта
54
54
  self._context_enrichers: List = [] # Обогащение контекста для AI
55
- self._response_processors: List = [] # Обработка ответа AI
56
55
  self._send_filters: List = [] # Фильтры перед отправкой пользователю
57
56
 
58
57
  # Флаги инициализации
@@ -438,28 +437,6 @@ class BotBuilder:
438
437
  logger.info(f"✅ Зарегистрирован обогатитель контекста: {handler.__name__}")
439
438
  return handler
440
439
 
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
440
  def filter_send(self, handler):
464
441
  """
465
442
  Регистрирует фильтр отправки (может блокировать отправку пользователю)
@@ -489,7 +466,6 @@ class BotBuilder:
489
466
  'validators': self._message_validators.copy(),
490
467
  'prompt_enrichers': self._prompt_enrichers.copy(),
491
468
  'context_enrichers': self._context_enrichers.copy(),
492
- 'response_processors': self._response_processors.copy(),
493
469
  'send_filters': self._send_filters.copy()
494
470
  }
495
471
 
@@ -539,9 +539,7 @@ async def process_user_message(message: Message, state: FSMContext, session_id:
539
539
  try:
540
540
  system_prompt_with_time = await enricher(
541
541
  system_prompt_with_time,
542
- message.from_user.id,
543
- session_id,
544
- supabase_client
542
+ message.from_user.id
545
543
  )
546
544
  logger.info(f"✅ Промпт обогащен '{enricher.__name__}'")
547
545
  except Exception as e:
@@ -567,9 +565,7 @@ async def process_user_message(message: Message, state: FSMContext, session_id:
567
565
  for enricher in context_enrichers:
568
566
  try:
569
567
  messages = await enricher(
570
- messages,
571
- message.from_user.id,
572
- session_id
568
+ messages
573
569
  )
574
570
  logger.info(f"✅ Контекст обогащен '{enricher.__name__}'")
575
571
  except Exception as e:
@@ -628,19 +624,6 @@ async def process_user_message(message: Message, state: FSMContext, session_id:
628
624
 
629
625
  logger.info(f"✅ Финальный текст для отправки: {len(response_text)} символов")
630
626
 
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
627
  # Обновляем этап сессии и качество лида
645
628
  if ai_metadata:
646
629
  logger.info("🔍 Анализ метаданных от ИИ:")
@@ -749,7 +732,7 @@ async def process_user_message(message: Message, state: FSMContext, session_id:
749
732
 
750
733
  logger.info(f"📱 Отправляем пользователю: {len(final_response)} символов")
751
734
 
752
- # ============ ХУК 5: ФИЛЬТРЫ ОТПРАВКИ ============
735
+ # ============ ХУК 4: ФИЛЬТРЫ ОТПРАВКИ ============
753
736
  send_filters = message_hooks.get('send_filters', [])
754
737
  for filter_func in send_filters:
755
738
  try:
@@ -7,10 +7,12 @@ from ..core.message_sender import (
7
7
  send_message_by_human,
8
8
  send_message_by_ai,
9
9
  send_message_to_users_by_stage,
10
+ send_message,
10
11
  )
11
12
 
12
13
  __all__ = [
13
14
  'send_message_by_human',
14
15
  'send_message_by_ai',
15
16
  'send_message_to_users_by_stage',
17
+ 'send_message', # Чистая отправка с файлами и кнопками
16
18
  ]
@@ -1259,7 +1259,7 @@ wheels = [
1259
1259
 
1260
1260
  [[package]]
1261
1261
  name = "smart-bot-factory"
1262
- version = "0.2.1"
1262
+ version = "0.2.2"
1263
1263
  source = { editable = "." }
1264
1264
  dependencies = [
1265
1265
  { name = "aiofiles" },