smart-bot-factory 0.1.2__py3-none-any.whl → 0.1.3__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 +51 -0
- smart_bot_factory/admin/__init__.py +16 -0
- smart_bot_factory/admin/admin_logic.py +430 -0
- smart_bot_factory/admin/admin_manager.py +141 -0
- smart_bot_factory/admin/admin_migration.sql +136 -0
- smart_bot_factory/admin/admin_tester.py +151 -0
- smart_bot_factory/admin/timeout_checker.py +499 -0
- smart_bot_factory/analytics/__init__.py +7 -0
- smart_bot_factory/analytics/analytics_manager.py +355 -0
- smart_bot_factory/cli.py +642 -0
- smart_bot_factory/config.py +235 -0
- smart_bot_factory/configs/growthmed-helper/env_example.txt +1 -0
- smart_bot_factory/configs/growthmed-helper/prompts/1sales_context.txt +9 -0
- smart_bot_factory/configs/growthmed-helper/prompts/2product_info.txt +582 -0
- smart_bot_factory/configs/growthmed-helper/prompts/3objection_handling.txt +66 -0
- smart_bot_factory/configs/growthmed-helper/prompts/final_instructions.txt +232 -0
- smart_bot_factory/configs/growthmed-helper/prompts/help_message.txt +28 -0
- smart_bot_factory/configs/growthmed-helper/prompts/welcome_message.txt +7 -0
- smart_bot_factory/configs/growthmed-helper/welcome_file/welcome_file_msg.txt +16 -0
- 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/configs/growthmed-october-24/prompts/1sales_context.txt +16 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/2product_info.txt +582 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/3objection_handling.txt +66 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/final_instructions.txt +212 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/help_message.txt +28 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/welcome_message.txt +8 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064229.txt +818 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064335.txt +32 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064638.txt +35 -0
- smart_bot_factory/configs/growthmed-october-24/tests/quick_scenarios.yaml +66 -0
- smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml +108 -0
- smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml +46 -0
- smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt +16 -0
- 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
- smart_bot_factory/core/__init__.py +22 -0
- smart_bot_factory/core/bot_utils.py +693 -0
- smart_bot_factory/core/conversation_manager.py +536 -0
- smart_bot_factory/core/decorators.py +229 -0
- smart_bot_factory/core/message_sender.py +249 -0
- smart_bot_factory/core/states.py +14 -0
- smart_bot_factory/creation/__init__.py +8 -0
- smart_bot_factory/creation/bot_builder.py +329 -0
- smart_bot_factory/creation/bot_testing.py +986 -0
- smart_bot_factory/database/database_structure.sql +57 -0
- smart_bot_factory/database/schema.sql +1094 -0
- smart_bot_factory/handlers/handlers.py +583 -0
- smart_bot_factory/integrations/__init__.py +9 -0
- smart_bot_factory/integrations/openai_client.py +435 -0
- smart_bot_factory/integrations/supabase_client.py +592 -0
- smart_bot_factory/setup_checker.py +476 -0
- smart_bot_factory/utils/__init__.py +9 -0
- smart_bot_factory/utils/debug_routing.py +103 -0
- smart_bot_factory/utils/prompt_loader.py +427 -0
- smart_bot_factory/uv.lock +2004 -0
- smart_bot_factory-0.1.3.dist-info/METADATA +126 -0
- smart_bot_factory-0.1.3.dist-info/RECORD +59 -0
- smart_bot_factory-0.1.3.dist-info/licenses/LICENSE +24 -0
- smart_bot_factory-0.1.2.dist-info/METADATA +0 -31
- smart_bot_factory-0.1.2.dist-info/RECORD +0 -4
- {smart_bot_factory-0.1.2.dist-info → smart_bot_factory-0.1.3.dist-info}/WHEEL +0 -0
- {smart_bot_factory-0.1.2.dist-info → smart_bot_factory-0.1.3.dist-info}/entry_points.txt +0 -0
smart_bot_factory/cli.py
ADDED
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI интерфейс для Smart Bot Factory
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import click
|
|
8
|
+
import subprocess
|
|
9
|
+
import shutil
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from project_root_finder import root
|
|
13
|
+
|
|
14
|
+
PROJECT_ROOT = root
|
|
15
|
+
|
|
16
|
+
@click.group()
|
|
17
|
+
def cli():
|
|
18
|
+
"""Smart Bot Factory - инструмент для создания умных чат-ботов"""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
@cli.command()
|
|
22
|
+
@click.argument("bot_id")
|
|
23
|
+
@click.argument("template", required=False, default="base")
|
|
24
|
+
def create(bot_id: str, template: str = "base"):
|
|
25
|
+
"""Создать нового бота"""
|
|
26
|
+
success = create_new_bot_structure(template, bot_id)
|
|
27
|
+
if not success:
|
|
28
|
+
sys.exit(1)
|
|
29
|
+
|
|
30
|
+
@cli.command()
|
|
31
|
+
def list():
|
|
32
|
+
"""Показать список доступных ботов"""
|
|
33
|
+
bots = list_bots_in_bots_folder()
|
|
34
|
+
if not bots:
|
|
35
|
+
click.echo("Нет доступных ботов")
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
click.echo("Доступные боты:")
|
|
39
|
+
for bot in sorted(bots):
|
|
40
|
+
click.echo(f" - {bot}")
|
|
41
|
+
|
|
42
|
+
@cli.command()
|
|
43
|
+
@click.argument("bot_id")
|
|
44
|
+
def run(bot_id: str):
|
|
45
|
+
"""Запустить бота"""
|
|
46
|
+
try:
|
|
47
|
+
# Проверяем существование бота
|
|
48
|
+
bot_path = PROJECT_ROOT / Path("bots") / bot_id
|
|
49
|
+
if not bot_path.exists():
|
|
50
|
+
raise click.ClickException(f"Бот {bot_id} не найден в папке bots/")
|
|
51
|
+
|
|
52
|
+
# Проверяем наличие основного файла бота в корневой директории
|
|
53
|
+
bot_file = PROJECT_ROOT / Path(f"{bot_id}.py")
|
|
54
|
+
if not bot_file.exists():
|
|
55
|
+
raise click.ClickException(f"Файл {bot_id}.py не найден в корневой директории")
|
|
56
|
+
|
|
57
|
+
# Проверяем наличие .env файла
|
|
58
|
+
env_file = bot_path / ".env"
|
|
59
|
+
if not env_file.exists():
|
|
60
|
+
raise click.ClickException(f"Файл .env не найден для бота {bot_id}")
|
|
61
|
+
|
|
62
|
+
# Настраиваем окружение для бота
|
|
63
|
+
from dotenv import load_dotenv
|
|
64
|
+
|
|
65
|
+
# Добавляем корень проекта в sys.path
|
|
66
|
+
sys.path.insert(0, str(PROJECT_ROOT))
|
|
67
|
+
|
|
68
|
+
# Загружаем .env файл
|
|
69
|
+
load_dotenv(env_file)
|
|
70
|
+
click.echo(f"Загружен .env файл: {env_file}")
|
|
71
|
+
|
|
72
|
+
# Устанавливаем переменные окружения
|
|
73
|
+
os.environ["BOT_ID"] = bot_id
|
|
74
|
+
|
|
75
|
+
# Устанавливаем путь к промптам
|
|
76
|
+
prompts_dir = bot_path / "prompts"
|
|
77
|
+
if prompts_dir.exists():
|
|
78
|
+
os.environ["PROMT_FILES_DIR"] = str(prompts_dir)
|
|
79
|
+
click.echo(f"Установлен путь к промптам: {prompts_dir}")
|
|
80
|
+
|
|
81
|
+
# Запускаем бота из корневой директории
|
|
82
|
+
click.echo(f"Запускаем бота {bot_id}...")
|
|
83
|
+
subprocess.run([sys.executable, str(bot_file)], check=True, cwd=str(PROJECT_ROOT))
|
|
84
|
+
|
|
85
|
+
except subprocess.CalledProcessError as e:
|
|
86
|
+
click.echo(f"Ошибка при запуске бота: {e}", err=True)
|
|
87
|
+
sys.exit(1)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
click.echo(f"Ошибка: {e}", err=True)
|
|
90
|
+
sys.exit(1)
|
|
91
|
+
|
|
92
|
+
@cli.command()
|
|
93
|
+
@click.argument("bot_id")
|
|
94
|
+
@click.option("--file", help="Запустить тесты только из указанного файла")
|
|
95
|
+
@click.option("-v", "--verbose", is_flag=True, help="Подробный вывод")
|
|
96
|
+
@click.option("--max-concurrent", default=5, help="Максимальное количество потоков")
|
|
97
|
+
def test(bot_id: str, file: str = None, verbose: bool = False, max_concurrent: int = 5):
|
|
98
|
+
"""Запустить тесты бота"""
|
|
99
|
+
try:
|
|
100
|
+
# Проверяем существование бота
|
|
101
|
+
bot_path = PROJECT_ROOT / "bots" / bot_id
|
|
102
|
+
if not bot_path.exists():
|
|
103
|
+
raise click.ClickException(f"Бот {bot_id} не найден в папке {PROJECT_ROOT}/bots/")
|
|
104
|
+
|
|
105
|
+
# Проверяем наличие тестов
|
|
106
|
+
tests_dir = bot_path / "tests"
|
|
107
|
+
if not tests_dir.exists():
|
|
108
|
+
click.echo(f"⚠️ Тесты не найдены для бота {bot_id}")
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
# Ищем YAML файлы с тестами
|
|
112
|
+
yaml_files = [str(f.name) for f in tests_dir.glob("*.yaml")]
|
|
113
|
+
|
|
114
|
+
if not yaml_files:
|
|
115
|
+
click.echo(f"⚠️ YAML тесты не найдены для бота {bot_id}")
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
click.echo(f"Запускаем тесты для бота {bot_id}...")
|
|
119
|
+
|
|
120
|
+
# Формируем команду для запуска
|
|
121
|
+
bot_testing_path = Path(__file__).parent / "creation" / "bot_testing.py"
|
|
122
|
+
cmd = [sys.executable, str(bot_testing_path), bot_id]
|
|
123
|
+
|
|
124
|
+
if file:
|
|
125
|
+
cmd.append(file)
|
|
126
|
+
|
|
127
|
+
if verbose:
|
|
128
|
+
cmd.append("-v")
|
|
129
|
+
|
|
130
|
+
if max_concurrent != 5:
|
|
131
|
+
cmd.extend(["--max-concurrent", str(max_concurrent)])
|
|
132
|
+
|
|
133
|
+
# Запускаем тесты
|
|
134
|
+
result = subprocess.run(cmd, check=False)
|
|
135
|
+
|
|
136
|
+
if result.returncode == 0:
|
|
137
|
+
click.echo("✅ Все тесты пройдены")
|
|
138
|
+
else:
|
|
139
|
+
click.echo("❌ Есть ошибки в тестах")
|
|
140
|
+
sys.exit(1)
|
|
141
|
+
|
|
142
|
+
except subprocess.CalledProcessError as e:
|
|
143
|
+
click.echo(f"Ошибка при запуске тестов: {e}", err=True)
|
|
144
|
+
sys.exit(1)
|
|
145
|
+
except Exception as e:
|
|
146
|
+
click.echo(f"Ошибка: {e}", err=True)
|
|
147
|
+
sys.exit(1)
|
|
148
|
+
|
|
149
|
+
@cli.command()
|
|
150
|
+
@click.argument("bot_id")
|
|
151
|
+
def config(bot_id: str):
|
|
152
|
+
"""Настроить конфигурацию бота"""
|
|
153
|
+
try:
|
|
154
|
+
# Проверяем существование бота
|
|
155
|
+
bot_path = PROJECT_ROOT / Path("bots") / bot_id
|
|
156
|
+
if not bot_path.exists():
|
|
157
|
+
raise click.ClickException(f"Бот {bot_id} не найден в папке bots/")
|
|
158
|
+
|
|
159
|
+
# Открываем .env файл в редакторе
|
|
160
|
+
env_file = bot_path / ".env"
|
|
161
|
+
if not env_file.exists():
|
|
162
|
+
raise click.ClickException(f"Файл .env не найден для бота {bot_id}")
|
|
163
|
+
|
|
164
|
+
# Определяем редактор
|
|
165
|
+
editor = os.environ.get('EDITOR', 'notepad' if os.name == 'nt' else 'nano')
|
|
166
|
+
|
|
167
|
+
click.echo(f"Открываем конфигурацию бота {bot_id}...")
|
|
168
|
+
subprocess.run([editor, str(env_file)], check=True)
|
|
169
|
+
|
|
170
|
+
except subprocess.CalledProcessError as e:
|
|
171
|
+
click.echo(f"Ошибка при открытии редактора: {e}", err=True)
|
|
172
|
+
sys.exit(1)
|
|
173
|
+
except Exception as e:
|
|
174
|
+
click.echo(f"Ошибка: {e}", err=True)
|
|
175
|
+
sys.exit(1)
|
|
176
|
+
|
|
177
|
+
@cli.command()
|
|
178
|
+
@click.argument("bot_id")
|
|
179
|
+
@click.option("--list", "list_prompts", is_flag=True, help="Показать список промптов")
|
|
180
|
+
@click.option("--edit", "edit_prompt", help="Редактировать промпт")
|
|
181
|
+
@click.option("--add", "add_prompt", help="Добавить новый промпт")
|
|
182
|
+
def prompts(bot_id: str, list_prompts: bool = False, edit_prompt: str = None, add_prompt: str = None):
|
|
183
|
+
"""Управление промптами бота"""
|
|
184
|
+
try:
|
|
185
|
+
# Проверяем существование бота
|
|
186
|
+
bot_path = PROJECT_ROOT / Path("bots") / bot_id
|
|
187
|
+
if not bot_path.exists():
|
|
188
|
+
raise click.ClickException(f"Бот {bot_id} не найден в папке bots/")
|
|
189
|
+
|
|
190
|
+
prompts_dir = bot_path / "prompts"
|
|
191
|
+
if not prompts_dir.exists():
|
|
192
|
+
raise click.ClickException(f"Папка промптов не найдена для бота {bot_id}")
|
|
193
|
+
|
|
194
|
+
if list_prompts:
|
|
195
|
+
# Показываем список промптов
|
|
196
|
+
prompt_files = [f.name for f in prompts_dir.glob("*.txt")]
|
|
197
|
+
|
|
198
|
+
if not prompt_files:
|
|
199
|
+
click.echo("Промпты не найдены")
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
click.echo(f"Промпты бота {bot_id}:")
|
|
203
|
+
for prompt_file in sorted(prompt_files):
|
|
204
|
+
click.echo(f" - {prompt_file[:-4]}")
|
|
205
|
+
|
|
206
|
+
elif edit_prompt:
|
|
207
|
+
# Редактируем промпт
|
|
208
|
+
prompt_file = prompts_dir / f"{edit_prompt}.txt"
|
|
209
|
+
if not prompt_file.exists():
|
|
210
|
+
raise click.ClickException(f"Промпт {edit_prompt} не найден")
|
|
211
|
+
|
|
212
|
+
editor = os.environ.get('EDITOR', 'notepad' if os.name == 'nt' else 'nano')
|
|
213
|
+
click.echo(f"Редактируем промпт {edit_prompt}...")
|
|
214
|
+
subprocess.run([editor, str(prompt_file)], check=True)
|
|
215
|
+
|
|
216
|
+
elif add_prompt:
|
|
217
|
+
# Добавляем новый промпт
|
|
218
|
+
prompt_file = prompts_dir / f"{add_prompt}.txt"
|
|
219
|
+
if prompt_file.exists():
|
|
220
|
+
raise click.ClickException(f"Промпт {add_prompt} уже существует")
|
|
221
|
+
|
|
222
|
+
# Создаем файл с базовым содержимым
|
|
223
|
+
prompt_file.write_text(
|
|
224
|
+
f"# Промпт: {add_prompt}\n\n"
|
|
225
|
+
"Введите содержимое промпта здесь...",
|
|
226
|
+
encoding='utf-8'
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Открываем в редакторе
|
|
230
|
+
editor = os.environ.get('EDITOR', 'notepad' if os.name == 'nt' else 'nano')
|
|
231
|
+
click.echo(f"📝 Создаем новый промпт {add_prompt}...")
|
|
232
|
+
subprocess.run([editor, str(prompt_file)], check=True)
|
|
233
|
+
|
|
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
|
+
except subprocess.CalledProcessError as e:
|
|
242
|
+
click.echo(f"Ошибка при открытии редактора: {e}", err=True)
|
|
243
|
+
sys.exit(1)
|
|
244
|
+
except Exception as e:
|
|
245
|
+
click.echo(f"Ошибка: {e}", err=True)
|
|
246
|
+
sys.exit(1)
|
|
247
|
+
|
|
248
|
+
@cli.command()
|
|
249
|
+
def path():
|
|
250
|
+
"""Показать путь к проекту"""
|
|
251
|
+
click.echo(PROJECT_ROOT)
|
|
252
|
+
|
|
253
|
+
@cli.command()
|
|
254
|
+
@click.argument("bot_id")
|
|
255
|
+
@click.option("--force", "-f", is_flag=True, help="Удалить без подтверждения")
|
|
256
|
+
def rm(bot_id: str, force: bool = False):
|
|
257
|
+
"""Удалить бота и все его файлы"""
|
|
258
|
+
try:
|
|
259
|
+
# Проверяем существование бота
|
|
260
|
+
bot_path = PROJECT_ROOT / Path("bots") / bot_id
|
|
261
|
+
if not bot_path.exists():
|
|
262
|
+
raise click.ClickException(f"Бот {bot_id} не найден в папке bots/")
|
|
263
|
+
|
|
264
|
+
# Проверяем наличие основного файла бота в корневой директории
|
|
265
|
+
bot_file = Path(f"{bot_id}.py")
|
|
266
|
+
if not bot_file.exists():
|
|
267
|
+
raise click.ClickException(f"Файл {bot_id}.py не найден в корневой директории")
|
|
268
|
+
|
|
269
|
+
# Показываем что будет удалено
|
|
270
|
+
click.echo("Будет удалено:")
|
|
271
|
+
click.echo(f" - Файл запускалки: {bot_file}")
|
|
272
|
+
click.echo(f" - Папка бота: {bot_path}")
|
|
273
|
+
|
|
274
|
+
# Запрашиваем подтверждение если не указан --force
|
|
275
|
+
if not force:
|
|
276
|
+
if not click.confirm(f"Вы уверены, что хотите удалить бота {bot_id}?"):
|
|
277
|
+
click.echo("Удаление отменено")
|
|
278
|
+
return
|
|
279
|
+
|
|
280
|
+
# Удаляем файл запускалки
|
|
281
|
+
if bot_file.exists():
|
|
282
|
+
bot_file.unlink()
|
|
283
|
+
click.echo(f"Файл {bot_file} удален")
|
|
284
|
+
|
|
285
|
+
# Удаляем папку бота
|
|
286
|
+
if bot_path.exists():
|
|
287
|
+
import shutil
|
|
288
|
+
shutil.rmtree(bot_path)
|
|
289
|
+
click.echo(f"Папка {bot_path} удалена")
|
|
290
|
+
|
|
291
|
+
click.echo(f"Бот {bot_id} полностью удален")
|
|
292
|
+
|
|
293
|
+
except Exception as e:
|
|
294
|
+
click.echo(f"Ошибка при удалении бота: {e}", err=True)
|
|
295
|
+
sys.exit(1)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@cli.command()
|
|
299
|
+
def link():
|
|
300
|
+
"""Создать UTM-ссылку для бота"""
|
|
301
|
+
try:
|
|
302
|
+
# Проверяем наличие скрипта генерации ссылок
|
|
303
|
+
link_script = Path("utm_link_generator.py")
|
|
304
|
+
if not link_script.exists():
|
|
305
|
+
raise click.ClickException("Скрипт utm_link_generator.py не найден")
|
|
306
|
+
|
|
307
|
+
# Запускаем ваш скрипт генерации ссылок
|
|
308
|
+
click.echo("Запускаем генератор UTM-ссылок...")
|
|
309
|
+
subprocess.run([sys.executable, "utm_link_generator.py"], check=True)
|
|
310
|
+
|
|
311
|
+
except subprocess.CalledProcessError as e:
|
|
312
|
+
click.echo(f"Ошибка при запуске генератора ссылок: {e}", err=True)
|
|
313
|
+
sys.exit(1)
|
|
314
|
+
except Exception as e:
|
|
315
|
+
click.echo(f"Ошибка: {e}", err=True)
|
|
316
|
+
sys.exit(1)
|
|
317
|
+
|
|
318
|
+
def create_new_bot_structure(template: str, bot_id: str) -> bool:
|
|
319
|
+
"""Создает новую структуру бота в папке bots/"""
|
|
320
|
+
try:
|
|
321
|
+
# Создаем папку bots если её нет
|
|
322
|
+
bots_dir = PROJECT_ROOT / Path("bots")
|
|
323
|
+
bots_dir.mkdir(exist_ok=True)
|
|
324
|
+
|
|
325
|
+
# Создаем папку для нового бота
|
|
326
|
+
bot_dir = bots_dir / bot_id
|
|
327
|
+
if bot_dir.exists():
|
|
328
|
+
click.echo(f"Бот {bot_id} уже существует")
|
|
329
|
+
return False
|
|
330
|
+
|
|
331
|
+
bot_dir.mkdir()
|
|
332
|
+
|
|
333
|
+
# Создаем структуру папок
|
|
334
|
+
(bot_dir / "prompts").mkdir()
|
|
335
|
+
(bot_dir / "tests").mkdir()
|
|
336
|
+
(bot_dir / "reports").mkdir()
|
|
337
|
+
(bot_dir / "welcome_files").mkdir()
|
|
338
|
+
|
|
339
|
+
if template == "base":
|
|
340
|
+
# Используем growthmed-october-24 как базовый шаблон
|
|
341
|
+
copy_from_growthmed_template(bot_dir, bot_id)
|
|
342
|
+
else:
|
|
343
|
+
# Используем другой шаблон из папки bots
|
|
344
|
+
copy_from_bot_template(template, bot_dir, bot_id)
|
|
345
|
+
|
|
346
|
+
click.echo(f"Бот {bot_id} создан в папке bots/{bot_id}/")
|
|
347
|
+
click.echo(f"Не забудьте настроить .env файл перед запуском")
|
|
348
|
+
return True
|
|
349
|
+
|
|
350
|
+
except Exception as e:
|
|
351
|
+
click.echo(f"Ошибка при создании бота: {e}")
|
|
352
|
+
return False
|
|
353
|
+
|
|
354
|
+
def list_bots_in_bots_folder() -> list:
|
|
355
|
+
"""Возвращает список ботов из папки bots/"""
|
|
356
|
+
bots_dir = PROJECT_ROOT / Path("bots")
|
|
357
|
+
if not bots_dir.exists():
|
|
358
|
+
return []
|
|
359
|
+
|
|
360
|
+
bots = []
|
|
361
|
+
for item in bots_dir.iterdir():
|
|
362
|
+
if item.is_dir() and Path(f"{item.name}.py").exists():
|
|
363
|
+
bots.append(item.name)
|
|
364
|
+
|
|
365
|
+
return bots
|
|
366
|
+
|
|
367
|
+
def create_bot_template(bot_id: str) -> str:
|
|
368
|
+
"""Создает шаблон основного файла бота"""
|
|
369
|
+
return f'''#!/usr/bin/env python3
|
|
370
|
+
"""
|
|
371
|
+
Бот {bot_id} - создан с помощью Smart Bot Factory
|
|
372
|
+
"""
|
|
373
|
+
|
|
374
|
+
import asyncio
|
|
375
|
+
import sys
|
|
376
|
+
import os
|
|
377
|
+
from pathlib import Path
|
|
378
|
+
|
|
379
|
+
# Добавляем корень проекта в путь
|
|
380
|
+
project_root = Path(__file__).parent
|
|
381
|
+
sys.path.insert(0, str(project_root))
|
|
382
|
+
|
|
383
|
+
# Устанавливаем переменные окружения ДО импорта библиотеки
|
|
384
|
+
bot_id = "{bot_id}"
|
|
385
|
+
config_dir = Path("bots") / bot_id
|
|
386
|
+
prompts_dir = config_dir / "prompts"
|
|
387
|
+
|
|
388
|
+
if prompts_dir.exists():
|
|
389
|
+
os.environ["PROMT_FILES_DIR"] = str(prompts_dir)
|
|
390
|
+
print(f"📁 Установлен путь к промптам: {{prompts_dir}}")
|
|
391
|
+
|
|
392
|
+
# Загружаем .env файл ДО импорта библиотеки
|
|
393
|
+
env_file = config_dir / ".env"
|
|
394
|
+
if env_file.exists():
|
|
395
|
+
from dotenv import load_dotenv
|
|
396
|
+
load_dotenv(env_file)
|
|
397
|
+
print(f"📄 Загружен .env файл: {{env_file}}")
|
|
398
|
+
else:
|
|
399
|
+
print(f"⚠️ .env файл не найден: {{env_file}}")
|
|
400
|
+
|
|
401
|
+
from smart_bot_factory import (
|
|
402
|
+
BotBuilder,
|
|
403
|
+
event_handler,
|
|
404
|
+
schedule_task,
|
|
405
|
+
send_message_by_human,
|
|
406
|
+
send_message_by_ai
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
# =============================================================================
|
|
410
|
+
# ОБРАБОТЧИКИ СОБЫТИЙ
|
|
411
|
+
# =============================================================================
|
|
412
|
+
|
|
413
|
+
@event_handler("example_event", "Пример обработчика события")
|
|
414
|
+
async def handle_example_event(user_id: int, event_data: dict):
|
|
415
|
+
"""Пример обработчика события"""
|
|
416
|
+
# Отправляем подтверждение пользователю
|
|
417
|
+
await send_message_by_human(
|
|
418
|
+
user_id=user_id,
|
|
419
|
+
message_text="✅ Событие обработано!"
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
return {{
|
|
423
|
+
"status": "success",
|
|
424
|
+
"message": "Событие обработано"
|
|
425
|
+
}}
|
|
426
|
+
|
|
427
|
+
# =============================================================================
|
|
428
|
+
# ЗАПЛАНИРОВАННЫЕ ЗАДАЧИ
|
|
429
|
+
# =============================================================================
|
|
430
|
+
|
|
431
|
+
@schedule_task("example_task", "Пример запланированной задачи")
|
|
432
|
+
async def example_task(user_id: int, message: str):
|
|
433
|
+
"""Пример запланированной задачи"""
|
|
434
|
+
# Отправляем сообщение
|
|
435
|
+
await send_message_by_human(
|
|
436
|
+
user_id=user_id,
|
|
437
|
+
message_text=f"🔔 Напоминание: {{message}}"
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
return {{
|
|
441
|
+
"status": "sent",
|
|
442
|
+
"user_id": user_id,
|
|
443
|
+
"message": message
|
|
444
|
+
}}
|
|
445
|
+
|
|
446
|
+
# =============================================================================
|
|
447
|
+
# ОСНОВНАЯ ФУНКЦИЯ
|
|
448
|
+
# =============================================================================
|
|
449
|
+
|
|
450
|
+
async def main():
|
|
451
|
+
"""Основная функция запуска бота"""
|
|
452
|
+
try:
|
|
453
|
+
# Создаем и собираем бота
|
|
454
|
+
bot_builder = BotBuilder("{bot_id}")
|
|
455
|
+
await bot_builder.build()
|
|
456
|
+
|
|
457
|
+
# Запускаем бота
|
|
458
|
+
await bot_builder.start()
|
|
459
|
+
|
|
460
|
+
except Exception as e:
|
|
461
|
+
print(f"❌ Ошибка запуска бота: {{e}}")
|
|
462
|
+
raise
|
|
463
|
+
|
|
464
|
+
if __name__ == "__main__":
|
|
465
|
+
asyncio.run(main())
|
|
466
|
+
'''
|
|
467
|
+
|
|
468
|
+
def create_env_template(bot_id: str) -> str:
|
|
469
|
+
"""Создает шаблон .env файла"""
|
|
470
|
+
return f'''# Telegram
|
|
471
|
+
TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
|
|
472
|
+
|
|
473
|
+
# Supabase
|
|
474
|
+
SUPABASE_URL=https://your-project.supabase.co
|
|
475
|
+
SUPABASE_KEY=your_supabase_anon_key
|
|
476
|
+
|
|
477
|
+
# OpenAI
|
|
478
|
+
OPENAI_API_KEY=sk-your-openai-api-key
|
|
479
|
+
OPENAI_MODEL=gpt-5-mini
|
|
480
|
+
OPENAI_MAX_TOKENS=1500
|
|
481
|
+
OPENAI_TEMPERATURE=0.7
|
|
482
|
+
|
|
483
|
+
# Промпты (каталог)
|
|
484
|
+
PROMT_FILES_DIR=prompts
|
|
485
|
+
|
|
486
|
+
# Файл после приветствия с подписью (если он есть - грузим его в папку welcome_file, если нет - ничего не делаем)
|
|
487
|
+
WELCOME_FILE_URL=welcome_files/
|
|
488
|
+
WELCOME_FILE_MSG=welcome_file_msg.txt
|
|
489
|
+
|
|
490
|
+
# 🆕 Администраторы (через запятую)
|
|
491
|
+
# Укажите Telegram ID админов
|
|
492
|
+
ADMIN_TELEGRAM_IDS=123456789,987654321
|
|
493
|
+
ADMIN_SESSION_TIMEOUT_MINUTES=30
|
|
494
|
+
|
|
495
|
+
# 🆕 Режим отладки (показывать JSON пользователям)
|
|
496
|
+
DEBUG_MODE=false
|
|
497
|
+
|
|
498
|
+
# Дополнительные настройки
|
|
499
|
+
MAX_CONTEXT_MESSAGES=50
|
|
500
|
+
LOG_LEVEL=INFO
|
|
501
|
+
MESSAGE_PARSE_MODE=Markdown
|
|
502
|
+
|
|
503
|
+
# Настройки продаж
|
|
504
|
+
LEAD_QUALIFICATION_THRESHOLD=7
|
|
505
|
+
SESSION_TIMEOUT_HOURS=24
|
|
506
|
+
|
|
507
|
+
# ⚠️ ВАЖНО: BOT_ID теперь НЕ нужен в .env!
|
|
508
|
+
# Bot ID автоматически определяется из имени файла запускалки
|
|
509
|
+
# Например: python {bot_id}.py → BOT_ID = {bot_id}
|
|
510
|
+
'''
|
|
511
|
+
|
|
512
|
+
def copy_from_growthmed_template(bot_dir: Path, bot_id: str):
|
|
513
|
+
"""Копирует шаблон из growthmed-october-24"""
|
|
514
|
+
try:
|
|
515
|
+
# Создаем основной файл бота в корневой директории проекта
|
|
516
|
+
bot_file = PROJECT_ROOT / Path(f"{bot_id}.py")
|
|
517
|
+
bot_file.write_text(create_bot_template(bot_id), encoding='utf-8')
|
|
518
|
+
|
|
519
|
+
# Копируем .env файл в папку бота
|
|
520
|
+
env_file = bot_dir / ".env"
|
|
521
|
+
env_file.write_text(create_env_template(bot_id), encoding='utf-8')
|
|
522
|
+
|
|
523
|
+
# Копируем промпты из growthmed-october-24
|
|
524
|
+
source_prompts = Path("configs/growthmed-october-24/prompts")
|
|
525
|
+
target_prompts = bot_dir / "prompts"
|
|
526
|
+
|
|
527
|
+
if source_prompts.exists():
|
|
528
|
+
for prompt_file in source_prompts.glob("*.txt"):
|
|
529
|
+
shutil.copy2(prompt_file, target_prompts / prompt_file.name)
|
|
530
|
+
click.echo("Промпты скопированы из growthmed-october-24")
|
|
531
|
+
else:
|
|
532
|
+
# Fallback к базовым промптам
|
|
533
|
+
create_basic_prompts(target_prompts)
|
|
534
|
+
click.echo("Созданы базовые промпты")
|
|
535
|
+
|
|
536
|
+
except Exception as e:
|
|
537
|
+
click.echo(f"Ошибка при копировании шаблона: {e}")
|
|
538
|
+
# Fallback к базовым промптам
|
|
539
|
+
create_basic_prompts(bot_dir / "prompts")
|
|
540
|
+
|
|
541
|
+
def copy_from_bot_template(template: str, bot_dir: Path, bot_id: str):
|
|
542
|
+
"""Копирует шаблон из существующего бота"""
|
|
543
|
+
try:
|
|
544
|
+
template_dir = PROJECT_ROOT / Path("bots") / template
|
|
545
|
+
if not template_dir.exists():
|
|
546
|
+
raise click.ClickException(f"Шаблон {template} не найден")
|
|
547
|
+
|
|
548
|
+
# Копируем основной файл бота в корневую директорию
|
|
549
|
+
template_bot_file = PROJECT_ROOT / Path(f"{template}.py")
|
|
550
|
+
if template_bot_file.exists():
|
|
551
|
+
bot_file = PROJECT_ROOT / Path(f"{bot_id}.py")
|
|
552
|
+
shutil.copy2(template_bot_file, bot_file)
|
|
553
|
+
|
|
554
|
+
# Заменяем название бота в файле
|
|
555
|
+
content = bot_file.read_text(encoding='utf-8')
|
|
556
|
+
content = content.replace(f'BotBuilder("{template}")', f'BotBuilder("{bot_id}")')
|
|
557
|
+
content = content.replace(f'bot_id="{template}"', f'bot_id="{bot_id}"')
|
|
558
|
+
bot_file.write_text(content, encoding='utf-8')
|
|
559
|
+
|
|
560
|
+
# Копируем .env файл
|
|
561
|
+
template_env = template_dir / ".env"
|
|
562
|
+
if template_env.exists():
|
|
563
|
+
env_file = bot_dir / ".env"
|
|
564
|
+
shutil.copy2(template_env, env_file)
|
|
565
|
+
|
|
566
|
+
# Заменяем BOT_ID в .env
|
|
567
|
+
env_content = env_file.read_text(encoding='utf-8')
|
|
568
|
+
env_content = env_content.replace(f'BOT_ID={template}', f'BOT_ID={bot_id}')
|
|
569
|
+
env_file.write_text(env_content, encoding='utf-8')
|
|
570
|
+
|
|
571
|
+
# Копируем промпты
|
|
572
|
+
template_prompts = template_dir / "prompts"
|
|
573
|
+
target_prompts = bot_dir / "prompts"
|
|
574
|
+
|
|
575
|
+
if template_prompts.exists():
|
|
576
|
+
for prompt_file in template_prompts.glob("*.txt"):
|
|
577
|
+
shutil.copy2(prompt_file, target_prompts / prompt_file.name)
|
|
578
|
+
|
|
579
|
+
# Копируем тесты
|
|
580
|
+
template_tests = template_dir / "tests"
|
|
581
|
+
target_tests = bot_dir / "tests"
|
|
582
|
+
|
|
583
|
+
if template_tests.exists():
|
|
584
|
+
for test_file in template_tests.glob("*"):
|
|
585
|
+
if test_file.is_file():
|
|
586
|
+
shutil.copy2(test_file, target_tests / test_file.name)
|
|
587
|
+
|
|
588
|
+
click.echo(f"Шаблон скопирован из {template}")
|
|
589
|
+
|
|
590
|
+
except Exception as e:
|
|
591
|
+
click.echo(f"Ошибка при копировании шаблона {template}: {e}")
|
|
592
|
+
raise
|
|
593
|
+
|
|
594
|
+
def create_basic_prompts(prompts_dir: Path):
|
|
595
|
+
"""Создает базовые промпты"""
|
|
596
|
+
# Системный промпт
|
|
597
|
+
(prompts_dir / "system_prompt.txt").write_text(
|
|
598
|
+
"Ты - помощник. Твоя задача помогать пользователям с их вопросами.\n"
|
|
599
|
+
"Будь дружелюбным и полезным.",
|
|
600
|
+
encoding='utf-8'
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
# Приветственное сообщение
|
|
604
|
+
(prompts_dir / "welcome_message.txt").write_text(
|
|
605
|
+
"👋 Привет! Я ваш помощник.\n\n"
|
|
606
|
+
"Чем могу помочь?",
|
|
607
|
+
encoding='utf-8'
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
# Финальные инструкции
|
|
611
|
+
(prompts_dir / "final_instructions.txt").write_text(
|
|
612
|
+
"""<instruction>
|
|
613
|
+
КРИТИЧЕСКИ ВАЖНО: В НАЧАЛЕ КАЖДОГО своего ответа добавляй служебную информацию в формате:
|
|
614
|
+
|
|
615
|
+
{
|
|
616
|
+
"этап": id,
|
|
617
|
+
"качество": 1-10,
|
|
618
|
+
"события": [
|
|
619
|
+
{
|
|
620
|
+
"тип": тип события,
|
|
621
|
+
"инфо": детали события
|
|
622
|
+
}
|
|
623
|
+
],
|
|
624
|
+
"файлы": [],
|
|
625
|
+
"каталоги": []
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
ДОСТУПНЫЕ ОБРАБОТЧИКИ СОБЫТИЙ:
|
|
629
|
+
- example_event: Пример обработчика события. Используй для демонстрации.
|
|
630
|
+
Пример: {"тип": "example_event", "инфо": {"data": "пример данных"}}
|
|
631
|
+
|
|
632
|
+
ДОСТУПНЫЕ ЗАПЛАНИРОВАННЫЕ ЗАДАЧИ:
|
|
633
|
+
- example_task: Пример запланированной задачи. Используй для демонстрации.
|
|
634
|
+
Пример: {"тип": "example_task", "инфо": "через 1 час: напомнить о чем-то"}
|
|
635
|
+
|
|
636
|
+
Используй эти обработчики и задачи, когда это уместно в диалоге.
|
|
637
|
+
</instruction>""",
|
|
638
|
+
encoding='utf-8'
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
if __name__ == "__main__":
|
|
642
|
+
cli()
|