smart-bot-factory 0.1.4__py3-none-any.whl → 0.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of smart-bot-factory might be problematic. Click here for more details.
- smart_bot_factory/__init__.py +0 -30
- smart_bot_factory/admin/admin_logic.py +11 -11
- smart_bot_factory/cli.py +147 -85
- smart_bot_factory/configs/growthmed-october-24/prompts/final_instructions.txt +2 -0
- smart_bot_factory/configs/growthmed-october-24/tests/quick_scenarios.yaml +95 -28
- smart_bot_factory/core/bot_utils.py +224 -88
- smart_bot_factory/core/conversation_manager.py +542 -535
- smart_bot_factory/core/decorators.py +927 -230
- smart_bot_factory/core/message_sender.py +2 -7
- smart_bot_factory/core/router.py +173 -0
- smart_bot_factory/core/router_manager.py +166 -0
- smart_bot_factory/creation/__init__.py +1 -2
- smart_bot_factory/creation/bot_builder.py +103 -13
- smart_bot_factory/creation/bot_testing.py +74 -13
- smart_bot_factory/event/__init__.py +12 -0
- smart_bot_factory/handlers/handlers.py +10 -2
- smart_bot_factory/integrations/supabase_client.py +272 -2
- smart_bot_factory/message/__init__.py +12 -0
- smart_bot_factory/router/__init__.py +9 -0
- smart_bot_factory/supabase/__init__.py +7 -0
- smart_bot_factory-0.1.6.dist-info/METADATA +466 -0
- {smart_bot_factory-0.1.4.dist-info → smart_bot_factory-0.1.6.dist-info}/RECORD +26 -31
- smart_bot_factory/analytics/__init__.py +0 -7
- smart_bot_factory/configs/growthmed-helper/prompts/1sales_context.txt +0 -9
- smart_bot_factory/configs/growthmed-helper/prompts/2product_info.txt +0 -582
- smart_bot_factory/configs/growthmed-helper/prompts/3objection_handling.txt +0 -66
- smart_bot_factory/configs/growthmed-helper/prompts/final_instructions.txt +0 -232
- smart_bot_factory/configs/growthmed-helper/prompts/help_message.txt +0 -28
- smart_bot_factory/configs/growthmed-helper/prompts/welcome_message.txt +0 -7
- smart_bot_factory/configs/growthmed-helper/welcome_file/welcome_file_msg.txt +0 -16
- smart_bot_factory/configs/growthmed-helper/welcome_file//342/225/250/320/267/342/225/250/342/225/241/342/225/250/342/225/221 /342/225/250/342/225/227/342/225/250/342/225/225/342/225/244/320/221/342/225/244/320/222 /342/225/250/342/224/220/342/225/250/342/225/233 152/342/225/250/320/264/342/225/250/320/247 /342/225/250/342/225/225 323/342/225/250/320/264/342/225/250/320/247 /342/225/250/342/224/244/342/225/250/342/225/227/342/225/244/320/237 /342/225/250/342/225/235/342/225/250/342/225/241/342/225/250/342/224/244/342/225/250/342/225/225/342/225/244/320/226/342/225/250/342/225/225/342/225/250/342/225/234/342/225/244/320/233.pdf +0 -0
- smart_bot_factory/core/__init__.py +0 -22
- smart_bot_factory/integrations/__init__.py +0 -9
- smart_bot_factory-0.1.4.dist-info/METADATA +0 -126
- /smart_bot_factory/{configs/growthmed-helper/env_example.txt → supabase/example_usage.py} +0 -0
- {smart_bot_factory-0.1.4.dist-info → smart_bot_factory-0.1.6.dist-info}/WHEEL +0 -0
- {smart_bot_factory-0.1.4.dist-info → smart_bot_factory-0.1.6.dist-info}/entry_points.txt +0 -0
- {smart_bot_factory-0.1.4.dist-info → smart_bot_factory-0.1.6.dist-info}/licenses/LICENSE +0 -0
smart_bot_factory/__init__.py
CHANGED
|
@@ -1,33 +1,3 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Smart Bot Factory - библиотека для создания умных чат-ботов
|
|
3
3
|
"""
|
|
4
|
-
|
|
5
|
-
from .creation.bot_builder import BotBuilder
|
|
6
|
-
from .core.decorators import event_handler, schedule_task
|
|
7
|
-
from .core.message_sender import send_message_by_ai, send_message_by_human
|
|
8
|
-
from .config import Config
|
|
9
|
-
from .integrations.openai_client import OpenAIClient
|
|
10
|
-
from .integrations.supabase_client import SupabaseClient
|
|
11
|
-
from .core.conversation_manager import ConversationManager
|
|
12
|
-
from .admin.admin_manager import AdminManager
|
|
13
|
-
from .utils.prompt_loader import PromptLoader
|
|
14
|
-
from .handlers.handlers import setup_handlers
|
|
15
|
-
from .admin.admin_logic import setup_admin_handlers
|
|
16
|
-
from .core.bot_utils import setup_utils_handlers, parse_ai_response, process_events
|
|
17
|
-
from .utils.debug_routing import setup_debug_handlers
|
|
18
|
-
from .core.states import UserStates, AdminStates
|
|
19
|
-
from .creation.bot_testing import main as bot_testing_main
|
|
20
|
-
from .analytics.analytics_manager import AnalyticsManager
|
|
21
|
-
from .admin.timeout_checker import check_timeouts, setup_bot_environment
|
|
22
|
-
from .setup_checker import check_setup
|
|
23
|
-
from .admin.admin_tester import test_admin_system
|
|
24
|
-
|
|
25
|
-
__all__ = [
|
|
26
|
-
'BotBuilder',
|
|
27
|
-
'event_handler',
|
|
28
|
-
'schedule_task',
|
|
29
|
-
'send_message_by_ai',
|
|
30
|
-
'send_message_by_human',
|
|
31
|
-
'OpenAIClient',
|
|
32
|
-
'SupabaseClient'
|
|
33
|
-
]
|
|
@@ -22,7 +22,7 @@ def setup_admin_handlers(dp):
|
|
|
22
22
|
|
|
23
23
|
async def admin_start_handler(message: Message, state: FSMContext):
|
|
24
24
|
"""Обработчик /start для админов в режиме администратора"""
|
|
25
|
-
from handlers import get_global_var
|
|
25
|
+
from ..handlers.handlers import get_global_var
|
|
26
26
|
admin_manager = get_global_var('admin_manager')
|
|
27
27
|
|
|
28
28
|
await state.set_state(AdminStates.admin_mode)
|
|
@@ -55,7 +55,7 @@ async def admin_start_handler(message: Message, state: FSMContext):
|
|
|
55
55
|
@admin_router.message(Command("стат"))
|
|
56
56
|
async def admin_stats_handler(message: Message, state: FSMContext):
|
|
57
57
|
"""Статистика воронки"""
|
|
58
|
-
from handlers import get_global_var
|
|
58
|
+
from ..handlers.handlers import get_global_var
|
|
59
59
|
admin_manager = get_global_var('admin_manager')
|
|
60
60
|
analytics_manager = get_global_var('analytics_manager')
|
|
61
61
|
|
|
@@ -82,7 +82,7 @@ async def admin_stats_handler(message: Message, state: FSMContext):
|
|
|
82
82
|
@admin_router.message(Command("история"))
|
|
83
83
|
async def admin_history_handler(message: Message, state: FSMContext):
|
|
84
84
|
"""История пользователя"""
|
|
85
|
-
from handlers import get_global_var
|
|
85
|
+
from ..handlers.handlers import get_global_var
|
|
86
86
|
admin_manager = get_global_var('admin_manager')
|
|
87
87
|
analytics_manager = get_global_var('analytics_manager')
|
|
88
88
|
|
|
@@ -118,7 +118,7 @@ async def admin_history_handler(message: Message, state: FSMContext):
|
|
|
118
118
|
@admin_router.message(Command("чат"))
|
|
119
119
|
async def admin_chat_handler(message: Message, state: FSMContext):
|
|
120
120
|
"""Начать диалог с пользователем"""
|
|
121
|
-
from handlers import get_global_var
|
|
121
|
+
from ..handlers.handlers import get_global_var
|
|
122
122
|
|
|
123
123
|
admin_manager = get_global_var('admin_manager')
|
|
124
124
|
supabase_client = get_global_var('supabase_client')
|
|
@@ -173,7 +173,7 @@ async def admin_chat_handler(message: Message, state: FSMContext):
|
|
|
173
173
|
@admin_router.message(Command("чаты"))
|
|
174
174
|
async def admin_active_chats_command(message: Message, state: FSMContext):
|
|
175
175
|
"""Показать активные диалоги админов"""
|
|
176
|
-
from handlers import get_global_var
|
|
176
|
+
from ..handlers.handlers import get_global_var
|
|
177
177
|
admin_manager = get_global_var('admin_manager')
|
|
178
178
|
conversation_manager = get_global_var('conversation_manager')
|
|
179
179
|
|
|
@@ -194,7 +194,7 @@ async def admin_active_chats_command(message: Message, state: FSMContext):
|
|
|
194
194
|
@admin_router.message(Command("стоп"))
|
|
195
195
|
async def admin_stop_handler(message: Message, state: FSMContext):
|
|
196
196
|
"""Завершить диалог"""
|
|
197
|
-
from handlers import get_global_var
|
|
197
|
+
from ..handlers.handlers import get_global_var
|
|
198
198
|
admin_manager = get_global_var('admin_manager')
|
|
199
199
|
conversation_manager = get_global_var('conversation_manager')
|
|
200
200
|
|
|
@@ -233,9 +233,9 @@ async def admin_stop_handler(message: Message, state: FSMContext):
|
|
|
233
233
|
@admin_router.message(Command("админ"))
|
|
234
234
|
async def admin_toggle_handler(message: Message, state: FSMContext):
|
|
235
235
|
"""Переключение режима админа"""
|
|
236
|
-
from handlers import get_global_var
|
|
236
|
+
from ..handlers.handlers import get_global_var
|
|
237
237
|
admin_manager = get_global_var('admin_manager')
|
|
238
|
-
from .handlers import user_start_handler
|
|
238
|
+
from ..handlers.handlers import user_start_handler
|
|
239
239
|
|
|
240
240
|
if not admin_manager.is_admin(message.from_user.id):
|
|
241
241
|
return
|
|
@@ -253,7 +253,7 @@ async def admin_toggle_handler(message: Message, state: FSMContext):
|
|
|
253
253
|
@admin_router.message(Command("debug_chat"))
|
|
254
254
|
async def debug_chat_handler(message: Message, state: FSMContext):
|
|
255
255
|
"""Отладка диалогов админов"""
|
|
256
|
-
from handlers import get_global_var
|
|
256
|
+
from ..handlers.handlers import get_global_var
|
|
257
257
|
admin_manager = get_global_var('admin_manager')
|
|
258
258
|
conversation_manager = get_global_var('conversation_manager')
|
|
259
259
|
supabase_client = get_global_var('supabase_client')
|
|
@@ -304,7 +304,7 @@ async def debug_chat_handler(message: Message, state: FSMContext):
|
|
|
304
304
|
@admin_router.callback_query(F.data.startswith("admin_"))
|
|
305
305
|
async def admin_callback_handler(callback: CallbackQuery, state: FSMContext):
|
|
306
306
|
"""Обработчик callback кнопок админов"""
|
|
307
|
-
from handlers import get_global_var
|
|
307
|
+
from ..handlers.handlers import get_global_var
|
|
308
308
|
admin_manager = get_global_var('admin_manager')
|
|
309
309
|
analytics_manager = get_global_var('analytics_manager')
|
|
310
310
|
conversation_manager = get_global_var('conversation_manager')
|
|
@@ -394,7 +394,7 @@ async def admin_callback_handler(callback: CallbackQuery, state: FSMContext):
|
|
|
394
394
|
@admin_router.message(StateFilter(AdminStates.admin_mode, AdminStates.in_conversation))
|
|
395
395
|
async def admin_message_handler(message: Message, state: FSMContext):
|
|
396
396
|
"""Обработчик сообщений админов"""
|
|
397
|
-
from handlers import get_global_var
|
|
397
|
+
from ..handlers.handlers import get_global_var
|
|
398
398
|
admin_manager = get_global_var('admin_manager')
|
|
399
399
|
conversation_manager = get_global_var('conversation_manager')
|
|
400
400
|
|
smart_bot_factory/cli.py
CHANGED
|
@@ -32,12 +32,12 @@ def list():
|
|
|
32
32
|
"""Показать список доступных ботов"""
|
|
33
33
|
bots = list_bots_in_bots_folder()
|
|
34
34
|
if not bots:
|
|
35
|
-
click.echo("Нет доступных ботов")
|
|
35
|
+
click.echo("🤖 Нет доступных ботов")
|
|
36
36
|
return
|
|
37
37
|
|
|
38
|
-
click.echo("Доступные боты:")
|
|
38
|
+
click.echo("🤖 Доступные боты:")
|
|
39
39
|
for bot in sorted(bots):
|
|
40
|
-
click.echo(f"
|
|
40
|
+
click.echo(f" 📱 {bot}")
|
|
41
41
|
|
|
42
42
|
@cli.command()
|
|
43
43
|
@click.argument("bot_id")
|
|
@@ -67,7 +67,7 @@ def run(bot_id: str):
|
|
|
67
67
|
|
|
68
68
|
# Загружаем .env файл
|
|
69
69
|
load_dotenv(env_file)
|
|
70
|
-
click.echo(f"Загружен .env файл: {env_file}")
|
|
70
|
+
click.echo(f"⚙️ Загружен .env файл: {env_file}")
|
|
71
71
|
|
|
72
72
|
# Устанавливаем переменные окружения
|
|
73
73
|
os.environ["BOT_ID"] = bot_id
|
|
@@ -76,17 +76,17 @@ def run(bot_id: str):
|
|
|
76
76
|
prompts_dir = bot_path / "prompts"
|
|
77
77
|
if prompts_dir.exists():
|
|
78
78
|
os.environ["PROMT_FILES_DIR"] = str(prompts_dir)
|
|
79
|
-
click.echo(f"Установлен путь к промптам: {prompts_dir}")
|
|
79
|
+
click.echo(f"📝 Установлен путь к промптам: {prompts_dir}")
|
|
80
80
|
|
|
81
81
|
# Запускаем бота из корневой директории
|
|
82
|
-
click.echo(f"Запускаем бота {bot_id}...")
|
|
82
|
+
click.echo(f"🚀 Запускаем бота {bot_id}...")
|
|
83
83
|
subprocess.run([sys.executable, str(bot_file)], check=True, cwd=str(PROJECT_ROOT))
|
|
84
84
|
|
|
85
85
|
except subprocess.CalledProcessError as e:
|
|
86
|
-
click.echo(f"Ошибка при запуске бота: {e}", err=True)
|
|
86
|
+
click.echo(f"❌ Ошибка при запуске бота: {e}", err=True)
|
|
87
87
|
sys.exit(1)
|
|
88
88
|
except Exception as e:
|
|
89
|
-
click.echo(f"Ошибка: {e}", err=True)
|
|
89
|
+
click.echo(f"❌ Ошибка: {e}", err=True)
|
|
90
90
|
sys.exit(1)
|
|
91
91
|
|
|
92
92
|
@cli.command()
|
|
@@ -115,7 +115,7 @@ def test(bot_id: str, file: str = None, verbose: bool = False, max_concurrent: i
|
|
|
115
115
|
click.echo(f"⚠️ YAML тесты не найдены для бота {bot_id}")
|
|
116
116
|
return
|
|
117
117
|
|
|
118
|
-
click.echo(f"Запускаем тесты для бота {bot_id}...")
|
|
118
|
+
click.echo(f"🧪 Запускаем тесты для бота {bot_id}...")
|
|
119
119
|
|
|
120
120
|
# Формируем команду для запуска
|
|
121
121
|
bot_testing_path = Path(__file__).parent / "creation" / "bot_testing.py"
|
|
@@ -140,10 +140,10 @@ def test(bot_id: str, file: str = None, verbose: bool = False, max_concurrent: i
|
|
|
140
140
|
sys.exit(1)
|
|
141
141
|
|
|
142
142
|
except subprocess.CalledProcessError as e:
|
|
143
|
-
click.echo(f"Ошибка при запуске тестов: {e}", err=True)
|
|
143
|
+
click.echo(f"❌ Ошибка при запуске тестов: {e}", err=True)
|
|
144
144
|
sys.exit(1)
|
|
145
145
|
except Exception as e:
|
|
146
|
-
click.echo(f"Ошибка: {e}", err=True)
|
|
146
|
+
click.echo(f"❌ Ошибка: {e}", err=True)
|
|
147
147
|
sys.exit(1)
|
|
148
148
|
|
|
149
149
|
@cli.command()
|
|
@@ -164,14 +164,14 @@ def config(bot_id: str):
|
|
|
164
164
|
# Определяем редактор
|
|
165
165
|
editor = os.environ.get('EDITOR', 'notepad' if os.name == 'nt' else 'nano')
|
|
166
166
|
|
|
167
|
-
click.echo(f"Открываем конфигурацию бота {bot_id}...")
|
|
167
|
+
click.echo(f"⚙️ Открываем конфигурацию бота {bot_id}...")
|
|
168
168
|
subprocess.run([editor, str(env_file)], check=True)
|
|
169
169
|
|
|
170
170
|
except subprocess.CalledProcessError as e:
|
|
171
|
-
click.echo(f"Ошибка при открытии редактора: {e}", err=True)
|
|
171
|
+
click.echo(f"❌ Ошибка при открытии редактора: {e}", err=True)
|
|
172
172
|
sys.exit(1)
|
|
173
173
|
except Exception as e:
|
|
174
|
-
click.echo(f"Ошибка: {e}", err=True)
|
|
174
|
+
click.echo(f"❌ Ошибка: {e}", err=True)
|
|
175
175
|
sys.exit(1)
|
|
176
176
|
|
|
177
177
|
@cli.command()
|
|
@@ -191,17 +191,24 @@ 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:
|
|
199
|
-
click.echo("Промпты не найдены")
|
|
199
|
+
click.echo("📝 Промпты не найдены")
|
|
200
200
|
return
|
|
201
201
|
|
|
202
|
-
click.echo(f"Промпты бота {bot_id}:")
|
|
202
|
+
click.echo(f"📝 Промпты бота {bot_id}:")
|
|
203
203
|
for prompt_file in sorted(prompt_files):
|
|
204
|
-
click.echo(f"
|
|
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
|
# Редактируем промпт
|
|
@@ -210,7 +217,7 @@ def prompts(bot_id: str, list_prompts: bool = False, edit_prompt: str = None, ad
|
|
|
210
217
|
raise click.ClickException(f"Промпт {edit_prompt} не найден")
|
|
211
218
|
|
|
212
219
|
editor = os.environ.get('EDITOR', 'notepad' if os.name == 'nt' else 'nano')
|
|
213
|
-
click.echo(f"Редактируем промпт {edit_prompt}...")
|
|
220
|
+
click.echo(f"✏️ Редактируем промпт {edit_prompt}...")
|
|
214
221
|
subprocess.run([editor, str(prompt_file)], check=True)
|
|
215
222
|
|
|
216
223
|
elif add_prompt:
|
|
@@ -231,18 +238,12 @@ 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
|
-
click.echo(f"Ошибка при открытии редактора: {e}", err=True)
|
|
243
|
+
click.echo(f"❌ Ошибка при открытии редактора: {e}", err=True)
|
|
243
244
|
sys.exit(1)
|
|
244
245
|
except Exception as e:
|
|
245
|
-
click.echo(f"Ошибка: {e}", err=True)
|
|
246
|
+
click.echo(f"❌ Ошибка: {e}", err=True)
|
|
246
247
|
sys.exit(1)
|
|
247
248
|
|
|
248
249
|
@cli.command()
|
|
@@ -259,39 +260,39 @@ 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
|
-
click.echo("Будет удалено:")
|
|
271
|
-
click.echo(f"
|
|
272
|
-
click.echo(f"
|
|
271
|
+
click.echo("🗑️ Будет удалено:")
|
|
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
|
-
click.echo("Удаление отменено")
|
|
277
|
+
if not click.confirm(f"⚠️ Вы уверены, что хотите удалить бота {bot_id}?"):
|
|
278
|
+
click.echo("❌ Удаление отменено")
|
|
278
279
|
return
|
|
279
280
|
|
|
280
281
|
# Удаляем файл запускалки
|
|
281
282
|
if bot_file.exists():
|
|
282
283
|
bot_file.unlink()
|
|
283
|
-
click.echo(f"Файл {bot_file} удален")
|
|
284
|
+
click.echo(f"✅ Файл {bot_file} удален")
|
|
284
285
|
|
|
285
286
|
# Удаляем папку бота
|
|
286
287
|
if bot_path.exists():
|
|
287
288
|
import shutil
|
|
288
289
|
shutil.rmtree(bot_path)
|
|
289
|
-
click.echo(f"Папка {bot_path} удалена")
|
|
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
|
-
click.echo(f"Ошибка при удалении бота: {e}", err=True)
|
|
295
|
+
click.echo(f"❌ Ошибка при удалении бота: {e}", err=True)
|
|
295
296
|
sys.exit(1)
|
|
296
297
|
|
|
297
298
|
|
|
@@ -332,7 +333,7 @@ def copy(source_bot_id: str, new_bot_id: str, force: bool = False):
|
|
|
332
333
|
click.echo(f"📝 Не забудьте настроить .env файл для нового бота")
|
|
333
334
|
|
|
334
335
|
except Exception as e:
|
|
335
|
-
click.echo(f"Ошибка при копировании бота: {e}", err=True)
|
|
336
|
+
click.echo(f"❌ Ошибка при копировании бота: {e}", err=True)
|
|
336
337
|
sys.exit(1)
|
|
337
338
|
|
|
338
339
|
@cli.command()
|
|
@@ -349,10 +350,10 @@ def link():
|
|
|
349
350
|
subprocess.run([sys.executable, str(link_script)], check=True)
|
|
350
351
|
|
|
351
352
|
except subprocess.CalledProcessError as e:
|
|
352
|
-
click.echo(f"Ошибка при запуске генератора ссылок: {e}", err=True)
|
|
353
|
+
click.echo(f"❌ Ошибка при запуске генератора ссылок: {e}", err=True)
|
|
353
354
|
sys.exit(1)
|
|
354
355
|
except Exception as e:
|
|
355
|
-
click.echo(f"Ошибка: {e}", err=True)
|
|
356
|
+
click.echo(f"❌ Ошибка: {e}", err=True)
|
|
356
357
|
sys.exit(1)
|
|
357
358
|
|
|
358
359
|
def create_new_bot_structure(template: str, bot_id: str) -> bool:
|
|
@@ -365,7 +366,7 @@ def create_new_bot_structure(template: str, bot_id: str) -> bool:
|
|
|
365
366
|
# Создаем папку для нового бота
|
|
366
367
|
bot_dir = bots_dir / bot_id
|
|
367
368
|
if bot_dir.exists():
|
|
368
|
-
click.echo(f"Бот {bot_id} уже существует")
|
|
369
|
+
click.echo(f"⚠️ Бот {bot_id} уже существует")
|
|
369
370
|
return False
|
|
370
371
|
|
|
371
372
|
bot_dir.mkdir()
|
|
@@ -384,12 +385,12 @@ def create_new_bot_structure(template: str, bot_id: str) -> bool:
|
|
|
384
385
|
# Используем другой шаблон из папки bots
|
|
385
386
|
copy_from_bot_template(template, bot_dir, bot_id)
|
|
386
387
|
|
|
387
|
-
click.echo(f"Бот {bot_id} создан в папке bots/{bot_id}/")
|
|
388
|
-
click.echo(f"Не забудьте настроить .env файл перед запуском")
|
|
388
|
+
click.echo(f"✅ Бот {bot_id} создан в папке bots/{bot_id}/")
|
|
389
|
+
click.echo(f"📝 Не забудьте настроить .env файл перед запуском")
|
|
389
390
|
return True
|
|
390
391
|
|
|
391
392
|
except Exception as e:
|
|
392
|
-
click.echo(f"Ошибка при создании бота: {e}")
|
|
393
|
+
click.echo(f"❌ Ошибка при создании бота: {e}")
|
|
393
394
|
return False
|
|
394
395
|
|
|
395
396
|
def list_bots_in_bots_folder() -> list:
|
|
@@ -414,25 +415,27 @@ def create_bot_template(bot_id: str) -> str:
|
|
|
414
415
|
|
|
415
416
|
import asyncio
|
|
416
417
|
|
|
417
|
-
from smart_bot_factory import
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
)
|
|
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
|
|
421
|
+
from smart_bot_factory.creation import BotBuilder
|
|
422
|
+
|
|
423
|
+
# Создаем роутер для всех обработчиков
|
|
424
|
+
router = Router("{bot_id}_handlers")
|
|
425
|
+
|
|
426
|
+
supabase_client = SupabaseClient("{bot_id}")
|
|
424
427
|
|
|
425
428
|
# =============================================================================
|
|
426
429
|
# ОБРАБОТЧИКИ СОБЫТИЙ
|
|
427
430
|
# =============================================================================
|
|
428
431
|
|
|
429
|
-
@event_handler("example_event"
|
|
430
|
-
async def handle_example_event(user_id: int, event_data:
|
|
432
|
+
@router.event_handler("example_event")
|
|
433
|
+
async def handle_example_event(user_id: int, event_data: str):
|
|
431
434
|
"""Пример обработчика события"""
|
|
432
435
|
# Отправляем подтверждение пользователю
|
|
433
436
|
await send_message_by_human(
|
|
434
437
|
user_id=user_id,
|
|
435
|
-
message_text="✅ Событие обработано!"
|
|
438
|
+
message_text=f"✅ Событие обработано! Данные: {{event_data}}"
|
|
436
439
|
)
|
|
437
440
|
|
|
438
441
|
return {{
|
|
@@ -441,24 +444,82 @@ async def handle_example_event(user_id: int, event_data: dict):
|
|
|
441
444
|
}}
|
|
442
445
|
|
|
443
446
|
# =============================================================================
|
|
444
|
-
#
|
|
447
|
+
# ВРЕМЕННЫЕ ЗАДАЧИ ДЛЯ ОДНОГО ПОЛЬЗОВАТЕЛЯ
|
|
445
448
|
# =============================================================================
|
|
446
449
|
|
|
447
|
-
@schedule_task("
|
|
448
|
-
async def
|
|
449
|
-
"""
|
|
450
|
-
# Отправляем сообщение
|
|
450
|
+
@router.schedule_task("send_reminder")
|
|
451
|
+
async def send_user_reminder(user_id: int, reminder_text: str):
|
|
452
|
+
"""Отправляет напоминание пользователю"""
|
|
451
453
|
await send_message_by_human(
|
|
452
454
|
user_id=user_id,
|
|
453
|
-
message_text=f"🔔 Напоминание: {{
|
|
455
|
+
message_text=f"🔔 Напоминание: {{reminder_text}}"
|
|
454
456
|
)
|
|
455
457
|
|
|
456
458
|
return {{
|
|
457
|
-
"status": "
|
|
458
|
-
"
|
|
459
|
-
"message": message
|
|
459
|
+
"status": "reminder_sent",
|
|
460
|
+
"message": f"Напоминание отправлено пользователю {{user_id}}"
|
|
460
461
|
}}
|
|
461
462
|
|
|
463
|
+
# =============================================================================
|
|
464
|
+
# ГЛОБАЛЬНЫЕ ОБРАБОТЧИКИ (для всех пользователей)
|
|
465
|
+
# =============================================================================
|
|
466
|
+
|
|
467
|
+
@router.global_handler("mass_notification", notify=True)
|
|
468
|
+
async def send_global_announcement(announcement_text: str):
|
|
469
|
+
"""Отправляет анонс всем пользователям бота"""
|
|
470
|
+
import logging
|
|
471
|
+
logger = logging.getLogger(__name__)
|
|
472
|
+
|
|
473
|
+
logger.info(f"🚀 Начинаем глобальную рассылку: '{{announcement_text[:50]}}...'")
|
|
474
|
+
|
|
475
|
+
# Проверяем доступность клиента
|
|
476
|
+
if not supabase_client:
|
|
477
|
+
logger.error("❌ Supabase клиент не найден для глобальной рассылки")
|
|
478
|
+
return {{"status": "error", "message": "Supabase клиент не найден"}}
|
|
479
|
+
|
|
480
|
+
try:
|
|
481
|
+
# Получаем всех пользователей из БД с учетом bot_id (изоляция данных)
|
|
482
|
+
users_response = supabase_client.client.table('sales_users').select(
|
|
483
|
+
'telegram_id'
|
|
484
|
+
).eq('bot_id', supabase_client.bot_id).execute()
|
|
485
|
+
|
|
486
|
+
if not users_response.data:
|
|
487
|
+
logger.warning("⚠️ Пользователи не найдены для глобальной рассылки")
|
|
488
|
+
return {{"status": "no_users", "message": "Пользователи не найдены"}}
|
|
489
|
+
|
|
490
|
+
total_users = len(users_response.data)
|
|
491
|
+
logger.info(f"👥 Найдено {{total_users}} пользователей для рассылки")
|
|
492
|
+
|
|
493
|
+
# Отправляем сообщение каждому пользователю
|
|
494
|
+
sent_count = 0
|
|
495
|
+
failed_count = 0
|
|
496
|
+
|
|
497
|
+
for user in users_response.data:
|
|
498
|
+
try:
|
|
499
|
+
await send_message_by_human(
|
|
500
|
+
user_id=user['telegram_id'],
|
|
501
|
+
message_text=f"📢 {{announcement_text}}"
|
|
502
|
+
)
|
|
503
|
+
sent_count += 1
|
|
504
|
+
# Небольшая задержка между отправками
|
|
505
|
+
await asyncio.sleep(0.1)
|
|
506
|
+
|
|
507
|
+
except Exception as e:
|
|
508
|
+
logger.error(f"❌ Ошибка отправки пользователю {{user['telegram_id']}}: {{e}}")
|
|
509
|
+
failed_count += 1
|
|
510
|
+
|
|
511
|
+
return {{
|
|
512
|
+
"status": "completed",
|
|
513
|
+
"sent_count": sent_count,
|
|
514
|
+
"failed_count": failed_count,
|
|
515
|
+
"total_users": total_users,
|
|
516
|
+
"message": f"Рассылка завершена: {{sent_count}} отправлено, {{failed_count}} ошибок"
|
|
517
|
+
}}
|
|
518
|
+
|
|
519
|
+
except Exception as e:
|
|
520
|
+
logger.error(f"❌ Критическая ошибка глобальной рассылки: {{e}}")
|
|
521
|
+
return {{"status": "error", "message": str(e)}}
|
|
522
|
+
|
|
462
523
|
# =============================================================================
|
|
463
524
|
# ОСНОВНАЯ ФУНКЦИЯ
|
|
464
525
|
# =============================================================================
|
|
@@ -468,6 +529,10 @@ async def main():
|
|
|
468
529
|
try:
|
|
469
530
|
# Создаем и собираем бота
|
|
470
531
|
bot_builder = BotBuilder("{bot_id}")
|
|
532
|
+
|
|
533
|
+
# Регистрируем роутер ПЕРЕД сборкой, чтобы обработчики были доступны
|
|
534
|
+
bot_builder.register_router(router)
|
|
535
|
+
|
|
471
536
|
await bot_builder.build()
|
|
472
537
|
|
|
473
538
|
# Запускаем бота
|
|
@@ -543,12 +608,12 @@ def copy_from_growthmed_template(bot_dir: Path, bot_id: str):
|
|
|
543
608
|
if source_prompts.exists():
|
|
544
609
|
for prompt_file in source_prompts.glob("*.txt"):
|
|
545
610
|
shutil.copy2(prompt_file, target_prompts / prompt_file.name)
|
|
546
|
-
click.echo("Промпты скопированы из growthmed-october-24")
|
|
611
|
+
click.echo("📝 Промпты скопированы из growthmed-october-24")
|
|
547
612
|
else:
|
|
548
613
|
click.echo(f"⚠️ Папка промптов не найдена: {source_prompts}")
|
|
549
614
|
# Fallback к базовым промптам
|
|
550
615
|
create_basic_prompts(target_prompts)
|
|
551
|
-
click.echo("Созданы базовые промпты")
|
|
616
|
+
click.echo("📝 Созданы базовые промпты")
|
|
552
617
|
|
|
553
618
|
# Копируем тесты из growthmed-october-24
|
|
554
619
|
source_tests = Path(__file__).parent / "configs" / "growthmed-october-24" / "tests"
|
|
@@ -558,7 +623,7 @@ def copy_from_growthmed_template(bot_dir: Path, bot_id: str):
|
|
|
558
623
|
for test_file in source_tests.glob("*"):
|
|
559
624
|
if test_file.is_file():
|
|
560
625
|
shutil.copy2(test_file, target_tests / test_file.name)
|
|
561
|
-
click.echo("Тесты скопированы из growthmed-october-24")
|
|
626
|
+
click.echo("🧪 Тесты скопированы из growthmed-october-24")
|
|
562
627
|
|
|
563
628
|
# Копируем welcome_files из growthmed-october-24
|
|
564
629
|
source_welcome = Path(__file__).parent / "configs" / "growthmed-october-24" / "welcome_file"
|
|
@@ -568,7 +633,7 @@ def copy_from_growthmed_template(bot_dir: Path, bot_id: str):
|
|
|
568
633
|
for welcome_file in source_welcome.glob("*"):
|
|
569
634
|
if welcome_file.is_file():
|
|
570
635
|
shutil.copy2(welcome_file, target_welcome / welcome_file.name)
|
|
571
|
-
click.echo("Welcome файлы скопированы из growthmed-october-24")
|
|
636
|
+
click.echo("📁 Welcome файлы скопированы из growthmed-october-24")
|
|
572
637
|
|
|
573
638
|
# Копируем files из growthmed-october-24
|
|
574
639
|
source_files = Path(__file__).parent / "configs" / "growthmed-october-24" / "files"
|
|
@@ -578,10 +643,10 @@ def copy_from_growthmed_template(bot_dir: Path, bot_id: str):
|
|
|
578
643
|
for file_item in source_files.glob("*"):
|
|
579
644
|
if file_item.is_file():
|
|
580
645
|
shutil.copy2(file_item, target_files / file_item.name)
|
|
581
|
-
click.echo("Файлы скопированы из growthmed-october-24")
|
|
646
|
+
click.echo("📎 Файлы скопированы из growthmed-october-24")
|
|
582
647
|
|
|
583
648
|
except Exception as e:
|
|
584
|
-
click.echo(f"Ошибка при копировании шаблона: {e}")
|
|
649
|
+
click.echo(f"❌ Ошибка при копировании шаблона: {e}")
|
|
585
650
|
# Fallback к базовым промптам
|
|
586
651
|
create_basic_prompts(bot_dir / "prompts")
|
|
587
652
|
|
|
@@ -615,18 +680,10 @@ def copy_bot_template(source_bot_id: str, new_bot_id: str):
|
|
|
615
680
|
new_bot_file.write_text(content, encoding='utf-8')
|
|
616
681
|
click.echo(f" 📄 Файл запускалки скопирован: {new_bot_id}.py")
|
|
617
682
|
|
|
618
|
-
#
|
|
619
|
-
source_env = source_dir / ".env"
|
|
683
|
+
# Создаем шаблон .env файла (НЕ копируем существующий)
|
|
620
684
|
new_env = new_dir / ".env"
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
shutil.copy2(source_env, new_env)
|
|
624
|
-
|
|
625
|
-
# Заменяем BOT_ID в .env
|
|
626
|
-
env_content = new_env.read_text(encoding='utf-8')
|
|
627
|
-
env_content = env_content.replace(f'BOT_ID={source_bot_id}', f'BOT_ID={new_bot_id}')
|
|
628
|
-
new_env.write_text(env_content, encoding='utf-8')
|
|
629
|
-
click.echo(f" ⚙️ .env файл скопирован и обновлен")
|
|
685
|
+
new_env.write_text(create_env_template(new_bot_id), encoding='utf-8')
|
|
686
|
+
click.echo(f" ⚙️ Создан шаблон .env файла")
|
|
630
687
|
|
|
631
688
|
# Копируем промпты
|
|
632
689
|
source_prompts = source_dir / "prompts"
|
|
@@ -668,7 +725,7 @@ def copy_bot_template(source_bot_id: str, new_bot_id: str):
|
|
|
668
725
|
click.echo(f" 📎 Файлы скопированы")
|
|
669
726
|
|
|
670
727
|
except Exception as e:
|
|
671
|
-
click.echo(f"Ошибка при копировании бота: {e}")
|
|
728
|
+
click.echo(f"❌ Ошибка при копировании бота: {e}")
|
|
672
729
|
raise
|
|
673
730
|
|
|
674
731
|
def copy_from_bot_template(template: str, bot_dir: Path, bot_id: str):
|
|
@@ -711,10 +768,10 @@ def copy_from_bot_template(template: str, bot_dir: Path, bot_id: str):
|
|
|
711
768
|
if test_file.is_file():
|
|
712
769
|
shutil.copy2(test_file, target_tests / test_file.name)
|
|
713
770
|
|
|
714
|
-
click.echo(f"Шаблон скопирован из {template}")
|
|
771
|
+
click.echo(f"📋 Шаблон скопирован из {template}")
|
|
715
772
|
|
|
716
773
|
except Exception as e:
|
|
717
|
-
click.echo(f"Ошибка при копировании шаблона {template}: {e}")
|
|
774
|
+
click.echo(f"❌ Ошибка при копировании шаблона {template}: {e}")
|
|
718
775
|
raise
|
|
719
776
|
|
|
720
777
|
def create_basic_prompts(prompts_dir: Path):
|
|
@@ -759,6 +816,11 @@ def create_basic_prompts(prompts_dir: Path):
|
|
|
759
816
|
- example_task: Пример запланированной задачи. Используй для демонстрации.
|
|
760
817
|
Пример: {"тип": "example_task", "инфо": "через 1 час: напомнить о чем-то"}
|
|
761
818
|
|
|
819
|
+
ДОСТУПНЫЕ ГЛОБАЛЬНЫЕ ОБРАБОТЧИКИ:
|
|
820
|
+
- global_announcement: Отправляет анонс всем пользователям. Используй для важных объявлений.
|
|
821
|
+
Пример: {"тип": "global_announcement", "инфо": "3600"} - анонс через 1 час
|
|
822
|
+
Формат: "инфо" содержит время в секундах для планирования.
|
|
823
|
+
|
|
762
824
|
Используй эти обработчики и задачи, когда это уместно в диалоге.
|
|
763
825
|
</instruction>""",
|
|
764
826
|
encoding='utf-8'
|
|
@@ -73,6 +73,8 @@ id этапа бери из stages
|
|
|
73
73
|
ТРЕБОВАНИЯ К формату:
|
|
74
74
|
- Всегда используй кавычки для строк
|
|
75
75
|
- Если в инструкциях явно не указано, что надо выслать какое-то событие, отсылай пустой массив []
|
|
76
|
+
- Если надо выслать какое либо событие и в инструкции написано запустить через какое то время, то в поле инфо нужно вывести число в СЕКУНДАХ
|
|
77
|
+
- Также в событиях связанных со временем может быть дополнительная информация, например инфо='30|Привет пользователь!' - 30 это время в секундах после | доп информация
|
|
76
78
|
- Если в инструкциях явно не указано, что надо вставить в служебную информацию какой-то файл(ы) или каталоги, отсылай пустой массив
|
|
77
79
|
|
|
78
80
|
ФАЙЛЫ:
|