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,553 @@
|
|
|
1
|
+
# Исправленный admin_logic.py с правильной обработкой диалогов
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from aiogram import F, Router
|
|
6
|
+
from aiogram.filters import Command, StateFilter
|
|
7
|
+
from aiogram.fsm.context import FSMContext
|
|
8
|
+
from aiogram.types import (CallbackQuery, InlineKeyboardButton,
|
|
9
|
+
InlineKeyboardMarkup, Message)
|
|
10
|
+
|
|
11
|
+
# Импортируем состояния
|
|
12
|
+
from ..core.states import AdminStates
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
# Создаем роутер для админских обработчиков
|
|
17
|
+
admin_router = Router()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def setup_admin_handlers(dp):
|
|
21
|
+
"""Настройка админских обработчиков"""
|
|
22
|
+
dp.include_router(admin_router)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@admin_router.message(Command(commands=["отмена", "cancel"]))
|
|
26
|
+
async def cancel_handler(message: Message, state: FSMContext):
|
|
27
|
+
"""Отмена текущего действия и очистка state"""
|
|
28
|
+
from ..handlers.handlers import get_global_var
|
|
29
|
+
|
|
30
|
+
admin_manager = get_global_var("admin_manager")
|
|
31
|
+
|
|
32
|
+
# Получаем текущий state
|
|
33
|
+
current_state = await state.get_state()
|
|
34
|
+
|
|
35
|
+
# Очищаем временные файлы если это создание события
|
|
36
|
+
if current_state and current_state.startswith("AdminStates:create_event"):
|
|
37
|
+
from .admin_events import cleanup_temp_files
|
|
38
|
+
|
|
39
|
+
await cleanup_temp_files(state)
|
|
40
|
+
|
|
41
|
+
# Очищаем state
|
|
42
|
+
await state.clear()
|
|
43
|
+
|
|
44
|
+
if current_state:
|
|
45
|
+
logger.info(
|
|
46
|
+
f"State очищен для пользователя {message.from_user.id}: {current_state}"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Если это админ, возвращаем в админ режим
|
|
50
|
+
if admin_manager.is_admin(message.from_user.id):
|
|
51
|
+
await state.set_state(AdminStates.admin_mode)
|
|
52
|
+
await message.answer(
|
|
53
|
+
"✅ Текущее действие отменено\n"
|
|
54
|
+
"Вы вернулись в админ режим\n\n"
|
|
55
|
+
"Используйте /admin для просмотра доступных команд",
|
|
56
|
+
parse_mode="Markdown",
|
|
57
|
+
)
|
|
58
|
+
else:
|
|
59
|
+
await message.answer(
|
|
60
|
+
"✅ Текущее действие отменено\n\n"
|
|
61
|
+
"Используйте /start для начала работы",
|
|
62
|
+
parse_mode="Markdown",
|
|
63
|
+
)
|
|
64
|
+
else:
|
|
65
|
+
await message.answer(
|
|
66
|
+
"ℹ️ Нет активных действий для отмены", parse_mode="Markdown"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def admin_start_handler(message: Message, state: FSMContext):
|
|
71
|
+
"""Обработчик /start для админов в режиме администратора"""
|
|
72
|
+
from ..handlers.handlers import get_global_var
|
|
73
|
+
|
|
74
|
+
admin_manager = get_global_var("admin_manager")
|
|
75
|
+
|
|
76
|
+
await state.set_state(AdminStates.admin_mode)
|
|
77
|
+
|
|
78
|
+
admin_status = admin_manager.get_admin_mode_text(message.from_user.id)
|
|
79
|
+
|
|
80
|
+
# Основное меню админа
|
|
81
|
+
keyboard = InlineKeyboardMarkup(
|
|
82
|
+
inline_keyboard=[
|
|
83
|
+
[InlineKeyboardButton(text="📊 Статистика", callback_data="admin_stats")],
|
|
84
|
+
[
|
|
85
|
+
InlineKeyboardButton(
|
|
86
|
+
text="💬 Активные чаты", callback_data="admin_active_chats"
|
|
87
|
+
)
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
InlineKeyboardButton(
|
|
91
|
+
text="🔄 Режим польз.", callback_data="admin_toggle_mode"
|
|
92
|
+
)
|
|
93
|
+
],
|
|
94
|
+
]
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
welcome_text = f"""
|
|
98
|
+
{admin_status}
|
|
99
|
+
|
|
100
|
+
🎛️ **Панель администратора**
|
|
101
|
+
|
|
102
|
+
Доступные команды:
|
|
103
|
+
• `/стат` - статистика воронки
|
|
104
|
+
• `/история user_id` - история пользователя
|
|
105
|
+
• `/чат user_id` - начать диалог
|
|
106
|
+
• `/чаты` - активные диалоги
|
|
107
|
+
• `/стоп` - завершить диалог
|
|
108
|
+
• `/админ` - переключить режим
|
|
109
|
+
• `/отмена` - отменить текущее действие
|
|
110
|
+
|
|
111
|
+
📅 **Управление событиями:**
|
|
112
|
+
• `/создать_событие` - создать новое событие
|
|
113
|
+
• `/список_событий` - список активных событий
|
|
114
|
+
• `/удалить_событие название` - отменить событие
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
await message.answer(welcome_text, reply_markup=keyboard, parse_mode="Markdown")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@admin_router.message(Command(commands=["стат", "stats"]))
|
|
121
|
+
async def admin_stats_handler(message: Message, state: FSMContext):
|
|
122
|
+
"""Статистика воронки"""
|
|
123
|
+
from ..handlers.handlers import get_global_var
|
|
124
|
+
|
|
125
|
+
admin_manager = get_global_var("admin_manager")
|
|
126
|
+
analytics_manager = get_global_var("analytics_manager")
|
|
127
|
+
|
|
128
|
+
if not admin_manager.is_admin(message.from_user.id):
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
# Получаем статистику
|
|
133
|
+
funnel_stats = await analytics_manager.get_funnel_stats(7)
|
|
134
|
+
events_stats = await analytics_manager.get_events_stats(7)
|
|
135
|
+
|
|
136
|
+
# Форматируем ответ
|
|
137
|
+
funnel_text = analytics_manager.format_funnel_stats(funnel_stats)
|
|
138
|
+
events_text = analytics_manager.format_events_stats(events_stats)
|
|
139
|
+
|
|
140
|
+
full_text = f"{funnel_text}\n\n{events_text}"
|
|
141
|
+
|
|
142
|
+
await message.answer(full_text)
|
|
143
|
+
|
|
144
|
+
except Exception as e:
|
|
145
|
+
logger.error(f"Ошибка получения статистики: {e}")
|
|
146
|
+
await message.answer("❌ Ошибка получения статистики")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@admin_router.message(Command(commands=["история", "history"]))
|
|
150
|
+
async def admin_history_handler(message: Message, state: FSMContext):
|
|
151
|
+
"""История пользователя"""
|
|
152
|
+
from ..handlers.handlers import get_global_var
|
|
153
|
+
|
|
154
|
+
admin_manager = get_global_var("admin_manager")
|
|
155
|
+
analytics_manager = get_global_var("analytics_manager")
|
|
156
|
+
|
|
157
|
+
if not admin_manager.is_admin(message.from_user.id):
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
parts = message.text.split()
|
|
162
|
+
if len(parts) < 2:
|
|
163
|
+
await message.answer("Укажите ID пользователя: /история 123456789")
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
user_id = int(parts[1])
|
|
167
|
+
|
|
168
|
+
# Получаем историю (та же функция что использует кнопка)
|
|
169
|
+
journey = await analytics_manager.get_user_journey(user_id)
|
|
170
|
+
|
|
171
|
+
if not journey:
|
|
172
|
+
await message.answer(f"❌ У пользователя {user_id} нет активной сессии")
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
# Используем ту же функцию форматирования что и кнопка
|
|
176
|
+
history_text = analytics_manager.format_user_journey(user_id, journey)
|
|
177
|
+
|
|
178
|
+
await message.answer(history_text)
|
|
179
|
+
|
|
180
|
+
except ValueError:
|
|
181
|
+
await message.answer("❌ Неверный формат ID пользователя")
|
|
182
|
+
except Exception as e:
|
|
183
|
+
logger.error(f"Ошибка получения истории: {e}")
|
|
184
|
+
await message.answer("❌ Ошибка получения истории")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@admin_router.message(Command(commands=["чат", "chat"]))
|
|
188
|
+
async def admin_chat_handler(message: Message, state: FSMContext):
|
|
189
|
+
"""Начать диалог с пользователем"""
|
|
190
|
+
from ..handlers.handlers import get_global_var
|
|
191
|
+
|
|
192
|
+
admin_manager = get_global_var("admin_manager")
|
|
193
|
+
supabase_client = get_global_var("supabase_client")
|
|
194
|
+
conversation_manager = get_global_var("conversation_manager")
|
|
195
|
+
|
|
196
|
+
if not admin_manager.is_admin(message.from_user.id):
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
# Парсим user_id из команды
|
|
201
|
+
parts = message.text.split()
|
|
202
|
+
if len(parts) < 2:
|
|
203
|
+
await message.answer("Укажите ID пользователя: /чат 123456789")
|
|
204
|
+
return
|
|
205
|
+
|
|
206
|
+
user_id = int(parts[1])
|
|
207
|
+
admin_id = message.from_user.id
|
|
208
|
+
|
|
209
|
+
logger.info(
|
|
210
|
+
f"👑 Админ {admin_id} хочет начать диалог с пользователем {user_id}"
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Проверяем, есть ли активная сессия у пользователя
|
|
214
|
+
session_info = await supabase_client.get_active_session(user_id)
|
|
215
|
+
if not session_info:
|
|
216
|
+
await message.answer(f"❌ У пользователя {user_id} нет активной сессии")
|
|
217
|
+
logger.warning(f"❌ У пользователя {user_id} нет активной сессии")
|
|
218
|
+
return
|
|
219
|
+
|
|
220
|
+
logger.info(
|
|
221
|
+
f"✅ У пользователя {user_id} есть активная сессия: {session_info['id']}"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Начинаем диалог
|
|
225
|
+
logger.info("🚀 Запускаем создание диалога...")
|
|
226
|
+
success = await conversation_manager.start_admin_conversation(admin_id, user_id)
|
|
227
|
+
|
|
228
|
+
if success:
|
|
229
|
+
# ✅ ИСПРАВЛЕНИЕ: Правильно переключаем состояние админа
|
|
230
|
+
await state.set_state(AdminStates.in_conversation)
|
|
231
|
+
await state.update_data(conversation_user_id=user_id)
|
|
232
|
+
|
|
233
|
+
await message.answer(
|
|
234
|
+
f"✅ Диалог с пользователем {user_id} начат\n💬 Ваши сообщения будут переданы пользователю\n⏹️ Используйте /стоп для завершения"
|
|
235
|
+
)
|
|
236
|
+
logger.info(
|
|
237
|
+
"✅ Диалог успешно создан, админ переключен в состояние in_conversation"
|
|
238
|
+
)
|
|
239
|
+
else:
|
|
240
|
+
await message.answer(
|
|
241
|
+
f"❌ Не удалось начать диалог с пользователем {user_id}"
|
|
242
|
+
)
|
|
243
|
+
logger.error("❌ Не удалось создать диалог")
|
|
244
|
+
|
|
245
|
+
except ValueError:
|
|
246
|
+
await message.answer("❌ Неверный формат ID пользователя")
|
|
247
|
+
logger.error(f"❌ Неверный формат ID пользователя: {message.text}")
|
|
248
|
+
except Exception as e:
|
|
249
|
+
logger.error(f"❌ Ошибка начала диалога: {e}")
|
|
250
|
+
await message.answer("❌ Ошибка начала диалога")
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
@admin_router.message(Command(commands=["чаты", "chats"]))
|
|
254
|
+
async def admin_active_chats_command(message: Message, state: FSMContext):
|
|
255
|
+
"""Показать активные диалоги админов"""
|
|
256
|
+
from ..handlers.handlers import get_global_var
|
|
257
|
+
|
|
258
|
+
admin_manager = get_global_var("admin_manager")
|
|
259
|
+
conversation_manager = get_global_var("conversation_manager")
|
|
260
|
+
|
|
261
|
+
if not admin_manager.is_admin(message.from_user.id):
|
|
262
|
+
return
|
|
263
|
+
|
|
264
|
+
try:
|
|
265
|
+
conversations = await conversation_manager.get_active_conversations()
|
|
266
|
+
formatted_text = conversation_manager.format_active_conversations(conversations)
|
|
267
|
+
|
|
268
|
+
# ✅ ИСПРАВЛЕНИЕ: Убираем parse_mode='Markdown' чтобы избежать ошибок парсинга
|
|
269
|
+
await message.answer(formatted_text)
|
|
270
|
+
|
|
271
|
+
except Exception as e:
|
|
272
|
+
logger.error(f"Ошибка получения активных чатов: {e}")
|
|
273
|
+
await message.answer("❌ Ошибка получения активных диалогов")
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@admin_router.message(Command(commands=["стоп", "stop"]))
|
|
277
|
+
async def admin_stop_handler(message: Message, state: FSMContext):
|
|
278
|
+
"""Завершить диалог"""
|
|
279
|
+
from ..handlers.handlers import get_global_var
|
|
280
|
+
|
|
281
|
+
admin_manager = get_global_var("admin_manager")
|
|
282
|
+
conversation_manager = get_global_var("conversation_manager")
|
|
283
|
+
|
|
284
|
+
if not admin_manager.is_admin(message.from_user.id):
|
|
285
|
+
return
|
|
286
|
+
|
|
287
|
+
try:
|
|
288
|
+
admin_id = message.from_user.id
|
|
289
|
+
|
|
290
|
+
# Проверяем есть ли активный диалог
|
|
291
|
+
conversation = await conversation_manager.get_admin_active_conversation(
|
|
292
|
+
admin_id
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
if conversation:
|
|
296
|
+
user_id = conversation["user_id"]
|
|
297
|
+
logger.info(
|
|
298
|
+
f"🛑 Завершаем диалог админа {admin_id} с пользователем {user_id}"
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
success = await conversation_manager.end_admin_conversation(admin_id)
|
|
302
|
+
|
|
303
|
+
if success:
|
|
304
|
+
# ✅ ИСПРАВЛЕНИЕ: Правильно переключаем состояние обратно
|
|
305
|
+
await state.set_state(AdminStates.admin_mode)
|
|
306
|
+
await state.update_data(conversation_user_id=None)
|
|
307
|
+
|
|
308
|
+
await message.answer(f"✅ Диалог с пользователем {user_id} завершен")
|
|
309
|
+
logger.info("✅ Диалог завершен, админ переключен в admin_mode")
|
|
310
|
+
else:
|
|
311
|
+
await message.answer("❌ Ошибка завершения диалога")
|
|
312
|
+
else:
|
|
313
|
+
await message.answer("❌ Нет активного диалога")
|
|
314
|
+
logger.info(f"❌ У админа {admin_id} нет активного диалога")
|
|
315
|
+
|
|
316
|
+
except Exception as e:
|
|
317
|
+
logger.error(f"Ошибка завершения диалога: {e}")
|
|
318
|
+
await message.answer("❌ Ошибка завершения диалога")
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@admin_router.message(Command(commands=["админ", "admin"]))
|
|
322
|
+
async def admin_toggle_handler(message: Message, state: FSMContext):
|
|
323
|
+
"""Переключение режима админа"""
|
|
324
|
+
from ..handlers.handlers import get_global_var
|
|
325
|
+
|
|
326
|
+
admin_manager = get_global_var("admin_manager")
|
|
327
|
+
|
|
328
|
+
if not admin_manager.is_admin(message.from_user.id):
|
|
329
|
+
return
|
|
330
|
+
|
|
331
|
+
new_mode = admin_manager.toggle_admin_mode(message.from_user.id)
|
|
332
|
+
|
|
333
|
+
if new_mode:
|
|
334
|
+
# Переключились в режим админа
|
|
335
|
+
await admin_start_handler(message, state)
|
|
336
|
+
else:
|
|
337
|
+
# Переключились в режим пользователя
|
|
338
|
+
await state.clear()
|
|
339
|
+
await message.answer(
|
|
340
|
+
"🔄 Переключен в режим пользователя\nНапишите /start для начала диалога"
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@admin_router.message(Command("debug_chat"))
|
|
345
|
+
async def debug_chat_handler(message: Message, state: FSMContext):
|
|
346
|
+
"""Отладка диалогов админов"""
|
|
347
|
+
from ..handlers.handlers import get_global_var
|
|
348
|
+
|
|
349
|
+
admin_manager = get_global_var("admin_manager")
|
|
350
|
+
conversation_manager = get_global_var("conversation_manager")
|
|
351
|
+
supabase_client = get_global_var("supabase_client")
|
|
352
|
+
|
|
353
|
+
if not admin_manager.is_admin(message.from_user.id):
|
|
354
|
+
return
|
|
355
|
+
|
|
356
|
+
parts = message.text.split()
|
|
357
|
+
if len(parts) < 2:
|
|
358
|
+
await message.answer("Использование: /debug_chat USER_ID")
|
|
359
|
+
return
|
|
360
|
+
|
|
361
|
+
try:
|
|
362
|
+
user_id = int(parts[1])
|
|
363
|
+
|
|
364
|
+
# 1. Проверяем запись в БД
|
|
365
|
+
conversation = await conversation_manager.is_user_in_admin_chat(user_id)
|
|
366
|
+
|
|
367
|
+
debug_info = [
|
|
368
|
+
f"🔍 ОТЛАДКА ДИАЛОГА С {user_id}",
|
|
369
|
+
"",
|
|
370
|
+
f"📊 Диалог в БД: {'✅' if conversation else '❌'}",
|
|
371
|
+
]
|
|
372
|
+
|
|
373
|
+
if conversation:
|
|
374
|
+
debug_info.extend(
|
|
375
|
+
[
|
|
376
|
+
f"👑 Админ: {conversation['admin_id']}",
|
|
377
|
+
f"🕐 Начат: {conversation['started_at']}",
|
|
378
|
+
]
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
# 2. Проверяем активную сессию пользователя
|
|
382
|
+
session_info = await supabase_client.get_active_session(user_id)
|
|
383
|
+
debug_info.append(f"🎯 Активная сессия: {'✅' if session_info else '❌'}")
|
|
384
|
+
|
|
385
|
+
if session_info:
|
|
386
|
+
debug_info.append(f"📝 ID сессии: {session_info['id']}")
|
|
387
|
+
|
|
388
|
+
# 3. Проверяем состояние пользователя (если он онлайн)
|
|
389
|
+
debug_info.append("")
|
|
390
|
+
debug_info.append(
|
|
391
|
+
"ℹ️ Для проверки состояния пользователь должен написать что-то"
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
await message.answer("\n".join(debug_info))
|
|
395
|
+
|
|
396
|
+
except Exception as e:
|
|
397
|
+
await message.answer(f"❌ Ошибка: {e}")
|
|
398
|
+
logger.error(f"Ошибка отладки: {e}")
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
@admin_router.callback_query(F.data.startswith("admin_"))
|
|
402
|
+
async def admin_callback_handler(callback: CallbackQuery, state: FSMContext):
|
|
403
|
+
"""Обработчик callback кнопок админов"""
|
|
404
|
+
from ..handlers.handlers import get_global_var
|
|
405
|
+
|
|
406
|
+
admin_manager = get_global_var("admin_manager")
|
|
407
|
+
analytics_manager = get_global_var("analytics_manager")
|
|
408
|
+
conversation_manager = get_global_var("conversation_manager")
|
|
409
|
+
|
|
410
|
+
if not admin_manager.is_admin(callback.from_user.id):
|
|
411
|
+
await callback.answer("Нет доступа")
|
|
412
|
+
return
|
|
413
|
+
|
|
414
|
+
data = callback.data
|
|
415
|
+
|
|
416
|
+
try:
|
|
417
|
+
if data == "admin_stats":
|
|
418
|
+
# Показываем статистику
|
|
419
|
+
funnel_stats = await analytics_manager.get_funnel_stats(7)
|
|
420
|
+
events_stats = await analytics_manager.get_events_stats(7)
|
|
421
|
+
|
|
422
|
+
funnel_text = analytics_manager.format_funnel_stats(funnel_stats)
|
|
423
|
+
events_text = analytics_manager.format_events_stats(events_stats)
|
|
424
|
+
|
|
425
|
+
await callback.message.answer(f"{funnel_text}\n\n{events_text}")
|
|
426
|
+
|
|
427
|
+
elif data == "admin_toggle_mode":
|
|
428
|
+
# Переключаем режим
|
|
429
|
+
new_mode = admin_manager.toggle_admin_mode(callback.from_user.id)
|
|
430
|
+
mode_text = "администратор" if new_mode else "пользователь"
|
|
431
|
+
await callback.answer(f"Режим переключен: {mode_text}")
|
|
432
|
+
|
|
433
|
+
if not new_mode:
|
|
434
|
+
await state.clear()
|
|
435
|
+
await callback.message.answer("🔄 Теперь вы в режиме пользователя")
|
|
436
|
+
|
|
437
|
+
elif data == "admin_active_chats":
|
|
438
|
+
# Показываем активные диалоги
|
|
439
|
+
conversations = await conversation_manager.get_active_conversations()
|
|
440
|
+
formatted_text = conversation_manager.format_active_conversations(
|
|
441
|
+
conversations
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
# ✅ ИСПРАВЛЕНИЕ: Убираем parse_mode='Markdown'
|
|
445
|
+
await callback.message.answer(formatted_text)
|
|
446
|
+
|
|
447
|
+
elif data.startswith("admin_history_"):
|
|
448
|
+
user_id = int(data.split("_")[2])
|
|
449
|
+
journey = await analytics_manager.get_user_journey(user_id)
|
|
450
|
+
history_text = analytics_manager.format_user_journey(user_id, journey)
|
|
451
|
+
await callback.message.answer(history_text)
|
|
452
|
+
|
|
453
|
+
elif data.startswith("admin_end_"):
|
|
454
|
+
user_id = int(data.split("_")[2])
|
|
455
|
+
|
|
456
|
+
# Проверяем есть ли активный диалог
|
|
457
|
+
conversation = await conversation_manager.get_admin_active_conversation(
|
|
458
|
+
callback.from_user.id
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
if conversation and conversation["user_id"] == user_id:
|
|
462
|
+
await conversation_manager.end_admin_conversation(callback.from_user.id)
|
|
463
|
+
|
|
464
|
+
# ✅ ИСПРАВЛЕНИЕ: Правильно переключаем состояние
|
|
465
|
+
await state.set_state(AdminStates.admin_mode)
|
|
466
|
+
await state.update_data(conversation_user_id=None)
|
|
467
|
+
|
|
468
|
+
await callback.answer("Диалог завершен")
|
|
469
|
+
await callback.message.answer(
|
|
470
|
+
f"✅ Диалог с пользователем {user_id} завершен"
|
|
471
|
+
)
|
|
472
|
+
logger.info(
|
|
473
|
+
"✅ Диалог завершен через кнопку, админ переключен в admin_mode"
|
|
474
|
+
)
|
|
475
|
+
else:
|
|
476
|
+
await callback.answer("Диалог не найден")
|
|
477
|
+
|
|
478
|
+
elif data.startswith("admin_chat_"):
|
|
479
|
+
user_id = int(data.split("_")[2])
|
|
480
|
+
admin_id = callback.from_user.id
|
|
481
|
+
|
|
482
|
+
success = await conversation_manager.start_admin_conversation(
|
|
483
|
+
admin_id, user_id
|
|
484
|
+
)
|
|
485
|
+
if success:
|
|
486
|
+
# ✅ ИСПРАВЛЕНИЕ: Правильно переключаем состояние
|
|
487
|
+
await state.set_state(AdminStates.in_conversation)
|
|
488
|
+
await state.update_data(conversation_user_id=user_id)
|
|
489
|
+
|
|
490
|
+
await callback.answer("Диалог начат")
|
|
491
|
+
await callback.message.answer(
|
|
492
|
+
f"✅ Диалог с пользователем {user_id} начат"
|
|
493
|
+
)
|
|
494
|
+
logger.info(
|
|
495
|
+
"✅ Диалог начат через кнопку, админ переключен в in_conversation"
|
|
496
|
+
)
|
|
497
|
+
else:
|
|
498
|
+
await callback.answer("Не удалось начать диалог")
|
|
499
|
+
|
|
500
|
+
await callback.answer()
|
|
501
|
+
|
|
502
|
+
except Exception as e:
|
|
503
|
+
logger.error(f"Ошибка обработки callback {data}: {e}")
|
|
504
|
+
await callback.answer("Ошибка")
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
@admin_router.message(
|
|
508
|
+
StateFilter(AdminStates.admin_mode, AdminStates.in_conversation),
|
|
509
|
+
F.text,
|
|
510
|
+
lambda message: not message.text.startswith("/"),
|
|
511
|
+
)
|
|
512
|
+
async def admin_message_handler(message: Message, state: FSMContext):
|
|
513
|
+
"""Обработчик сообщений админов"""
|
|
514
|
+
from ..handlers.handlers import get_global_var
|
|
515
|
+
|
|
516
|
+
admin_manager = get_global_var("admin_manager")
|
|
517
|
+
conversation_manager = get_global_var("conversation_manager")
|
|
518
|
+
|
|
519
|
+
if not admin_manager.is_admin(message.from_user.id):
|
|
520
|
+
return
|
|
521
|
+
|
|
522
|
+
try:
|
|
523
|
+
logger.info(
|
|
524
|
+
f"👑 Получено сообщение от админа {message.from_user.id}: '{message.text}'"
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
# Пытаемся обработать как админское сообщение
|
|
528
|
+
handled = await conversation_manager.route_admin_message(message, state)
|
|
529
|
+
|
|
530
|
+
if handled:
|
|
531
|
+
logger.info("✅ Сообщение админа обработано и переслано пользователю")
|
|
532
|
+
else:
|
|
533
|
+
# Не админское сообщение - показываем справку
|
|
534
|
+
logger.info("❌ Сообщение админа не обработано, показываем справку")
|
|
535
|
+
await message.answer(
|
|
536
|
+
"""
|
|
537
|
+
👑 **Режим администратора**
|
|
538
|
+
|
|
539
|
+
Доступные команды:
|
|
540
|
+
• `/стат` - статистика воронки
|
|
541
|
+
• `/история user_id` - история пользователя
|
|
542
|
+
• `/чат user_id` - начать диалог
|
|
543
|
+
• `/стоп` - завершить диалог
|
|
544
|
+
• `/админ` - переключить режим
|
|
545
|
+
|
|
546
|
+
💡 Если вы в диалоге с пользователем, просто напишите сообщение - оно будет переслано пользователю.
|
|
547
|
+
""",
|
|
548
|
+
parse_mode="Markdown",
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
except Exception as e:
|
|
552
|
+
logger.error(f"Ошибка обработки сообщения админа: {e}")
|
|
553
|
+
await message.answer("❌ Ошибка обработки команды")
|