smart-bot-factory 0.1.3__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 -48
- smart_bot_factory/admin/admin_logic.py +11 -11
- smart_bot_factory/cli.py +299 -106
- 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 +268 -95
- smart_bot_factory/core/conversation_manager.py +542 -535
- smart_bot_factory/core/decorators.py +943 -229
- 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/utm_link_generator.py +106 -0
- smart_bot_factory-0.1.5.dist-info/METADATA +466 -0
- {smart_bot_factory-0.1.3.dist-info → smart_bot_factory-0.1.5.dist-info}/RECORD +26 -31
- 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/uv.lock +0 -2004
- smart_bot_factory-0.1.3.dist-info/METADATA +0 -126
- {smart_bot_factory-0.1.3.dist-info → smart_bot_factory-0.1.5.dist-info}/WHEEL +0 -0
- {smart_bot_factory-0.1.3.dist-info → smart_bot_factory-0.1.5.dist-info}/entry_points.txt +0 -0
- {smart_bot_factory-0.1.3.dist-info → smart_bot_factory-0.1.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
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
|
|
@@ -29,14 +29,14 @@ async def send_message_by_ai(
|
|
|
29
29
|
try:
|
|
30
30
|
# Импортируем необходимые компоненты
|
|
31
31
|
from .bot_utils import parse_ai_response, process_events
|
|
32
|
-
from
|
|
33
|
-
from .openai_client import OpenAIClient
|
|
34
|
-
from .supabase_client import SupabaseClient
|
|
35
|
-
from .prompt_loader import PromptLoader
|
|
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
36
|
from aiogram import Bot
|
|
37
37
|
|
|
38
38
|
# Получаем компоненты из глобального контекста
|
|
39
|
-
from .handlers import get_global_var
|
|
39
|
+
from ..handlers.handlers import get_global_var
|
|
40
40
|
bot = get_global_var('bot')
|
|
41
41
|
supabase_client = get_global_var('supabase_client')
|
|
42
42
|
openai_client = get_global_var('openai_client')
|
|
@@ -211,7 +211,7 @@ async def send_message_by_human(
|
|
|
211
211
|
"""
|
|
212
212
|
try:
|
|
213
213
|
# Импортируем необходимые компоненты
|
|
214
|
-
from .handlers import get_global_var
|
|
214
|
+
from ..handlers.handlers import get_global_var
|
|
215
215
|
bot = get_global_var('bot')
|
|
216
216
|
supabase_client = get_global_var('supabase_client')
|
|
217
217
|
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Роутер для Smart Bot Factory - аналог aiogram Router
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, List, Any, Callable, Optional
|
|
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)})"
|
|
@@ -0,0 +1,165 @@
|
|
|
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())})"
|
|
@@ -14,8 +14,10 @@ from ..integrations.openai_client import OpenAIClient
|
|
|
14
14
|
from ..integrations.supabase_client import SupabaseClient
|
|
15
15
|
from ..core.conversation_manager import ConversationManager
|
|
16
16
|
from ..admin.admin_manager import AdminManager
|
|
17
|
+
from ..analytics.analytics_manager import AnalyticsManager
|
|
17
18
|
from ..utils.prompt_loader import PromptLoader
|
|
18
19
|
from ..core.decorators import get_handlers_for_prompt
|
|
20
|
+
from ..core.router_manager import RouterManager
|
|
19
21
|
|
|
20
22
|
logger = logging.getLogger(__name__)
|
|
21
23
|
|
|
@@ -42,7 +44,9 @@ class BotBuilder:
|
|
|
42
44
|
self.supabase_client: Optional[SupabaseClient] = None
|
|
43
45
|
self.conversation_manager: Optional[ConversationManager] = None
|
|
44
46
|
self.admin_manager: Optional[AdminManager] = None
|
|
47
|
+
self.analytics_manager: Optional[AnalyticsManager] = None
|
|
45
48
|
self.prompt_loader: Optional[PromptLoader] = None
|
|
49
|
+
self.router_manager: Optional[RouterManager] = None
|
|
46
50
|
|
|
47
51
|
# Флаги инициализации
|
|
48
52
|
self._initialized = False
|
|
@@ -146,10 +150,24 @@ class BotBuilder:
|
|
|
146
150
|
await self.admin_manager.sync_admins_from_config()
|
|
147
151
|
logger.info(f"✅ Admin Manager инициализирован")
|
|
148
152
|
|
|
153
|
+
# Analytics Manager
|
|
154
|
+
self.analytics_manager = AnalyticsManager(self.supabase_client)
|
|
155
|
+
logger.info(f"✅ Analytics Manager инициализирован")
|
|
156
|
+
|
|
149
157
|
# Conversation Manager
|
|
150
|
-
|
|
158
|
+
parse_mode = os.environ.get('MESSAGE_PARSE_MODE', 'Markdown')
|
|
159
|
+
admin_session_timeout_minutes = int(os.environ.get('ADMIN_SESSION_TIMEOUT_MINUTES', '30'))
|
|
160
|
+
|
|
161
|
+
self.conversation_manager = ConversationManager(self.supabase_client, self.admin_manager, parse_mode, admin_session_timeout_minutes)
|
|
151
162
|
logger.info(f"✅ Conversation Manager инициализирован")
|
|
152
163
|
|
|
164
|
+
# Router Manager (создаем только если еще не создан)
|
|
165
|
+
if not self.router_manager:
|
|
166
|
+
self.router_manager = RouterManager()
|
|
167
|
+
logger.info(f"✅ Router Manager инициализирован")
|
|
168
|
+
else:
|
|
169
|
+
logger.info(f"✅ Router Manager уже был создан ранее")
|
|
170
|
+
|
|
153
171
|
# Prompt Loader
|
|
154
172
|
self.prompt_loader = PromptLoader(
|
|
155
173
|
prompts_dir=self.config.PROMT_FILES_DIR
|
|
@@ -164,7 +182,11 @@ class BotBuilder:
|
|
|
164
182
|
logger.info(f"🔧 Обновление промптов с информацией об обработчиках")
|
|
165
183
|
|
|
166
184
|
# Получаем информацию о доступных обработчиках
|
|
167
|
-
|
|
185
|
+
# Сначала пробуем получить из роутеров, если нет - из старых декораторов
|
|
186
|
+
if self.router_manager:
|
|
187
|
+
event_handlers_info = self.router_manager.get_handlers_for_prompt()
|
|
188
|
+
else:
|
|
189
|
+
event_handlers_info = get_handlers_for_prompt()
|
|
168
190
|
|
|
169
191
|
# Если есть обработчики, добавляем их в системный промпт
|
|
170
192
|
if event_handlers_info:
|
|
@@ -192,6 +214,7 @@ class BotBuilder:
|
|
|
192
214
|
"supabase_client": self.supabase_client is not None,
|
|
193
215
|
"conversation_manager": self.conversation_manager is not None,
|
|
194
216
|
"admin_manager": self.admin_manager is not None,
|
|
217
|
+
"analytics_manager": self.analytics_manager is not None,
|
|
195
218
|
"prompt_loader": self.prompt_loader is not None
|
|
196
219
|
},
|
|
197
220
|
"tools": {
|
|
@@ -199,6 +222,57 @@ class BotBuilder:
|
|
|
199
222
|
}
|
|
200
223
|
}
|
|
201
224
|
|
|
225
|
+
def set_global_vars_in_module(self, module_name: str):
|
|
226
|
+
"""
|
|
227
|
+
Устанавливает глобальные переменные в указанном модуле для удобного доступа
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
module_name: Имя модуля (например, 'valera', 'my_bot')
|
|
231
|
+
"""
|
|
232
|
+
try:
|
|
233
|
+
import sys
|
|
234
|
+
import importlib
|
|
235
|
+
|
|
236
|
+
# Получаем модуль бота
|
|
237
|
+
bot_module = sys.modules.get(module_name)
|
|
238
|
+
if not bot_module:
|
|
239
|
+
logger.warning(f"⚠️ Модуль '{module_name}' не найден для установки глобальных переменных")
|
|
240
|
+
return
|
|
241
|
+
|
|
242
|
+
# Устанавливаем глобальные переменные
|
|
243
|
+
bot_module.supabase_client = self.supabase_client
|
|
244
|
+
bot_module.openai_client = self.openai_client
|
|
245
|
+
bot_module.config = self.config
|
|
246
|
+
bot_module.admin_manager = self.admin_manager
|
|
247
|
+
bot_module.analytics_manager = self.analytics_manager
|
|
248
|
+
bot_module.conversation_manager = self.conversation_manager
|
|
249
|
+
bot_module.prompt_loader = self.prompt_loader
|
|
250
|
+
|
|
251
|
+
logger.info(f"✅ Глобальные переменные установлены в модуле '{module_name}'")
|
|
252
|
+
|
|
253
|
+
except Exception as e:
|
|
254
|
+
logger.warning(f"⚠️ Не удалось установить глобальные переменные в модуле '{module_name}': {e}")
|
|
255
|
+
|
|
256
|
+
def register_router(self, router):
|
|
257
|
+
"""
|
|
258
|
+
Регистрирует роутер в менеджере роутеров
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
router: Роутер для регистрации
|
|
262
|
+
"""
|
|
263
|
+
# Если RouterManager еще не инициализирован, создаем его
|
|
264
|
+
if not self.router_manager:
|
|
265
|
+
from ..core.router_manager import RouterManager
|
|
266
|
+
self.router_manager = RouterManager()
|
|
267
|
+
logger.info(f"✅ Router Manager создан для регистрации роутера '{router.name}'")
|
|
268
|
+
|
|
269
|
+
self.router_manager.register_router(router)
|
|
270
|
+
logger.info(f"✅ Роутер '{router.name}' зарегистрирован в боте {self.bot_id}")
|
|
271
|
+
|
|
272
|
+
def get_router_manager(self) -> RouterManager:
|
|
273
|
+
"""Получает менеджер роутеров"""
|
|
274
|
+
return self.router_manager
|
|
275
|
+
|
|
202
276
|
async def start(self):
|
|
203
277
|
"""
|
|
204
278
|
Запускает бота (аналог main.py)
|
|
@@ -242,6 +316,7 @@ class BotBuilder:
|
|
|
242
316
|
handlers_module.openai_client = self.openai_client
|
|
243
317
|
handlers_module.prompt_loader = self.prompt_loader
|
|
244
318
|
handlers_module.admin_manager = self.admin_manager
|
|
319
|
+
handlers_module.analytics_manager = self.analytics_manager
|
|
245
320
|
handlers_module.conversation_manager = self.conversation_manager
|
|
246
321
|
logger.info("✅ Глобальные переменные установлены в handlers")
|
|
247
322
|
except Exception as e:
|
|
@@ -256,6 +331,7 @@ class BotBuilder:
|
|
|
256
331
|
admin_logic_module.openai_client = self.openai_client
|
|
257
332
|
admin_logic_module.prompt_loader = self.prompt_loader
|
|
258
333
|
admin_logic_module.admin_manager = self.admin_manager
|
|
334
|
+
admin_logic_module.analytics_manager = self.analytics_manager
|
|
259
335
|
admin_logic_module.conversation_manager = self.conversation_manager
|
|
260
336
|
logger.info("✅ Глобальные переменные установлены в admin_logic")
|
|
261
337
|
except Exception as e:
|
|
@@ -271,6 +347,7 @@ class BotBuilder:
|
|
|
271
347
|
bot_utils_module.openai_client = self.openai_client
|
|
272
348
|
bot_utils_module.prompt_loader = self.prompt_loader
|
|
273
349
|
bot_utils_module.admin_manager = self.admin_manager
|
|
350
|
+
bot_utils_module.analytics_manager = self.analytics_manager
|
|
274
351
|
bot_utils_module.conversation_manager = self.conversation_manager
|
|
275
352
|
logger.info("✅ Глобальные переменные установлены в bot_utils")
|
|
276
353
|
except Exception as e:
|
|
@@ -301,6 +378,37 @@ class BotBuilder:
|
|
|
301
378
|
setup_admin_handlers(dp) # Админские команды (/админ, /стат, /чат)
|
|
302
379
|
setup_handlers(dp) # Основные пользовательские обработчики
|
|
303
380
|
|
|
381
|
+
# Устанавливаем глобальные переменные в модуле бота для удобного доступа
|
|
382
|
+
self.set_global_vars_in_module(self.bot_id)
|
|
383
|
+
|
|
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
|
+
|
|
405
|
+
# Устанавливаем роутер-менеджер в декораторы
|
|
406
|
+
if self.router_manager:
|
|
407
|
+
from ..core.decorators import set_router_manager
|
|
408
|
+
set_router_manager(self.router_manager)
|
|
409
|
+
|
|
410
|
+
# Фоновые задачи выполняются через asyncio.create_task в decorators.py
|
|
411
|
+
|
|
304
412
|
# Логируем информацию о запуске
|
|
305
413
|
logger.info(f"✅ Бот {self.bot_id} запущен и готов к работе!")
|
|
306
414
|
logger.info(f" 📊 Изоляция данных: bot_id = {self.config.BOT_ID}")
|
|
@@ -308,12 +416,12 @@ class BotBuilder:
|
|
|
308
416
|
logger.info(f" 📝 Загружено промптов: {len(self.config.PROMPT_FILES)}")
|
|
309
417
|
|
|
310
418
|
# Четкое сообщение о запуске
|
|
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")
|
|
419
|
+
print(f"\n🤖 БОТ {self.bot_id.upper()} УСПЕШНО ЗАПУЩЕН!")
|
|
420
|
+
print(f"📱 Telegram Bot ID: {self.config.BOT_ID}")
|
|
421
|
+
print(f"👑 Админов: {len(self.config.ADMIN_TELEGRAM_IDS)}")
|
|
422
|
+
print(f"📝 Промптов: {len(self.config.PROMPT_FILES)}")
|
|
423
|
+
print(f"⏳ Ожидание сообщений...")
|
|
424
|
+
print(f"⏹️ Для остановки нажмите Ctrl+C\n")
|
|
317
425
|
|
|
318
426
|
# Запуск polling (бесконечная обработка сообщений)
|
|
319
427
|
await dp.start_polling(bot)
|