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
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Обновленный config.py с автоопределением bot_id
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import logging
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from dotenv import load_dotenv
|
|
8
|
+
from typing import List
|
|
9
|
+
|
|
10
|
+
# Переменные из .env файла загружаются в BotBuilder
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class Config:
|
|
16
|
+
"""Конфигурация приложения"""
|
|
17
|
+
|
|
18
|
+
# 🆕 Bot ID автоматически из переменной окружения (устанавливается в запускалке)
|
|
19
|
+
BOT_ID: str = field(init=False)
|
|
20
|
+
|
|
21
|
+
# Telegram Bot Token
|
|
22
|
+
TELEGRAM_BOT_TOKEN: str = os.getenv('TELEGRAM_BOT_TOKEN', '')
|
|
23
|
+
|
|
24
|
+
# Supabase настройки
|
|
25
|
+
SUPABASE_URL: str = os.getenv('SUPABASE_URL', '')
|
|
26
|
+
SUPABASE_KEY: str = os.getenv('SUPABASE_KEY', '')
|
|
27
|
+
|
|
28
|
+
# OpenAI настройки
|
|
29
|
+
OPENAI_API_KEY: str = os.getenv('OPENAI_API_KEY', '')
|
|
30
|
+
OPENAI_MODEL: str = os.getenv('OPENAI_MODEL', 'gpt-5-mini')
|
|
31
|
+
OPENAI_MAX_TOKENS: int = int(os.getenv('OPENAI_MAX_TOKENS', '1500'))
|
|
32
|
+
OPENAI_TEMPERATURE: float = float(os.getenv('OPENAI_TEMPERATURE', '0.7'))
|
|
33
|
+
|
|
34
|
+
# Каталог с файлами промптов
|
|
35
|
+
PROMT_FILES_DIR: str = os.getenv('PROMT_FILES_DIR', 'prompts')
|
|
36
|
+
|
|
37
|
+
# Файл после стартого сообщения
|
|
38
|
+
WELCOME_FILE_DIR: str = os.getenv('WELCOME_FILE_URL', 'welcome_file')
|
|
39
|
+
WELCOME_FILE_MSG: str = os.getenv('WELCOME_FILE_MSG', 'welcome_file_msg.txt')
|
|
40
|
+
|
|
41
|
+
# Настройки базы данных
|
|
42
|
+
MAX_CONTEXT_MESSAGES: int = int(os.getenv('MAX_CONTEXT_MESSAGES', '50'))
|
|
43
|
+
|
|
44
|
+
# Настройки логирования
|
|
45
|
+
LOG_LEVEL: str = os.getenv('LOG_LEVEL', 'INFO')
|
|
46
|
+
|
|
47
|
+
# Настройки продаж
|
|
48
|
+
LEAD_QUALIFICATION_THRESHOLD: int = int(os.getenv('LEAD_QUALIFICATION_THRESHOLD', '7'))
|
|
49
|
+
SESSION_TIMEOUT_HOURS: int = int(os.getenv('SESSION_TIMEOUT_HOURS', '24'))
|
|
50
|
+
|
|
51
|
+
# Настройки форматирования сообщений
|
|
52
|
+
MESSAGE_PARSE_MODE: str = os.getenv('MESSAGE_PARSE_MODE', 'Markdown')
|
|
53
|
+
|
|
54
|
+
# Список найденных файлов промптов (заполняется автоматически)
|
|
55
|
+
PROMPT_FILES: List[str] = field(default_factory=list)
|
|
56
|
+
|
|
57
|
+
# Администраторы (заполняется из переменной окружения)
|
|
58
|
+
ADMIN_TELEGRAM_IDS: List[int] = field(default_factory=list)
|
|
59
|
+
ADMIN_SESSION_TIMEOUT_MINUTES: int = int(os.getenv('ADMIN_SESSION_TIMEOUT_MINUTES', '30'))
|
|
60
|
+
|
|
61
|
+
# Режим отладки - показывать JSON пользователю
|
|
62
|
+
DEBUG_MODE: bool = os.getenv('DEBUG_MODE', 'false').lower() == 'true'
|
|
63
|
+
|
|
64
|
+
def __post_init__(self):
|
|
65
|
+
"""Проверка обязательных параметров и инициализация"""
|
|
66
|
+
|
|
67
|
+
# 🆕 Автоматически получаем BOT_ID из переменной окружения
|
|
68
|
+
self.BOT_ID = os.getenv('BOT_ID', '')
|
|
69
|
+
|
|
70
|
+
if not self.BOT_ID or not self.BOT_ID.strip():
|
|
71
|
+
error_msg = "BOT_ID не установлен. Запускайте бота через запускалку (например: python growthmed-october-24.py)"
|
|
72
|
+
logger.error(error_msg)
|
|
73
|
+
raise ValueError(error_msg)
|
|
74
|
+
|
|
75
|
+
# Проверяем формат BOT_ID
|
|
76
|
+
import re
|
|
77
|
+
if not re.match(r'^[a-z0-9\-]+$', self.BOT_ID):
|
|
78
|
+
error_msg = f"BOT_ID должен содержать только латинские буквы, цифры и дефисы: {self.BOT_ID}"
|
|
79
|
+
logger.error(error_msg)
|
|
80
|
+
raise ValueError(error_msg)
|
|
81
|
+
|
|
82
|
+
# Сканируем каталог с промптами
|
|
83
|
+
self._scan_prompt_files()
|
|
84
|
+
|
|
85
|
+
# Парсим список админов
|
|
86
|
+
self._parse_admin_ids()
|
|
87
|
+
|
|
88
|
+
# Проверяем обязательные поля
|
|
89
|
+
required_fields = [
|
|
90
|
+
'TELEGRAM_BOT_TOKEN',
|
|
91
|
+
'SUPABASE_URL',
|
|
92
|
+
'SUPABASE_KEY',
|
|
93
|
+
'OPENAI_API_KEY'
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
missing_fields = []
|
|
97
|
+
for field_name in required_fields:
|
|
98
|
+
if not getattr(self, field_name):
|
|
99
|
+
missing_fields.append(field_name)
|
|
100
|
+
|
|
101
|
+
if missing_fields:
|
|
102
|
+
error_msg = f"Отсутствуют обязательные переменные окружения: {', '.join(missing_fields)}"
|
|
103
|
+
logger.error(error_msg)
|
|
104
|
+
raise ValueError(error_msg)
|
|
105
|
+
|
|
106
|
+
# Настройка уровня логирования
|
|
107
|
+
log_level = getattr(logging, self.LOG_LEVEL.upper(), logging.INFO)
|
|
108
|
+
|
|
109
|
+
# Настраиваем корневой логгер
|
|
110
|
+
root_logger = logging.getLogger()
|
|
111
|
+
root_logger.setLevel(log_level)
|
|
112
|
+
|
|
113
|
+
# Настраиваем форматтер
|
|
114
|
+
if not root_logger.handlers:
|
|
115
|
+
handler = logging.StreamHandler()
|
|
116
|
+
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
117
|
+
handler.setFormatter(formatter)
|
|
118
|
+
root_logger.addHandler(handler)
|
|
119
|
+
|
|
120
|
+
# Устанавливаем уровень для всех модулей
|
|
121
|
+
logging.getLogger('openai_client').setLevel(log_level)
|
|
122
|
+
logging.getLogger('supabase_client').setLevel(log_level)
|
|
123
|
+
logging.getLogger('handlers').setLevel(log_level)
|
|
124
|
+
logging.getLogger('bot_utils').setLevel(log_level)
|
|
125
|
+
logging.getLogger('conversation_manager').setLevel(log_level)
|
|
126
|
+
logging.getLogger('admin_manager').setLevel(log_level)
|
|
127
|
+
logging.getLogger('prompt_loader').setLevel(log_level)
|
|
128
|
+
logging.getLogger('admin_logic').setLevel(log_level)
|
|
129
|
+
logging.getLogger('debug_routing').setLevel(log_level)
|
|
130
|
+
|
|
131
|
+
# Валидация значений
|
|
132
|
+
if self.OPENAI_MAX_TOKENS < 100 or self.OPENAI_MAX_TOKENS > 4000:
|
|
133
|
+
logger.warning(f"Необычное значение OPENAI_MAX_TOKENS: {self.OPENAI_MAX_TOKENS}")
|
|
134
|
+
|
|
135
|
+
if self.OPENAI_TEMPERATURE < 0 or self.OPENAI_TEMPERATURE > 1:
|
|
136
|
+
logger.warning(f"Необычное значение OPENAI_TEMPERATURE: {self.OPENAI_TEMPERATURE}")
|
|
137
|
+
|
|
138
|
+
logger.info(f"✅ Конфигурация загружена успешно")
|
|
139
|
+
logger.info(f"🤖 Bot ID (автоопределен): {self.BOT_ID}")
|
|
140
|
+
logger.debug(f"Модель OpenAI: {self.OPENAI_MODEL}")
|
|
141
|
+
logger.debug(f"Каталог промптов: {self.PROMT_FILES_DIR}")
|
|
142
|
+
logger.debug(f"Найдено файлов: {len(self.PROMPT_FILES)}")
|
|
143
|
+
logger.info(f"👥 Админов настроено: {len(self.ADMIN_TELEGRAM_IDS)}")
|
|
144
|
+
if self.DEBUG_MODE:
|
|
145
|
+
logger.warning("🐛 Режим отладки ВКЛЮЧЕН - JSON будет показываться пользователям")
|
|
146
|
+
|
|
147
|
+
def get_summary(self) -> dict:
|
|
148
|
+
"""Возвращает сводку конфигурации (без секретов)"""
|
|
149
|
+
return {
|
|
150
|
+
'bot_id': self.BOT_ID, # 🆕 автоопределенный
|
|
151
|
+
'openai_model': self.OPENAI_MODEL,
|
|
152
|
+
'max_tokens': self.OPENAI_MAX_TOKENS,
|
|
153
|
+
'temperature': self.OPENAI_TEMPERATURE,
|
|
154
|
+
'prompts_dir': self.PROMT_FILES_DIR,
|
|
155
|
+
'max_context': self.MAX_CONTEXT_MESSAGES,
|
|
156
|
+
'log_level': self.LOG_LEVEL,
|
|
157
|
+
'prompt_files_count': len(self.PROMPT_FILES),
|
|
158
|
+
'prompt_files': self.PROMPT_FILES,
|
|
159
|
+
'has_telegram_token': bool(self.TELEGRAM_BOT_TOKEN),
|
|
160
|
+
'has_supabase_config': bool(self.SUPABASE_URL and self.SUPABASE_KEY),
|
|
161
|
+
'has_openai_key': bool(self.OPENAI_API_KEY),
|
|
162
|
+
'admin_count': len(self.ADMIN_TELEGRAM_IDS),
|
|
163
|
+
'debug_mode': self.DEBUG_MODE
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
def _parse_admin_ids(self):
|
|
167
|
+
"""Парсит список ID админов из переменной окружения"""
|
|
168
|
+
admin_ids_str = os.getenv('ADMIN_TELEGRAM_IDS', '')
|
|
169
|
+
|
|
170
|
+
if not admin_ids_str.strip():
|
|
171
|
+
logger.warning("⚠️ ADMIN_TELEGRAM_IDS не настроен - админские функции недоступны")
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
# Парсим строку вида "123456,789012,345678"
|
|
176
|
+
ids = admin_ids_str.split(',')
|
|
177
|
+
admin_ids = [int(id_str.strip()) for id_str in ids if id_str.strip()]
|
|
178
|
+
|
|
179
|
+
if not admin_ids:
|
|
180
|
+
logger.warning("⚠️ ADMIN_TELEGRAM_IDS пуст - админские функции недоступны")
|
|
181
|
+
else:
|
|
182
|
+
self.ADMIN_TELEGRAM_IDS.extend(admin_ids)
|
|
183
|
+
logger.info(f"👥 Загружены админы: {self.ADMIN_TELEGRAM_IDS}")
|
|
184
|
+
|
|
185
|
+
except ValueError as e:
|
|
186
|
+
logger.error(f"❌ Ошибка парсинга ADMIN_TELEGRAM_IDS: {e}")
|
|
187
|
+
logger.error(f" Формат должен быть: ADMIN_TELEGRAM_IDS=123456,789012")
|
|
188
|
+
|
|
189
|
+
def _scan_prompt_files(self):
|
|
190
|
+
"""Сканирует каталог с промптами и проверяет необходимые файлы"""
|
|
191
|
+
prompts_dir = Path(self.PROMT_FILES_DIR).absolute()
|
|
192
|
+
|
|
193
|
+
# Проверяем существование каталога
|
|
194
|
+
if not prompts_dir.exists():
|
|
195
|
+
error_msg = f"Каталог с промптами не найден: {prompts_dir.absolute()}"
|
|
196
|
+
logger.error(error_msg)
|
|
197
|
+
raise FileNotFoundError(error_msg)
|
|
198
|
+
|
|
199
|
+
if not prompts_dir.is_dir():
|
|
200
|
+
error_msg = f"Путь не является каталогом: {prompts_dir.absolute()}"
|
|
201
|
+
logger.error(error_msg)
|
|
202
|
+
raise NotADirectoryError(error_msg)
|
|
203
|
+
|
|
204
|
+
# Ищем все .txt файлы
|
|
205
|
+
txt_files = list(prompts_dir.glob('*.txt'))
|
|
206
|
+
|
|
207
|
+
if not txt_files:
|
|
208
|
+
error_msg = f"В каталоге {prompts_dir.absolute()} не найдено ни одного .txt файла"
|
|
209
|
+
logger.error(error_msg)
|
|
210
|
+
raise FileNotFoundError(error_msg)
|
|
211
|
+
|
|
212
|
+
# Проверяем обязательное наличие welcome_message.txt
|
|
213
|
+
welcome_file = prompts_dir / 'welcome_message.txt'
|
|
214
|
+
if not welcome_file.exists():
|
|
215
|
+
error_msg = f"Обязательный файл welcome_message.txt не найден в {prompts_dir}"
|
|
216
|
+
logger.error(error_msg)
|
|
217
|
+
raise FileNotFoundError(error_msg)
|
|
218
|
+
|
|
219
|
+
# Формируем список файлов промптов (исключая welcome_message.txt и help_message.txt)
|
|
220
|
+
excluded_files = {'welcome_message.txt', 'help_message.txt'}
|
|
221
|
+
for txt_file in txt_files:
|
|
222
|
+
if txt_file.name not in excluded_files:
|
|
223
|
+
self.PROMPT_FILES.append(txt_file.name)
|
|
224
|
+
|
|
225
|
+
if not self.PROMPT_FILES:
|
|
226
|
+
error_msg = f"В каталоге {prompts_dir.absolute()} найден только welcome_message.txt, но нет других файлов промптов"
|
|
227
|
+
logger.error(error_msg)
|
|
228
|
+
raise FileNotFoundError(error_msg)
|
|
229
|
+
|
|
230
|
+
# Сортируем для предсказуемого порядка
|
|
231
|
+
self.PROMPT_FILES.sort()
|
|
232
|
+
|
|
233
|
+
logger.info(f"📁 Найдено файлов промптов: {len(self.PROMPT_FILES)}")
|
|
234
|
+
logger.info(f"📝 Файлы промптов: {', '.join(self.PROMPT_FILES)}")
|
|
235
|
+
logger.info(f"👋 Файл приветствия: welcome_message.txt")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<role>Ты - персональный помощник по росту медицинского бизнеса.</role>
|
|
2
|
+
|
|
3
|
+
<task>твоя задача - проконсультировать о конференции GrowthMED в Москве 24-25 октября,
|
|
4
|
+
рассказать почему и кому она важна и полезна, а также записать человека на конференцию
|
|
5
|
+
или на консультацию с менеджером по конференции.
|
|
6
|
+
|
|
7
|
+
Если же конференция неинтересна или билет уже куплен - выяснить текущие проблемы-цели бизнеса пользователя
|
|
8
|
+
и предложить услуги самого GrowthMED или ее партнеров.
|
|
9
|
+
</task>
|