smart-bot-factory 0.1.2__py3-none-any.whl → 0.1.4__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 +33 -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 +768 -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 +703 -0
- smart_bot_factory/core/conversation_manager.py +536 -0
- smart_bot_factory/core/decorators.py +230 -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/utm_link_generator.py +106 -0
- smart_bot_factory-0.1.4.dist-info/METADATA +126 -0
- smart_bot_factory-0.1.4.dist-info/RECORD +59 -0
- smart_bot_factory-0.1.4.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.4.dist-info}/WHEEL +0 -0
- {smart_bot_factory-0.1.2.dist-info → smart_bot_factory-0.1.4.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
# Обновленный prompt_loader.py с поддержкой финальных инструкций
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import aiofiles
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import List, Dict
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
class PromptLoader:
|
|
11
|
+
"""Класс для загрузки промптов из локального каталога"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, prompts_dir: str):
|
|
14
|
+
self.prompts_dir = Path(prompts_dir)
|
|
15
|
+
self.welcome_file = self.prompts_dir / 'welcome_message.txt'
|
|
16
|
+
self.help_file = self.prompts_dir / 'help_message.txt'
|
|
17
|
+
self.final_instructions_file = self.prompts_dir / 'final_instructions.txt'
|
|
18
|
+
|
|
19
|
+
# Автоматически находим все .txt файлы промптов (кроме специальных)
|
|
20
|
+
all_txt_files = list(self.prompts_dir.glob('*.txt'))
|
|
21
|
+
special_files = {'welcome_message.txt', 'help_message.txt', 'final_instructions.txt'}
|
|
22
|
+
self.prompt_files = [f.name for f in all_txt_files if f.name not in special_files]
|
|
23
|
+
|
|
24
|
+
logger.info(f"Инициализирован загрузчик промптов: {self.prompts_dir}")
|
|
25
|
+
logger.info(f"Найдено файлов промптов: {len(self.prompt_files)}")
|
|
26
|
+
logger.info(f"Файлы промптов: {self.prompt_files}")
|
|
27
|
+
|
|
28
|
+
async def load_system_prompt(self) -> str:
|
|
29
|
+
"""
|
|
30
|
+
Загружает и объединяет все файлы промптов в один системный промпт
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Объединенный системный промпт с инструкциями по JSON
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
prompt_parts = []
|
|
37
|
+
|
|
38
|
+
for filename in self.prompt_files:
|
|
39
|
+
logger.debug(f"Загружаем промпт из {filename}")
|
|
40
|
+
content = await self._load_file(filename)
|
|
41
|
+
|
|
42
|
+
if content:
|
|
43
|
+
# Добавляем заголовок секции
|
|
44
|
+
section_name = self._get_section_name(filename)
|
|
45
|
+
prompt_parts.append(f"\n### {section_name} ###\n")
|
|
46
|
+
prompt_parts.append(content.strip())
|
|
47
|
+
prompt_parts.append("\n")
|
|
48
|
+
else:
|
|
49
|
+
logger.warning(f"Файл {filename} пуст")
|
|
50
|
+
|
|
51
|
+
if not prompt_parts:
|
|
52
|
+
error_msg = "Не удалось загрузить ни одного промпт файла"
|
|
53
|
+
logger.error(error_msg)
|
|
54
|
+
raise ValueError(error_msg)
|
|
55
|
+
|
|
56
|
+
# Добавляем инструкции по JSON метаданным
|
|
57
|
+
json_instructions = self._get_json_instructions()
|
|
58
|
+
prompt_parts.append("\n")
|
|
59
|
+
prompt_parts.append(json_instructions)
|
|
60
|
+
|
|
61
|
+
# Объединяем все части
|
|
62
|
+
full_prompt = "".join(prompt_parts).strip()
|
|
63
|
+
|
|
64
|
+
logger.info(f"Системный промпт загружен успешно ({len(full_prompt)} символов)")
|
|
65
|
+
return full_prompt
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logger.error(f"Ошибка при загрузке системного промпта: {e}")
|
|
69
|
+
raise
|
|
70
|
+
|
|
71
|
+
async def load_final_instructions(self) -> str:
|
|
72
|
+
"""
|
|
73
|
+
Загружает финальные инструкции из final_instructions.txt
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Финальные инструкции или пустая строка если файла нет
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
logger.debug(f"Загружаем финальные инструкции из {self.final_instructions_file.name}")
|
|
80
|
+
|
|
81
|
+
if not self.final_instructions_file.exists():
|
|
82
|
+
logger.debug(f"Файл {self.final_instructions_file.name} не найден - пропускаем")
|
|
83
|
+
return ""
|
|
84
|
+
|
|
85
|
+
async with aiofiles.open(self.final_instructions_file, 'r', encoding='utf-8') as f:
|
|
86
|
+
content = await f.read()
|
|
87
|
+
|
|
88
|
+
if not content.strip():
|
|
89
|
+
logger.debug(f"Файл {self.final_instructions_file.name} пуст - пропускаем")
|
|
90
|
+
return ""
|
|
91
|
+
|
|
92
|
+
logger.info(f"Финальные инструкции загружены ({len(content)} символов)")
|
|
93
|
+
return content.strip()
|
|
94
|
+
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logger.error(f"Ошибка при загрузке финальных инструкций: {e}")
|
|
97
|
+
# Не прерываем работу - финальные инструкции опциональны
|
|
98
|
+
return ""
|
|
99
|
+
|
|
100
|
+
def _get_json_instructions(self) -> str:
|
|
101
|
+
"""Возвращает инструкции по JSON метаданным для ИИ"""
|
|
102
|
+
return """
|
|
103
|
+
### КРИТИЧЕСКИ ВАЖНЫЕ ИНСТРУКЦИИ ПО JSON МЕТАДАННЫМ ###
|
|
104
|
+
|
|
105
|
+
В КОНЦЕ КАЖДОГО своего ответа ОБЯЗАТЕЛЬНО добавляй служебную информацию в JSON формате:
|
|
106
|
+
|
|
107
|
+
{
|
|
108
|
+
"этап": "introduction|consult|offer|contacts",
|
|
109
|
+
"качество": 1-10,
|
|
110
|
+
"события": [
|
|
111
|
+
{
|
|
112
|
+
"тип": "телефон|консультация|покупка|отказ",
|
|
113
|
+
"инфо": "детали события"
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
"файлы": ["file1.pdf", "file2.mp4"],
|
|
117
|
+
"каталоги": ["каталог1", "каталог2"]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
ОПИСАНИЕ ЭТАПОВ:
|
|
121
|
+
- introduction: знакомство, сбор базовой информации о клиенте
|
|
122
|
+
- consult: консультирование, ответы на вопросы о конференции
|
|
123
|
+
- offer: предложение тарифов, обработка возражений
|
|
124
|
+
- contacts: получение контактов, завершение сделки
|
|
125
|
+
|
|
126
|
+
СИСТЕМА ОЦЕНКИ КАЧЕСТВА (1-10):
|
|
127
|
+
1-3: низкий интерес, много возражений, скептически настроен
|
|
128
|
+
4-6: средний интерес, есть вопросы, обдумывает
|
|
129
|
+
7-8: высокий интерес, готов к покупке, активно интересуется
|
|
130
|
+
9-10: горячий лид, предоставил контакты или готов к действию
|
|
131
|
+
|
|
132
|
+
СОБЫТИЯ - добавляй ТОЛЬКО когда происходит что-то из этого:
|
|
133
|
+
- "телефон": пользователь предоставил номер телефона
|
|
134
|
+
- "консультация": пользователь просит живую консультацию по телефону
|
|
135
|
+
- "покупка": пользователь готов купить/записаться на конференцию
|
|
136
|
+
- "отказ": пользователь явно отказывается участвовать
|
|
137
|
+
|
|
138
|
+
ВАЖНО:
|
|
139
|
+
- Добавляй файлы и катологи только в том случае, если они относятся к этому этапу. (Прописаны в самом этапе) Если не относятся - отсылай пустой массив []
|
|
140
|
+
|
|
141
|
+
ПРИМЕРЫ ПРАВИЛЬНОГО ИСПОЛЬЗОВАНИЯ:
|
|
142
|
+
|
|
143
|
+
Пример 1 - обычный диалог:
|
|
144
|
+
"Расскажу подробнее о конференции GrowthMED. Она пройдет 24-25 октября..."
|
|
145
|
+
|
|
146
|
+
{
|
|
147
|
+
"этап": "consult",
|
|
148
|
+
"качество": 6,
|
|
149
|
+
"события": [],
|
|
150
|
+
"файлы": [],
|
|
151
|
+
"каталоги": []
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
Пример 2 - получен телефон:
|
|
155
|
+
"Отлично! Записал ваш номер. Мы перезвоним в течение 10 минут!"
|
|
156
|
+
|
|
157
|
+
{
|
|
158
|
+
"этап": "contacts",
|
|
159
|
+
"качество": 9,
|
|
160
|
+
"события": [
|
|
161
|
+
{
|
|
162
|
+
"тип": "телефон",
|
|
163
|
+
"инфо": "Иван Петров +79219603144"
|
|
164
|
+
}
|
|
165
|
+
],
|
|
166
|
+
"файлы": [],
|
|
167
|
+
"каталоги": []
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
Пример 3 - отправка презентации:
|
|
171
|
+
"Отправляю вам презентацию о нашей компании и прайс-лист с актуальными ценами."
|
|
172
|
+
|
|
173
|
+
{
|
|
174
|
+
"этап": "offer",
|
|
175
|
+
"качество": 7,
|
|
176
|
+
"события": [
|
|
177
|
+
{
|
|
178
|
+
"тип": "консультация",
|
|
179
|
+
"инфо": "Запросил материалы"
|
|
180
|
+
}
|
|
181
|
+
],
|
|
182
|
+
"файлы": ["презентация.pdf", "прайс.pdf"],
|
|
183
|
+
"каталоги": []
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
Пример 4 - отправка файлов из каталога:
|
|
187
|
+
"В каталоге 'примеры_работ' вы можете посмотреть наши последние проекты."
|
|
188
|
+
|
|
189
|
+
{
|
|
190
|
+
"этап": "presentation",
|
|
191
|
+
"качество": 8,
|
|
192
|
+
"события": [],
|
|
193
|
+
"файлы": [],
|
|
194
|
+
"каталоги": ["примеры_работ"]
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
Пример 5 - комбинированная отправка:
|
|
198
|
+
"Отправляю вам коммерческое предложение и примеры похожих проектов из нашего портфолио."
|
|
199
|
+
|
|
200
|
+
{
|
|
201
|
+
"этап": "offer",
|
|
202
|
+
"качество": 9,
|
|
203
|
+
"события": [
|
|
204
|
+
{
|
|
205
|
+
"тип": "предложение",
|
|
206
|
+
"инфо": "Отправлено КП"
|
|
207
|
+
}
|
|
208
|
+
],
|
|
209
|
+
"файлы": ["коммерческое_предложение.pdf"],
|
|
210
|
+
"каталоги": ["портфолио_2023"]
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
ТРЕБОВАНИЯ К JSON:
|
|
214
|
+
- JSON должен быть валидным и находиться в самом конце ответа
|
|
215
|
+
- Всегда используй кавычки для строк
|
|
216
|
+
- Массив "события" может быть пустым []
|
|
217
|
+
- Если событий нет - не добавляй их в массив
|
|
218
|
+
- Качество должно быть числом от 1 до 10
|
|
219
|
+
|
|
220
|
+
ПОМНИ: Этот JSON критически важен для работы системы администрирования и аналитики!
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
async def load_welcome_message(self) -> str:
|
|
224
|
+
"""
|
|
225
|
+
Загружает приветственное сообщение из welcome_message.txt
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Текст приветственного сообщения
|
|
229
|
+
"""
|
|
230
|
+
try:
|
|
231
|
+
logger.debug(f"Загружаем приветственное сообщение из {self.welcome_file.name}")
|
|
232
|
+
|
|
233
|
+
if not self.welcome_file.exists():
|
|
234
|
+
error_msg = f"Файл приветствия не найден: {self.welcome_file}"
|
|
235
|
+
logger.error(error_msg)
|
|
236
|
+
raise FileNotFoundError(error_msg)
|
|
237
|
+
|
|
238
|
+
async with aiofiles.open(self.welcome_file, 'r', encoding='utf-8') as f:
|
|
239
|
+
content = await f.read()
|
|
240
|
+
|
|
241
|
+
if not content.strip():
|
|
242
|
+
error_msg = f"Файл приветствия пуст: {self.welcome_file}"
|
|
243
|
+
logger.error(error_msg)
|
|
244
|
+
raise ValueError(error_msg)
|
|
245
|
+
|
|
246
|
+
logger.info(f"Приветственное сообщение загружено ({len(content)} символов)")
|
|
247
|
+
return content.strip()
|
|
248
|
+
|
|
249
|
+
except Exception as e:
|
|
250
|
+
logger.error(f"Ошибка при загрузке приветственного сообщения: {e}")
|
|
251
|
+
raise
|
|
252
|
+
|
|
253
|
+
async def load_help_message(self) -> str:
|
|
254
|
+
"""
|
|
255
|
+
Загружает справочное сообщение из help_message.txt
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Текст справочного сообщения
|
|
259
|
+
"""
|
|
260
|
+
try:
|
|
261
|
+
logger.debug(f"Загружаем справочное сообщение из {self.help_file.name}")
|
|
262
|
+
|
|
263
|
+
if self.help_file.exists():
|
|
264
|
+
async with aiofiles.open(self.help_file, 'r', encoding='utf-8') as f:
|
|
265
|
+
content = await f.read()
|
|
266
|
+
|
|
267
|
+
if content.strip():
|
|
268
|
+
logger.info(f"Справочное сообщение загружено ({len(content)} символов)")
|
|
269
|
+
return content.strip()
|
|
270
|
+
|
|
271
|
+
# Fallback если файл не найден или пуст
|
|
272
|
+
logger.warning("Файл help_message.txt не найден или пуст, используем дефолтную справку")
|
|
273
|
+
return "🤖 **Ваш помощник готов к работе!**\n\n**Команды:**\n/start - Начать диалог\n/help - Показать справку\n/status - Проверить статус"
|
|
274
|
+
|
|
275
|
+
except Exception as e:
|
|
276
|
+
logger.error(f"Ошибка при загрузке справочного сообщения: {e}")
|
|
277
|
+
# Возвращаем простую справку в случае ошибки
|
|
278
|
+
return "🤖 Ваш помощник готов к работе! Напишите /start для начала диалога."
|
|
279
|
+
|
|
280
|
+
async def _load_file(self, filename: str) -> str:
|
|
281
|
+
"""Загружает содержимое файла из каталога промптов"""
|
|
282
|
+
file_path = self.prompts_dir / filename
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
if not file_path.exists():
|
|
286
|
+
error_msg = f"Файл промпта не найден: {file_path}"
|
|
287
|
+
logger.error(error_msg)
|
|
288
|
+
raise FileNotFoundError(error_msg)
|
|
289
|
+
|
|
290
|
+
async with aiofiles.open(file_path, 'r', encoding='utf-8') as f:
|
|
291
|
+
content = await f.read()
|
|
292
|
+
|
|
293
|
+
if not content.strip():
|
|
294
|
+
logger.warning(f"Файл {filename} пуст")
|
|
295
|
+
return ""
|
|
296
|
+
|
|
297
|
+
logger.debug(f"Загружен файл {filename} ({len(content)} символов)")
|
|
298
|
+
return content
|
|
299
|
+
|
|
300
|
+
except Exception as e:
|
|
301
|
+
logger.error(f"Ошибка чтения файла {file_path}: {e}")
|
|
302
|
+
raise
|
|
303
|
+
|
|
304
|
+
def _get_section_name(self, filename: str) -> str:
|
|
305
|
+
"""Получает название секции по имени файла"""
|
|
306
|
+
name_mapping = {
|
|
307
|
+
'system_prompt.txt': 'СИСТЕМНЫЙ ПРОМПТ',
|
|
308
|
+
'sales_context.txt': 'КОНТЕКСТ ПРОДАЖ',
|
|
309
|
+
'product_info.txt': 'ИНФОРМАЦИЯ О ПРОДУКТЕ',
|
|
310
|
+
'objection_handling.txt': 'ОБРАБОТКА ВОЗРАЖЕНИЙ',
|
|
311
|
+
'1sales_context.txt': 'КОНТЕКСТ ПРОДАЖ',
|
|
312
|
+
'2product_info.txt': 'ИНФОРМАЦИЯ О ПРОДУКТЕ',
|
|
313
|
+
'3objection_handling.txt': 'ОБРАБОТКА ВОЗРАЖЕНИЙ',
|
|
314
|
+
'final_instructions.txt': 'ФИНАЛЬНЫЕ ИНСТРУКЦИИ' # 🆕
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return name_mapping.get(filename, filename.replace('.txt', '').upper())
|
|
318
|
+
|
|
319
|
+
async def reload_prompts(self) -> str:
|
|
320
|
+
"""Перезагружает промпты (для обновления без перезапуска бота)"""
|
|
321
|
+
logger.info("Перезагрузка промптов...")
|
|
322
|
+
return await self.load_system_prompt()
|
|
323
|
+
|
|
324
|
+
async def validate_prompts(self) -> Dict[str, bool]:
|
|
325
|
+
"""Проверяет доступность всех файлов промптов и приветственного сообщения"""
|
|
326
|
+
results = {}
|
|
327
|
+
|
|
328
|
+
# Проверяем файлы промптов
|
|
329
|
+
for filename in self.prompt_files:
|
|
330
|
+
file_path = self.prompts_dir / filename
|
|
331
|
+
try:
|
|
332
|
+
if file_path.exists():
|
|
333
|
+
async with aiofiles.open(file_path, 'r', encoding='utf-8') as f:
|
|
334
|
+
content = await f.read()
|
|
335
|
+
results[filename] = bool(content.strip() and len(content.strip()) > 10)
|
|
336
|
+
else:
|
|
337
|
+
results[filename] = False
|
|
338
|
+
except Exception:
|
|
339
|
+
results[filename] = False
|
|
340
|
+
|
|
341
|
+
# Проверяем файл приветственного сообщения
|
|
342
|
+
try:
|
|
343
|
+
if self.welcome_file.exists():
|
|
344
|
+
async with aiofiles.open(self.welcome_file, 'r', encoding='utf-8') as f:
|
|
345
|
+
content = await f.read()
|
|
346
|
+
results['welcome_message.txt'] = bool(content.strip() and len(content.strip()) > 5)
|
|
347
|
+
else:
|
|
348
|
+
results['welcome_message.txt'] = False
|
|
349
|
+
except Exception:
|
|
350
|
+
results['welcome_message.txt'] = False
|
|
351
|
+
|
|
352
|
+
# Проверяем файл справки (опционально)
|
|
353
|
+
try:
|
|
354
|
+
if self.help_file.exists():
|
|
355
|
+
async with aiofiles.open(self.help_file, 'r', encoding='utf-8') as f:
|
|
356
|
+
content = await f.read()
|
|
357
|
+
results['help_message.txt'] = bool(content.strip() and len(content.strip()) > 5)
|
|
358
|
+
else:
|
|
359
|
+
results['help_message.txt'] = False # Не критично
|
|
360
|
+
except Exception:
|
|
361
|
+
results['help_message.txt'] = False
|
|
362
|
+
|
|
363
|
+
# 🆕 Проверяем финальные инструкции (опционально)
|
|
364
|
+
try:
|
|
365
|
+
if self.final_instructions_file.exists():
|
|
366
|
+
async with aiofiles.open(self.final_instructions_file, 'r', encoding='utf-8') as f:
|
|
367
|
+
content = await f.read()
|
|
368
|
+
results['final_instructions.txt'] = bool(content.strip() and len(content.strip()) > 5)
|
|
369
|
+
else:
|
|
370
|
+
results['final_instructions.txt'] = False # Не критично - опциональный файл
|
|
371
|
+
except Exception:
|
|
372
|
+
results['final_instructions.txt'] = False
|
|
373
|
+
|
|
374
|
+
return results
|
|
375
|
+
|
|
376
|
+
def get_prompt_info(self) -> Dict[str, any]:
|
|
377
|
+
"""Возвращает информацию о конфигурации промптов"""
|
|
378
|
+
return {
|
|
379
|
+
'prompts_dir': str(self.prompts_dir),
|
|
380
|
+
'prompt_files': self.prompt_files,
|
|
381
|
+
'welcome_file': 'welcome_message.txt',
|
|
382
|
+
'help_file': 'help_message.txt',
|
|
383
|
+
'final_instructions_file': 'final_instructions.txt', # 🆕
|
|
384
|
+
'total_files': len(self.prompt_files) + 1, # +1 для welcome message
|
|
385
|
+
'json_instructions_included': True
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
async def test_json_parsing(self, test_response: str) -> Dict[str, any]:
|
|
389
|
+
"""Тестирует парсинг JSON из ответа ИИ (для отладки)"""
|
|
390
|
+
import json
|
|
391
|
+
import re
|
|
392
|
+
|
|
393
|
+
try:
|
|
394
|
+
# Используем тот же алгоритм что и в main.py
|
|
395
|
+
json_pattern = r'\{[^{}]*"этап"[^{}]*\}$'
|
|
396
|
+
match = re.search(json_pattern, test_response.strip())
|
|
397
|
+
|
|
398
|
+
if match:
|
|
399
|
+
json_str = match.group(0)
|
|
400
|
+
response_text = test_response[:match.start()].strip()
|
|
401
|
+
|
|
402
|
+
try:
|
|
403
|
+
metadata = json.loads(json_str)
|
|
404
|
+
return {
|
|
405
|
+
'success': True,
|
|
406
|
+
'response_text': response_text,
|
|
407
|
+
'metadata': metadata,
|
|
408
|
+
'json_str': json_str
|
|
409
|
+
}
|
|
410
|
+
except json.JSONDecodeError as e:
|
|
411
|
+
return {
|
|
412
|
+
'success': False,
|
|
413
|
+
'error': f"JSON decode error: {e}",
|
|
414
|
+
'json_str': json_str
|
|
415
|
+
}
|
|
416
|
+
else:
|
|
417
|
+
return {
|
|
418
|
+
'success': False,
|
|
419
|
+
'error': "JSON pattern not found",
|
|
420
|
+
'response_text': test_response
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
except Exception as e:
|
|
424
|
+
return {
|
|
425
|
+
'success': False,
|
|
426
|
+
'error': f"Parse error: {e}"
|
|
427
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Генератор UTM-ссылок для Telegram ботов
|
|
4
|
+
Создает ссылки в формате: @https://t.me/bot?start=source-vk_campaign-summer2025
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
def get_user_input():
|
|
8
|
+
"""Получает данные от пользователя через консоль"""
|
|
9
|
+
print("🔗 Генератор UTM-ссылок для Telegram")
|
|
10
|
+
print("=" * 50)
|
|
11
|
+
|
|
12
|
+
# Основные параметры
|
|
13
|
+
bot_username = input("Введите username бота (без @): ").strip()
|
|
14
|
+
if not bot_username:
|
|
15
|
+
print("❌ Username бота обязателен!")
|
|
16
|
+
return None
|
|
17
|
+
|
|
18
|
+
print("\n📊 Введите UTM-метки (нажмите Enter для пропуска):")
|
|
19
|
+
|
|
20
|
+
# UTM параметры (соответствуют полям в базе данных)
|
|
21
|
+
utm_source = input("utm_source (источник): ").strip()
|
|
22
|
+
utm_medium = input("utm_medium (канал): ").strip()
|
|
23
|
+
utm_campaign = input("utm_campaign (кампания): ").strip()
|
|
24
|
+
utm_content = input("utm_content (контент): ").strip()
|
|
25
|
+
utm_term = input("utm_term (ключевое слово): ").strip()
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
'bot_username': bot_username,
|
|
29
|
+
'utm_source': utm_source,
|
|
30
|
+
'utm_medium': utm_medium,
|
|
31
|
+
'utm_campaign': utm_campaign,
|
|
32
|
+
'utm_content': utm_content,
|
|
33
|
+
'utm_term': utm_term
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
def create_utm_string(utm_data):
|
|
37
|
+
"""Создает строку UTM параметров в формате source-vk_campaign-summer2025"""
|
|
38
|
+
utm_parts = []
|
|
39
|
+
|
|
40
|
+
# Маппинг полей базы данных на новый формат (без utm, в нижнем регистре)
|
|
41
|
+
field_mapping = {
|
|
42
|
+
'utm_source': 'source',
|
|
43
|
+
'utm_medium': 'medium',
|
|
44
|
+
'utm_campaign': 'campaign',
|
|
45
|
+
'utm_content': 'content',
|
|
46
|
+
'utm_term': 'term'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for db_field, utm_field in field_mapping.items():
|
|
50
|
+
value = utm_data.get(db_field)
|
|
51
|
+
if value:
|
|
52
|
+
utm_parts.append(f"{utm_field}-{value}")
|
|
53
|
+
|
|
54
|
+
return "_".join(utm_parts)
|
|
55
|
+
|
|
56
|
+
def generate_telegram_link(bot_username, utm_string):
|
|
57
|
+
"""Генерирует полную ссылку на Telegram бота"""
|
|
58
|
+
return f"https://t.me/{bot_username}?start={utm_string}"
|
|
59
|
+
|
|
60
|
+
def check_size_and_validate(utm_string):
|
|
61
|
+
"""Проверяет размер строки после start= и валидирует"""
|
|
62
|
+
MAX_SIZE = 64
|
|
63
|
+
|
|
64
|
+
if len(utm_string) > MAX_SIZE:
|
|
65
|
+
return False, f"Строка слишком большая: {len(utm_string)} символов (максимум {MAX_SIZE})"
|
|
66
|
+
|
|
67
|
+
return True, f"Размер OK: {len(utm_string)} символов"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def main():
|
|
71
|
+
"""Основная функция"""
|
|
72
|
+
try:
|
|
73
|
+
# Получаем данные от пользователя
|
|
74
|
+
data = get_user_input()
|
|
75
|
+
if not data:
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
# Создаем UTM строку
|
|
79
|
+
utm_string = create_utm_string(data)
|
|
80
|
+
|
|
81
|
+
if not utm_string:
|
|
82
|
+
print("❌ Не указано ни одной UTM-метки!")
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
# Проверяем размер
|
|
86
|
+
is_valid, size_message = check_size_and_validate(utm_string)
|
|
87
|
+
|
|
88
|
+
print(f"\n📏 {size_message}")
|
|
89
|
+
|
|
90
|
+
if not is_valid:
|
|
91
|
+
print("❌ Ссылка превышает максимальный размер!")
|
|
92
|
+
print("💡 Сократите значения UTM-меток или уберите менее важные")
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
# Генерируем и выводим ссылку
|
|
96
|
+
telegram_link = generate_telegram_link(data['bot_username'], utm_string)
|
|
97
|
+
|
|
98
|
+
print(f"\n✅ Сгенерированная ссылка:")
|
|
99
|
+
print(f"🔗 {telegram_link}")
|
|
100
|
+
except KeyboardInterrupt:
|
|
101
|
+
print("\n\n👋 Отменено пользователем")
|
|
102
|
+
except Exception as e:
|
|
103
|
+
print(f"\n❌ Ошибка: {e}")
|
|
104
|
+
|
|
105
|
+
if __name__ == "__main__":
|
|
106
|
+
main()
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: smart-bot-factory
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: Библиотека для создания умных чат-ботов
|
|
5
|
+
Author-email: Kopatych <kopatych@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: chatbot,cli,openai,supabase,telegram
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Framework :: AsyncIO
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Communications :: Chat
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Requires-Dist: aiofiles>=23.0.0
|
|
24
|
+
Requires-Dist: aiogram>=3.4.1
|
|
25
|
+
Requires-Dist: click>=8.0.0
|
|
26
|
+
Requires-Dist: openai>=1.12.0
|
|
27
|
+
Requires-Dist: project-root-finder>=1.9
|
|
28
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
29
|
+
Requires-Dist: pytz>=2023.3
|
|
30
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
31
|
+
Requires-Dist: supabase>=2.3.4
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# Smart Bot Factory
|
|
35
|
+
|
|
36
|
+
Библиотека для создания умных чат-ботов с использованием OpenAI, Telegram и Supabase.
|
|
37
|
+
|
|
38
|
+
## Установка
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install smart-bot-factory
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Быстрый старт
|
|
45
|
+
|
|
46
|
+
1. Создайте нового бота:
|
|
47
|
+
```bash
|
|
48
|
+
sbf create my-bot
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
2. Настройте конфигурацию в `bots/my-bot/.env`
|
|
52
|
+
|
|
53
|
+
3. Запустите бота:
|
|
54
|
+
```bash
|
|
55
|
+
sbf run my-bot
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Возможности
|
|
59
|
+
|
|
60
|
+
- 🤖 Интеграция с OpenAI GPT для умных ответов
|
|
61
|
+
- 📱 Поддержка Telegram Bot API через aiogram
|
|
62
|
+
- 💾 Хранение данных в Supabase
|
|
63
|
+
- 🔄 Система событий и обработчиков
|
|
64
|
+
- ⏰ Планировщик задач
|
|
65
|
+
- 🧪 Встроенная система тестирования
|
|
66
|
+
- 📝 Управление промптами
|
|
67
|
+
- 🛠️ Удобный CLI интерфейс
|
|
68
|
+
|
|
69
|
+
## CLI команды
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Создать нового бота
|
|
73
|
+
sbf create my-bot
|
|
74
|
+
|
|
75
|
+
# Запустить бота
|
|
76
|
+
sbf run my-bot
|
|
77
|
+
|
|
78
|
+
# Показать список ботов
|
|
79
|
+
sbf list
|
|
80
|
+
|
|
81
|
+
# Управление промптами
|
|
82
|
+
sbf prompts my-bot --list
|
|
83
|
+
sbf prompts my-bot --edit welcome_message
|
|
84
|
+
sbf prompts my-bot --add new_prompt
|
|
85
|
+
|
|
86
|
+
# Запустить тесты
|
|
87
|
+
sbf test my-bot
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Пример использования
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from smart_bot_factory import BotBuilder, event_handler, schedule_task
|
|
94
|
+
|
|
95
|
+
# Обработчик события
|
|
96
|
+
@event_handler("book_appointment", "Запись на прием")
|
|
97
|
+
async def handle_booking(user_id: int, event_data: dict):
|
|
98
|
+
# Логика обработки записи на прием
|
|
99
|
+
return {"status": "success"}
|
|
100
|
+
|
|
101
|
+
# Запланированная задача
|
|
102
|
+
@schedule_task("send_reminder", "Отправка напоминания")
|
|
103
|
+
async def send_reminder(user_id: int, message: str):
|
|
104
|
+
# Логика отправки напоминания
|
|
105
|
+
return {"status": "sent"}
|
|
106
|
+
|
|
107
|
+
# Запуск бота
|
|
108
|
+
async def main():
|
|
109
|
+
bot = BotBuilder("my-bot")
|
|
110
|
+
await bot.build()
|
|
111
|
+
await bot.start()
|
|
112
|
+
|
|
113
|
+
if __name__ == "__main__":
|
|
114
|
+
asyncio.run(main())
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Требования
|
|
118
|
+
|
|
119
|
+
- Python 3.9+
|
|
120
|
+
- OpenAI API ключ
|
|
121
|
+
- Telegram Bot Token
|
|
122
|
+
- Supabase проект
|
|
123
|
+
|
|
124
|
+
## Лицензия
|
|
125
|
+
|
|
126
|
+
MIT
|