smart-bot-factory 0.1.5__py3-none-any.whl → 0.1.6__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/cli.py +28 -33
- smart_bot_factory/core/bot_utils.py +28 -55
- smart_bot_factory/core/decorators.py +25 -41
- smart_bot_factory/core/message_sender.py +0 -5
- smart_bot_factory/core/router.py +2 -1
- smart_bot_factory/core/router_manager.py +1 -0
- smart_bot_factory/creation/bot_builder.py +9 -27
- smart_bot_factory/event/__init__.py +12 -0
- smart_bot_factory/message/__init__.py +12 -0
- smart_bot_factory/router/__init__.py +9 -0
- smart_bot_factory/supabase/__init__.py +7 -0
- smart_bot_factory/supabase/example_usage.py +1 -0
- {smart_bot_factory-0.1.5.dist-info → smart_bot_factory-0.1.6.dist-info}/METADATA +1 -1
- {smart_bot_factory-0.1.5.dist-info → smart_bot_factory-0.1.6.dist-info}/RECORD +17 -17
- smart_bot_factory/analytics/__init__.py +0 -7
- smart_bot_factory/clients/__init__.py +0 -33
- smart_bot_factory/core/__init__.py +0 -43
- smart_bot_factory/core/globals.py +0 -68
- smart_bot_factory/integrations/__init__.py +0 -10
- {smart_bot_factory-0.1.5.dist-info → smart_bot_factory-0.1.6.dist-info}/WHEEL +0 -0
- {smart_bot_factory-0.1.5.dist-info → smart_bot_factory-0.1.6.dist-info}/entry_points.txt +0 -0
- {smart_bot_factory-0.1.5.dist-info → smart_bot_factory-0.1.6.dist-info}/licenses/LICENSE +0 -0
smart_bot_factory/cli.py
CHANGED
|
@@ -35,9 +35,9 @@ def list():
|
|
|
35
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")
|
|
@@ -191,8 +191,8 @@ def prompts(bot_id: str, list_prompts: bool = False, edit_prompt: str = None, ad
|
|
|
191
191
|
if not prompts_dir.exists():
|
|
192
192
|
raise click.ClickException(f"Папка промптов не найдена для бота {bot_id}")
|
|
193
193
|
|
|
194
|
-
if list_prompts:
|
|
195
|
-
# Показываем список промптов
|
|
194
|
+
if list_prompts or (not edit_prompt and not add_prompt):
|
|
195
|
+
# Показываем список промптов (по умолчанию или с флагом --list)
|
|
196
196
|
prompt_files = [f.name for f in prompts_dir.glob("*.txt")]
|
|
197
197
|
|
|
198
198
|
if not prompt_files:
|
|
@@ -202,6 +202,13 @@ def prompts(bot_id: str, list_prompts: bool = False, edit_prompt: str = None, ad
|
|
|
202
202
|
click.echo(f"📝 Промпты бота {bot_id}:")
|
|
203
203
|
for prompt_file in sorted(prompt_files):
|
|
204
204
|
click.echo(f" 📄 {prompt_file[:-4]}")
|
|
205
|
+
|
|
206
|
+
# Показываем справку только если не указан флаг --list
|
|
207
|
+
if not list_prompts:
|
|
208
|
+
click.echo()
|
|
209
|
+
click.echo("Использование:")
|
|
210
|
+
click.echo(" sbf prompts <bot_id> --edit <prompt_name> # Редактировать промпт")
|
|
211
|
+
click.echo(" sbf prompts <bot_id> --add <prompt_name> # Добавить новый промпт")
|
|
205
212
|
|
|
206
213
|
elif edit_prompt:
|
|
207
214
|
# Редактируем промпт
|
|
@@ -231,12 +238,6 @@ def prompts(bot_id: str, list_prompts: bool = False, edit_prompt: str = None, ad
|
|
|
231
238
|
click.echo(f"📝 Создаем новый промпт {add_prompt}...")
|
|
232
239
|
subprocess.run([editor, str(prompt_file)], check=True)
|
|
233
240
|
|
|
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
|
|
|
241
242
|
except subprocess.CalledProcessError as e:
|
|
242
243
|
click.echo(f"❌ Ошибка при открытии редактора: {e}", err=True)
|
|
@@ -259,21 +260,21 @@ def rm(bot_id: str, force: bool = False):
|
|
|
259
260
|
# Проверяем существование бота
|
|
260
261
|
bot_path = PROJECT_ROOT / Path("bots") / bot_id
|
|
261
262
|
if not bot_path.exists():
|
|
262
|
-
raise click.ClickException(f"Бот {bot_id} не найден в папке bots/")
|
|
263
|
+
raise click.ClickException(f"🤖 Бот {bot_id} не найден в папке bots/")
|
|
263
264
|
|
|
264
265
|
# Проверяем наличие основного файла бота в корневой директории
|
|
265
266
|
bot_file = Path(f"{bot_id}.py")
|
|
266
267
|
if not bot_file.exists():
|
|
267
|
-
raise click.ClickException(f"Файл {bot_id}.py не найден в корневой директории")
|
|
268
|
+
raise click.ClickException(f"📄 Файл {bot_id}.py не найден в корневой директории")
|
|
268
269
|
|
|
269
270
|
# Показываем что будет удалено
|
|
270
271
|
click.echo("🗑️ Будет удалено:")
|
|
271
|
-
click.echo(f"
|
|
272
|
-
click.echo(f"
|
|
272
|
+
click.echo(f" 📄 Файл запускалки: {bot_file}")
|
|
273
|
+
click.echo(f" 📁 Папка бота: {bot_path}")
|
|
273
274
|
|
|
274
275
|
# Запрашиваем подтверждение если не указан --force
|
|
275
276
|
if not force:
|
|
276
|
-
if not click.confirm(f"
|
|
277
|
+
if not click.confirm(f"⚠️ Вы уверены, что хотите удалить бота {bot_id}?"):
|
|
277
278
|
click.echo("❌ Удаление отменено")
|
|
278
279
|
return
|
|
279
280
|
|
|
@@ -288,7 +289,7 @@ def rm(bot_id: str, force: bool = False):
|
|
|
288
289
|
shutil.rmtree(bot_path)
|
|
289
290
|
click.echo(f"✅ Папка {bot_path} удалена")
|
|
290
291
|
|
|
291
|
-
click.echo(f"
|
|
292
|
+
click.echo(f"🎉 Бот {bot_id} полностью удален")
|
|
292
293
|
|
|
293
294
|
except Exception as e:
|
|
294
295
|
click.echo(f"❌ Ошибка при удалении бота: {e}", err=True)
|
|
@@ -318,8 +319,8 @@ def copy(source_bot_id: str, new_bot_id: str, force: bool = False):
|
|
|
318
319
|
|
|
319
320
|
if new_bot_path.exists() or new_bot_file.exists():
|
|
320
321
|
if not force:
|
|
321
|
-
if not click.confirm(f"
|
|
322
|
-
click.echo("
|
|
322
|
+
if not click.confirm(f"Бот {new_bot_id} уже существует. Перезаписать?"):
|
|
323
|
+
click.echo("Копирование отменено")
|
|
323
324
|
return
|
|
324
325
|
else:
|
|
325
326
|
click.echo(f"⚠️ Перезаписываем существующего бота {new_bot_id}")
|
|
@@ -414,14 +415,16 @@ def create_bot_template(bot_id: str) -> str:
|
|
|
414
415
|
|
|
415
416
|
import asyncio
|
|
416
417
|
|
|
417
|
-
from smart_bot_factory.
|
|
418
|
-
from smart_bot_factory.
|
|
419
|
-
from smart_bot_factory.
|
|
418
|
+
from smart_bot_factory.router import Router
|
|
419
|
+
from smart_bot_factory.message import send_message_by_human
|
|
420
|
+
from smart_bot_factory.supabase import SupabaseClient
|
|
420
421
|
from smart_bot_factory.creation import BotBuilder
|
|
421
422
|
|
|
422
423
|
# Создаем роутер для всех обработчиков
|
|
423
424
|
router = Router("{bot_id}_handlers")
|
|
424
425
|
|
|
426
|
+
supabase_client = SupabaseClient("{bot_id}")
|
|
427
|
+
|
|
425
428
|
# =============================================================================
|
|
426
429
|
# ОБРАБОТЧИКИ СОБЫТИЙ
|
|
427
430
|
# =============================================================================
|
|
@@ -677,18 +680,10 @@ def copy_bot_template(source_bot_id: str, new_bot_id: str):
|
|
|
677
680
|
new_bot_file.write_text(content, encoding='utf-8')
|
|
678
681
|
click.echo(f" 📄 Файл запускалки скопирован: {new_bot_id}.py")
|
|
679
682
|
|
|
680
|
-
#
|
|
681
|
-
source_env = source_dir / ".env"
|
|
683
|
+
# Создаем шаблон .env файла (НЕ копируем существующий)
|
|
682
684
|
new_env = new_dir / ".env"
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
shutil.copy2(source_env, new_env)
|
|
686
|
-
|
|
687
|
-
# Заменяем BOT_ID в .env
|
|
688
|
-
env_content = new_env.read_text(encoding='utf-8')
|
|
689
|
-
env_content = env_content.replace(f'BOT_ID={source_bot_id}', f'BOT_ID={new_bot_id}')
|
|
690
|
-
new_env.write_text(env_content, encoding='utf-8')
|
|
691
|
-
click.echo(f" ⚙️ .env файл скопирован и обновлен")
|
|
685
|
+
new_env.write_text(create_env_template(new_bot_id), encoding='utf-8')
|
|
686
|
+
click.echo(f" ⚙️ Создан шаблон .env файла")
|
|
692
687
|
|
|
693
688
|
# Копируем промпты
|
|
694
689
|
source_prompts = source_dir / "prompts"
|
|
@@ -31,45 +31,6 @@ def get_global_var(var_name):
|
|
|
31
31
|
|
|
32
32
|
logger = logging.getLogger(__name__)
|
|
33
33
|
|
|
34
|
-
def parse_time_expression(time_text: str) -> int:
|
|
35
|
-
"""
|
|
36
|
-
Парсит временные выражения и возвращает количество секунд
|
|
37
|
-
|
|
38
|
-
Поддерживаемые форматы:
|
|
39
|
-
- "30" -> 30 секунд (основной формат)
|
|
40
|
-
- "Запуск через 30 секунд" -> 30 секунд
|
|
41
|
-
- "через 5 минут" -> 300 секунд
|
|
42
|
-
- "через 1 час" -> 3600 секунд
|
|
43
|
-
- "через 2 часа" -> 7200 секунд
|
|
44
|
-
"""
|
|
45
|
-
# Сначала пробуем простое число (основной формат)
|
|
46
|
-
try:
|
|
47
|
-
return int(time_text)
|
|
48
|
-
except ValueError:
|
|
49
|
-
pass
|
|
50
|
-
|
|
51
|
-
# Убираем лишние символы и приводим к нижнему регистру
|
|
52
|
-
text = re.sub(r'[^\w\s\d]', '', time_text.lower())
|
|
53
|
-
|
|
54
|
-
# Ищем числа в тексте
|
|
55
|
-
numbers = re.findall(r'\d+', text)
|
|
56
|
-
if not numbers:
|
|
57
|
-
raise ValueError(f"Не найдено число в выражении: {time_text}")
|
|
58
|
-
|
|
59
|
-
number = int(numbers[0])
|
|
60
|
-
|
|
61
|
-
# Определяем единицу времени
|
|
62
|
-
if 'секунд' in text:
|
|
63
|
-
return number
|
|
64
|
-
elif 'минут' in text:
|
|
65
|
-
return number * 60
|
|
66
|
-
elif 'час' in text:
|
|
67
|
-
return number * 3600
|
|
68
|
-
elif 'день' in text or 'дней' in text:
|
|
69
|
-
return number * 86400
|
|
70
|
-
else:
|
|
71
|
-
# По умолчанию считаем секундами
|
|
72
|
-
return number
|
|
73
34
|
|
|
74
35
|
# Создаем роутер для общих команд
|
|
75
36
|
utils_router = Router()
|
|
@@ -244,10 +205,12 @@ async def process_events(session_id: str, events: list, user_id: int):
|
|
|
244
205
|
event_handlers = router_manager.get_event_handlers()
|
|
245
206
|
scheduled_tasks = router_manager.get_scheduled_tasks()
|
|
246
207
|
global_handlers = router_manager.get_global_handlers()
|
|
208
|
+
logger.debug(f"🔍 RouterManager найден: {len(global_handlers)} глобальных обработчиков")
|
|
247
209
|
else:
|
|
248
210
|
event_handlers = _event_handlers
|
|
249
211
|
scheduled_tasks = _scheduled_tasks
|
|
250
212
|
global_handlers = _global_handlers
|
|
213
|
+
logger.warning("⚠️ RouterManager не найден, используем старые декораторы")
|
|
251
214
|
|
|
252
215
|
# Сначала пробуем как обычное событие
|
|
253
216
|
if event_type in event_handlers:
|
|
@@ -266,49 +229,59 @@ async def process_events(session_id: str, events: list, user_id: int):
|
|
|
266
229
|
# Если не user_event, пробуем как запланированную задачу
|
|
267
230
|
elif event_type in scheduled_tasks:
|
|
268
231
|
try:
|
|
269
|
-
|
|
232
|
+
# Извлекаем время из event_info (AI должен передавать число секунд)
|
|
233
|
+
try:
|
|
234
|
+
delay_seconds = int(event_info)
|
|
235
|
+
except ValueError:
|
|
236
|
+
# Если не число, используем значение по умолчанию
|
|
237
|
+
delay_seconds = 3600
|
|
238
|
+
logger.warning(f" ⚠️ Не удалось извлечь время из '{event_info}', используем 3600с")
|
|
239
|
+
|
|
270
240
|
logger.info(f" ⏰ Сохраняем как scheduled_task: '{event_type}' через {delay_seconds}с")
|
|
271
241
|
result = await schedule_task_for_later_with_db(event_type, user_id, event_info, delay_seconds, session_id)
|
|
272
242
|
event_id = result['event_id']
|
|
273
243
|
should_notify = result.get('notify', False)
|
|
274
244
|
logger.info(f" 💾 Задача сохранена в БД: {event_id}")
|
|
275
245
|
|
|
276
|
-
except
|
|
246
|
+
except Exception as e:
|
|
277
247
|
if "once_only=True" in str(e):
|
|
278
248
|
logger.info(f" 🔄 Задача '{event_type}' уже запланирована, пропускаем")
|
|
279
249
|
continue
|
|
280
250
|
else:
|
|
281
|
-
logger.error(f" ❌ Ошибка
|
|
282
|
-
|
|
283
|
-
result = await schedule_task_for_later_with_db(event_type, user_id, event_info, 3600, session_id)
|
|
284
|
-
event_id = result['event_id']
|
|
285
|
-
should_notify = result.get('notify', False)
|
|
286
|
-
logger.info(f" ⏰ Задача сохранена с fallback временем (1 час): {event_id}")
|
|
251
|
+
logger.error(f" ❌ Ошибка планирования scheduled_task '{event_type}': {e}")
|
|
252
|
+
continue
|
|
287
253
|
|
|
288
254
|
# Если не scheduled_task, пробуем как глобальный обработчик
|
|
289
255
|
elif event_type in global_handlers:
|
|
290
256
|
try:
|
|
291
|
-
|
|
257
|
+
# Извлекаем время из event_info (AI должен передавать число секунд)
|
|
258
|
+
try:
|
|
259
|
+
delay_seconds = int(event_info)
|
|
260
|
+
except ValueError:
|
|
261
|
+
# Если не число, используем значение по умолчанию
|
|
262
|
+
delay_seconds = 3600
|
|
263
|
+
logger.warning(f" ⚠️ Не удалось извлечь время из '{event_info}', используем 3600с")
|
|
264
|
+
|
|
292
265
|
logger.info(f" 🌍 Сохраняем как global_handler: '{event_type}' через {delay_seconds}с")
|
|
293
266
|
result = await schedule_global_handler_for_later_with_db(event_type, delay_seconds, event_info)
|
|
294
267
|
event_id = result['event_id']
|
|
295
268
|
should_notify = result.get('notify', False)
|
|
296
269
|
logger.info(f" 💾 Глобальное событие сохранено в БД: {event_id}")
|
|
297
270
|
|
|
298
|
-
except
|
|
271
|
+
except Exception as e:
|
|
299
272
|
if "once_only=True" in str(e):
|
|
300
273
|
logger.info(f" 🔄 Глобальное событие '{event_type}' уже запланировано, пропускаем")
|
|
301
274
|
continue
|
|
302
275
|
else:
|
|
303
|
-
logger.error(f" ❌ Ошибка
|
|
304
|
-
|
|
305
|
-
result = await schedule_global_handler_for_later_with_db(event_type, 3600, event_info)
|
|
306
|
-
event_id = result['event_id']
|
|
307
|
-
should_notify = result.get('notify', False)
|
|
308
|
-
logger.info(f" 🌍 Глобальное событие сохранено с fallback временем (1 час): {event_id}")
|
|
276
|
+
logger.error(f" ❌ Ошибка планирования global_handler '{event_type}': {e}")
|
|
277
|
+
continue
|
|
309
278
|
|
|
310
279
|
else:
|
|
311
280
|
logger.warning(f" ⚠️ Обработчик '{event_type}' не найден среди зарегистрированных")
|
|
281
|
+
logger.debug(f" 🔍 Доступные обработчики:")
|
|
282
|
+
logger.debug(f" - event_handlers: {list(event_handlers.keys())}")
|
|
283
|
+
logger.debug(f" - scheduled_tasks: {list(scheduled_tasks.keys())}")
|
|
284
|
+
logger.debug(f" - global_handlers: {list(global_handlers.keys())}")
|
|
312
285
|
|
|
313
286
|
# Выполняем немедленные события
|
|
314
287
|
if should_execute_immediately and event_id:
|
|
@@ -292,18 +292,16 @@ async def execute_scheduled_task(task_name: str, user_id: int, user_data: str) -
|
|
|
292
292
|
|
|
293
293
|
async def execute_global_handler(handler_type: str, *args, **kwargs) -> Any:
|
|
294
294
|
"""Выполняет глобальный обработчик по типу"""
|
|
295
|
-
|
|
296
|
-
if
|
|
297
|
-
global_handlers =
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
return await handler_info['handler'](*args, **kwargs)
|
|
295
|
+
router_manager = get_router_manager()
|
|
296
|
+
if router_manager:
|
|
297
|
+
global_handlers = router_manager.get_global_handlers()
|
|
298
|
+
else:
|
|
299
|
+
global_handlers = _global_handlers
|
|
301
300
|
|
|
302
|
-
|
|
303
|
-
if handler_type not in _global_handlers:
|
|
301
|
+
if handler_type not in global_handlers:
|
|
304
302
|
raise ValueError(f"Глобальный обработчик '{handler_type}' не найден")
|
|
305
303
|
|
|
306
|
-
handler_info =
|
|
304
|
+
handler_info = global_handlers[handler_type]
|
|
307
305
|
return await handler_info['handler'](*args, **kwargs)
|
|
308
306
|
|
|
309
307
|
async def schedule_task_for_later(task_name: str, delay_seconds: int, user_id: int, user_data: str):
|
|
@@ -461,12 +459,7 @@ async def save_immediate_event(
|
|
|
461
459
|
raise RuntimeError("Supabase клиент не инициализирован")
|
|
462
460
|
|
|
463
461
|
# Проверяем, нужно ли предотвращать дублирование
|
|
464
|
-
|
|
465
|
-
if _router_manager:
|
|
466
|
-
event_handlers = _router_manager.get_event_handlers()
|
|
467
|
-
event_handler_info = event_handlers.get(event_type, {})
|
|
468
|
-
else:
|
|
469
|
-
event_handler_info = _event_handlers.get(event_type, {})
|
|
462
|
+
event_handler_info = _event_handlers.get(event_type, {})
|
|
470
463
|
once_only = event_handler_info.get('once_only', True)
|
|
471
464
|
|
|
472
465
|
if once_only:
|
|
@@ -510,12 +503,7 @@ async def save_scheduled_task(
|
|
|
510
503
|
raise RuntimeError("Supabase клиент не инициализирован")
|
|
511
504
|
|
|
512
505
|
# Проверяем, нужно ли предотвращать дублирование
|
|
513
|
-
|
|
514
|
-
if _router_manager:
|
|
515
|
-
scheduled_tasks = _router_manager.get_scheduled_tasks()
|
|
516
|
-
task_info = scheduled_tasks.get(task_name, {})
|
|
517
|
-
else:
|
|
518
|
-
task_info = _scheduled_tasks.get(task_name, {})
|
|
506
|
+
task_info = _scheduled_tasks.get(task_name, {})
|
|
519
507
|
once_only = task_info.get('once_only', True)
|
|
520
508
|
|
|
521
509
|
if once_only:
|
|
@@ -559,12 +547,13 @@ async def save_global_event(
|
|
|
559
547
|
raise RuntimeError("Supabase клиент не инициализирован")
|
|
560
548
|
|
|
561
549
|
# Проверяем, нужно ли предотвращать дублирование
|
|
562
|
-
|
|
563
|
-
if
|
|
564
|
-
global_handlers =
|
|
565
|
-
handler_info = global_handlers.get(handler_type, {})
|
|
550
|
+
router_manager = get_router_manager()
|
|
551
|
+
if router_manager:
|
|
552
|
+
global_handlers = router_manager.get_global_handlers()
|
|
566
553
|
else:
|
|
567
|
-
|
|
554
|
+
global_handlers = _global_handlers
|
|
555
|
+
|
|
556
|
+
handler_info = global_handlers.get(handler_type, {})
|
|
568
557
|
once_only = handler_info.get('once_only', True)
|
|
569
558
|
|
|
570
559
|
if once_only:
|
|
@@ -714,14 +703,8 @@ async def process_scheduled_event(event: Dict):
|
|
|
714
703
|
async def schedule_task_for_later_with_db(task_name: str, user_id: int, user_data: str, delay_seconds: int, session_id: str = None):
|
|
715
704
|
"""Планирует выполнение задачи через указанное время с сохранением в БД"""
|
|
716
705
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
scheduled_tasks = _router_manager.get_scheduled_tasks()
|
|
720
|
-
if task_name not in scheduled_tasks:
|
|
721
|
-
raise ValueError(f"Задача '{task_name}' не найдена")
|
|
722
|
-
else:
|
|
723
|
-
if task_name not in _scheduled_tasks:
|
|
724
|
-
raise ValueError(f"Задача '{task_name}' не найдена")
|
|
706
|
+
if task_name not in _scheduled_tasks:
|
|
707
|
+
raise ValueError(f"Задача '{task_name}' не найдена")
|
|
725
708
|
|
|
726
709
|
logger.info(f"⏰ Планируем задачу '{task_name}' через {delay_seconds} секунд")
|
|
727
710
|
|
|
@@ -782,14 +765,15 @@ async def schedule_task_for_later_with_db(task_name: str, user_id: int, user_dat
|
|
|
782
765
|
async def schedule_global_handler_for_later_with_db(handler_type: str, delay_seconds: int, handler_data: str):
|
|
783
766
|
"""Планирует выполнение глобального обработчика через указанное время с сохранением в БД"""
|
|
784
767
|
|
|
785
|
-
# Проверяем через
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
raise ValueError(f"Глобальный обработчик '{handler_type}' не найден")
|
|
768
|
+
# Проверяем обработчик через RouterManager или fallback к старым декораторам
|
|
769
|
+
router_manager = get_router_manager()
|
|
770
|
+
if router_manager:
|
|
771
|
+
global_handlers = router_manager.get_global_handlers()
|
|
790
772
|
else:
|
|
791
|
-
|
|
792
|
-
|
|
773
|
+
global_handlers = _global_handlers
|
|
774
|
+
|
|
775
|
+
if handler_type not in global_handlers:
|
|
776
|
+
raise ValueError(f"Глобальный обработчик '{handler_type}' не найден")
|
|
793
777
|
|
|
794
778
|
logger.info(f"🌍 Планируем глобальный обработчик '{handler_type}' через {delay_seconds} секунд")
|
|
795
779
|
|
|
@@ -29,11 +29,6 @@ async def send_message_by_ai(
|
|
|
29
29
|
try:
|
|
30
30
|
# Импортируем необходимые компоненты
|
|
31
31
|
from .bot_utils import parse_ai_response, process_events
|
|
32
|
-
from ..config import Config
|
|
33
|
-
from ..integrations.openai_client import OpenAIClient
|
|
34
|
-
from ..integrations.supabase_client import SupabaseClient
|
|
35
|
-
from ..utils.prompt_loader import PromptLoader
|
|
36
|
-
from aiogram import Bot
|
|
37
32
|
|
|
38
33
|
# Получаем компоненты из глобального контекста
|
|
39
34
|
from ..handlers.handlers import get_global_var
|
smart_bot_factory/core/router.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Роутер для Smart Bot Factory - аналог aiogram Router
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from typing import Dict,
|
|
5
|
+
from typing import Dict, Any, Callable
|
|
6
6
|
import logging
|
|
7
7
|
|
|
8
8
|
logger = logging.getLogger(__name__)
|
|
@@ -170,3 +170,4 @@ class Router:
|
|
|
170
170
|
|
|
171
171
|
def __repr__(self):
|
|
172
172
|
return f"Router(name='{self.name}', events={len(self._event_handlers)}, tasks={len(self._scheduled_tasks)}, globals={len(self._global_handlers)})"
|
|
173
|
+
|
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
-
import sys
|
|
7
|
-
import asyncio
|
|
8
6
|
import logging
|
|
9
7
|
from pathlib import Path
|
|
10
|
-
from typing import Optional, Dict, Any
|
|
8
|
+
from typing import Optional, Dict, Any
|
|
11
9
|
|
|
12
10
|
from ..config import Config
|
|
13
11
|
from ..integrations.openai_client import OpenAIClient
|
|
@@ -236,8 +234,13 @@ class BotBuilder:
|
|
|
236
234
|
# Получаем модуль бота
|
|
237
235
|
bot_module = sys.modules.get(module_name)
|
|
238
236
|
if not bot_module:
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
# Пытаемся импортировать модуль, если он не загружен
|
|
238
|
+
try:
|
|
239
|
+
bot_module = importlib.import_module(module_name)
|
|
240
|
+
logger.info(f"📦 Модуль '{module_name}' импортирован для установки глобальных переменных")
|
|
241
|
+
except ImportError as ie:
|
|
242
|
+
logger.warning(f"⚠️ Не удалось импортировать модуль '{module_name}': {ie}")
|
|
243
|
+
return
|
|
241
244
|
|
|
242
245
|
# Устанавливаем глобальные переменные
|
|
243
246
|
bot_module.supabase_client = self.supabase_client
|
|
@@ -302,8 +305,7 @@ class BotBuilder:
|
|
|
302
305
|
prompts_status = await self.prompt_loader.validate_prompts()
|
|
303
306
|
logger.info(f"Статус промптов: {prompts_status}")
|
|
304
307
|
|
|
305
|
-
|
|
306
|
-
import sys
|
|
308
|
+
|
|
307
309
|
import importlib
|
|
308
310
|
|
|
309
311
|
# Устанавливаем глобальные переменные в модулях handlers и admin_logic
|
|
@@ -381,26 +383,6 @@ class BotBuilder:
|
|
|
381
383
|
# Устанавливаем глобальные переменные в модуле бота для удобного доступа
|
|
382
384
|
self.set_global_vars_in_module(self.bot_id)
|
|
383
385
|
|
|
384
|
-
# Устанавливаем глобальные переменные в core.globals для внутреннего использования
|
|
385
|
-
from ..core.globals import set_globals
|
|
386
|
-
set_globals(
|
|
387
|
-
supabase_client=self.supabase_client,
|
|
388
|
-
openai_client=self.openai_client,
|
|
389
|
-
config=self.config,
|
|
390
|
-
admin_manager=self.admin_manager,
|
|
391
|
-
analytics_manager=self.analytics_manager,
|
|
392
|
-
conversation_manager=self.conversation_manager,
|
|
393
|
-
prompt_loader=self.prompt_loader,
|
|
394
|
-
bot=bot,
|
|
395
|
-
dp=dp
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
# Устанавливаем клиенты в clients модуль для пользователей
|
|
399
|
-
from ..clients import set_clients
|
|
400
|
-
set_clients(
|
|
401
|
-
supabase=self.supabase_client,
|
|
402
|
-
openai=self.openai_client
|
|
403
|
-
)
|
|
404
386
|
|
|
405
387
|
# Устанавливаем роутер-менеджер в декораторы
|
|
406
388
|
if self.router_manager:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
smart_bot_factory/__init__.py,sha256=W5k9awLVi0R_N3fSul7VNkKRdSfExZjNb_M2yNzepg8,102
|
|
2
|
-
smart_bot_factory/cli.py,sha256=
|
|
2
|
+
smart_bot_factory/cli.py,sha256=5kcxBPgi1RoNT_boSw00dyf8SqopuwRp5k20xoMmDfw,36850
|
|
3
3
|
smart_bot_factory/config.py,sha256=kB3G2hGMrrCSOAvlrddf8x43bgfgEaQqOCKOeELAzfM,11640
|
|
4
4
|
smart_bot_factory/setup_checker.py,sha256=fqRzyptyMzcb2I0boUaj0rWLdarqaN6ViX6suwTEeWc,20123
|
|
5
5
|
smart_bot_factory/utm_link_generator.py,sha256=jw7c84Y_ZvLpV4jaBWw0GK3-lCUKIIa8sZ8YNxoWIlc,4166
|
|
@@ -9,9 +9,7 @@ smart_bot_factory/admin/admin_manager.py,sha256=xlyG9mIjPmtUhS4E9lp36T7o5Kfp5PZp
|
|
|
9
9
|
smart_bot_factory/admin/admin_migration.sql,sha256=kleMPJBSe2Z7ZZz7rNyOX_yoh4GZivGesqAX90U5PGs,5667
|
|
10
10
|
smart_bot_factory/admin/admin_tester.py,sha256=PGFpf7fmD5Wxea31xR2ZM_A_QpvrB73gsbxvUrHQBkg,6463
|
|
11
11
|
smart_bot_factory/admin/timeout_checker.py,sha256=TzA2FGrxwE8fuhKerGnGrt4qYMEZdIR8x3SQAnIW5YQ,24490
|
|
12
|
-
smart_bot_factory/analytics/__init__.py,sha256=kPkReaG-cch-eDrTXjynTY1BZL4JxPytxKaAvvPZrvU,137
|
|
13
12
|
smart_bot_factory/analytics/analytics_manager.py,sha256=JQgKGkPPt07vI6KYWpa7OAimgl5SyF5cbiM89N4LKIQ,16298
|
|
14
|
-
smart_bot_factory/clients/__init__.py,sha256=Uqnf_GjPdsWFMkUT1OPJvF4mZ0tRUn33-Xk57iHgX-g,1099
|
|
15
13
|
smart_bot_factory/configs/growthmed-october-24/prompts/1sales_context.txt,sha256=uJ4WviYIr9xJaFdu-APrrmBgIWVwdJymcN04upIviLY,1614
|
|
16
14
|
smart_bot_factory/configs/growthmed-october-24/prompts/2product_info.txt,sha256=tWP9gdn58vu9BhYpDx1lGuPNT24j5p1XPiCSD-CdXiU,24191
|
|
17
15
|
smart_bot_factory/configs/growthmed-october-24/prompts/3objection_handling.txt,sha256=zwUGKp3mp1dAQUPjLQsCsczwRJJOqn9GGvVzVBCpvfY,6985
|
|
@@ -26,29 +24,31 @@ smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml,sh
|
|
|
26
24
|
smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml,sha256=bzDulOU4a2LyWlcHzlQU8GYhOky2WTfyizGfjX4ioMY,2436
|
|
27
25
|
smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt,sha256=Db21Mm0r8SBWFdX9EeIF2FZtLQ2cvuwVlSRJd2KEYCg,922
|
|
28
26
|
smart_bot_factory/configs/growthmed-october-24/welcome_file/Чек лист по 152ФЗ и 323ФЗ для медицины.pdf,sha256=BiAiQHNnQXJPMsks9AeL6s0beEjRFkRMJLMlAn4WorA,5284954
|
|
29
|
-
smart_bot_factory/core/
|
|
30
|
-
smart_bot_factory/core/bot_utils.py,sha256=BQwbrNOITPRHlC2cKUHNcyv1vBytvyPL6eGVr9trsrk,42396
|
|
27
|
+
smart_bot_factory/core/bot_utils.py,sha256=Xw4I-l7a6PNKXJnko60WWaFqEG-wFwxEdFN_wGho4nQ,41704
|
|
31
28
|
smart_bot_factory/core/conversation_manager.py,sha256=eoHL7MCEz68DRvTVwRwZgf2PWwGv4T6J9D-I-thETi8,28289
|
|
32
|
-
smart_bot_factory/core/decorators.py,sha256=
|
|
33
|
-
smart_bot_factory/core/
|
|
34
|
-
smart_bot_factory/core/
|
|
35
|
-
smart_bot_factory/core/
|
|
36
|
-
smart_bot_factory/core/router_manager.py,sha256=zDi2BdAB-MPSbjpA50nbMtQiT6cpK0suqtkTXLriWiA,8411
|
|
29
|
+
smart_bot_factory/core/decorators.py,sha256=HxF0NtPpS4MVkeRCMRwar4kuI6Xa6wbIyVwqGrDPCYc,43927
|
|
30
|
+
smart_bot_factory/core/message_sender.py,sha256=EuPoX488Vz_gEwT8LDeOBTb7L9kb2eptvKENnEsVWyM,9872
|
|
31
|
+
smart_bot_factory/core/router.py,sha256=xhXBQMlyM2ML6CFa3Co0wNvZkeFkOK2expexkoVdE6E,8151
|
|
32
|
+
smart_bot_factory/core/router_manager.py,sha256=FvRHdxxhYqh1JHE-OjY7xjjPrCu-LGTILIWaxXfXi6U,8413
|
|
37
33
|
smart_bot_factory/core/states.py,sha256=AOc19_yAsDW_8md-2oiowjBhuW3bwcsmMxszCXWZZTQ,355
|
|
38
34
|
smart_bot_factory/creation/__init__.py,sha256=IgDk8GDS3pg7Pw_Et41J33ZmeZIU5dRwQdTmYKXfJfE,128
|
|
39
|
-
smart_bot_factory/creation/bot_builder.py,sha256=
|
|
35
|
+
smart_bot_factory/creation/bot_builder.py,sha256=D1_UkwZRgpL6wlwNodiK7zZAdMHrmT-iZYH0ZmomRPM,21627
|
|
40
36
|
smart_bot_factory/creation/bot_testing.py,sha256=JDWXyJfZmbgo-DLdAPk8Sd9FiehtHHa4sLD17lBrTOc,55669
|
|
41
37
|
smart_bot_factory/database/database_structure.sql,sha256=26gFtMC2jdQGQF7Zb_F4Br56rMd4hUDTk9FkNZYneLo,2789
|
|
42
38
|
smart_bot_factory/database/schema.sql,sha256=-6kOmA9QnSkUtmGI2iQRbTvbdiqOhEOQcuz1lJn79mU,28059
|
|
39
|
+
smart_bot_factory/event/__init__.py,sha256=hPL449RULIOB-OXv1ZbGNiHctAYaOMUqhSWGPrDHYBM,212
|
|
43
40
|
smart_bot_factory/handlers/handlers.py,sha256=0ZdiWMwAx-EXx3Fmwk2nilhl76KTuyMCQt2C70Ttlvo,32530
|
|
44
|
-
smart_bot_factory/integrations/__init__.py,sha256=V-6nxjE7LIpAh6QYxJ1c2Uhb4Vn0L9p79e7dLtYFlOo,134
|
|
45
41
|
smart_bot_factory/integrations/openai_client.py,sha256=aMcDrKO0GEx3ZSVEOGDeDtFCDWSXs6biUfgrbRK8yTU,23180
|
|
46
42
|
smart_bot_factory/integrations/supabase_client.py,sha256=IDpmWzcsGv75bPHpUHla5XuFvvpEz2DGU0Npho0pPzw,41298
|
|
43
|
+
smart_bot_factory/message/__init__.py,sha256=yYwzh0X6l46FCZIZQ51bo9QUSfwZKJqFMYZ7H7hlz4g,242
|
|
44
|
+
smart_bot_factory/router/__init__.py,sha256=fLXSXfa-MaI9ickXoqha3PqpjxunsNLyVNlHIoIbr1c,115
|
|
45
|
+
smart_bot_factory/supabase/__init__.py,sha256=gMrumVhgpWiZtTgCCgHU6u1tzITtR_Lkpkrnwe-n7JA,181
|
|
46
|
+
smart_bot_factory/supabase/example_usage.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
47
47
|
smart_bot_factory/utils/__init__.py,sha256=5zNjw491bj5VkOhoEAwh2hjmv8nldyDOTrG7pbGpz6A,285
|
|
48
48
|
smart_bot_factory/utils/debug_routing.py,sha256=BOoDhKBg7UXe5uHQxRk3TSfPfLPOFqt0N7lAo6kjCOo,4719
|
|
49
49
|
smart_bot_factory/utils/prompt_loader.py,sha256=JSn7CsWnToSbHYtURdeuZn7ectyDqQGrPGHN2ixIGkw,19930
|
|
50
|
-
smart_bot_factory-0.1.
|
|
51
|
-
smart_bot_factory-0.1.
|
|
52
|
-
smart_bot_factory-0.1.
|
|
53
|
-
smart_bot_factory-0.1.
|
|
54
|
-
smart_bot_factory-0.1.
|
|
50
|
+
smart_bot_factory-0.1.6.dist-info/METADATA,sha256=j01STTwywDWpvidmwS3y090Udr0eogmq0IM3h9Lb1S8,13195
|
|
51
|
+
smart_bot_factory-0.1.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
52
|
+
smart_bot_factory-0.1.6.dist-info/entry_points.txt,sha256=ybKEAI0WSb7WoRiey7QE-HHfn88UGV7nxLDxXq7b7SU,50
|
|
53
|
+
smart_bot_factory-0.1.6.dist-info/licenses/LICENSE,sha256=OrK3cwdUTzNzIhJvSPtJaVMoYIyC_sSx5EFE_FDMvGs,1092
|
|
54
|
+
smart_bot_factory-0.1.6.dist-info/RECORD,,
|
|
@@ -1,33 +0,0 @@
|
|
|
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
|
-
]
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Core модули smart_bot_factory
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
# Импортируем основные компоненты для удобства
|
|
6
|
-
from .globals import (
|
|
7
|
-
get_supabase_client,
|
|
8
|
-
get_openai_client,
|
|
9
|
-
get_config,
|
|
10
|
-
get_admin_manager,
|
|
11
|
-
get_analytics_manager,
|
|
12
|
-
get_conversation_manager,
|
|
13
|
-
get_prompt_loader,
|
|
14
|
-
get_bot,
|
|
15
|
-
get_dp
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
from .decorators import event_handler, schedule_task, global_handler
|
|
19
|
-
from .message_sender import send_message_by_human
|
|
20
|
-
from .router import Router
|
|
21
|
-
from .router_manager import RouterManager
|
|
22
|
-
|
|
23
|
-
__all__ = [
|
|
24
|
-
# Функции получения (для внутреннего использования)
|
|
25
|
-
'get_supabase_client',
|
|
26
|
-
'get_openai_client',
|
|
27
|
-
'get_config',
|
|
28
|
-
'get_admin_manager',
|
|
29
|
-
'get_analytics_manager',
|
|
30
|
-
'get_conversation_manager',
|
|
31
|
-
'get_prompt_loader',
|
|
32
|
-
'get_bot',
|
|
33
|
-
'get_dp',
|
|
34
|
-
# Декораторы
|
|
35
|
-
'event_handler',
|
|
36
|
-
'schedule_task',
|
|
37
|
-
'global_handler',
|
|
38
|
-
# Роутеры
|
|
39
|
-
'Router',
|
|
40
|
-
'RouterManager',
|
|
41
|
-
# Утилиты
|
|
42
|
-
'send_message_by_human'
|
|
43
|
-
]
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Глобальные переменные для удобного доступа к компонентам бота
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from typing import Optional
|
|
6
|
-
from ..integrations.supabase_client import SupabaseClient
|
|
7
|
-
from ..integrations.openai_client import OpenAIClient
|
|
8
|
-
from ..config import Config
|
|
9
|
-
from ..admin.admin_manager import AdminManager
|
|
10
|
-
from ..analytics.analytics_manager import AnalyticsManager
|
|
11
|
-
from ..core.conversation_manager import ConversationManager
|
|
12
|
-
from ..utils.prompt_loader import PromptLoader
|
|
13
|
-
|
|
14
|
-
# Глобальные переменные (будут установлены при инициализации бота)
|
|
15
|
-
supabase_client: Optional[SupabaseClient] = None
|
|
16
|
-
openai_client: Optional[OpenAIClient] = None
|
|
17
|
-
config: Optional[Config] = None
|
|
18
|
-
admin_manager: Optional[AdminManager] = None
|
|
19
|
-
analytics_manager: Optional[AnalyticsManager] = None
|
|
20
|
-
conversation_manager: Optional[ConversationManager] = None
|
|
21
|
-
prompt_loader: Optional[PromptLoader] = None
|
|
22
|
-
bot: Optional[object] = None # aiogram Bot
|
|
23
|
-
dp: Optional[object] = None # aiogram Dispatcher
|
|
24
|
-
|
|
25
|
-
def set_globals(**kwargs):
|
|
26
|
-
"""Устанавливает глобальные переменные"""
|
|
27
|
-
global supabase_client, openai_client, config, admin_manager
|
|
28
|
-
global analytics_manager, conversation_manager, prompt_loader, bot, dp
|
|
29
|
-
|
|
30
|
-
for key, value in kwargs.items():
|
|
31
|
-
if key in globals():
|
|
32
|
-
globals()[key] = value
|
|
33
|
-
|
|
34
|
-
def get_supabase_client() -> Optional[SupabaseClient]:
|
|
35
|
-
"""Получает клиент Supabase"""
|
|
36
|
-
return supabase_client
|
|
37
|
-
|
|
38
|
-
def get_openai_client() -> Optional[OpenAIClient]:
|
|
39
|
-
"""Получает клиент OpenAI"""
|
|
40
|
-
return openai_client
|
|
41
|
-
|
|
42
|
-
def get_config() -> Optional[Config]:
|
|
43
|
-
"""Получает конфигурацию"""
|
|
44
|
-
return config
|
|
45
|
-
|
|
46
|
-
def get_admin_manager() -> Optional[AdminManager]:
|
|
47
|
-
"""Получает менеджер админов"""
|
|
48
|
-
return admin_manager
|
|
49
|
-
|
|
50
|
-
def get_analytics_manager() -> Optional[AnalyticsManager]:
|
|
51
|
-
"""Получает менеджер аналитики"""
|
|
52
|
-
return analytics_manager
|
|
53
|
-
|
|
54
|
-
def get_conversation_manager() -> Optional[ConversationManager]:
|
|
55
|
-
"""Получает менеджер разговоров"""
|
|
56
|
-
return conversation_manager
|
|
57
|
-
|
|
58
|
-
def get_prompt_loader() -> Optional[PromptLoader]:
|
|
59
|
-
"""Получает загрузчик промптов"""
|
|
60
|
-
return prompt_loader
|
|
61
|
-
|
|
62
|
-
def get_bot() -> Optional[object]:
|
|
63
|
-
"""Получает экземпляр бота"""
|
|
64
|
-
return bot
|
|
65
|
-
|
|
66
|
-
def get_dp() -> Optional[object]:
|
|
67
|
-
"""Получает диспетчер"""
|
|
68
|
-
return dp
|
|
File without changes
|
|
File without changes
|
|
File without changes
|