smart-bot-factory 0.3.4__tar.gz → 0.3.5__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 (89) hide show
  1. smart_bot_factory-0.3.5/.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 +20 -0
  2. smart_bot_factory-0.3.5/.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 +26 -0
  3. smart_bot_factory-0.3.5/.github/workflows/ci.yml +59 -0
  4. smart_bot_factory-0.3.5/.github/workflows/publish-private.yml +34 -0
  5. smart_bot_factory-0.3.5/.github/workflows/publish.yml +31 -0
  6. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/.gitignore +0 -5
  7. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/PKG-INFO +1 -3
  8. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/best-valera.py +5 -7
  9. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/best-valera/prompts/final_instructions.txt +1 -1
  10. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/pyproject.toml +1 -3
  11. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/core/bot_utils.py +34 -9
  12. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/core/decorators.py +28 -16
  13. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/core/message_sender.py +11 -2
  14. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/core/router.py +59 -8
  15. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/handlers/handlers.py +17 -4
  16. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/utils/prompt_loader.py +12 -8
  17. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/uv.lock +1 -38
  18. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/.python-version +0 -0
  19. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/LICENSE +0 -0
  20. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/README.md +0 -0
  21. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/best-valera/prompts/1sales_context.txt +0 -0
  22. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/best-valera/prompts/2product_info.txt +0 -0
  23. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/best-valera/prompts/3objection_handling.txt +0 -0
  24. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/best-valera/prompts/help_message.txt +0 -0
  25. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/best-valera/prompts/welcome_message.txt +0 -0
  26. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/best-valera/tests/quick_scenarios.yaml +0 -0
  27. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/best-valera/tests/realistic_scenarios.yaml +0 -0
  28. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/best-valera/tests/scenario_examples.yaml +0 -0
  29. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/best-valera/welcome_files/welcome_file_msg.txt +0 -0
  30. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/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
  31. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/valera/prompts/2product_info.txt +0 -0
  32. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/valera/prompts/3objection_handling.txt +0 -0
  33. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/valera/prompts/final_instructions.txt +0 -0
  34. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/valera/prompts/help_message.txt +0 -0
  35. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/valera/prompts/welcome_message.txt +0 -0
  36. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/valera/tests/quick_scenarios.yaml +0 -0
  37. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/valera/tests/realistic_scenarios.yaml +0 -0
  38. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/valera/tests/scenario_examples.yaml +0 -0
  39. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/bots/valera/welcome_files/welcome_file_msg.txt +0 -0
  40. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/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
  41. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/publish.py +0 -0
  42. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/__init__.py +0 -0
  43. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/admin/__init__.py +0 -0
  44. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/admin/admin_events.py +0 -0
  45. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/admin/admin_logic.py +0 -0
  46. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/admin/admin_manager.py +0 -0
  47. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/admin/admin_migration.sql +0 -0
  48. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/admin/admin_tester.py +0 -0
  49. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/admin/timeout_checker.py +0 -0
  50. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/aiogram_calendar/__init__.py +0 -0
  51. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/aiogram_calendar/common.py +0 -0
  52. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/aiogram_calendar/dialog_calendar.py +0 -0
  53. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/aiogram_calendar/schemas.py +0 -0
  54. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/aiogram_calendar/simple_calendar.py +0 -0
  55. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/analytics/analytics_manager.py +0 -0
  56. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/cli.py +0 -0
  57. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/config.py +0 -0
  58. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/configs/growthmed-october-24/prompts/1sales_context.txt +0 -0
  59. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/configs/growthmed-october-24/prompts/2product_info.txt +0 -0
  60. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/configs/growthmed-october-24/prompts/3objection_handling.txt +0 -0
  61. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/configs/growthmed-october-24/prompts/final_instructions.txt +0 -0
  62. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/configs/growthmed-october-24/prompts/help_message.txt +0 -0
  63. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/configs/growthmed-october-24/prompts/welcome_message.txt +0 -0
  64. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064229.txt +0 -0
  65. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064335.txt +0 -0
  66. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064638.txt +0 -0
  67. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/configs/growthmed-october-24/tests/quick_scenarios.yaml +0 -0
  68. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml +0 -0
  69. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml +0 -0
  70. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt +0 -0
  71. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/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
  72. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/core/conversation_manager.py +0 -0
  73. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/core/router_manager.py +0 -0
  74. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/core/states.py +0 -0
  75. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/creation/__init__.py +0 -0
  76. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/creation/bot_builder.py +0 -0
  77. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/creation/bot_testing.py +0 -0
  78. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/event/__init__.py +0 -0
  79. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/integrations/openai_client.py +0 -0
  80. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/integrations/supabase_client.py +0 -0
  81. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/message/__init__.py +0 -0
  82. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/router/__init__.py +0 -0
  83. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/setup_checker.py +0 -0
  84. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/supabase/__init__.py +0 -0
  85. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/supabase/client.py +0 -0
  86. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/utils/__init__.py +0 -0
  87. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/utils/debug_routing.py +0 -0
  88. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/utils/user_prompt_loader.py +0 -0
  89. {smart_bot_factory-0.3.4 → smart_bot_factory-0.3.5}/smart_bot_factory/utm_link_generator.py +0 -0
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: "✨ Запрос функции"
3
+ about: Предложить новую функцию или улучшение
4
+ title: ''
5
+ labels: enhancement
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ## 🎯 Описание функции
11
+ Что вы хотите добавить/улучшить?
12
+
13
+ ## 💡 Мотивация
14
+ Какую проблему это решит?
15
+
16
+ ## 📋 Детали реализации
17
+ Как вы видите это решение?
18
+
19
+ ## 🔄 Альтернативы
20
+ Рассматривали ли другие варианты?
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: "\U0001F41B Баг репорт"
3
+ about: Сообщить об ошибке
4
+ title: ''
5
+ labels: bug
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ## 🐛 Описание проблемы
11
+ Опишите что пошло не так
12
+
13
+ ## 🔄 Как воспроизвести
14
+ 1. Пользователь написал...
15
+ 2. Бот ответил...
16
+ 3. Произошла ошибка...
17
+
18
+ ## ✅ Ожидаемое поведение
19
+ Что должно было произойти
20
+
21
+ ## 📱 Окружение
22
+ - **Bot ID:** (например, growthmed-october-24)
23
+ - **Время:** (когда произошло)
24
+ - **User ID:** (если известен)
25
+
26
+ ## 📋 Логи
@@ -0,0 +1,59 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.12", "3.13"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install dependencies
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install -e .
28
+
29
+ - name: Run tests
30
+ run: |
31
+ pytest --cov=smart_bot_factory tests/
32
+
33
+ - name: Upload coverage to Codecov
34
+ uses: codecov/codecov-action@v4
35
+ with:
36
+ token: ${{ secrets.CODECOV_TOKEN }}
37
+ fail_ci_if_error: true
38
+
39
+ lint:
40
+ runs-on: ubuntu-latest
41
+
42
+ steps:
43
+ - uses: actions/checkout@v4
44
+
45
+ - name: Set up Python
46
+ uses: actions/setup-python@v5
47
+ with:
48
+ python-version: "3.12"
49
+
50
+ - name: Install dependencies
51
+ run: |
52
+ python -m pip install --upgrade pip
53
+ pip install flake8 black isort
54
+
55
+ - name: Check code style
56
+ run: |
57
+ flake8 src/smart_bot_factory tests
58
+ black --check src/smart_bot_factory tests
59
+ isort --check-only src/smart_bot_factory tests
@@ -0,0 +1,34 @@
1
+ name: Publish to GitHub Packages
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ contents: read
12
+ packages: write
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v4
19
+ with:
20
+ python-version: "3.9"
21
+
22
+ - name: Install build tools
23
+ run: |
24
+ python -m pip install --upgrade pip
25
+ pip install build twine
26
+
27
+ - name: Build package
28
+ run: python -m build
29
+
30
+ - name: Publish to GitHub Packages
31
+ env:
32
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33
+ run: |
34
+ python -m twine upload --repository-url https://maven.pkg.github.com/${{ github.repository }} dist/*
@@ -0,0 +1,31 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+
13
+ - name: Set up Python
14
+ uses: actions/setup-python@v4
15
+ with:
16
+ python-version: "3.9"
17
+
18
+ - name: Install build tools
19
+ run: |
20
+ python -m pip install --upgrade pip
21
+ pip install build twine
22
+
23
+ - name: Build package
24
+ run: python -m build
25
+
26
+ - name: Publish to PyPI
27
+ env:
28
+ TWINE_USERNAME: __token__
29
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
30
+ run: |
31
+ twine upload dist/*
@@ -213,8 +213,3 @@ cython_debug/
213
213
  marimo/_static/
214
214
  marimo/_lsp/
215
215
  __marimo__/
216
-
217
- # Temporary event files
218
- temp_event_files/
219
- temp_with_msg/
220
- temp_after_msg/
@@ -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
@@ -69,7 +69,7 @@ async def handle_phone_collection(user_id: int, phone_number: str):
69
69
 
70
70
  return {"status": "success", "phone": phone_number}
71
71
 
72
- @event_router.event_handler("collect_name", once_only=True)
72
+ @event_router.event_handler("collect_name", send_ai_response=False, once_only=False)
73
73
  async def handle_name_collection(user_id: int, client_name: str):
74
74
  """
75
75
  Обрабатывает получение имени клиента
@@ -78,13 +78,11 @@ async def handle_name_collection(user_id: int, client_name: str):
78
78
  """
79
79
  logger.info(f"👤 Получено имя от пользователя {user_id}: {client_name}")
80
80
 
81
- session = await supabase_client.get_active_session(user_id)
82
- if session:
83
- metadata = session.get('metadata', {})
84
- metadata['confirmed_name'] = client_name
85
- await supabase_client.update_session_metadata(session['id'], metadata)
81
+ await send_message_by_human(
82
+ user_id=user_id,
83
+ message_text=f"✅ Спасибо! Ваш номер {client_name} сохранен"
84
+ )
86
85
 
87
- return {"status": "success", "name": client_name}
88
86
 
89
87
  # =============================================================================
90
88
  # ЗАПЛАНИРОВАННЫЕ ЗАДАЧИ (выполняются через время)
@@ -127,7 +127,7 @@ id этапа бери из stages
127
127
  на данном этапе выясни его имя, а также кем он работает и профиль его компании. после этого переходи к этапу consult.
128
128
  если он уже в первом сообщении дал эту информацию - не переспрашивай.
129
129
 
130
- Когда пользователь вписал свое имя впиши событие - тип=send_reminder, инфо=afdf, тип=mass_notification, инфо=ghbdtn, тип=имя инфо=Имя пользователя, тип=booking_reminder
130
+ Когда пользователь вписал свое имя - впиши в служебную информацию событие тип=collect_name, инфо=имя пользователя
131
131
  </stage>
132
132
 
133
133
  <stage id="consult">
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "smart-bot-factory"
3
- version = "0.3.4"
3
+ version = "0.3.5"
4
4
  description = "Библиотека для создания умных чат-ботов"
5
5
  authors = [
6
6
  {name = "Kopatych", email = "eserov73@gmail.com"}
@@ -33,8 +33,6 @@ dependencies = [
33
33
  # CLI интерфейс
34
34
  "click>=8.0.0",
35
35
  "project-root-finder>=1.9",
36
- "python-dateutil>=2.9.0.post0",
37
- "aiogram-media-group>=0.5.1",
38
36
  ]
39
37
  requires-python = ">=3.9"
40
38
  readme = "README.md"
@@ -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
 
@@ -33,18 +33,6 @@ wheels = [
33
33
  { url = "https://files.pythonhosted.org/packages/ba/e5/9f9fae7b50ed502e33121dd62a7e9b076d00630eaafe1dd7fda64f7e8625/aiogram-3.22.0-py3-none-any.whl", hash = "sha256:1c6eceb078ff62cf0556a5466cf3e7e8119678c26cc56803b7ac5f73633934a8", size = 698216, upload-time = "2025-08-17T16:20:43.354Z" },
34
34
  ]
35
35
 
36
- [[package]]
37
- name = "aiogram-media-group"
38
- version = "0.5.1"
39
- source = { registry = "https://pypi.org/simple" }
40
- dependencies = [
41
- { name = "aiogram" },
42
- ]
43
- sdist = { url = "https://files.pythonhosted.org/packages/47/6e/f10d3e4980b5b81b963ed2cbcbf666b1c02bfa723cfb9a37f43c5f0ffbdd/aiogram-media-group-0.5.1.tar.gz", hash = "sha256:a5ab6c1dea4a0729c25fdb90b2310537d71d8d3554765d302b37156edaa51d33", size = 5533, upload-time = "2022-08-10T20:46:46.699Z" }
44
- wheels = [
45
- { url = "https://files.pythonhosted.org/packages/da/22/5124e475fac271b5437e634ea967130e4ac98c18c9a0f00a6b7ca9ebcbf3/aiogram_media_group-0.5.1-py3-none-any.whl", hash = "sha256:31fd150769075e23e6e161b444d553b8b62a33cd7d85ef770393572470a5ca38", size = 7536, upload-time = "2022-08-10T20:46:48.669Z" },
46
- ]
47
-
48
36
  [[package]]
49
37
  name = "aiohappyeyeballs"
50
38
  version = "2.6.1"
@@ -1436,18 +1424,6 @@ crypto = [
1436
1424
  { name = "cryptography" },
1437
1425
  ]
1438
1426
 
1439
- [[package]]
1440
- name = "python-dateutil"
1441
- version = "2.9.0.post0"
1442
- source = { registry = "https://pypi.org/simple" }
1443
- dependencies = [
1444
- { name = "six" },
1445
- ]
1446
- sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
1447
- wheels = [
1448
- { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
1449
- ]
1450
-
1451
1427
  [[package]]
1452
1428
  name = "python-dotenv"
1453
1429
  version = "1.1.1"
@@ -1659,28 +1635,17 @@ wheels = [
1659
1635
  { url = "https://files.pythonhosted.org/packages/91/ff/2e2eed29e02c14a5cb6c57f09b2d5b40e65d6cc71f45b52e0be295ccbc2f/secretstorage-3.4.0-py3-none-any.whl", hash = "sha256:0e3b6265c2c63509fb7415717607e4b2c9ab767b7f344a57473b779ca13bd02e", size = 15272, upload-time = "2025-09-09T16:42:12.744Z" },
1660
1636
  ]
1661
1637
 
1662
- [[package]]
1663
- name = "six"
1664
- version = "1.17.0"
1665
- source = { registry = "https://pypi.org/simple" }
1666
- sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
1667
- wheels = [
1668
- { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
1669
- ]
1670
-
1671
1638
  [[package]]
1672
1639
  name = "smart-bot-factory"
1673
- version = "0.3.3"
1640
+ version = "0.3.4"
1674
1641
  source = { editable = "." }
1675
1642
  dependencies = [
1676
1643
  { name = "aiofiles" },
1677
1644
  { name = "aiogram" },
1678
- { name = "aiogram-media-group" },
1679
1645
  { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
1680
1646
  { name = "click", version = "8.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
1681
1647
  { name = "openai" },
1682
1648
  { name = "project-root-finder" },
1683
- { name = "python-dateutil" },
1684
1649
  { name = "python-dotenv" },
1685
1650
  { name = "pytz" },
1686
1651
  { name = "pyyaml" },
@@ -1696,11 +1661,9 @@ dev = [
1696
1661
  requires-dist = [
1697
1662
  { name = "aiofiles", specifier = ">=23.0.0" },
1698
1663
  { name = "aiogram", specifier = ">=3.4.1" },
1699
- { name = "aiogram-media-group", specifier = ">=0.5.1" },
1700
1664
  { name = "click", specifier = ">=8.0.0" },
1701
1665
  { name = "openai", specifier = ">=1.12.0" },
1702
1666
  { name = "project-root-finder", specifier = ">=1.9" },
1703
- { name = "python-dateutil", specifier = ">=2.9.0.post0" },
1704
1667
  { name = "python-dotenv", specifier = ">=1.0.1" },
1705
1668
  { name = "pytz", specifier = ">=2023.3" },
1706
1669
  { name = "pyyaml", specifier = ">=6.0.2" },