smart-bot-factory 1.1.1__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.
Files changed (73) hide show
  1. smart_bot_factory/__init__.py +3 -0
  2. smart_bot_factory/admin/__init__.py +18 -0
  3. smart_bot_factory/admin/admin_events.py +1223 -0
  4. smart_bot_factory/admin/admin_logic.py +553 -0
  5. smart_bot_factory/admin/admin_manager.py +156 -0
  6. smart_bot_factory/admin/admin_tester.py +157 -0
  7. smart_bot_factory/admin/timeout_checker.py +547 -0
  8. smart_bot_factory/aiogram_calendar/__init__.py +14 -0
  9. smart_bot_factory/aiogram_calendar/common.py +64 -0
  10. smart_bot_factory/aiogram_calendar/dialog_calendar.py +259 -0
  11. smart_bot_factory/aiogram_calendar/schemas.py +99 -0
  12. smart_bot_factory/aiogram_calendar/simple_calendar.py +224 -0
  13. smart_bot_factory/analytics/analytics_manager.py +414 -0
  14. smart_bot_factory/cli.py +806 -0
  15. smart_bot_factory/config.py +258 -0
  16. smart_bot_factory/configs/growthmed-october-24/prompts/1sales_context.txt +16 -0
  17. smart_bot_factory/configs/growthmed-october-24/prompts/2product_info.txt +582 -0
  18. smart_bot_factory/configs/growthmed-october-24/prompts/3objection_handling.txt +66 -0
  19. smart_bot_factory/configs/growthmed-october-24/prompts/final_instructions.txt +212 -0
  20. smart_bot_factory/configs/growthmed-october-24/prompts/help_message.txt +28 -0
  21. smart_bot_factory/configs/growthmed-october-24/prompts/welcome_message.txt +8 -0
  22. smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064229.txt +818 -0
  23. smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064335.txt +32 -0
  24. smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064638.txt +35 -0
  25. smart_bot_factory/configs/growthmed-october-24/tests/quick_scenarios.yaml +133 -0
  26. smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml +108 -0
  27. smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml +46 -0
  28. smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt +16 -0
  29. smart_bot_factory/configs/growthmed-october-24/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
  30. smart_bot_factory/core/bot_utils.py +1108 -0
  31. smart_bot_factory/core/conversation_manager.py +653 -0
  32. smart_bot_factory/core/decorators.py +2464 -0
  33. smart_bot_factory/core/message_sender.py +729 -0
  34. smart_bot_factory/core/router.py +347 -0
  35. smart_bot_factory/core/router_manager.py +218 -0
  36. smart_bot_factory/core/states.py +27 -0
  37. smart_bot_factory/creation/__init__.py +7 -0
  38. smart_bot_factory/creation/bot_builder.py +1093 -0
  39. smart_bot_factory/creation/bot_testing.py +1122 -0
  40. smart_bot_factory/dashboard/__init__.py +3 -0
  41. smart_bot_factory/event/__init__.py +7 -0
  42. smart_bot_factory/handlers/handlers.py +2013 -0
  43. smart_bot_factory/integrations/langchain_openai.py +542 -0
  44. smart_bot_factory/integrations/openai_client.py +513 -0
  45. smart_bot_factory/integrations/supabase_client.py +1678 -0
  46. smart_bot_factory/memory/__init__.py +8 -0
  47. smart_bot_factory/memory/memory_manager.py +299 -0
  48. smart_bot_factory/memory/static_memory.py +214 -0
  49. smart_bot_factory/message/__init__.py +56 -0
  50. smart_bot_factory/rag/__init__.py +5 -0
  51. smart_bot_factory/rag/decorators.py +29 -0
  52. smart_bot_factory/rag/router.py +54 -0
  53. smart_bot_factory/rag/templates/__init__.py +3 -0
  54. smart_bot_factory/rag/templates/create_table.sql +7 -0
  55. smart_bot_factory/rag/templates/create_table_and_function_template.py +94 -0
  56. smart_bot_factory/rag/templates/match_function.sql +61 -0
  57. smart_bot_factory/rag/templates/match_services_template.py +82 -0
  58. smart_bot_factory/rag/vectorstore.py +449 -0
  59. smart_bot_factory/router/__init__.py +10 -0
  60. smart_bot_factory/setup_checker.py +512 -0
  61. smart_bot_factory/supabase/__init__.py +7 -0
  62. smart_bot_factory/supabase/client.py +631 -0
  63. smart_bot_factory/utils/__init__.py +11 -0
  64. smart_bot_factory/utils/debug_routing.py +114 -0
  65. smart_bot_factory/utils/prompt_loader.py +529 -0
  66. smart_bot_factory/utils/tool_router.py +68 -0
  67. smart_bot_factory/utils/user_prompt_loader.py +55 -0
  68. smart_bot_factory/utm_link_generator.py +123 -0
  69. smart_bot_factory-1.1.1.dist-info/METADATA +1135 -0
  70. smart_bot_factory-1.1.1.dist-info/RECORD +73 -0
  71. smart_bot_factory-1.1.1.dist-info/WHEEL +4 -0
  72. smart_bot_factory-1.1.1.dist-info/entry_points.txt +2 -0
  73. smart_bot_factory-1.1.1.dist-info/licenses/LICENSE +24 -0
