smart-bot-factory 0.1.5__tar.gz → 0.1.7__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 (96) hide show
  1. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/CLIENTS_USAGE.md +1 -0
  2. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/PKG-INFO +1 -1
  3. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/ROUTER_ARCHITECTURE.md +1 -0
  4. smart_bot_factory-0.1.5/valera.py → smart_bot_factory-0.1.7/best-valera.py +9 -7
  5. {smart_bot_factory-0.1.5/smart_bot_factory/configs/growthmed-october-24 → smart_bot_factory-0.1.7/bots/best-valera}/prompts/final_instructions.txt +4 -0
  6. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/example_usage.py +69 -69
  7. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/pyproject.toml +1 -1
  8. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/cli.py +28 -33
  9. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/core/bot_utils.py +28 -55
  10. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/core/decorators.py +25 -41
  11. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/core/message_sender.py +0 -5
  12. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/core/router.py +2 -1
  13. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/core/router_manager.py +1 -0
  14. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/creation/bot_builder.py +9 -27
  15. smart_bot_factory-0.1.7/smart_bot_factory/event/__init__.py +12 -0
  16. smart_bot_factory-0.1.7/smart_bot_factory/message/__init__.py +12 -0
  17. smart_bot_factory-0.1.7/smart_bot_factory/router/__init__.py +9 -0
  18. smart_bot_factory-0.1.7/smart_bot_factory/supabase/__init__.py +7 -0
  19. smart_bot_factory-0.1.7/smart_bot_factory/supabase/client.py +524 -0
  20. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/uv.lock +1 -1
  21. smart_bot_factory-0.1.5/smart_bot_factory/analytics/__init__.py +0 -7
  22. smart_bot_factory-0.1.5/smart_bot_factory/clients/__init__.py +0 -33
  23. smart_bot_factory-0.1.5/smart_bot_factory/core/__init__.py +0 -43
  24. smart_bot_factory-0.1.5/smart_bot_factory/core/globals.py +0 -68
  25. smart_bot_factory-0.1.5/smart_bot_factory/integrations/__init__.py +0 -10
  26. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/.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
  27. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/.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
  28. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/.github/workflows/ci.yml +0 -0
  29. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/.github/workflows/publish-private.yml +0 -0
  30. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/.github/workflows/publish.yml +0 -0
  31. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/.gitignore +0 -0
  32. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/.python-version +0 -0
  33. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/Dockerfile +0 -0
  34. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/LICENSE +0 -0
  35. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/README.md +0 -0
  36. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/bot_testing.py +0 -0
  37. {smart_bot_factory-0.1.5/bots/valera → smart_bot_factory-0.1.7/bots/best-valera}/prompts/1sales_context.txt +0 -0
  38. {smart_bot_factory-0.1.5/bots/valera → smart_bot_factory-0.1.7/bots/best-valera}/prompts/2product_info.txt +0 -0
  39. {smart_bot_factory-0.1.5/bots/valera → smart_bot_factory-0.1.7/bots/best-valera}/prompts/3objection_handling.txt +0 -0
  40. {smart_bot_factory-0.1.5/bots/valera → smart_bot_factory-0.1.7/bots/best-valera}/prompts/help_message.txt +0 -0
  41. {smart_bot_factory-0.1.5/bots/valera → smart_bot_factory-0.1.7/bots/best-valera}/prompts/welcome_message.txt +0 -0
  42. {smart_bot_factory-0.1.5/bots/valera → smart_bot_factory-0.1.7/bots/best-valera}/tests/quick_scenarios.yaml +0 -0
  43. {smart_bot_factory-0.1.5/bots/valera → smart_bot_factory-0.1.7/bots/best-valera}/tests/realistic_scenarios.yaml +0 -0
  44. {smart_bot_factory-0.1.5/bots/valera → smart_bot_factory-0.1.7/bots/best-valera}/tests/scenario_examples.yaml +0 -0
  45. {smart_bot_factory-0.1.5/bots/valera → smart_bot_factory-0.1.7/bots/best-valera}/welcome_files/welcome_file_msg.txt +0 -0
  46. {smart_bot_factory-0.1.5/bots/valera → smart_bot_factory-0.1.7/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
  47. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/configs/valera/cats//320/224/320/276/320/263/320/276/320/262/320/276/321/200.pdf" +0 -0
  48. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/configs/valera/cats//320/272/320/276/320/275/320/270.jpg" +0 -0
  49. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/configs/valera/cats//320/272/320/276/321/202.jpg" +0 -0
  50. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/configs/valera/cats//320/273/320/265/321/201.jpg" +0 -0
  51. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/configs/valera/tests/fixes_elina.yaml +0 -0
  52. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/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
  53. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/create_tag.sh +0 -0
  54. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/docker-compose.yml +0 -0
  55. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/env.example +0 -0
  56. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/requirements.txt +0 -0
  57. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/save_backup.sh +0 -0
  58. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/save_fixes.sh +0 -0
  59. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/__init__.py +0 -0
  60. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/admin/__init__.py +0 -0
  61. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/admin/admin_logic.py +0 -0
  62. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/admin/admin_manager.py +0 -0
  63. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/admin/admin_migration.sql +0 -0
  64. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/admin/admin_tester.py +0 -0
  65. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/admin/timeout_checker.py +0 -0
  66. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/analytics/analytics_manager.py +0 -0
  67. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/config.py +0 -0
  68. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/configs/growthmed-october-24/prompts/1sales_context.txt +0 -0
  69. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/configs/growthmed-october-24/prompts/2product_info.txt +0 -0
  70. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/configs/growthmed-october-24/prompts/3objection_handling.txt +0 -0
  71. {smart_bot_factory-0.1.5/bots/valera → smart_bot_factory-0.1.7/smart_bot_factory/configs/growthmed-october-24}/prompts/final_instructions.txt +0 -0
  72. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/configs/growthmed-october-24/prompts/help_message.txt +0 -0
  73. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/configs/growthmed-october-24/prompts/welcome_message.txt +0 -0
  74. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064229.txt +0 -0
  75. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064335.txt +0 -0
  76. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064638.txt +0 -0
  77. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/configs/growthmed-october-24/tests/quick_scenarios.yaml +0 -0
  78. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml +0 -0
  79. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml +0 -0
  80. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt +0 -0
  81. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/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
  82. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/core/conversation_manager.py +0 -0
  83. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/core/states.py +0 -0
  84. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/creation/__init__.py +0 -0
  85. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/creation/bot_testing.py +0 -0
  86. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/database/database_structure.sql +0 -0
  87. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/database/schema.sql +0 -0
  88. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/handlers/handlers.py +0 -0
  89. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/integrations/openai_client.py +0 -0
  90. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/integrations/supabase_client.py +0 -0
  91. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/setup_checker.py +0 -0
  92. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/utils/__init__.py +0 -0
  93. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/utils/debug_routing.py +0 -0
  94. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/utils/prompt_loader.py +0 -0
  95. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/smart_bot_factory/utm_link_generator.py +0 -0
  96. {smart_bot_factory-0.1.5 → smart_bot_factory-0.1.7}/system_prompt_example.txt +0 -0
@@ -71,3 +71,4 @@ async def send_mass_notification(message_text: str):
71
71
  - Клиенты доступны только после запуска бота через `BotBuilder`
72
72
  - Все клиенты типизированы для полной поддержки автозаполнения
73
73
  - Остальные глобальные переменные (config, admin_manager и т.д.) недоступны пользователю
74
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: smart-bot-factory
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: Библиотека для создания умных чат-ботов
5
5
  Author-email: Kopatych <herousplay@gmail.com>
6
6
  License: MIT
@@ -149,3 +149,4 @@ for router_stat in stats['routers']:
149
149
  4. **Зарегистрируйте** роутеры в `BotBuilder`
150
150
 
151
151
  Теперь ваш код организован, модулен и легко расширяем! 🎉
152
+
@@ -1,23 +1,25 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- Бот valera - создан с помощью Smart Bot Factory
3
+ Бот best-valera - создан с помощью Smart Bot Factory
4
4
  """
5
5
 
6
6
  import asyncio
7
7
 
8
- from smart_bot_factory.core import Router
9
- from smart_bot_factory.core import send_message_by_human
10
- from smart_bot_factory.clients import supabase_client
8
+ from smart_bot_factory.router import Router
9
+ from smart_bot_factory.message import send_message_by_human, send_message_to_users_by_stage
10
+ from smart_bot_factory.supabase import SupabaseClient
11
11
  from smart_bot_factory.creation import BotBuilder
12
12
 
13
13
  # Создаем роутер для всех обработчиков
14
- router = Router("valera_handlers")
14
+ router = Router("best-valera_handlers")
15
+
16
+ supabase_client = SupabaseClient("best-valera")
15
17
 
16
18
  # =============================================================================
17
19
  # ОБРАБОТЧИКИ СОБЫТИЙ
18
20
  # =============================================================================
19
21
 
20
- @router.event_handler("example_event")
22
+ @router.event_handler("имя")
21
23
  async def handle_example_event(user_id: int, event_data: str):
22
24
  """Пример обработчика события"""
23
25
  # Отправляем подтверждение пользователю
@@ -116,7 +118,7 @@ async def main():
116
118
  """Основная функция запуска бота"""
117
119
  try:
118
120
  # Создаем и собираем бота
119
- bot_builder = BotBuilder("valera")
121
+ bot_builder = BotBuilder("best-valera")
120
122
 
121
123
  # Регистрируем роутер ПЕРЕД сборкой, чтобы обработчики были доступны
122
124
  bot_builder.register_router(router)
@@ -128,6 +128,8 @@ id этапа бери из stages
128
128
  <stage id="introduction">
129
129
  на данном этапе выясни его имя, а также кем он работает и профиль его компании. после этого переходи к этапу consult.
130
130
  если он уже в первом сообщении дал эту информацию - не переспрашивай.
131
+
132
+ Когда узнаешь имя пользователя, добавь событие тип='имя', в инфо добавь имя пользователя
131
133
  </stage>
132
134
 
133
135
  <stage id="consult">
@@ -135,6 +137,8 @@ id этапа бери из stages
135
137
  если видишь, что особо вопросов больше не осталось переходи на этап offer.
136
138
  будь краток, пиши по делу и только ключевую информацию. избегай длинных сообщений.
137
139
  НЕ надо в первом же сообщении вываливать инфомацию из FAQ, пиши покороче.d
140
+
141
+ Если пользователь просит перевсти на консультацию добавь событие - тип=mass_notification запусти через минуту
138
142
  </stage>
139
143
 
140
144
  <stage id="offer">
@@ -1,69 +1,69 @@
1
- #!/usr/bin/env python3
2
- """
3
- Пример использования простого импорта компонентов бота
4
- """
5
-
6
- from smart_bot_factory.core import global_handler
7
- from smart_bot_factory.clients import supabase_client, openai_client
8
-
9
- # Теперь можно просто использовать импортированные переменные
10
- async def example_function():
11
- """Пример функции с использованием импортированных компонентов"""
12
-
13
- # Проверяем доступность
14
- if not supabase_client:
15
- print("Supabase клиент не доступен")
16
- return
17
-
18
- if not openai_client:
19
- print("OpenAI клиент не доступен")
20
- return
21
-
22
- # Используем напрямую - теперь автозаполнение работает!
23
- try:
24
- # Получаем пользователей из БД с учетом bot_id
25
- # IDE теперь знает тип supabase_client и предлагает методы
26
- users = supabase_client.client.table('sales_users').select('*').eq('bot_id', supabase_client.bot_id).execute()
27
- print(f"Найдено пользователей: {len(users.data)}")
28
-
29
- # Пример использования OpenAI клиента
30
- # IDE знает тип openai_client и предлагает методы
31
- print("OpenAI клиент доступен для использования")
32
-
33
- except Exception as e:
34
- print(f"Ошибка: {e}")
35
-
36
- # Пример глобального обработчика с простым импортом
37
- @global_handler("example_notification")
38
- async def send_example_notification(message_text: str):
39
- """Пример глобального обработчика с простым импортом"""
40
-
41
- # Просто используем импортированный supabase_client
42
- if not supabase_client:
43
- return {"status": "error", "message": "Supabase клиент не найден"}
44
-
45
- try:
46
- # Получаем всех пользователей с учетом bot_id
47
- users_response = supabase_client.client.table('sales_users').select('telegram_id').eq('bot_id', supabase_client.bot_id).execute()
48
-
49
- if not users_response.data:
50
- return {"status": "no_users", "message": "Пользователи не найдены"}
51
-
52
- # Отправляем сообщения (пример)
53
- sent_count = 0
54
- for user in users_response.data:
55
- # Здесь была бы отправка сообщения
56
- sent_count += 1
57
-
58
- return {
59
- "status": "completed",
60
- "sent_count": sent_count,
61
- "message": f"Отправлено {sent_count} сообщений"
62
- }
63
-
64
- except Exception as e:
65
- return {"status": "error", "message": str(e)}
66
-
67
- if __name__ == "__main__":
68
- import asyncio
69
- asyncio.run(example_function())
1
+ #!/usr/bin/env python3
2
+ """
3
+ Пример использования простого импорта компонентов бота
4
+ """
5
+
6
+ from smart_bot_factory.core import global_handler
7
+ from smart_bot_factory.clients import supabase_client, openai_client
8
+
9
+ # Теперь можно просто использовать импортированные переменные
10
+ async def example_function():
11
+ """Пример функции с использованием импортированных компонентов"""
12
+
13
+ # Проверяем доступность
14
+ if not supabase_client:
15
+ print("Supabase клиент не доступен")
16
+ return
17
+
18
+ if not openai_client:
19
+ print("OpenAI клиент не доступен")
20
+ return
21
+
22
+ # Используем напрямую - теперь автозаполнение работает!
23
+ try:
24
+ # Получаем пользователей из БД с учетом bot_id
25
+ # IDE теперь знает тип supabase_client и предлагает методы
26
+ users = supabase_client.client.table('sales_users').select('*').eq('bot_id', supabase_client.bot_id).execute()
27
+ print(f"Найдено пользователей: {len(users.data)}")
28
+
29
+ # Пример использования OpenAI клиента
30
+ # IDE знает тип openai_client и предлагает методы
31
+ print("OpenAI клиент доступен для использования")
32
+
33
+ except Exception as e:
34
+ print(f"Ошибка: {e}")
35
+
36
+ # Пример глобального обработчика с простым импортом
37
+ @global_handler("example_notification")
38
+ async def send_example_notification(message_text: str):
39
+ """Пример глобального обработчика с простым импортом"""
40
+
41
+ # Просто используем импортированный supabase_client
42
+ if not supabase_client:
43
+ return {"status": "error", "message": "Supabase клиент не найден"}
44
+
45
+ try:
46
+ # Получаем всех пользователей с учетом bot_id
47
+ users_response = supabase_client.client.table('sales_users').select('telegram_id').eq('bot_id', supabase_client.bot_id).execute()
48
+
49
+ if not users_response.data:
50
+ return {"status": "no_users", "message": "Пользователи не найдены"}
51
+
52
+ # Отправляем сообщения (пример)
53
+ sent_count = 0
54
+ for user in users_response.data:
55
+ # Здесь была бы отправка сообщения
56
+ sent_count += 1
57
+
58
+ return {
59
+ "status": "completed",
60
+ "sent_count": sent_count,
61
+ "message": f"Отправлено {sent_count} сообщений"
62
+ }
63
+
64
+ except Exception as e:
65
+ return {"status": "error", "message": str(e)}
66
+
67
+ if __name__ == "__main__":
68
+ import asyncio
69
+ asyncio.run(example_function())
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "smart-bot-factory"
3
- version = "0.1.5"
3
+ version = "0.1.7"
4
4
  description = "Библиотека для создания умных чат-ботов"
5
5
  authors = [
6
6
  {name = "Kopatych", email = "herousplay@gmail.com"}
@@ -35,9 +35,9 @@ def list():
35
35
  click.echo("🤖 Нет доступных ботов")
36
36
  return
37
37
 
38
- click.echo("🤖 Доступные боты:")
39
- for bot in sorted(bots):
40
- click.echo(f" 📱 {bot}")
38
+ click.echo("🤖 Доступные боты:")
39
+ for bot in sorted(bots):
40
+ click.echo(f" 📱 {bot}")
41
41
 
42
42
  @cli.command()
43
43
  @click.argument("bot_id")
@@ -191,8 +191,8 @@ def prompts(bot_id: str, list_prompts: bool = False, edit_prompt: str = None, ad
191
191
  if not prompts_dir.exists():
192
192
  raise click.ClickException(f"Папка промптов не найдена для бота {bot_id}")
193
193
 
194
- if list_prompts:
195
- # Показываем список промптов
194
+ if list_prompts or (not edit_prompt and not add_prompt):
195
+ # Показываем список промптов (по умолчанию или с флагом --list)
196
196
  prompt_files = [f.name for f in prompts_dir.glob("*.txt")]
197
197
 
198
198
  if not prompt_files:
@@ -202,6 +202,13 @@ def prompts(bot_id: str, list_prompts: bool = False, edit_prompt: str = None, ad
202
202
  click.echo(f"📝 Промпты бота {bot_id}:")
203
203
  for prompt_file in sorted(prompt_files):
204
204
  click.echo(f" 📄 {prompt_file[:-4]}")
205
+
206
+ # Показываем справку только если не указан флаг --list
207
+ if not list_prompts:
208
+ click.echo()
209
+ click.echo("Использование:")
210
+ click.echo(" sbf prompts <bot_id> --edit <prompt_name> # Редактировать промпт")
211
+ click.echo(" sbf prompts <bot_id> --add <prompt_name> # Добавить новый промпт")
205
212
 
206
213
  elif edit_prompt:
207
214
  # Редактируем промпт
@@ -231,12 +238,6 @@ def prompts(bot_id: str, list_prompts: bool = False, edit_prompt: str = None, ad
231
238
  click.echo(f"📝 Создаем новый промпт {add_prompt}...")
232
239
  subprocess.run([editor, str(prompt_file)], check=True)
233
240
 
234
- else:
235
- # Показываем справку
236
- click.echo("📖 Использование:")
237
- click.echo(" 📋 sbf prompts <bot_id> --list # Показать список промптов")
238
- click.echo(" ✏️ sbf prompts <bot_id> --edit <prompt_name> # Редактировать промпт")
239
- click.echo(" ➕ sbf prompts <bot_id> --add <prompt_name> # Добавить новый промпт")
240
241
 
241
242
  except subprocess.CalledProcessError as e:
242
243
  click.echo(f"❌ Ошибка при открытии редактора: {e}", err=True)
@@ -259,21 +260,21 @@ def rm(bot_id: str, force: bool = False):
259
260
  # Проверяем существование бота
260
261
  bot_path = PROJECT_ROOT / Path("bots") / bot_id
261
262
  if not bot_path.exists():
262
- raise click.ClickException(f"Бот {bot_id} не найден в папке bots/")
263
+ raise click.ClickException(f"🤖 Бот {bot_id} не найден в папке bots/")
263
264
 
264
265
  # Проверяем наличие основного файла бота в корневой директории
265
266
  bot_file = Path(f"{bot_id}.py")
266
267
  if not bot_file.exists():
267
- raise click.ClickException(f"Файл {bot_id}.py не найден в корневой директории")
268
+ raise click.ClickException(f"📄 Файл {bot_id}.py не найден в корневой директории")
268
269
 
269
270
  # Показываем что будет удалено
270
271
  click.echo("🗑️ Будет удалено:")
271
- click.echo(f" 📄 Файл запускалки: {bot_file}")
272
- click.echo(f" 📁 Папка бота: {bot_path}")
272
+ click.echo(f" 📄 Файл запускалки: {bot_file}")
273
+ click.echo(f" 📁 Папка бота: {bot_path}")
273
274
 
274
275
  # Запрашиваем подтверждение если не указан --force
275
276
  if not force:
276
- if not click.confirm(f" Вы уверены, что хотите удалить бота {bot_id}?"):
277
+ if not click.confirm(f"⚠️ Вы уверены, что хотите удалить бота {bot_id}?"):
277
278
  click.echo("❌ Удаление отменено")
278
279
  return
279
280
 
@@ -288,7 +289,7 @@ def rm(bot_id: str, force: bool = False):
288
289
  shutil.rmtree(bot_path)
289
290
  click.echo(f"✅ Папка {bot_path} удалена")
290
291
 
291
- click.echo(f"🗑️ Бот {bot_id} полностью удален")
292
+ click.echo(f"🎉 Бот {bot_id} полностью удален")
292
293
 
293
294
  except Exception as e:
294
295
  click.echo(f"❌ Ошибка при удалении бота: {e}", err=True)
@@ -318,8 +319,8 @@ def copy(source_bot_id: str, new_bot_id: str, force: bool = False):
318
319
 
319
320
  if new_bot_path.exists() or new_bot_file.exists():
320
321
  if not force:
321
- if not click.confirm(f"⚠️ Бот {new_bot_id} уже существует. Перезаписать?"):
322
- click.echo("Копирование отменено")
322
+ if not click.confirm(f"Бот {new_bot_id} уже существует. Перезаписать?"):
323
+ click.echo("Копирование отменено")
323
324
  return
324
325
  else:
325
326
  click.echo(f"⚠️ Перезаписываем существующего бота {new_bot_id}")
@@ -414,14 +415,16 @@ def create_bot_template(bot_id: str) -> str:
414
415
 
415
416
  import asyncio
416
417
 
417
- from smart_bot_factory.core import Router
418
- from smart_bot_factory.core import send_message_by_human
419
- from smart_bot_factory.clients import supabase_client
418
+ from smart_bot_factory.router import Router
419
+ from smart_bot_factory.message import send_message_by_human
420
+ from smart_bot_factory.supabase import SupabaseClient
420
421
  from smart_bot_factory.creation import BotBuilder
421
422
 
422
423
  # Создаем роутер для всех обработчиков
423
424
  router = Router("{bot_id}_handlers")
424
425
 
426
+ supabase_client = SupabaseClient("{bot_id}")
427
+
425
428
  # =============================================================================
426
429
  # ОБРАБОТЧИКИ СОБЫТИЙ
427
430
  # =============================================================================
@@ -677,18 +680,10 @@ def copy_bot_template(source_bot_id: str, new_bot_id: str):
677
680
  new_bot_file.write_text(content, encoding='utf-8')
678
681
  click.echo(f" 📄 Файл запускалки скопирован: {new_bot_id}.py")
679
682
 
680
- # Копируем .env файл
681
- source_env = source_dir / ".env"
683
+ # Создаем шаблон .env файла (НЕ копируем существующий)
682
684
  new_env = new_dir / ".env"
683
-
684
- if source_env.exists():
685
- shutil.copy2(source_env, new_env)
686
-
687
- # Заменяем BOT_ID в .env
688
- env_content = new_env.read_text(encoding='utf-8')
689
- env_content = env_content.replace(f'BOT_ID={source_bot_id}', f'BOT_ID={new_bot_id}')
690
- new_env.write_text(env_content, encoding='utf-8')
691
- click.echo(f" ⚙️ .env файл скопирован и обновлен")
685
+ new_env.write_text(create_env_template(new_bot_id), encoding='utf-8')
686
+ click.echo(f" ⚙️ Создан шаблон .env файла")
692
687
 
693
688
  # Копируем промпты
694
689
  source_prompts = source_dir / "prompts"
@@ -31,45 +31,6 @@ def get_global_var(var_name):
31
31
 
32
32
  logger = logging.getLogger(__name__)
33
33
 
34
- def parse_time_expression(time_text: str) -> int:
35
- """
36
- Парсит временные выражения и возвращает количество секунд
37
-
38
- Поддерживаемые форматы:
39
- - "30" -> 30 секунд (основной формат)
40
- - "Запуск через 30 секунд" -> 30 секунд
41
- - "через 5 минут" -> 300 секунд
42
- - "через 1 час" -> 3600 секунд
43
- - "через 2 часа" -> 7200 секунд
44
- """
45
- # Сначала пробуем простое число (основной формат)
46
- try:
47
- return int(time_text)
48
- except ValueError:
49
- pass
50
-
51
- # Убираем лишние символы и приводим к нижнему регистру
52
- text = re.sub(r'[^\w\s\d]', '', time_text.lower())
53
-
54
- # Ищем числа в тексте
55
- numbers = re.findall(r'\d+', text)
56
- if not numbers:
57
- raise ValueError(f"Не найдено число в выражении: {time_text}")
58
-
59
- number = int(numbers[0])
60
-
61
- # Определяем единицу времени
62
- if 'секунд' in text:
63
- return number
64
- elif 'минут' in text:
65
- return number * 60
66
- elif 'час' in text:
67
- return number * 3600
68
- elif 'день' in text or 'дней' in text:
69
- return number * 86400
70
- else:
71
- # По умолчанию считаем секундами
72
- return number
73
34
 
74
35
  # Создаем роутер для общих команд
75
36
  utils_router = Router()
@@ -244,10 +205,12 @@ async def process_events(session_id: str, events: list, user_id: int):
244
205
  event_handlers = router_manager.get_event_handlers()
245
206
  scheduled_tasks = router_manager.get_scheduled_tasks()
246
207
  global_handlers = router_manager.get_global_handlers()
208
+ logger.debug(f"🔍 RouterManager найден: {len(global_handlers)} глобальных обработчиков")
247
209
  else:
248
210
  event_handlers = _event_handlers
249
211
  scheduled_tasks = _scheduled_tasks
250
212
  global_handlers = _global_handlers
213
+ logger.warning("⚠️ RouterManager не найден, используем старые декораторы")
251
214
 
252
215
  # Сначала пробуем как обычное событие
253
216
  if event_type in event_handlers:
@@ -266,49 +229,59 @@ async def process_events(session_id: str, events: list, user_id: int):
266
229
  # Если не user_event, пробуем как запланированную задачу
267
230
  elif event_type in scheduled_tasks:
268
231
  try:
269
- delay_seconds = parse_time_expression(event_info)
232
+ # Извлекаем время из event_info (AI должен передавать число секунд)
233
+ try:
234
+ delay_seconds = int(event_info)
235
+ except ValueError:
236
+ # Если не число, используем значение по умолчанию
237
+ delay_seconds = 3600
238
+ logger.warning(f" ⚠️ Не удалось извлечь время из '{event_info}', используем 3600с")
239
+
270
240
  logger.info(f" ⏰ Сохраняем как scheduled_task: '{event_type}' через {delay_seconds}с")
271
241
  result = await schedule_task_for_later_with_db(event_type, user_id, event_info, delay_seconds, session_id)
272
242
  event_id = result['event_id']
273
243
  should_notify = result.get('notify', False)
274
244
  logger.info(f" 💾 Задача сохранена в БД: {event_id}")
275
245
 
276
- except ValueError as e:
246
+ except Exception as e:
277
247
  if "once_only=True" in str(e):
278
248
  logger.info(f" 🔄 Задача '{event_type}' уже запланирована, пропускаем")
279
249
  continue
280
250
  else:
281
- logger.error(f" ❌ Ошибка парсинга времени для scheduled_task '{event_type}': {e}")
282
- # Fallback - планируем через 1 час
283
- result = await schedule_task_for_later_with_db(event_type, user_id, event_info, 3600, session_id)
284
- event_id = result['event_id']
285
- should_notify = result.get('notify', False)
286
- logger.info(f" ⏰ Задача сохранена с fallback временем (1 час): {event_id}")
251
+ logger.error(f" ❌ Ошибка планирования scheduled_task '{event_type}': {e}")
252
+ continue
287
253
 
288
254
  # Если не scheduled_task, пробуем как глобальный обработчик
289
255
  elif event_type in global_handlers:
290
256
  try:
291
- delay_seconds = parse_time_expression(event_info)
257
+ # Извлекаем время из event_info (AI должен передавать число секунд)
258
+ try:
259
+ delay_seconds = int(event_info)
260
+ except ValueError:
261
+ # Если не число, используем значение по умолчанию
262
+ delay_seconds = 3600
263
+ logger.warning(f" ⚠️ Не удалось извлечь время из '{event_info}', используем 3600с")
264
+
292
265
  logger.info(f" 🌍 Сохраняем как global_handler: '{event_type}' через {delay_seconds}с")
293
266
  result = await schedule_global_handler_for_later_with_db(event_type, delay_seconds, event_info)
294
267
  event_id = result['event_id']
295
268
  should_notify = result.get('notify', False)
296
269
  logger.info(f" 💾 Глобальное событие сохранено в БД: {event_id}")
297
270
 
298
- except ValueError as e:
271
+ except Exception as e:
299
272
  if "once_only=True" in str(e):
300
273
  logger.info(f" 🔄 Глобальное событие '{event_type}' уже запланировано, пропускаем")
301
274
  continue
302
275
  else:
303
- logger.error(f" ❌ Ошибка парсинга времени для global_handler '{event_type}': {e}")
304
- # Fallback - планируем через 1 час
305
- result = await schedule_global_handler_for_later_with_db(event_type, 3600, event_info)
306
- event_id = result['event_id']
307
- should_notify = result.get('notify', False)
308
- logger.info(f" 🌍 Глобальное событие сохранено с fallback временем (1 час): {event_id}")
276
+ logger.error(f" ❌ Ошибка планирования global_handler '{event_type}': {e}")
277
+ continue
309
278
 
310
279
  else:
311
280
  logger.warning(f" ⚠️ Обработчик '{event_type}' не найден среди зарегистрированных")
281
+ logger.debug(f" 🔍 Доступные обработчики:")
282
+ logger.debug(f" - event_handlers: {list(event_handlers.keys())}")
283
+ logger.debug(f" - scheduled_tasks: {list(scheduled_tasks.keys())}")
284
+ logger.debug(f" - global_handlers: {list(global_handlers.keys())}")
312
285
 
313
286
  # Выполняем немедленные события
314
287
  if should_execute_immediately and event_id:
@@ -292,18 +292,16 @@ async def execute_scheduled_task(task_name: str, user_id: int, user_data: str) -
292
292
 
293
293
  async def execute_global_handler(handler_type: str, *args, **kwargs) -> Any:
294
294
  """Выполняет глобальный обработчик по типу"""
295
- # Сначала пробуем получить из роутеров
296
- if _router_manager:
297
- global_handlers = _router_manager.get_global_handlers()
298
- if handler_type in global_handlers:
299
- handler_info = global_handlers[handler_type]
300
- return await handler_info['handler'](*args, **kwargs)
295
+ router_manager = get_router_manager()
296
+ if router_manager:
297
+ global_handlers = router_manager.get_global_handlers()
298
+ else:
299
+ global_handlers = _global_handlers
301
300
 
302
- # Fallback к старым декораторам
303
- if handler_type not in _global_handlers:
301
+ if handler_type not in global_handlers:
304
302
  raise ValueError(f"Глобальный обработчик '{handler_type}' не найден")
305
303
 
306
- handler_info = _global_handlers[handler_type]
304
+ handler_info = global_handlers[handler_type]
307
305
  return await handler_info['handler'](*args, **kwargs)
308
306
 
309
307
  async def schedule_task_for_later(task_name: str, delay_seconds: int, user_id: int, user_data: str):
@@ -461,12 +459,7 @@ async def save_immediate_event(
461
459
  raise RuntimeError("Supabase клиент не инициализирован")
462
460
 
463
461
  # Проверяем, нужно ли предотвращать дублирование
464
- # Получаем информацию об обработчике через роутер-менеджер или fallback
465
- if _router_manager:
466
- event_handlers = _router_manager.get_event_handlers()
467
- event_handler_info = event_handlers.get(event_type, {})
468
- else:
469
- event_handler_info = _event_handlers.get(event_type, {})
462
+ event_handler_info = _event_handlers.get(event_type, {})
470
463
  once_only = event_handler_info.get('once_only', True)
471
464
 
472
465
  if once_only:
@@ -510,12 +503,7 @@ async def save_scheduled_task(
510
503
  raise RuntimeError("Supabase клиент не инициализирован")
511
504
 
512
505
  # Проверяем, нужно ли предотвращать дублирование
513
- # Получаем информацию о задаче через роутер-менеджер или fallback
514
- if _router_manager:
515
- scheduled_tasks = _router_manager.get_scheduled_tasks()
516
- task_info = scheduled_tasks.get(task_name, {})
517
- else:
518
- task_info = _scheduled_tasks.get(task_name, {})
506
+ task_info = _scheduled_tasks.get(task_name, {})
519
507
  once_only = task_info.get('once_only', True)
520
508
 
521
509
  if once_only:
@@ -559,12 +547,13 @@ async def save_global_event(
559
547
  raise RuntimeError("Supabase клиент не инициализирован")
560
548
 
561
549
  # Проверяем, нужно ли предотвращать дублирование
562
- # Получаем информацию о глобальном обработчике через роутер-менеджер или fallback
563
- if _router_manager:
564
- global_handlers = _router_manager.get_global_handlers()
565
- handler_info = global_handlers.get(handler_type, {})
550
+ router_manager = get_router_manager()
551
+ if router_manager:
552
+ global_handlers = router_manager.get_global_handlers()
566
553
  else:
567
- handler_info = _global_handlers.get(handler_type, {})
554
+ global_handlers = _global_handlers
555
+
556
+ handler_info = global_handlers.get(handler_type, {})
568
557
  once_only = handler_info.get('once_only', True)
569
558
 
570
559
  if once_only:
@@ -714,14 +703,8 @@ async def process_scheduled_event(event: Dict):
714
703
  async def schedule_task_for_later_with_db(task_name: str, user_id: int, user_data: str, delay_seconds: int, session_id: str = None):
715
704
  """Планирует выполнение задачи через указанное время с сохранением в БД"""
716
705
 
717
- # Проверяем через роутер-менеджер или fallback к старым декораторам
718
- if _router_manager:
719
- scheduled_tasks = _router_manager.get_scheduled_tasks()
720
- if task_name not in scheduled_tasks:
721
- raise ValueError(f"Задача '{task_name}' не найдена")
722
- else:
723
- if task_name not in _scheduled_tasks:
724
- raise ValueError(f"Задача '{task_name}' не найдена")
706
+ if task_name not in _scheduled_tasks:
707
+ raise ValueError(f"Задача '{task_name}' не найдена")
725
708
 
726
709
  logger.info(f"⏰ Планируем задачу '{task_name}' через {delay_seconds} секунд")
727
710
 
@@ -782,14 +765,15 @@ async def schedule_task_for_later_with_db(task_name: str, user_id: int, user_dat
782
765
  async def schedule_global_handler_for_later_with_db(handler_type: str, delay_seconds: int, handler_data: str):
783
766
  """Планирует выполнение глобального обработчика через указанное время с сохранением в БД"""
784
767
 
785
- # Проверяем через роутер-менеджер или fallback к старым декораторам
786
- if _router_manager:
787
- global_handlers = _router_manager.get_global_handlers()
788
- if handler_type not in global_handlers:
789
- raise ValueError(f"Глобальный обработчик '{handler_type}' не найден")
768
+ # Проверяем обработчик через RouterManager или fallback к старым декораторам
769
+ router_manager = get_router_manager()
770
+ if router_manager:
771
+ global_handlers = router_manager.get_global_handlers()
790
772
  else:
791
- if handler_type not in _global_handlers:
792
- raise ValueError(f"Глобальный обработчик '{handler_type}' не найден")
773
+ global_handlers = _global_handlers
774
+
775
+ if handler_type not in global_handlers:
776
+ raise ValueError(f"Глобальный обработчик '{handler_type}' не найден")
793
777
 
794
778
  logger.info(f"🌍 Планируем глобальный обработчик '{handler_type}' через {delay_seconds} секунд")
795
779
 
@@ -29,11 +29,6 @@ async def send_message_by_ai(
29
29
  try:
30
30
  # Импортируем необходимые компоненты
31
31
  from .bot_utils import parse_ai_response, process_events
32
- from ..config import Config
33
- from ..integrations.openai_client import OpenAIClient
34
- from ..integrations.supabase_client import SupabaseClient
35
- from ..utils.prompt_loader import PromptLoader
36
- from aiogram import Bot
37
32
 
38
33
  # Получаем компоненты из глобального контекста
39
34
  from ..handlers.handlers import get_global_var
@@ -2,7 +2,7 @@
2
2
  Роутер для Smart Bot Factory - аналог aiogram Router
3
3
  """
4
4
 
5
- from typing import Dict, List, Any, Callable, Optional
5
+ from typing import Dict, Any, Callable
6
6
  import logging
7
7
 
8
8
  logger = logging.getLogger(__name__)
@@ -170,3 +170,4 @@ class Router:
170
170
 
171
171
  def __repr__(self):
172
172
  return f"Router(name='{self.name}', events={len(self._event_handlers)}, tasks={len(self._scheduled_tasks)}, globals={len(self._global_handlers)})"
173
+