smart-bot-factory 0.1.4__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/__init__.py +0 -30
- smart_bot_factory/admin/admin_logic.py +11 -11
- smart_bot_factory/cli.py +147 -85
- 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/bot_utils.py +224 -88
- smart_bot_factory/core/conversation_manager.py +542 -535
- smart_bot_factory/core/decorators.py +927 -230
- smart_bot_factory/core/message_sender.py +2 -7
- smart_bot_factory/core/router.py +173 -0
- smart_bot_factory/core/router_manager.py +166 -0
- smart_bot_factory/creation/__init__.py +1 -2
- smart_bot_factory/creation/bot_builder.py +103 -13
- smart_bot_factory/creation/bot_testing.py +74 -13
- smart_bot_factory/event/__init__.py +12 -0
- smart_bot_factory/handlers/handlers.py +10 -2
- smart_bot_factory/integrations/supabase_client.py +272 -2
- 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-0.1.6.dist-info/METADATA +466 -0
- {smart_bot_factory-0.1.4.dist-info → smart_bot_factory-0.1.6.dist-info}/RECORD +26 -31
- smart_bot_factory/analytics/__init__.py +0 -7
- 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/core/__init__.py +0 -22
- smart_bot_factory/integrations/__init__.py +0 -9
- smart_bot_factory-0.1.4.dist-info/METADATA +0 -126
- /smart_bot_factory/{configs/growthmed-helper/env_example.txt → supabase/example_usage.py} +0 -0
- {smart_bot_factory-0.1.4.dist-info → smart_bot_factory-0.1.6.dist-info}/WHEEL +0 -0
- {smart_bot_factory-0.1.4.dist-info → smart_bot_factory-0.1.6.dist-info}/entry_points.txt +0 -0
- {smart_bot_factory-0.1.4.dist-info → smart_bot_factory-0.1.6.dist-info}/licenses/LICENSE +0 -0
|
@@ -29,14 +29,9 @@ 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 .openai_client import OpenAIClient
|
|
34
|
-
from .supabase_client import SupabaseClient
|
|
35
|
-
from .prompt_loader import PromptLoader
|
|
36
|
-
from aiogram import Bot
|
|
37
32
|
|
|
38
33
|
# Получаем компоненты из глобального контекста
|
|
39
|
-
from .handlers import get_global_var
|
|
34
|
+
from ..handlers.handlers import get_global_var
|
|
40
35
|
bot = get_global_var('bot')
|
|
41
36
|
supabase_client = get_global_var('supabase_client')
|
|
42
37
|
openai_client = get_global_var('openai_client')
|
|
@@ -211,7 +206,7 @@ async def send_message_by_human(
|
|
|
211
206
|
"""
|
|
212
207
|
try:
|
|
213
208
|
# Импортируем необходимые компоненты
|
|
214
|
-
from .handlers import get_global_var
|
|
209
|
+
from ..handlers.handlers import get_global_var
|
|
215
210
|
bot = get_global_var('bot')
|
|
216
211
|
supabase_client = get_global_var('supabase_client')
|
|
217
212
|
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Роутер для Smart Bot Factory - аналог aiogram Router
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, Any, Callable
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
class Router:
|
|
11
|
+
"""
|
|
12
|
+
Роутер для организации обработчиков событий, задач и глобальных обработчиков
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, name: str = None):
|
|
16
|
+
"""
|
|
17
|
+
Инициализация роутера
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
name: Имя роутера для логирования
|
|
21
|
+
"""
|
|
22
|
+
self.name = name or f"Router_{id(self)}"
|
|
23
|
+
self._event_handlers: Dict[str, Dict[str, Any]] = {}
|
|
24
|
+
self._scheduled_tasks: Dict[str, Dict[str, Any]] = {}
|
|
25
|
+
self._global_handlers: Dict[str, Dict[str, Any]] = {}
|
|
26
|
+
|
|
27
|
+
logger.info(f"🔄 Создан роутер: {self.name}")
|
|
28
|
+
|
|
29
|
+
def event_handler(self, event_type: str, notify: bool = False, once_only: bool = True):
|
|
30
|
+
"""
|
|
31
|
+
Декоратор для регистрации обработчика события в роутере
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
event_type: Тип события
|
|
35
|
+
notify: Уведомлять ли админов
|
|
36
|
+
once_only: Выполнять ли только один раз
|
|
37
|
+
"""
|
|
38
|
+
def decorator(func: Callable) -> Callable:
|
|
39
|
+
self._event_handlers[event_type] = {
|
|
40
|
+
'handler': func,
|
|
41
|
+
'name': func.__name__,
|
|
42
|
+
'notify': notify,
|
|
43
|
+
'once_only': once_only,
|
|
44
|
+
'router': self.name
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
logger.info(f"📝 Роутер {self.name}: зарегистрирован обработчик события '{event_type}': {func.__name__}")
|
|
48
|
+
|
|
49
|
+
from functools import wraps
|
|
50
|
+
@wraps(func)
|
|
51
|
+
async def wrapper(*args, **kwargs):
|
|
52
|
+
try:
|
|
53
|
+
return await func(*args, **kwargs)
|
|
54
|
+
except Exception as e:
|
|
55
|
+
logger.error(f"Ошибка выполнения обработчика '{event_type}' в роутере {self.name}: {e}")
|
|
56
|
+
raise
|
|
57
|
+
return wrapper
|
|
58
|
+
return decorator
|
|
59
|
+
|
|
60
|
+
def schedule_task(self, task_name: str, notify: bool = False, smart_check: bool = True, once_only: bool = True):
|
|
61
|
+
"""
|
|
62
|
+
Декоратор для регистрации запланированной задачи в роутере
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
task_name: Название задачи
|
|
66
|
+
notify: Уведомлять ли админов
|
|
67
|
+
smart_check: Использовать ли умную проверку
|
|
68
|
+
once_only: Выполнять ли только один раз
|
|
69
|
+
"""
|
|
70
|
+
def decorator(func: Callable) -> Callable:
|
|
71
|
+
self._scheduled_tasks[task_name] = {
|
|
72
|
+
'handler': func,
|
|
73
|
+
'name': func.__name__,
|
|
74
|
+
'notify': notify,
|
|
75
|
+
'smart_check': smart_check,
|
|
76
|
+
'once_only': once_only,
|
|
77
|
+
'router': self.name
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
logger.info(f"⏰ Роутер {self.name}: зарегистрирована задача '{task_name}': {func.__name__}")
|
|
81
|
+
|
|
82
|
+
from functools import wraps
|
|
83
|
+
@wraps(func)
|
|
84
|
+
async def wrapper(*args, **kwargs):
|
|
85
|
+
try:
|
|
86
|
+
return await func(*args, **kwargs)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.error(f"Ошибка выполнения задачи '{task_name}' в роутере {self.name}: {e}")
|
|
89
|
+
raise
|
|
90
|
+
return wrapper
|
|
91
|
+
return decorator
|
|
92
|
+
|
|
93
|
+
def global_handler(self, handler_type: str, notify: bool = False, once_only: bool = True):
|
|
94
|
+
"""
|
|
95
|
+
Декоратор для регистрации глобального обработчика в роутере
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
handler_type: Тип глобального обработчика
|
|
99
|
+
notify: Уведомлять ли админов
|
|
100
|
+
once_only: Выполнять ли только один раз
|
|
101
|
+
"""
|
|
102
|
+
def decorator(func: Callable) -> Callable:
|
|
103
|
+
self._global_handlers[handler_type] = {
|
|
104
|
+
'handler': func,
|
|
105
|
+
'name': func.__name__,
|
|
106
|
+
'notify': notify,
|
|
107
|
+
'once_only': once_only,
|
|
108
|
+
'router': self.name
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
logger.info(f"🌍 Роутер {self.name}: зарегистрирован глобальный обработчик '{handler_type}': {func.__name__}")
|
|
112
|
+
|
|
113
|
+
from functools import wraps
|
|
114
|
+
@wraps(func)
|
|
115
|
+
async def wrapper(*args, **kwargs):
|
|
116
|
+
try:
|
|
117
|
+
return await func(*args, **kwargs)
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.error(f"Ошибка выполнения глобального обработчика '{handler_type}' в роутере {self.name}: {e}")
|
|
120
|
+
raise
|
|
121
|
+
return wrapper
|
|
122
|
+
return decorator
|
|
123
|
+
|
|
124
|
+
def get_event_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
125
|
+
"""Получает все обработчики событий роутера"""
|
|
126
|
+
return self._event_handlers.copy()
|
|
127
|
+
|
|
128
|
+
def get_scheduled_tasks(self) -> Dict[str, Dict[str, Any]]:
|
|
129
|
+
"""Получает все запланированные задачи роутера"""
|
|
130
|
+
return self._scheduled_tasks.copy()
|
|
131
|
+
|
|
132
|
+
def get_global_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
133
|
+
"""Получает все глобальные обработчики роутера"""
|
|
134
|
+
return self._global_handlers.copy()
|
|
135
|
+
|
|
136
|
+
def get_all_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
137
|
+
"""Получает все обработчики роутера"""
|
|
138
|
+
all_handlers = {}
|
|
139
|
+
all_handlers.update(self._event_handlers)
|
|
140
|
+
all_handlers.update(self._scheduled_tasks)
|
|
141
|
+
all_handlers.update(self._global_handlers)
|
|
142
|
+
return all_handlers
|
|
143
|
+
|
|
144
|
+
def include_router(self, router: 'Router'):
|
|
145
|
+
"""
|
|
146
|
+
Включает другой роутер в текущий
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
router: Роутер для включения
|
|
150
|
+
"""
|
|
151
|
+
# Добавляем обработчики событий
|
|
152
|
+
for event_type, handler_info in router.get_event_handlers().items():
|
|
153
|
+
if event_type in self._event_handlers:
|
|
154
|
+
logger.warning(f"⚠️ Конфликт обработчиков событий '{event_type}' между роутерами {self.name} и {router.name}")
|
|
155
|
+
self._event_handlers[event_type] = handler_info
|
|
156
|
+
|
|
157
|
+
# Добавляем запланированные задачи
|
|
158
|
+
for task_name, task_info in router.get_scheduled_tasks().items():
|
|
159
|
+
if task_name in self._scheduled_tasks:
|
|
160
|
+
logger.warning(f"⚠️ Конфликт задач '{task_name}' между роутерами {self.name} и {router.name}")
|
|
161
|
+
self._scheduled_tasks[task_name] = task_info
|
|
162
|
+
|
|
163
|
+
# Добавляем глобальные обработчики
|
|
164
|
+
for handler_type, handler_info in router.get_global_handlers().items():
|
|
165
|
+
if handler_type in self._global_handlers:
|
|
166
|
+
logger.warning(f"⚠️ Конфликт глобальных обработчиков '{handler_type}' между роутерами {self.name} и {router.name}")
|
|
167
|
+
self._global_handlers[handler_type] = handler_info
|
|
168
|
+
|
|
169
|
+
logger.info(f"🔗 Роутер {self.name}: включен роутер {router.name}")
|
|
170
|
+
|
|
171
|
+
def __repr__(self):
|
|
172
|
+
return f"Router(name='{self.name}', events={len(self._event_handlers)}, tasks={len(self._scheduled_tasks)}, globals={len(self._global_handlers)})"
|
|
173
|
+
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Менеджер роутеров для Smart Bot Factory
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, List, Any, Optional
|
|
6
|
+
import logging
|
|
7
|
+
from .router import Router
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
class RouterManager:
|
|
12
|
+
"""
|
|
13
|
+
Менеджер для управления роутерами и их обработчиками
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self._routers: List[Router] = []
|
|
18
|
+
self._combined_handlers: Dict[str, Dict[str, Any]] = {
|
|
19
|
+
'event_handlers': {},
|
|
20
|
+
'scheduled_tasks': {},
|
|
21
|
+
'global_handlers': {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
logger.info("🔄 Создан менеджер роутеров")
|
|
25
|
+
|
|
26
|
+
def register_router(self, router: Router):
|
|
27
|
+
"""
|
|
28
|
+
Регистрирует роутер в менеджере
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
router: Роутер для регистрации
|
|
32
|
+
"""
|
|
33
|
+
if router not in self._routers:
|
|
34
|
+
self._routers.append(router)
|
|
35
|
+
self._update_combined_handlers()
|
|
36
|
+
logger.info(f"✅ Зарегистрирован роутер: {router.name}")
|
|
37
|
+
else:
|
|
38
|
+
logger.warning(f"⚠️ Роутер {router.name} уже зарегистрирован")
|
|
39
|
+
|
|
40
|
+
def unregister_router(self, router: Router):
|
|
41
|
+
"""
|
|
42
|
+
Отменяет регистрацию роутера
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
router: Роутер для отмены регистрации
|
|
46
|
+
"""
|
|
47
|
+
if router in self._routers:
|
|
48
|
+
self._routers.remove(router)
|
|
49
|
+
self._update_combined_handlers()
|
|
50
|
+
logger.info(f"❌ Отменена регистрация роутера: {router.name}")
|
|
51
|
+
else:
|
|
52
|
+
logger.warning(f"⚠️ Роутер {router.name} не найден в зарегистрированных")
|
|
53
|
+
|
|
54
|
+
def _update_combined_handlers(self):
|
|
55
|
+
"""Обновляет объединенные обработчики всех роутеров"""
|
|
56
|
+
# Очищаем текущие обработчики
|
|
57
|
+
self._combined_handlers = {
|
|
58
|
+
'event_handlers': {},
|
|
59
|
+
'scheduled_tasks': {},
|
|
60
|
+
'global_handlers': {}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Собираем обработчики из всех роутеров
|
|
64
|
+
for router in self._routers:
|
|
65
|
+
# Обработчики событий
|
|
66
|
+
for event_type, handler_info in router.get_event_handlers().items():
|
|
67
|
+
if event_type in self._combined_handlers['event_handlers']:
|
|
68
|
+
existing_router = self._combined_handlers['event_handlers'][event_type]['router']
|
|
69
|
+
logger.warning(f"⚠️ Конфликт обработчиков событий '{event_type}' между роутерами {existing_router} и {router.name}")
|
|
70
|
+
self._combined_handlers['event_handlers'][event_type] = handler_info
|
|
71
|
+
|
|
72
|
+
# Запланированные задачи
|
|
73
|
+
for task_name, task_info in router.get_scheduled_tasks().items():
|
|
74
|
+
if task_name in self._combined_handlers['scheduled_tasks']:
|
|
75
|
+
existing_router = self._combined_handlers['scheduled_tasks'][task_name]['router']
|
|
76
|
+
logger.warning(f"⚠️ Конфликт задач '{task_name}' между роутерами {existing_router} и {router.name}")
|
|
77
|
+
self._combined_handlers['scheduled_tasks'][task_name] = task_info
|
|
78
|
+
|
|
79
|
+
# Глобальные обработчики
|
|
80
|
+
for handler_type, handler_info in router.get_global_handlers().items():
|
|
81
|
+
if handler_type in self._combined_handlers['global_handlers']:
|
|
82
|
+
existing_router = self._combined_handlers['global_handlers'][handler_type]['router']
|
|
83
|
+
logger.warning(f"⚠️ Конфликт глобальных обработчиков '{handler_type}' между роутерами {existing_router} и {router.name}")
|
|
84
|
+
self._combined_handlers['global_handlers'][handler_type] = handler_info
|
|
85
|
+
|
|
86
|
+
total_handlers = (len(self._combined_handlers['event_handlers']) +
|
|
87
|
+
len(self._combined_handlers['scheduled_tasks']) +
|
|
88
|
+
len(self._combined_handlers['global_handlers']))
|
|
89
|
+
|
|
90
|
+
logger.info(f"📊 Обновлены объединенные обработчики: {total_handlers} обработчиков из {len(self._routers)} роутеров")
|
|
91
|
+
|
|
92
|
+
def get_event_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
93
|
+
"""Получает все обработчики событий"""
|
|
94
|
+
return self._combined_handlers['event_handlers'].copy()
|
|
95
|
+
|
|
96
|
+
def get_scheduled_tasks(self) -> Dict[str, Dict[str, Any]]:
|
|
97
|
+
"""Получает все запланированные задачи"""
|
|
98
|
+
return self._combined_handlers['scheduled_tasks'].copy()
|
|
99
|
+
|
|
100
|
+
def get_global_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
101
|
+
"""Получает все глобальные обработчики"""
|
|
102
|
+
return self._combined_handlers['global_handlers'].copy()
|
|
103
|
+
|
|
104
|
+
def get_all_handlers(self) -> Dict[str, Dict[str, Any]]:
|
|
105
|
+
"""Получает все обработчики всех типов"""
|
|
106
|
+
all_handlers = {}
|
|
107
|
+
all_handlers.update(self._combined_handlers['event_handlers'])
|
|
108
|
+
all_handlers.update(self._combined_handlers['scheduled_tasks'])
|
|
109
|
+
all_handlers.update(self._combined_handlers['global_handlers'])
|
|
110
|
+
return all_handlers
|
|
111
|
+
|
|
112
|
+
def get_handlers_for_prompt(self) -> str:
|
|
113
|
+
"""Возвращает описание всех обработчиков для добавления в промпт ИИ"""
|
|
114
|
+
prompt_parts: List[str] = []
|
|
115
|
+
|
|
116
|
+
if not any(self._combined_handlers.values()):
|
|
117
|
+
return ""
|
|
118
|
+
|
|
119
|
+
if self._combined_handlers['event_handlers']:
|
|
120
|
+
prompt_parts.append("ДОСТУПНЫЕ ОБРАБОТЧИКИ СОБЫТИЙ:")
|
|
121
|
+
for event_type, handler_info in self._combined_handlers['event_handlers'].items():
|
|
122
|
+
router_name = handler_info.get('router', 'unknown')
|
|
123
|
+
prompt_parts.append(f"- {event_type}: {handler_info['name']} (роутер: {router_name})")
|
|
124
|
+
|
|
125
|
+
if self._combined_handlers['scheduled_tasks']:
|
|
126
|
+
prompt_parts.append("\nДОСТУПНЫЕ ЗАДАЧИ ДЛЯ ПЛАНИРОВАНИЯ:")
|
|
127
|
+
for task_name, task_info in self._combined_handlers['scheduled_tasks'].items():
|
|
128
|
+
router_name = task_info.get('router', 'unknown')
|
|
129
|
+
prompt_parts.append(f"- {task_name}: {task_info['name']} (роутер: {router_name})")
|
|
130
|
+
|
|
131
|
+
if self._combined_handlers['global_handlers']:
|
|
132
|
+
prompt_parts.append("\nДОСТУПНЫЕ ГЛОБАЛЬНЫЕ ОБРАБОТЧИКИ:")
|
|
133
|
+
for handler_type, handler_info in self._combined_handlers['global_handlers'].items():
|
|
134
|
+
router_name = handler_info.get('router', 'unknown')
|
|
135
|
+
prompt_parts.append(f"- {handler_type}: {handler_info['name']} (роутер: {router_name})")
|
|
136
|
+
|
|
137
|
+
return "\n".join(prompt_parts)
|
|
138
|
+
|
|
139
|
+
def get_router_by_name(self, name: str) -> Optional[Router]:
|
|
140
|
+
"""Получает роутер по имени"""
|
|
141
|
+
for router in self._routers:
|
|
142
|
+
if router.name == name:
|
|
143
|
+
return router
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
def get_router_stats(self) -> Dict[str, Any]:
|
|
147
|
+
"""Получает статистику по роутерам"""
|
|
148
|
+
stats = {
|
|
149
|
+
"total_routers": len(self._routers),
|
|
150
|
+
"routers": []
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
for router in self._routers:
|
|
154
|
+
router_stats = {
|
|
155
|
+
"name": router.name,
|
|
156
|
+
"event_handlers": len(router.get_event_handlers()),
|
|
157
|
+
"scheduled_tasks": len(router.get_scheduled_tasks()),
|
|
158
|
+
"global_handlers": len(router.get_global_handlers())
|
|
159
|
+
}
|
|
160
|
+
stats["routers"].append(router_stats)
|
|
161
|
+
|
|
162
|
+
return stats
|
|
163
|
+
|
|
164
|
+
def __repr__(self):
|
|
165
|
+
return f"RouterManager(routers={len(self._routers)}, handlers={len(self.get_all_handlers())})"
|
|
166
|
+
|
|
@@ -3,19 +3,19 @@
|
|
|
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
|
|
14
12
|
from ..integrations.supabase_client import SupabaseClient
|
|
15
13
|
from ..core.conversation_manager import ConversationManager
|
|
16
14
|
from ..admin.admin_manager import AdminManager
|
|
15
|
+
from ..analytics.analytics_manager import AnalyticsManager
|
|
17
16
|
from ..utils.prompt_loader import PromptLoader
|
|
18
17
|
from ..core.decorators import get_handlers_for_prompt
|
|
18
|
+
from ..core.router_manager import RouterManager
|
|
19
19
|
|
|
20
20
|
logger = logging.getLogger(__name__)
|
|
21
21
|
|
|
@@ -42,7 +42,9 @@ class BotBuilder:
|
|
|
42
42
|
self.supabase_client: Optional[SupabaseClient] = None
|
|
43
43
|
self.conversation_manager: Optional[ConversationManager] = None
|
|
44
44
|
self.admin_manager: Optional[AdminManager] = None
|
|
45
|
+
self.analytics_manager: Optional[AnalyticsManager] = None
|
|
45
46
|
self.prompt_loader: Optional[PromptLoader] = None
|
|
47
|
+
self.router_manager: Optional[RouterManager] = None
|
|
46
48
|
|
|
47
49
|
# Флаги инициализации
|
|
48
50
|
self._initialized = False
|
|
@@ -146,10 +148,24 @@ class BotBuilder:
|
|
|
146
148
|
await self.admin_manager.sync_admins_from_config()
|
|
147
149
|
logger.info(f"✅ Admin Manager инициализирован")
|
|
148
150
|
|
|
151
|
+
# Analytics Manager
|
|
152
|
+
self.analytics_manager = AnalyticsManager(self.supabase_client)
|
|
153
|
+
logger.info(f"✅ Analytics Manager инициализирован")
|
|
154
|
+
|
|
149
155
|
# Conversation Manager
|
|
150
|
-
|
|
156
|
+
parse_mode = os.environ.get('MESSAGE_PARSE_MODE', 'Markdown')
|
|
157
|
+
admin_session_timeout_minutes = int(os.environ.get('ADMIN_SESSION_TIMEOUT_MINUTES', '30'))
|
|
158
|
+
|
|
159
|
+
self.conversation_manager = ConversationManager(self.supabase_client, self.admin_manager, parse_mode, admin_session_timeout_minutes)
|
|
151
160
|
logger.info(f"✅ Conversation Manager инициализирован")
|
|
152
161
|
|
|
162
|
+
# Router Manager (создаем только если еще не создан)
|
|
163
|
+
if not self.router_manager:
|
|
164
|
+
self.router_manager = RouterManager()
|
|
165
|
+
logger.info(f"✅ Router Manager инициализирован")
|
|
166
|
+
else:
|
|
167
|
+
logger.info(f"✅ Router Manager уже был создан ранее")
|
|
168
|
+
|
|
153
169
|
# Prompt Loader
|
|
154
170
|
self.prompt_loader = PromptLoader(
|
|
155
171
|
prompts_dir=self.config.PROMT_FILES_DIR
|
|
@@ -164,7 +180,11 @@ class BotBuilder:
|
|
|
164
180
|
logger.info(f"🔧 Обновление промптов с информацией об обработчиках")
|
|
165
181
|
|
|
166
182
|
# Получаем информацию о доступных обработчиках
|
|
167
|
-
|
|
183
|
+
# Сначала пробуем получить из роутеров, если нет - из старых декораторов
|
|
184
|
+
if self.router_manager:
|
|
185
|
+
event_handlers_info = self.router_manager.get_handlers_for_prompt()
|
|
186
|
+
else:
|
|
187
|
+
event_handlers_info = get_handlers_for_prompt()
|
|
168
188
|
|
|
169
189
|
# Если есть обработчики, добавляем их в системный промпт
|
|
170
190
|
if event_handlers_info:
|
|
@@ -192,6 +212,7 @@ class BotBuilder:
|
|
|
192
212
|
"supabase_client": self.supabase_client is not None,
|
|
193
213
|
"conversation_manager": self.conversation_manager is not None,
|
|
194
214
|
"admin_manager": self.admin_manager is not None,
|
|
215
|
+
"analytics_manager": self.analytics_manager is not None,
|
|
195
216
|
"prompt_loader": self.prompt_loader is not None
|
|
196
217
|
},
|
|
197
218
|
"tools": {
|
|
@@ -199,6 +220,62 @@ class BotBuilder:
|
|
|
199
220
|
}
|
|
200
221
|
}
|
|
201
222
|
|
|
223
|
+
def set_global_vars_in_module(self, module_name: str):
|
|
224
|
+
"""
|
|
225
|
+
Устанавливает глобальные переменные в указанном модуле для удобного доступа
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
module_name: Имя модуля (например, 'valera', 'my_bot')
|
|
229
|
+
"""
|
|
230
|
+
try:
|
|
231
|
+
import sys
|
|
232
|
+
import importlib
|
|
233
|
+
|
|
234
|
+
# Получаем модуль бота
|
|
235
|
+
bot_module = sys.modules.get(module_name)
|
|
236
|
+
if not bot_module:
|
|
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
|
|
244
|
+
|
|
245
|
+
# Устанавливаем глобальные переменные
|
|
246
|
+
bot_module.supabase_client = self.supabase_client
|
|
247
|
+
bot_module.openai_client = self.openai_client
|
|
248
|
+
bot_module.config = self.config
|
|
249
|
+
bot_module.admin_manager = self.admin_manager
|
|
250
|
+
bot_module.analytics_manager = self.analytics_manager
|
|
251
|
+
bot_module.conversation_manager = self.conversation_manager
|
|
252
|
+
bot_module.prompt_loader = self.prompt_loader
|
|
253
|
+
|
|
254
|
+
logger.info(f"✅ Глобальные переменные установлены в модуле '{module_name}'")
|
|
255
|
+
|
|
256
|
+
except Exception as e:
|
|
257
|
+
logger.warning(f"⚠️ Не удалось установить глобальные переменные в модуле '{module_name}': {e}")
|
|
258
|
+
|
|
259
|
+
def register_router(self, router):
|
|
260
|
+
"""
|
|
261
|
+
Регистрирует роутер в менеджере роутеров
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
router: Роутер для регистрации
|
|
265
|
+
"""
|
|
266
|
+
# Если RouterManager еще не инициализирован, создаем его
|
|
267
|
+
if not self.router_manager:
|
|
268
|
+
from ..core.router_manager import RouterManager
|
|
269
|
+
self.router_manager = RouterManager()
|
|
270
|
+
logger.info(f"✅ Router Manager создан для регистрации роутера '{router.name}'")
|
|
271
|
+
|
|
272
|
+
self.router_manager.register_router(router)
|
|
273
|
+
logger.info(f"✅ Роутер '{router.name}' зарегистрирован в боте {self.bot_id}")
|
|
274
|
+
|
|
275
|
+
def get_router_manager(self) -> RouterManager:
|
|
276
|
+
"""Получает менеджер роутеров"""
|
|
277
|
+
return self.router_manager
|
|
278
|
+
|
|
202
279
|
async def start(self):
|
|
203
280
|
"""
|
|
204
281
|
Запускает бота (аналог main.py)
|
|
@@ -228,8 +305,7 @@ class BotBuilder:
|
|
|
228
305
|
prompts_status = await self.prompt_loader.validate_prompts()
|
|
229
306
|
logger.info(f"Статус промптов: {prompts_status}")
|
|
230
307
|
|
|
231
|
-
|
|
232
|
-
import sys
|
|
308
|
+
|
|
233
309
|
import importlib
|
|
234
310
|
|
|
235
311
|
# Устанавливаем глобальные переменные в модулях handlers и admin_logic
|
|
@@ -242,6 +318,7 @@ class BotBuilder:
|
|
|
242
318
|
handlers_module.openai_client = self.openai_client
|
|
243
319
|
handlers_module.prompt_loader = self.prompt_loader
|
|
244
320
|
handlers_module.admin_manager = self.admin_manager
|
|
321
|
+
handlers_module.analytics_manager = self.analytics_manager
|
|
245
322
|
handlers_module.conversation_manager = self.conversation_manager
|
|
246
323
|
logger.info("✅ Глобальные переменные установлены в handlers")
|
|
247
324
|
except Exception as e:
|
|
@@ -256,6 +333,7 @@ class BotBuilder:
|
|
|
256
333
|
admin_logic_module.openai_client = self.openai_client
|
|
257
334
|
admin_logic_module.prompt_loader = self.prompt_loader
|
|
258
335
|
admin_logic_module.admin_manager = self.admin_manager
|
|
336
|
+
admin_logic_module.analytics_manager = self.analytics_manager
|
|
259
337
|
admin_logic_module.conversation_manager = self.conversation_manager
|
|
260
338
|
logger.info("✅ Глобальные переменные установлены в admin_logic")
|
|
261
339
|
except Exception as e:
|
|
@@ -271,6 +349,7 @@ class BotBuilder:
|
|
|
271
349
|
bot_utils_module.openai_client = self.openai_client
|
|
272
350
|
bot_utils_module.prompt_loader = self.prompt_loader
|
|
273
351
|
bot_utils_module.admin_manager = self.admin_manager
|
|
352
|
+
bot_utils_module.analytics_manager = self.analytics_manager
|
|
274
353
|
bot_utils_module.conversation_manager = self.conversation_manager
|
|
275
354
|
logger.info("✅ Глобальные переменные установлены в bot_utils")
|
|
276
355
|
except Exception as e:
|
|
@@ -301,6 +380,17 @@ class BotBuilder:
|
|
|
301
380
|
setup_admin_handlers(dp) # Админские команды (/админ, /стат, /чат)
|
|
302
381
|
setup_handlers(dp) # Основные пользовательские обработчики
|
|
303
382
|
|
|
383
|
+
# Устанавливаем глобальные переменные в модуле бота для удобного доступа
|
|
384
|
+
self.set_global_vars_in_module(self.bot_id)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
# Устанавливаем роутер-менеджер в декораторы
|
|
388
|
+
if self.router_manager:
|
|
389
|
+
from ..core.decorators import set_router_manager
|
|
390
|
+
set_router_manager(self.router_manager)
|
|
391
|
+
|
|
392
|
+
# Фоновые задачи выполняются через asyncio.create_task в decorators.py
|
|
393
|
+
|
|
304
394
|
# Логируем информацию о запуске
|
|
305
395
|
logger.info(f"✅ Бот {self.bot_id} запущен и готов к работе!")
|
|
306
396
|
logger.info(f" 📊 Изоляция данных: bot_id = {self.config.BOT_ID}")
|
|
@@ -308,12 +398,12 @@ class BotBuilder:
|
|
|
308
398
|
logger.info(f" 📝 Загружено промптов: {len(self.config.PROMPT_FILES)}")
|
|
309
399
|
|
|
310
400
|
# Четкое сообщение о запуске
|
|
311
|
-
print(f"\nБОТ {self.bot_id.upper()} УСПЕШНО ЗАПУЩЕН!")
|
|
312
|
-
print(f"Telegram Bot ID: {self.config.BOT_ID}")
|
|
313
|
-
print(f"Админов: {len(self.config.ADMIN_TELEGRAM_IDS)}")
|
|
314
|
-
print(f"Промптов: {len(self.config.PROMPT_FILES)}")
|
|
315
|
-
print(f"Ожидание сообщений...")
|
|
316
|
-
print(f"Для остановки нажмите Ctrl+C\n")
|
|
401
|
+
print(f"\n🤖 БОТ {self.bot_id.upper()} УСПЕШНО ЗАПУЩЕН!")
|
|
402
|
+
print(f"📱 Telegram Bot ID: {self.config.BOT_ID}")
|
|
403
|
+
print(f"👑 Админов: {len(self.config.ADMIN_TELEGRAM_IDS)}")
|
|
404
|
+
print(f"📝 Промптов: {len(self.config.PROMPT_FILES)}")
|
|
405
|
+
print(f"⏳ Ожидание сообщений...")
|
|
406
|
+
print(f"⏹️ Для остановки нажмите Ctrl+C\n")
|
|
317
407
|
|
|
318
408
|
# Запуск polling (бесконечная обработка сообщений)
|
|
319
409
|
await dp.start_polling(bot)
|