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.

Files changed (37) hide show
  1. smart_bot_factory/__init__.py +0 -48
  2. smart_bot_factory/admin/admin_logic.py +11 -11
  3. smart_bot_factory/cli.py +299 -106
  4. smart_bot_factory/clients/__init__.py +33 -0
  5. smart_bot_factory/configs/growthmed-october-24/prompts/final_instructions.txt +2 -0
  6. smart_bot_factory/configs/growthmed-october-24/tests/quick_scenarios.yaml +95 -28
  7. smart_bot_factory/core/__init__.py +43 -22
  8. smart_bot_factory/core/bot_utils.py +268 -95
  9. smart_bot_factory/core/conversation_manager.py +542 -535
  10. smart_bot_factory/core/decorators.py +943 -229
  11. smart_bot_factory/core/globals.py +68 -0
  12. smart_bot_factory/core/message_sender.py +6 -6
  13. smart_bot_factory/core/router.py +172 -0
  14. smart_bot_factory/core/router_manager.py +165 -0
  15. smart_bot_factory/creation/__init__.py +1 -2
  16. smart_bot_factory/creation/bot_builder.py +116 -8
  17. smart_bot_factory/creation/bot_testing.py +74 -13
  18. smart_bot_factory/handlers/handlers.py +10 -2
  19. smart_bot_factory/integrations/__init__.py +1 -0
  20. smart_bot_factory/integrations/supabase_client.py +272 -2
  21. smart_bot_factory/utm_link_generator.py +106 -0
  22. smart_bot_factory-0.1.5.dist-info/METADATA +466 -0
  23. {smart_bot_factory-0.1.3.dist-info → smart_bot_factory-0.1.5.dist-info}/RECORD +26 -31
  24. smart_bot_factory/configs/growthmed-helper/env_example.txt +0 -1
  25. smart_bot_factory/configs/growthmed-helper/prompts/1sales_context.txt +0 -9
  26. smart_bot_factory/configs/growthmed-helper/prompts/2product_info.txt +0 -582
  27. smart_bot_factory/configs/growthmed-helper/prompts/3objection_handling.txt +0 -66
  28. smart_bot_factory/configs/growthmed-helper/prompts/final_instructions.txt +0 -232
  29. smart_bot_factory/configs/growthmed-helper/prompts/help_message.txt +0 -28
  30. smart_bot_factory/configs/growthmed-helper/prompts/welcome_message.txt +0 -7
  31. smart_bot_factory/configs/growthmed-helper/welcome_file/welcome_file_msg.txt +0 -16
  32. 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
  33. smart_bot_factory/uv.lock +0 -2004
  34. smart_bot_factory-0.1.3.dist-info/METADATA +0 -126
  35. {smart_bot_factory-0.1.3.dist-info → smart_bot_factory-0.1.5.dist-info}/WHEEL +0 -0
  36. {smart_bot_factory-0.1.3.dist-info → smart_bot_factory-0.1.5.dist-info}/entry_points.txt +0 -0
  37. {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 .config import Config
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())})"
@@ -3,6 +3,5 @@ Creation модули smart_bot_factory
3
3
  """
4
4
 
5
5
  from ..creation.bot_builder import BotBuilder
6
- from ..creation.bot_testing import main as bot_testing_main
7
6
 
8
- __all__ = ['BotBuilder', 'bot_testing_main']
7
+ __all__ = ['BotBuilder']
@@ -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
- self.conversation_manager = ConversationManager(self.supabase_client, self.admin_manager)
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
- event_handlers_info = get_handlers_for_prompt()
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)