@@ -0,0 +1,806 @@
1
+ """
2
+ CLI интерфейс для Smart Bot Factory
3
+ """
4
+
5
+ import os
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ import click
12
+ from project_root_finder import root
13
+
14
+ PROJECT_ROOT = root
15
+
16
+
17
+ @click.group()
18
+ def cli():
19
+ """Smart Bot Factory - инструмент для создания умных чат-ботов"""
20
+ pass
21
+
22
+
23
+ @cli.command()
24
+ @click.argument("bot_id")
25
+ @click.argument("template", required=False, default="base")
26
+ def create(bot_id: str, template: str = "base"):
27
+ """Создать нового бота"""
28
+ success = create_new_bot_structure(template, bot_id)
29
+ if not success:
30
+ sys.exit(1)
31
+
32
+
33
+ @cli.command()
34
+ def list():
35
+ """Показать список доступных ботов"""
36
+ bots = list_bots_in_bots_folder()
37
+ if not bots:
38
+ click.echo("🤖 Нет доступных ботов")
39
+ return
40
+
41
+ click.echo("🤖 Доступные боты:")
42
+ for bot in sorted(bots):
43
+ click.echo(f" 📱 {bot}")
44
+
45
+
46
+ @cli.command()
47
+ @click.argument("bot_id")
48
+ def run(bot_id: str):
49
+ """Запустить бота"""
50
+ try:
51
+ # Проверяем существование бота
52
+ bot_path = PROJECT_ROOT / Path("bots") / bot_id
53
+ if not bot_path.exists():
54
+ raise click.ClickException(f"Бот {bot_id} не найден в папке bots/")
55
+
56
+ # Проверяем наличие основного файла бота в корневой директории
57
+ bot_file = PROJECT_ROOT / Path(f"{bot_id}.py")
58
+ if not bot_file.exists():
59
+ raise click.ClickException(
60
+ f"Файл {bot_id}.py не найден в корневой директории"
61
+ )
62
+
63
+ # Проверяем наличие .env файла
64
+ env_file = bot_path / ".env"
65
+ if not env_file.exists():
66
+ raise click.ClickException(f"Файл .env не найден для бота {bot_id}")
67
+
68
+ # Настраиваем окружение для бота
69
+ from dotenv import load_dotenv
70
+
71
+ # Добавляем корень проекта в sys.path
72
+ sys.path.insert(0, str(PROJECT_ROOT))
73
+
74
+ # Загружаем .env файл
75
+ load_dotenv(env_file)
76
+ click.echo(f"📄 Загружен .env файл: {env_file}")
77
+
78
+ # Устанавливаем переменные окружения
79
+ os.environ["BOT_ID"] = bot_id
80
+
81
+ # Устанавливаем путь к промптам
82
+ prompts_dir = bot_path / "prompts"
83
+ if prompts_dir.exists():
84
+ os.environ["PROMT_FILES_DIR"] = str(prompts_dir)
85
+ click.echo(f"📝 Установлен путь к промптам: {prompts_dir}")
86
+
87
+ # Запускаем бота из корневой директории
88
+ click.echo(f"🚀 Запускаем бота {bot_id}...")
89
+ subprocess.run(
90
+ [sys.executable, str(bot_file)], check=True, cwd=str(PROJECT_ROOT)
91
+ )
92
+
93
+ except subprocess.CalledProcessError as e:
94
+ click.echo(f"❌ Ошибка при запуске бота: {e}", err=True)
95
+ sys.exit(1)
96
+ except Exception as e:
97
+ click.echo(f"❌ Ошибка: {e}", err=True)
98
+ sys.exit(1)
99
+
100
+
101
+ @cli.command()
102
+ @click.argument("bot_id")
103
+ @click.option("--file", help="Запустить тесты только из указанного файла")
104
+ @click.option("-v", "--verbose", is_flag=True, help="Подробный вывод")
105
+ @click.option("--max-concurrent", default=5, help="Максимальное количество потоков")
106
+ def test(bot_id: str, file: str = None, verbose: bool = False, max_concurrent: int = 5):
107
+ """Запустить тесты бота"""
108
+ try:
109
+ # Проверяем существование бота
110
+ bot_path = PROJECT_ROOT / "bots" / bot_id
111
+ if not bot_path.exists():
112
+ raise click.ClickException(
113
+ f"Бот {bot_id} не найден в папке {PROJECT_ROOT}/bots/"
114
+ )
115
+
116
+ # Проверяем наличие тестов
117
+ tests_dir = bot_path / "tests"
118
+ if not tests_dir.exists():
119
+ click.echo(f"⚠️ Тесты не найдены для бота {bot_id}")
120
+ return
121
+
122
+ # Ищем YAML файлы с тестами
123
+ yaml_files = [str(f.name) for f in tests_dir.glob("*.yaml")]
124
+
125
+ if not yaml_files:
126
+ click.echo(f"⚠️ YAML тесты не найдены для бота {bot_id}")
127
+ return
128
+
129
+ click.echo(f"🧪 Запускаем тесты для бота {bot_id}...")
130
+
131
+ # Формируем команду для запуска
132
+ bot_testing_path = Path(__file__).parent / "creation" / "bot_testing.py"
133
+ cmd = [sys.executable, str(bot_testing_path), bot_id]
134
+
135
+ if file:
136
+ cmd.append(file)
137
+
138
+ if verbose:
139
+ cmd.append("-v")
140
+
141
+ if max_concurrent != 5:
142
+ cmd.extend(["--max-concurrent", str(max_concurrent)])
143
+
144
+ # Запускаем тесты
145
+ result = subprocess.run(cmd, check=False)
146
+
147
+ if result.returncode == 0:
148
+ click.echo("✅ Все тесты пройдены")
149
+ else:
150
+ click.echo("❌ Есть ошибки в тестах")
151
+ sys.exit(1)
152
+
153
+ except subprocess.CalledProcessError as e:
154
+ click.echo(f"❌ Ошибка при запуске тестов: {e}", err=True)
155
+ sys.exit(1)
156
+ except Exception as e:
157
+ click.echo(f"❌ Ошибка: {e}", err=True)
158
+ sys.exit(1)
159
+
160
+
161
+ @cli.command()
162
+ @click.argument("bot_id")
163
+ def config(bot_id: str):
164
+ """Настроить конфигурацию бота"""
165
+ try:
166
+ # Проверяем существование бота
167
+ bot_path = PROJECT_ROOT / Path("bots") / bot_id
168
+ if not bot_path.exists():
169
+ raise click.ClickException(f"Бот {bot_id} не найден в папке bots/")
170
+
171
+ # Открываем .env файл в редакторе
172
+ env_file = bot_path / ".env"
173
+ if not env_file.exists():
174
+ raise click.ClickException(f"Файл .env не найден для бота {bot_id}")
175
+
176
+ # Определяем редактор
177
+ editor = os.environ.get("EDITOR", "notepad" if os.name == "nt" else "nano")
178
+
179
+ click.echo(f"⚙️ Открываем конфигурацию бота {bot_id}...")
180
+ subprocess.run([editor, str(env_file)], check=True)
181
+
182
+ except subprocess.CalledProcessError as e:
183
+ click.echo(f"❌ Ошибка при открытии редактора: {e}", err=True)
184
+ sys.exit(1)
185
+ except Exception as e:
186
+ click.echo(f"❌ Ошибка: {e}", err=True)
187
+ sys.exit(1)
188
+
189
+
190
+ @cli.command()
191
+ @click.argument("bot_id")
192
+ @click.option("--list", "list_prompts", is_flag=True, help="Показать список промптов")
193
+ @click.option("--edit", "edit_prompt", help="Редактировать промпт")
194
+ @click.option("--add", "add_prompt", help="Добавить новый промпт")
195
+ def prompts(
196
+ bot_id: str,
197
+ list_prompts: bool = False,
198
+ edit_prompt: str = None,
199
+ add_prompt: str = None,
200
+ ):
201
+ """Управление промптами бота"""
202
+ try:
203
+ # Проверяем существование бота
204
+ bot_path = PROJECT_ROOT / Path("bots") / bot_id
205
+ if not bot_path.exists():
206
+ raise click.ClickException(f"Бот {bot_id} не найден в папке bots/")
207
+
208
+ prompts_dir = bot_path / "prompts"
209
+ if not prompts_dir.exists():
210
+ raise click.ClickException(f"Папка промптов не найдена для бота {bot_id}")
211
+
212
+ if list_prompts or (not edit_prompt and not add_prompt):
213
+ # Показываем список промптов (по умолчанию или с флагом --list)
214
+ prompt_files = [f.name for f in prompts_dir.glob("*.txt")]
215
+
216
+ if not prompt_files:
217
+ click.echo("📝 Промпты не найдены")
218
+ return
219
+
220
+ click.echo(f"📝 Промпты бота {bot_id}:")
221
+ for prompt_file in sorted(prompt_files):
222
+ click.echo(f" 📄 {prompt_file[:-4]}")
223
+
224
+ # Показываем справку только если не указан флаг --list
225
+ if not list_prompts:
226
+ click.echo()
227
+ click.echo("Использование:")
228
+ click.echo(
229
+ " sbf prompts <bot_id> --edit <prompt_name> # Редактировать промпт"
230
+ )
231
+ click.echo(
232
+ " sbf prompts <bot_id> --add <prompt_name> # Добавить новый промпт"
233
+ )
234
+
235
+ elif edit_prompt:
236
+ # Редактируем промпт
237
+ prompt_file = prompts_dir / f"{edit_prompt}.txt"
238
+ if not prompt_file.exists():
239
+ raise click.ClickException(f"Промпт {edit_prompt} не найден")
240
+
241
+ editor = os.environ.get("EDITOR", "notepad" if os.name == "nt" else "nano")
242
+ click.echo(f"✏️ Редактируем промпт {edit_prompt}...")
243
+ subprocess.run([editor, str(prompt_file)], check=True)
244
+
245
+ elif add_prompt:
246
+ # Добавляем новый промпт
247
+ prompt_file = prompts_dir / f"{add_prompt}.txt"
248
+ if prompt_file.exists():
249
+ raise click.ClickException(f"Промпт {add_prompt} уже существует")
250
+
251
+ # Создаем файл с базовым содержимым
252
+ prompt_file.write_text(
253
+ f"# Промпт: {add_prompt}\n\n" "Введите содержимое промпта здесь...",
254
+ encoding="utf-8",
255
+ )
256
+
257
+ # Открываем в редакторе
258
+ editor = os.environ.get("EDITOR", "notepad" if os.name == "nt" else "nano")
259
+ click.echo(f"📝 Создаем новый промпт {add_prompt}...")
260
+ subprocess.run([editor, str(prompt_file)], check=True)
261
+
262
+ except subprocess.CalledProcessError as e:
263
+ click.echo(f"❌ Ошибка при открытии редактора: {e}", err=True)
264
+ sys.exit(1)
265
+ except Exception as e:
266
+ click.echo(f"❌ Ошибка: {e}", err=True)
267
+ sys.exit(1)
268
+
269
+
270
+ @cli.command()
271
+ def path():
272
+ """Показать путь к проекту"""
273
+ click.echo(PROJECT_ROOT)
274
+
275
+
276
+ @cli.command()
277
+ @click.argument("bot_id")
278
+ @click.option("--force", "-f", is_flag=True, help="Удалить без подтверждения")
279
+ def rm(bot_id: str, force: bool = False):
280
+ """Удалить бота и все его файлы"""
281
+ try:
282
+ # Проверяем существование бота
283
+ bot_path = PROJECT_ROOT / Path("bots") / bot_id
284
+ if not bot_path.exists():
285
+ raise click.ClickException(f"🤖 Бот {bot_id} не найден в папке bots/")
286
+
287
+ # Проверяем наличие основного файла бота в корневой директории
288
+ bot_file = Path(f"{bot_id}.py")
289
+ if not bot_file.exists():
290
+ raise click.ClickException(
291
+ f"📄 Файл {bot_id}.py не найден в корневой директории"
292
+ )
293
+
294
+ # Показываем что будет удалено
295
+ click.echo("🗑️ Будет удалено:")
296
+ click.echo(f" 📄 Файл запускалки: {bot_file}")
297
+ click.echo(f" 📁 Папка бота: {bot_path}")
298
+
299
+ # Запрашиваем подтверждение если не указан --force
300
+ if not force:
301
+ if not click.confirm(f"⚠️ Вы уверены, что хотите удалить бота {bot_id}?"):
302
+ click.echo("❌ Удаление отменено")
303
+ return
304
+
305
+ # Удаляем файл запускалки
306
+ if bot_file.exists():
307
+ bot_file.unlink()
308
+ click.echo(f"✅ Файл {bot_file} удален")
309
+
310
+ # Удаляем папку бота
311
+ if bot_path.exists():
312
+ import shutil
313
+
314
+ shutil.rmtree(bot_path)
315
+ click.echo(f"✅ Папка {bot_path} удалена")
316
+
317
+ click.echo(f"🎉 Бот {bot_id} полностью удален")
318
+
319
+ except Exception as e:
320
+ click.echo(f"❌ Ошибка при удалении бота: {e}", err=True)
321
+ sys.exit(1)
322
+
323
+
324
+ @cli.command()
325
+ @click.argument("source_bot_id")
326
+ @click.argument("new_bot_id")
327
+ @click.option(
328
+ "--force",
329
+ "-f",
330
+ is_flag=True,
331
+ help="Перезаписать существующего бота без подтверждения",
332
+ )
333
+ def copy(source_bot_id: str, new_bot_id: str, force: bool = False):
334
+ """Скопировать существующего бота как шаблон"""
335
+ try:
336
+ # Проверяем существование исходного бота
337
+ source_bot_path = PROJECT_ROOT / "bots" / source_bot_id
338
+ if not source_bot_path.exists():
339
+ raise click.ClickException(
340
+ f"Исходный бот {source_bot_id} не найден в папке bots/"
341
+ )
342
+
343
+ # Проверяем наличие файла запускалки исходного бота
344
+ source_bot_file = PROJECT_ROOT / f"{source_bot_id}.py"
345
+ if not source_bot_file.exists():
346
+ raise click.ClickException(
347
+ f"Файл запускалки {source_bot_id}.py не найден в корневой директории"
348
+ )
349
+
350
+ # Проверяем, не существует ли уже новый бот
351
+ new_bot_path = PROJECT_ROOT / "bots" / new_bot_id
352
+ new_bot_file = PROJECT_ROOT / f"{new_bot_id}.py"
353
+
354
+ if new_bot_path.exists() or new_bot_file.exists():
355
+ if not force:
356
+ if not click.confirm(f"Бот {new_bot_id} уже существует. Перезаписать?"):
357
+ click.echo("Копирование отменено")
358
+ return
359
+ else:
360
+ click.echo(f"⚠️ Перезаписываем существующего бота {new_bot_id}")
361
+
362
+ # Копируем бота
363
+ click.echo(f"📋 Копируем бота {source_bot_id} → {new_bot_id}...")
364
+ copy_bot_template(source_bot_id, new_bot_id)
365
+
366
+ click.echo(f"✅ Бот {new_bot_id} успешно скопирован из {source_bot_id}")
367
+ click.echo("📝 Не забудьте настроить .env файл для нового бота")
368
+
369
+ except Exception as e:
370
+ click.echo(f"❌ Ошибка при копировании бота: {e}", err=True)
371
+ sys.exit(1)
372
+
373
+
374
+ @cli.command()
375
+ def link():
376
+ """Создать UTM-ссылку для бота"""
377
+ try:
378
+ # Проверяем наличие скрипта генерации ссылок
379
+ link_script = Path(__file__).parent / "utm_link_generator.py"
380
+ if not link_script.exists():
381
+ raise click.ClickException("Скрипт utm_link_generator.py не найден")
382
+
383
+ # Запускаем скрипт генерации ссылок
384
+ click.echo("🔗 Запускаем генератор UTM-ссылок...")
385
+ subprocess.run([sys.executable, str(link_script)], check=True)
386
+
387
+ except subprocess.CalledProcessError as e:
388
+ click.echo(f"❌ Ошибка при запуске генератора ссылок: {e}", err=True)
389
+ sys.exit(1)
390
+ except Exception as e:
391
+ click.echo(f"❌ Ошибка: {e}", err=True)
392
+ sys.exit(1)
393
+
394
+
395
+ def create_new_bot_structure(template: str, bot_id: str) -> bool:
396
+ """Создает новую структуру бота в папке bots/"""
397
+ try:
398
+ # Создаем папку bots если её нет
399
+ bots_dir = PROJECT_ROOT / Path("bots")
400
+ bots_dir.mkdir(exist_ok=True)
401
+
402
+ # Создаем папку для нового бота
403
+ bot_dir = bots_dir / bot_id
404
+ if bot_dir.exists():
405
+ click.echo(f"⚠️ Бот {bot_id} уже существует")
406
+ return False
407
+
408
+ bot_dir.mkdir()
409
+
410
+ # Создаем структуру папок
411
+ (bot_dir / "prompts").mkdir()
412
+ (bot_dir / "tests").mkdir()
413
+ (bot_dir / "reports").mkdir()
414
+ (bot_dir / "welcome_files").mkdir()
415
+ (bot_dir / "files").mkdir()
416
+
417
+ if template == "base":
418
+ # Используем growthmed-october-24 как базовый шаблон
419
+ copy_from_growthmed_template(bot_dir, bot_id)
420
+ else:
421
+ # Используем другой шаблон из папки bots
422
+ copy_from_bot_template(template, bot_dir, bot_id)
423
+
424
+ click.echo(f"✅ Бот {bot_id} создан в папке bots/{bot_id}/")
425
+ click.echo("📝 Не забудьте настроить .env файл перед запуском")
426
+ return True
427
+
428
+ except Exception as e:
429
+ click.echo(f"❌ Ошибка при создании бота: {e}")
430
+ return False
431
+
432
+
433
+ def list_bots_in_bots_folder() -> list:
434
+ """Возвращает список ботов из папки bots/"""
435
+ bots_dir = PROJECT_ROOT / Path("bots")
436
+ if not bots_dir.exists():
437
+ return []
438
+
439
+ bots = []
440
+ for item in bots_dir.iterdir():
441
+ if item.is_dir() and Path(f"{item.name}.py").exists():
442
+ bots.append(item.name)
443
+
444
+ return bots
445
+
446
+
447
+ def create_bot_template(bot_id: str) -> str:
448
+ """Создает шаблон основного файла бота"""
449
+ return f'''"""
450
+ {bot_id.replace("-", " ").title()} Bot - Умный Telegram бот на Smart Bot Factory
451
+ """
452
+
453
+ import asyncio
454
+ from smart_bot_factory.router import EventRouter
455
+ from smart_bot_factory.message import send_message_by_human
456
+ from smart_bot_factory.creation import BotBuilder
457
+
458
+ # Инициализация
459
+ event_router = EventRouter("{bot_id}")
460
+ bot_builder = BotBuilder("{bot_id}")
461
+
462
+ # =============================================================================
463
+ # ОБРАБОТЧИКИ СОБЫТИЙ
464
+ # =============================================================================
465
+
466
+ @event_router.event_handler("collect_contact", notify=True, once_only=True)
467
+ async def handle_contact(user_id: int, contact_data: str):
468
+ """
469
+ Обрабатывает получение контактных данных
470
+
471
+ ИИ создает: {{"тип": "collect_contact", "инфо": "+79001234567"}}
472
+ """
473
+ await send_message_by_human(
474
+ user_id=user_id,
475
+ message_text=f"✅ Спасибо! Ваши данные сохранены: {{contact_data}}"
476
+ )
477
+
478
+ return {{"status": "success", "contact": contact_data}}
479
+
480
+ # =============================================================================
481
+ # ЗАПУСК
482
+ # =============================================================================
483
+
484
+ async def main():
485
+ # ========== РЕГИСТРАЦИЯ РОУТЕРОВ ==========
486
+ bot_builder.register_routers(event_router)
487
+
488
+ # Можно добавить Telegram роутеры:
489
+ # from aiogram import Router
490
+ # telegram_router = Router(name="commands")
491
+ # bot_builder.register_telegram_router(telegram_router)
492
+
493
+ # ========== КАСТОМИЗАЦИЯ (до build) ==========
494
+ # Установить кастомный PromptLoader:
495
+ # from smart_bot_factory.utils import UserPromptLoader
496
+ # custom_loader = UserPromptLoader("{bot_id}")
497
+ # bot_builder.set_prompt_loader(custom_loader)
498
+
499
+ # ========== СБОРКА И ЗАПУСК ==========
500
+ await bot_builder.build()
501
+ await bot_builder.start()
502
+
503
+ if __name__ == "__main__":
504
+ asyncio.run(main())
505
+ '''
506
+
507
+
508
+ def create_env_template(bot_id: str) -> str:
509
+ """Создает шаблон .env файла"""
510
+ return f"""# Telegram
511
+ TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
512
+
513
+ # Supabase
514
+ SUPABASE_URL=https://your-project.supabase.co
515
+ SUPABASE_KEY=your_supabase_anon_key
516
+
517
+ # OpenAI
518
+ OPENAI_API_KEY=sk-your-openai-api-key
519
+ OPENAI_MODEL=gpt-5-mini
520
+ OPENAI_MAX_TOKENS=1500
521
+ OPENAI_TEMPERATURE=0.7
522
+
523
+ # Промпты (каталог)
524
+ PROMT_FILES_DIR=prompts
525
+
526
+ # Файл после приветствия с подписью (если он есть - грузим его в папку welcome_file, если нет - ничего не делаем)
527
+ WELCOME_FILE_URL=welcome_files/
528
+ WELCOME_FILE_MSG=welcome_file_msg.txt
529
+
530
+ # 🆕 Администраторы (через запятую)
531
+ # Укажите Telegram ID админов
532
+ ADMIN_TELEGRAM_IDS=123456789,987654321
533
+ ADMIN_SESSION_TIMEOUT_MINUTES=30
534
+
535
+ # 🆕 Режим отладки (показывать JSON пользователям)
536
+ DEBUG_MODE=false
537
+
538
+ # Дополнительные настройки
539
+ MAX_CONTEXT_MESSAGES=50
540
+ LOG_LEVEL=INFO
541
+ MESSAGE_PARSE_MODE=Markdown
542
+
543
+ # Настройки продаж
544
+ LEAD_QUALIFICATION_THRESHOLD=7
545
+ SESSION_TIMEOUT_HOURS=24
546
+
547
+ # ⚠️ ВАЖНО: BOT_ID теперь НЕ нужен в .env!
548
+ # Bot ID автоматически определяется из имени файла запускалки
549
+ # Например: python {bot_id}.py → BOT_ID = {bot_id}
550
+ """
551
+
552
+
553
+ def copy_from_growthmed_template(bot_dir: Path, bot_id: str):
554
+ """Копирует шаблон из growthmed-october-24"""
555
+ try:
556
+ # Создаем основной файл бота в корневой директории проекта
557
+ bot_file = PROJECT_ROOT / Path(f"{bot_id}.py")
558
+ bot_file.write_text(create_bot_template(bot_id), encoding="utf-8")
559
+
560
+ # Создаем .env файл в папке бота (НЕ копируем из шаблона)
561
+ env_file = bot_dir / ".env"
562
+ env_file.write_text(create_env_template(bot_id), encoding="utf-8")
563
+
564
+ # Копируем промпты из growthmed-october-24
565
+ source_prompts = (
566
+ Path(__file__).parent / "configs" / "growthmed-october-24" / "prompts"
567
+ )
568
+ target_prompts = bot_dir / "prompts"
569
+
570
+ if source_prompts.exists():
571
+ for prompt_file in source_prompts.glob("*.txt"):
572
+ shutil.copy2(prompt_file, target_prompts / prompt_file.name)
573
+ click.echo("📝 Промпты скопированы из growthmed-october-24")
574
+ else:
575
+ click.echo(f"⚠️ Папка промптов не найдена: {source_prompts}")
576
+ # Fallback к базовым промптам
577
+ create_basic_prompts(target_prompts)
578
+ click.echo("📝 Созданы базовые промпты")
579
+
580
+ # Копируем тесты из growthmed-october-24
581
+ source_tests = (
582
+ Path(__file__).parent / "configs" / "growthmed-october-24" / "tests"
583
+ )
584
+ target_tests = bot_dir / "tests"
585
+
586
+ if source_tests.exists():
587
+ for test_file in source_tests.glob("*"):
588
+ if test_file.is_file():
589
+ shutil.copy2(test_file, target_tests / test_file.name)
590
+ click.echo("🧪 Тесты скопированы из growthmed-october-24")
591
+
592
+ # Копируем welcome_files из growthmed-october-24
593
+ source_welcome = (
594
+ Path(__file__).parent / "configs" / "growthmed-october-24" / "welcome_file"
595
+ )
596
+ target_welcome = bot_dir / "welcome_files"
597
+
598
+ if source_welcome.exists():
599
+ for welcome_file in source_welcome.glob("*"):
600
+ if welcome_file.is_file():
601
+ shutil.copy2(welcome_file, target_welcome / welcome_file.name)
602
+ click.echo("📁 Welcome файлы скопированы из growthmed-october-24")
603
+
604
+ # Копируем files из growthmed-october-24
605
+ source_files = (
606
+ Path(__file__).parent / "configs" / "growthmed-october-24" / "files"
607
+ )
608
+ target_files = bot_dir / "files"
609
+
610
+ if source_files.exists():
611
+ for file_item in source_files.glob("*"):
612
+ if file_item.is_file():
613
+ shutil.copy2(file_item, target_files / file_item.name)
614
+ click.echo("📎 Файлы скопированы из growthmed-october-24")
615
+
616
+ except Exception as e:
617
+ click.echo(f"❌ Ошибка при копировании шаблона: {e}")
618
+ # Fallback к базовым промптам
619
+ create_basic_prompts(bot_dir / "prompts")
620
+
621
+
622
+ def copy_bot_template(source_bot_id: str, new_bot_id: str):
623
+ """Копирует существующего бота как шаблон для нового бота"""
624
+ try:
625
+ source_dir = PROJECT_ROOT / "bots" / source_bot_id
626
+ new_dir = PROJECT_ROOT / "bots" / new_bot_id
627
+
628
+ # Создаем папку для нового бота
629
+ new_dir.mkdir(exist_ok=True)
630
+
631
+ # Создаем структуру папок
632
+ (new_dir / "prompts").mkdir(exist_ok=True)
633
+ (new_dir / "tests").mkdir(exist_ok=True)
634
+ (new_dir / "reports").mkdir(exist_ok=True)
635
+ (new_dir / "welcome_files").mkdir(exist_ok=True)
636
+ (new_dir / "files").mkdir(exist_ok=True)
637
+
638
+ # Копируем основной файл бота в корневую директорию
639
+ source_bot_file = PROJECT_ROOT / f"{source_bot_id}.py"
640
+ new_bot_file = PROJECT_ROOT / f"{new_bot_id}.py"
641
+
642
+ if source_bot_file.exists():
643
+ shutil.copy2(source_bot_file, new_bot_file)
644
+
645
+ # Заменяем название бота в файле
646
+ content = new_bot_file.read_text(encoding="utf-8")
647
+ content = content.replace(
648
+ f'BotBuilder("{source_bot_id}")', f'BotBuilder("{new_bot_id}")'
649
+ )
650
+ content = content.replace(
651
+ f'bot_id="{source_bot_id}"', f'bot_id="{new_bot_id}"'
652
+ )
653
+ new_bot_file.write_text(content, encoding="utf-8")
654
+ click.echo(f" 📄 Файл запускалки скопирован: {new_bot_id}.py")
655
+
656
+ # Создаем шаблон .env файла (НЕ копируем существующий)
657
+ new_env = new_dir / ".env"
658
+ new_env.write_text(create_env_template(new_bot_id), encoding="utf-8")
659
+ click.echo(" ⚙️ Создан шаблон .env файла")
660
+
661
+ # Копируем промпты
662
+ source_prompts = source_dir / "prompts"
663
+ new_prompts = new_dir / "prompts"
664
+
665
+ if source_prompts.exists():
666
+ for prompt_file in source_prompts.glob("*.txt"):
667
+ shutil.copy2(prompt_file, new_prompts / prompt_file.name)
668
+ click.echo(" 📝 Промпты скопированы")
669
+
670
+ # Копируем тесты
671
+ source_tests = source_dir / "tests"
672
+ new_tests = new_dir / "tests"
673
+
674
+ if source_tests.exists():
675
+ for test_file in source_tests.glob("*"):
676
+ if test_file.is_file():
677
+ shutil.copy2(test_file, new_tests / test_file.name)
678
+ click.echo(" 🧪 Тесты скопированы")
679
+
680
+ # Копируем welcome_files
681
+ source_welcome = source_dir / "welcome_files"
682
+ new_welcome = new_dir / "welcome_files"
683
+
684
+ if source_welcome.exists():
685
+ for welcome_file in source_welcome.glob("*"):
686
+ if welcome_file.is_file():
687
+ shutil.copy2(welcome_file, new_welcome / welcome_file.name)
688
+ click.echo(" 📁 Welcome файлы скопированы")
689
+
690
+ # Копируем files
691
+ source_files = source_dir / "files"
692
+ new_files = new_dir / "files"
693
+
694
+ if source_files.exists():
695
+ for file_item in source_files.glob("*"):
696
+ if file_item.is_file():
697
+ shutil.copy2(file_item, new_files / file_item.name)
698
+ click.echo(" 📎 Файлы скопированы")
699
+
700
+ except Exception as e:
701
+ click.echo(f"❌ Ошибка при копировании бота: {e}")
702
+ raise
703
+
704
+
705
+ def copy_from_bot_template(template: str, bot_dir: Path, bot_id: str):
706
+ """Копирует шаблон из существующего бота (для команды create)"""
707
+ try:
708
+ template_dir = PROJECT_ROOT / Path("bots") / template
709
+ if not template_dir.exists():
710
+ raise click.ClickException(f"Шаблон {template} не найден")
711
+
712
+ # Копируем основной файл бота в корневую директорию
713
+ template_bot_file = PROJECT_ROOT / Path(f"{template}.py")
714
+ if template_bot_file.exists():
715
+ bot_file = PROJECT_ROOT / Path(f"{bot_id}.py")
716
+ shutil.copy2(template_bot_file, bot_file)
717
+
718
+ # Заменяем название бота в файле
719
+ content = bot_file.read_text(encoding="utf-8")
720
+ content = content.replace(
721
+ f'BotBuilder("{template}")', f'BotBuilder("{bot_id}")'
722
+ )
723
+ content = content.replace(f'bot_id="{template}"', f'bot_id="{bot_id}"')
724
+ bot_file.write_text(content, encoding="utf-8")
725
+
726
+ # Создаем .env файл в папке бота (НЕ копируем из шаблона)
727
+ env_file = bot_dir / ".env"
728
+ env_file.write_text(create_env_template(bot_id), encoding="utf-8")
729
+
730
+ # Копируем промпты
731
+ template_prompts = template_dir / "prompts"
732
+ target_prompts = bot_dir / "prompts"
733
+
734
+ if template_prompts.exists():
735
+ for prompt_file in template_prompts.glob("*.txt"):
736
+ shutil.copy2(prompt_file, target_prompts / prompt_file.name)
737
+
738
+ # Копируем тесты
739
+ template_tests = template_dir / "tests"
740
+ target_tests = bot_dir / "tests"
741
+
742
+ if template_tests.exists():
743
+ for test_file in template_tests.glob("*"):
744
+ if test_file.is_file():
745
+ shutil.copy2(test_file, target_tests / test_file.name)
746
+
747
+ click.echo(f"📋 Шаблон скопирован из {template}")
748
+
749
+ except Exception as e:
750
+ click.echo(f"❌ Ошибка при копировании шаблона {template}: {e}")
751
+ raise
752
+
753
+
754
+ def create_basic_prompts(prompts_dir: Path):
755
+ """Создает базовые промпты"""
756
+ # Системный промпт
757
+ (prompts_dir / "system_prompt.txt").write_text(
758
+ "Ты - помощник. Твоя задача помогать пользователям с их вопросами.\n"
759
+ "Будь дружелюбным и полезным.",
760
+ encoding="utf-8",
761
+ )
762
+
763
+ # Приветственное сообщение
764
+ (prompts_dir / "welcome_message.txt").write_text(
765
+ "👋 Привет! Я ваш помощник.\n\n" "Чем могу помочь?", encoding="utf-8"
766
+ )
767
+
768
+ # Финальные инструкции
769
+ (prompts_dir / "final_instructions.txt").write_text(
770
+ """<instruction>
771
+ КРИТИЧЕСКИ ВАЖНО: В НАЧАЛЕ КАЖДОГО своего ответа добавляй служебную информацию в формате:
772
+
773
+ {
774
+ "этап": id,
775
+ "качество": 1-10,
776
+ "события": [
777
+ {
778
+ "тип": тип события,
779
+ "инфо": детали события
780
+ }
781
+ ],
782
+ "файлы": [],
783
+ "каталоги": []
784
+ }
785
+
786
+ ДОСТУПНЫЕ ОБРАБОТЧИКИ СОБЫТИЙ:
787
+ - example_event: Пример обработчика события. Используй для демонстрации.
788
+ Пример: {"тип": "example_event", "инфо": {"data": "пример данных"}}
789
+
790
+ ДОСТУПНЫЕ ЗАПЛАНИРОВАННЫЕ ЗАДАЧИ:
791
+ - example_task: Пример запланированной задачи. Используй для демонстрации.
792
+ Пример: {"тип": "example_task", "инфо": "через 1 час: напомнить о чем-то"}
793
+
794
+ ДОСТУПНЫЕ ГЛОБАЛЬНЫЕ ОБРАБОТЧИКИ:
795
+ - global_announcement: Отправляет анонс всем пользователям. Используй для важных объявлений.
796
+ Пример: {"тип": "global_announcement", "инфо": "3600"} - анонс через 1 час
797
+ Формат: "инфо" содержит время в секундах для планирования.
798
+
799
+ Используй эти обработчики и задачи, когда это уместно в диалоге.
800
+ </instruction>""",
801
+ encoding="utf-8",
802
+ )
803
+
804
+
805
+ if __name__ == "__main__":
806
+ cli()