smart-bot-factory 1.1.1__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.
- smart_bot_factory/__init__.py +3 -0
- smart_bot_factory/admin/__init__.py +18 -0
- smart_bot_factory/admin/admin_events.py +1223 -0
- smart_bot_factory/admin/admin_logic.py +553 -0
- smart_bot_factory/admin/admin_manager.py +156 -0
- smart_bot_factory/admin/admin_tester.py +157 -0
- smart_bot_factory/admin/timeout_checker.py +547 -0
- smart_bot_factory/aiogram_calendar/__init__.py +14 -0
- smart_bot_factory/aiogram_calendar/common.py +64 -0
- smart_bot_factory/aiogram_calendar/dialog_calendar.py +259 -0
- smart_bot_factory/aiogram_calendar/schemas.py +99 -0
- smart_bot_factory/aiogram_calendar/simple_calendar.py +224 -0
- smart_bot_factory/analytics/analytics_manager.py +414 -0
- smart_bot_factory/cli.py +806 -0
- smart_bot_factory/config.py +258 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/1sales_context.txt +16 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/2product_info.txt +582 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/3objection_handling.txt +66 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/final_instructions.txt +212 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/help_message.txt +28 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/welcome_message.txt +8 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064229.txt +818 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064335.txt +32 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064638.txt +35 -0
- smart_bot_factory/configs/growthmed-october-24/tests/quick_scenarios.yaml +133 -0
- smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml +108 -0
- smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml +46 -0
- smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt +16 -0
- smart_bot_factory/configs/growthmed-october-24/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/bot_utils.py +1108 -0
- smart_bot_factory/core/conversation_manager.py +653 -0
- smart_bot_factory/core/decorators.py +2464 -0
- smart_bot_factory/core/message_sender.py +729 -0
- smart_bot_factory/core/router.py +347 -0
- smart_bot_factory/core/router_manager.py +218 -0
- smart_bot_factory/core/states.py +27 -0
- smart_bot_factory/creation/__init__.py +7 -0
- smart_bot_factory/creation/bot_builder.py +1093 -0
- smart_bot_factory/creation/bot_testing.py +1122 -0
- smart_bot_factory/dashboard/__init__.py +3 -0
- smart_bot_factory/event/__init__.py +7 -0
- smart_bot_factory/handlers/handlers.py +2013 -0
- smart_bot_factory/integrations/langchain_openai.py +542 -0
- smart_bot_factory/integrations/openai_client.py +513 -0
- smart_bot_factory/integrations/supabase_client.py +1678 -0
- smart_bot_factory/memory/__init__.py +8 -0
- smart_bot_factory/memory/memory_manager.py +299 -0
- smart_bot_factory/memory/static_memory.py +214 -0
- smart_bot_factory/message/__init__.py +56 -0
- smart_bot_factory/rag/__init__.py +5 -0
- smart_bot_factory/rag/decorators.py +29 -0
- smart_bot_factory/rag/router.py +54 -0
- smart_bot_factory/rag/templates/__init__.py +3 -0
- smart_bot_factory/rag/templates/create_table.sql +7 -0
- smart_bot_factory/rag/templates/create_table_and_function_template.py +94 -0
- smart_bot_factory/rag/templates/match_function.sql +61 -0
- smart_bot_factory/rag/templates/match_services_template.py +82 -0
- smart_bot_factory/rag/vectorstore.py +449 -0
- smart_bot_factory/router/__init__.py +10 -0
- smart_bot_factory/setup_checker.py +512 -0
- smart_bot_factory/supabase/__init__.py +7 -0
- smart_bot_factory/supabase/client.py +631 -0
- smart_bot_factory/utils/__init__.py +11 -0
- smart_bot_factory/utils/debug_routing.py +114 -0
- smart_bot_factory/utils/prompt_loader.py +529 -0
- smart_bot_factory/utils/tool_router.py +68 -0
- smart_bot_factory/utils/user_prompt_loader.py +55 -0
- smart_bot_factory/utm_link_generator.py +123 -0
- smart_bot_factory-1.1.1.dist-info/METADATA +1135 -0
- smart_bot_factory-1.1.1.dist-info/RECORD +73 -0
- smart_bot_factory-1.1.1.dist-info/WHEEL +4 -0
- smart_bot_factory-1.1.1.dist-info/entry_points.txt +2 -0
- smart_bot_factory-1.1.1.dist-info/licenses/LICENSE +24 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Dict, List, Set
|
|
3
|
+
|
|
4
|
+
from aiogram.types import User
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AdminManager:
|
|
10
|
+
"""Управление администраторами бота"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, config, supabase_client):
|
|
13
|
+
self.config = config
|
|
14
|
+
self.supabase = supabase_client
|
|
15
|
+
self.admin_ids: Set[int] = set(config.ADMIN_TELEGRAM_IDS)
|
|
16
|
+
self.admin_modes: Dict[int, bool] = {} # admin_id -> is_in_admin_mode
|
|
17
|
+
|
|
18
|
+
logger.info(f"Инициализирован менеджер админов: {len(self.admin_ids)} админов")
|
|
19
|
+
|
|
20
|
+
async def sync_admins_from_config(self):
|
|
21
|
+
"""Синхронизирует админов из конфига с базой данных"""
|
|
22
|
+
if not self.admin_ids:
|
|
23
|
+
logger.warning("Нет админов в конфигурации")
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
for admin_id in self.admin_ids:
|
|
28
|
+
await self.supabase.sync_admin(
|
|
29
|
+
{
|
|
30
|
+
"telegram_id": admin_id,
|
|
31
|
+
"username": None, # будет обновлено при первом сообщении
|
|
32
|
+
"first_name": None,
|
|
33
|
+
"last_name": None,
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Устанавливаем режим администратора по умолчанию
|
|
38
|
+
if admin_id not in self.admin_modes:
|
|
39
|
+
self.admin_modes[admin_id] = True
|
|
40
|
+
|
|
41
|
+
logger.info(f"Синхронизированы админы: {self.admin_ids}")
|
|
42
|
+
|
|
43
|
+
except Exception as e:
|
|
44
|
+
logger.error(f"Ошибка синхронизации админов: {e}")
|
|
45
|
+
raise
|
|
46
|
+
|
|
47
|
+
async def update_admin_info(self, user: User):
|
|
48
|
+
"""Обновляет информацию об админе"""
|
|
49
|
+
if not self.is_admin(user.id):
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
await self.supabase.sync_admin(
|
|
54
|
+
{
|
|
55
|
+
"telegram_id": user.id,
|
|
56
|
+
"username": user.username,
|
|
57
|
+
"first_name": user.first_name,
|
|
58
|
+
"last_name": user.last_name,
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
except Exception as e:
|
|
63
|
+
logger.error(f"Ошибка обновления информации админа {user.id}: {e}")
|
|
64
|
+
|
|
65
|
+
def is_admin(self, telegram_id: int) -> bool:
|
|
66
|
+
"""Проверяет, является ли пользователь админом"""
|
|
67
|
+
return telegram_id in self.admin_ids
|
|
68
|
+
|
|
69
|
+
def is_in_admin_mode(self, telegram_id: int) -> bool:
|
|
70
|
+
"""Проверяет, находится ли админ в режиме администратора"""
|
|
71
|
+
if not self.is_admin(telegram_id):
|
|
72
|
+
return False
|
|
73
|
+
return self.admin_modes.get(telegram_id, True)
|
|
74
|
+
|
|
75
|
+
def toggle_admin_mode(self, telegram_id: int) -> bool:
|
|
76
|
+
"""Переключает режим админа. Возвращает новое состояние"""
|
|
77
|
+
if not self.is_admin(telegram_id):
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
current_mode = self.admin_modes.get(telegram_id, True)
|
|
81
|
+
new_mode = not current_mode
|
|
82
|
+
self.admin_modes[telegram_id] = new_mode
|
|
83
|
+
|
|
84
|
+
logger.info(
|
|
85
|
+
f"Админ {telegram_id} переключен в режим: {'администратор' if new_mode else 'пользователь'}"
|
|
86
|
+
)
|
|
87
|
+
return new_mode
|
|
88
|
+
|
|
89
|
+
def set_admin_mode(self, telegram_id: int, is_admin_mode: bool):
|
|
90
|
+
"""Устанавливает режим админа"""
|
|
91
|
+
if not self.is_admin(telegram_id):
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
self.admin_modes[telegram_id] = is_admin_mode
|
|
95
|
+
logger.info(
|
|
96
|
+
f"Режим админа {telegram_id} установлен: {'администратор' if is_admin_mode else 'пользователь'}"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
async def get_active_admins(self) -> List[int]:
|
|
100
|
+
"""Возвращает список активных админов в режиме администратора"""
|
|
101
|
+
return [
|
|
102
|
+
admin_id for admin_id in self.admin_ids if self.is_in_admin_mode(admin_id)
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
def get_admin_mode_text(self, telegram_id: int) -> str:
|
|
106
|
+
"""Возвращает текстовое описание режима админа"""
|
|
107
|
+
if not self.is_admin(telegram_id):
|
|
108
|
+
return "Не администратор"
|
|
109
|
+
|
|
110
|
+
if self.is_in_admin_mode(telegram_id):
|
|
111
|
+
return "👑 Режим администратора"
|
|
112
|
+
else:
|
|
113
|
+
return "👤 Режим пользователя"
|
|
114
|
+
|
|
115
|
+
def format_admin_status(self, telegram_id: int) -> str:
|
|
116
|
+
"""Форматирует статус админа для отображения"""
|
|
117
|
+
if not self.is_admin(telegram_id):
|
|
118
|
+
return ""
|
|
119
|
+
|
|
120
|
+
mode = "👑 АДМИН" if self.is_in_admin_mode(telegram_id) else "👤 ПОЛЬЗ"
|
|
121
|
+
return f"[{mode}]"
|
|
122
|
+
|
|
123
|
+
async def notify_admins(self, message: str, exclude_admin: int = None):
|
|
124
|
+
"""Отправляет уведомление всем активным админам"""
|
|
125
|
+
from ..handlers.handlers import get_global_var
|
|
126
|
+
bot = get_global_var("bot")
|
|
127
|
+
|
|
128
|
+
active_admins = await self.get_active_admins()
|
|
129
|
+
|
|
130
|
+
if exclude_admin:
|
|
131
|
+
active_admins = [aid for aid in active_admins if aid != exclude_admin]
|
|
132
|
+
|
|
133
|
+
sent_count = 0
|
|
134
|
+
for admin_id in active_admins:
|
|
135
|
+
try:
|
|
136
|
+
await bot.send_message(admin_id, message, parse_mode="Markdown")
|
|
137
|
+
sent_count += 1
|
|
138
|
+
except Exception as e:
|
|
139
|
+
logger.error(f"Ошибка отправки уведомления админу {admin_id}: {e}")
|
|
140
|
+
|
|
141
|
+
logger.info(f"Уведомление отправлено {sent_count} админам")
|
|
142
|
+
return sent_count
|
|
143
|
+
|
|
144
|
+
def get_stats(self) -> Dict[str, any]:
|
|
145
|
+
"""Возвращает статистику по админам"""
|
|
146
|
+
total_admins = len(self.admin_ids)
|
|
147
|
+
active_admins = len(
|
|
148
|
+
[aid for aid in self.admin_ids if self.is_in_admin_mode(aid)]
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
"total_admins": total_admins,
|
|
153
|
+
"active_admins": active_admins,
|
|
154
|
+
"admin_ids": list(self.admin_ids),
|
|
155
|
+
"modes": dict(self.admin_modes),
|
|
156
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Утилита для тестирования системы администрирования бота
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import logging
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from ..analytics.analytics_manager import AnalyticsManager
|
|
10
|
+
from ..config import Config
|
|
11
|
+
from ..core.conversation_manager import ConversationManager
|
|
12
|
+
from ..integrations.supabase_client import SupabaseClient
|
|
13
|
+
from .admin_manager import AdminManager
|
|
14
|
+
from .timeout_checker import setup_bot_environment
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def test_admin_system(bot_name: str = "growthmed-october-24") -> bool:
|
|
20
|
+
"""
|
|
21
|
+
Тестирует систему администрирования бота
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
bot_name: Имя бота для тестирования
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
bool: True если все тесты пройдены, False если найдены проблемы
|
|
28
|
+
"""
|
|
29
|
+
logger.info(f"🚀 Тестирование системы администрирования: {bot_name}")
|
|
30
|
+
logger.info(f"🤖 Bot ID будет автоопределен как: {bot_name}\n")
|
|
31
|
+
|
|
32
|
+
# Настраиваем окружение для бота (автоматически устанавливает BOT_ID)
|
|
33
|
+
config_dir = setup_bot_environment(bot_name)
|
|
34
|
+
if not config_dir:
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
# Инициализируем конфигурацию
|
|
38
|
+
config = Config()
|
|
39
|
+
logger.info("📋 Конфигурация:")
|
|
40
|
+
logger.info(f" BOT_ID: {config.BOT_ID}")
|
|
41
|
+
logger.info(
|
|
42
|
+
f" ADMIN_SESSION_TIMEOUT_MINUTES: {config.ADMIN_SESSION_TIMEOUT_MINUTES}"
|
|
43
|
+
)
|
|
44
|
+
logger.info(f" PROMT_FILES_DIR: {config.PROMT_FILES_DIR}")
|
|
45
|
+
logger.info(f" Найдено промпт-файлов: {len(config.PROMPT_FILES)}")
|
|
46
|
+
logger.info("")
|
|
47
|
+
|
|
48
|
+
# Проверяем админов
|
|
49
|
+
if not config.ADMIN_TELEGRAM_IDS:
|
|
50
|
+
logger.warning("⚠️ Админы не настроены (ADMIN_TELEGRAM_IDS пуст)")
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
logger.info(f"👑 Админы: {config.ADMIN_TELEGRAM_IDS}")
|
|
54
|
+
logger.info("")
|
|
55
|
+
|
|
56
|
+
# Проверяем подключение к БД
|
|
57
|
+
try:
|
|
58
|
+
supabase_client = SupabaseClient(config.SUPABASE_URL, config.SUPABASE_KEY)
|
|
59
|
+
await supabase_client.initialize()
|
|
60
|
+
logger.info("✅ Подключение к Supabase успешно")
|
|
61
|
+
|
|
62
|
+
# Проверяем таблицы
|
|
63
|
+
tables = [
|
|
64
|
+
"sales_admins",
|
|
65
|
+
"admin_user_conversations",
|
|
66
|
+
"session_events",
|
|
67
|
+
"sales_chat_sessions",
|
|
68
|
+
"sales_messages",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
logger.info("\n📊 Проверка таблиц:")
|
|
72
|
+
for table in tables:
|
|
73
|
+
try:
|
|
74
|
+
supabase_client.client.table(table).select("*").limit(1).execute()
|
|
75
|
+
logger.info(f" ✅ {table}")
|
|
76
|
+
except Exception as e:
|
|
77
|
+
logger.error(f" ❌ {table}: {e}")
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
# Проверяем AdminManager
|
|
81
|
+
admin_manager = AdminManager(config, supabase_client)
|
|
82
|
+
logger.info(
|
|
83
|
+
f"\n👑 AdminManager инициализирован ({len(admin_manager.admin_ids)} админов)"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Проверяем ConversationManager
|
|
87
|
+
conversation_manager = ConversationManager(supabase_client, admin_manager)
|
|
88
|
+
logger.info("✅ ConversationManager инициализирован")
|
|
89
|
+
|
|
90
|
+
# Проверяем AnalyticsManager
|
|
91
|
+
analytics_manager = AnalyticsManager(supabase_client)
|
|
92
|
+
|
|
93
|
+
# Тестируем получение статистики
|
|
94
|
+
await analytics_manager.get_funnel_stats(1)
|
|
95
|
+
logger.info("✅ AnalyticsManager работает")
|
|
96
|
+
|
|
97
|
+
# Проверяем активные диалоги
|
|
98
|
+
conversations = await conversation_manager.get_active_conversations()
|
|
99
|
+
logger.info(f"\n💬 Активные диалоги: {len(conversations)}")
|
|
100
|
+
|
|
101
|
+
if conversations:
|
|
102
|
+
for conv in conversations:
|
|
103
|
+
logger.info(
|
|
104
|
+
f" • Диалог {conv['id']}: админ {conv['admin_id']} с пользователем {conv['user_id']}"
|
|
105
|
+
)
|
|
106
|
+
else:
|
|
107
|
+
logger.info(" Нет активных диалогов")
|
|
108
|
+
logger.info(" 💡 Создайте диалог командой /чат USER_ID для тестирования")
|
|
109
|
+
|
|
110
|
+
# Проверяем форматирование диалогов
|
|
111
|
+
if conversations:
|
|
112
|
+
formatted = conversation_manager.format_active_conversations(conversations)
|
|
113
|
+
logger.info("\n📝 Форматирование диалогов:")
|
|
114
|
+
logger.info(formatted)
|
|
115
|
+
|
|
116
|
+
logger.info("\n✅ Админская система готова к работе")
|
|
117
|
+
return True
|
|
118
|
+
|
|
119
|
+
except Exception as e:
|
|
120
|
+
logger.error(f"❌ Ошибка тестирования: {e}")
|
|
121
|
+
logger.exception("Стек ошибки:")
|
|
122
|
+
return False
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def main():
|
|
126
|
+
"""Точка входа для запуска из командной строки"""
|
|
127
|
+
# Настройка логирования
|
|
128
|
+
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
129
|
+
|
|
130
|
+
logger.info("🔍 Утилита тестирования админской системы")
|
|
131
|
+
logger.info("Использование:")
|
|
132
|
+
logger.info(" python -m smart_bot_factory.admin_tester [bot_name]")
|
|
133
|
+
logger.info(" python -m smart_bot_factory.admin_tester growthmed-october-24")
|
|
134
|
+
logger.info("")
|
|
135
|
+
|
|
136
|
+
if len(sys.argv) > 1 and sys.argv[1] in ["-h", "--help", "help"]:
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
# Определяем какого бота тестировать
|
|
140
|
+
bot_name = "growthmed-october-24" # по умолчанию
|
|
141
|
+
if len(sys.argv) > 1:
|
|
142
|
+
bot_name = sys.argv[1]
|
|
143
|
+
|
|
144
|
+
try:
|
|
145
|
+
success = asyncio.run(test_admin_system(bot_name))
|
|
146
|
+
if not success:
|
|
147
|
+
sys.exit(1)
|
|
148
|
+
except KeyboardInterrupt:
|
|
149
|
+
logger.info("\n⏹️ Прервано пользователем")
|
|
150
|
+
except Exception as e:
|
|
151
|
+
logger.error(f"\n💥 Критическая ошибка: {e}")
|
|
152
|
+
logger.exception("Стек ошибки:")
|
|
153
|
+
sys.exit(1)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
if __name__ == "__main__":
|
|
157
|
+
main()
|