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