smart-bot-factory 0.1.2__py3-none-any.whl → 0.1.4__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 +33 -0
- smart_bot_factory/admin/__init__.py +16 -0
- smart_bot_factory/admin/admin_logic.py +430 -0
- smart_bot_factory/admin/admin_manager.py +141 -0
- smart_bot_factory/admin/admin_migration.sql +136 -0
- smart_bot_factory/admin/admin_tester.py +151 -0
- smart_bot_factory/admin/timeout_checker.py +499 -0
- smart_bot_factory/analytics/__init__.py +7 -0
- smart_bot_factory/analytics/analytics_manager.py +355 -0
- smart_bot_factory/cli.py +768 -0
- smart_bot_factory/config.py +235 -0
- smart_bot_factory/configs/growthmed-helper/env_example.txt +1 -0
- smart_bot_factory/configs/growthmed-helper/prompts/1sales_context.txt +9 -0
- smart_bot_factory/configs/growthmed-helper/prompts/2product_info.txt +582 -0
- smart_bot_factory/configs/growthmed-helper/prompts/3objection_handling.txt +66 -0
- smart_bot_factory/configs/growthmed-helper/prompts/final_instructions.txt +232 -0
- smart_bot_factory/configs/growthmed-helper/prompts/help_message.txt +28 -0
- smart_bot_factory/configs/growthmed-helper/prompts/welcome_message.txt +7 -0
- smart_bot_factory/configs/growthmed-helper/welcome_file/welcome_file_msg.txt +16 -0
- 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/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 +66 -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/__init__.py +22 -0
- smart_bot_factory/core/bot_utils.py +703 -0
- smart_bot_factory/core/conversation_manager.py +536 -0
- smart_bot_factory/core/decorators.py +230 -0
- smart_bot_factory/core/message_sender.py +249 -0
- smart_bot_factory/core/states.py +14 -0
- smart_bot_factory/creation/__init__.py +8 -0
- smart_bot_factory/creation/bot_builder.py +329 -0
- smart_bot_factory/creation/bot_testing.py +986 -0
- smart_bot_factory/database/database_structure.sql +57 -0
- smart_bot_factory/database/schema.sql +1094 -0
- smart_bot_factory/handlers/handlers.py +583 -0
- smart_bot_factory/integrations/__init__.py +9 -0
- smart_bot_factory/integrations/openai_client.py +435 -0
- smart_bot_factory/integrations/supabase_client.py +592 -0
- smart_bot_factory/setup_checker.py +476 -0
- smart_bot_factory/utils/__init__.py +9 -0
- smart_bot_factory/utils/debug_routing.py +103 -0
- smart_bot_factory/utils/prompt_loader.py +427 -0
- smart_bot_factory/utm_link_generator.py +106 -0
- smart_bot_factory-0.1.4.dist-info/METADATA +126 -0
- smart_bot_factory-0.1.4.dist-info/RECORD +59 -0
- smart_bot_factory-0.1.4.dist-info/licenses/LICENSE +24 -0
- smart_bot_factory-0.1.2.dist-info/METADATA +0 -31
- smart_bot_factory-0.1.2.dist-info/RECORD +0 -4
- {smart_bot_factory-0.1.2.dist-info → smart_bot_factory-0.1.4.dist-info}/WHEEL +0 -0
- {smart_bot_factory-0.1.2.dist-info → smart_bot_factory-0.1.4.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Модуль для проверки корректности таймаутов диалогов админов
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import logging
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import sys
|
|
10
|
+
import os
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
from ..config import Config
|
|
14
|
+
from ..integrations.supabase_client import SupabaseClient
|
|
15
|
+
from ..core.conversation_manager import ConversationManager
|
|
16
|
+
from ..admin.admin_manager import AdminManager
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
def setup_bot_environment(bot_name: str = "growthmed-october-24") -> Optional[Path]:
|
|
21
|
+
"""Настраивает окружение для указанного бота с автоопределением BOT_ID"""
|
|
22
|
+
root_dir = Path(os.getcwd()) # Используем текущую директорию как корневую
|
|
23
|
+
config_dir = root_dir / 'bots' / bot_name
|
|
24
|
+
|
|
25
|
+
logger.info(f"🔍 Ищем конфигурацию бота в: {config_dir}")
|
|
26
|
+
|
|
27
|
+
# Сохраняем оригинальный путь для возврата
|
|
28
|
+
original_cwd = os.getcwd()
|
|
29
|
+
|
|
30
|
+
if not config_dir.exists():
|
|
31
|
+
logger.error(f"❌ Папка конфигурации не найдена: {config_dir}")
|
|
32
|
+
logger.info(f" Доступные боты:")
|
|
33
|
+
bots_dir = root_dir / 'bots'
|
|
34
|
+
if bots_dir.exists():
|
|
35
|
+
for bot_dir in bots_dir.iterdir():
|
|
36
|
+
if bot_dir.is_dir():
|
|
37
|
+
logger.info(f" - {bot_dir.name}")
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
# Проверяем наличие промптов
|
|
41
|
+
prompts_dir = config_dir / 'prompts'
|
|
42
|
+
if not prompts_dir.exists():
|
|
43
|
+
logger.error(f"❌ Папка с промптами не найдена: {prompts_dir}")
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
logger.info(f"✅ Найдена папка промптов: {prompts_dir}")
|
|
47
|
+
|
|
48
|
+
# Устанавливаем BOT_ID из имени бота
|
|
49
|
+
os.environ['BOT_ID'] = bot_name
|
|
50
|
+
logger.info(f"🤖 Автоматически установлен BOT_ID: {bot_name}")
|
|
51
|
+
|
|
52
|
+
# Загружаем .env из конфигурации бота
|
|
53
|
+
env_file = config_dir / '.env'
|
|
54
|
+
if env_file.exists():
|
|
55
|
+
logger.info(f"🔧 Загружаем .env из: {env_file}")
|
|
56
|
+
from dotenv import load_dotenv
|
|
57
|
+
load_dotenv(env_file)
|
|
58
|
+
else:
|
|
59
|
+
logger.error(f"❌ Файл .env не найден: {env_file}")
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
# Сохраняем текущую директорию и меняем её
|
|
63
|
+
original_cwd = os.getcwd()
|
|
64
|
+
os.chdir(str(config_dir))
|
|
65
|
+
logger.info(f"📁 Изменена рабочая директория: {os.getcwd()}")
|
|
66
|
+
|
|
67
|
+
# Проверяем что промпты доступны относительно новой директории
|
|
68
|
+
local_prompts = Path('prompts')
|
|
69
|
+
if local_prompts.exists():
|
|
70
|
+
logger.info(f"✅ Промпты доступны из рабочей директории: {local_prompts.absolute()}")
|
|
71
|
+
else:
|
|
72
|
+
logger.error(f"❌ Промпты не найдены в рабочей директории: {local_prompts.absolute()}")
|
|
73
|
+
os.chdir(original_cwd) # Восстанавливаем директорию
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
return config_dir
|
|
77
|
+
|
|
78
|
+
async def debug_timeout_issue(bot_name: str = "growthmed-october-24") -> bool:
|
|
79
|
+
"""
|
|
80
|
+
Диагностирует проблему с таймаутом диалогов
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
bot_name: Имя бота для диагностики
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
bool: True если диагностика прошла успешно, False если найдены проблемы
|
|
87
|
+
"""
|
|
88
|
+
logger.info("🔍 Диагностика проблемы с таймаутом диалогов\n")
|
|
89
|
+
logger.info(f"🚀 Диагностика для бота: {bot_name}")
|
|
90
|
+
logger.info(f"🤖 Bot ID будет автоопределен как: {bot_name}\n")
|
|
91
|
+
|
|
92
|
+
# Настраиваем окружение для бота (автоматически устанавливает BOT_ID)
|
|
93
|
+
config_dir = setup_bot_environment(bot_name)
|
|
94
|
+
if not config_dir:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
# Инициализируем конфигурацию
|
|
98
|
+
config = Config()
|
|
99
|
+
logger.info(f"📋 Конфигурация:")
|
|
100
|
+
logger.info(f" BOT_ID: {config.BOT_ID}")
|
|
101
|
+
logger.info(f" ADMIN_SESSION_TIMEOUT_MINUTES: {config.ADMIN_SESSION_TIMEOUT_MINUTES}")
|
|
102
|
+
logger.info(f" PROMT_FILES_DIR: {config.PROMT_FILES_DIR}")
|
|
103
|
+
logger.info(f" Найдено промпт-файлов: {len(config.PROMPT_FILES)}")
|
|
104
|
+
logger.info("")
|
|
105
|
+
|
|
106
|
+
# Проверяем часовые пояса
|
|
107
|
+
logger.info(f"🕐 Временные зоны:")
|
|
108
|
+
now_naive = datetime.now()
|
|
109
|
+
now_utc = datetime.now(timezone.utc)
|
|
110
|
+
logger.info(f" datetime.now() (локальное): {now_naive}")
|
|
111
|
+
logger.info(f" datetime.now(timezone.utc): {now_utc}")
|
|
112
|
+
logger.info(f" Разница: {(now_naive.replace(tzinfo=timezone.utc) - now_utc).total_seconds() / 3600:.1f} часов")
|
|
113
|
+
logger.info("")
|
|
114
|
+
|
|
115
|
+
# Проверяем активные диалоги в БД
|
|
116
|
+
try:
|
|
117
|
+
supabase_client = SupabaseClient(config.SUPABASE_URL, config.SUPABASE_KEY)
|
|
118
|
+
await supabase_client.initialize()
|
|
119
|
+
|
|
120
|
+
response = supabase_client.client.table('admin_user_conversations').select(
|
|
121
|
+
'id', 'admin_id', 'user_id', 'started_at', 'auto_end_at'
|
|
122
|
+
).eq('status', 'active').execute()
|
|
123
|
+
|
|
124
|
+
conversations = response.data
|
|
125
|
+
|
|
126
|
+
logger.info(f"📊 Активные диалоги в БД: {len(conversations)}")
|
|
127
|
+
|
|
128
|
+
problems_found = 0
|
|
129
|
+
|
|
130
|
+
for i, conv in enumerate(conversations, 1):
|
|
131
|
+
logger.info(f"\n{i}. Диалог ID: {conv['id']}")
|
|
132
|
+
logger.info(f" Админ: {conv['admin_id']}, Пользователь: {conv['user_id']}")
|
|
133
|
+
|
|
134
|
+
# Парсим времена
|
|
135
|
+
started_at = conv['started_at']
|
|
136
|
+
auto_end_at = conv['auto_end_at']
|
|
137
|
+
|
|
138
|
+
logger.info(f" started_at (сырое): {started_at}")
|
|
139
|
+
logger.info(f" auto_end_at (сырое): {auto_end_at}")
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
# Парсим как делает код
|
|
143
|
+
if started_at.endswith('Z'):
|
|
144
|
+
start_time = datetime.fromisoformat(started_at.replace('Z', '+00:00'))
|
|
145
|
+
elif '+' in started_at or started_at.count(':') >= 3:
|
|
146
|
+
start_time = datetime.fromisoformat(started_at)
|
|
147
|
+
else:
|
|
148
|
+
naive_time = datetime.fromisoformat(started_at)
|
|
149
|
+
start_time = naive_time.replace(tzinfo=timezone.utc)
|
|
150
|
+
|
|
151
|
+
if auto_end_at.endswith('Z'):
|
|
152
|
+
end_time = datetime.fromisoformat(auto_end_at.replace('Z', '+00:00'))
|
|
153
|
+
elif '+' in auto_end_at or auto_end_at.count(':') >= 3:
|
|
154
|
+
end_time = datetime.fromisoformat(auto_end_at)
|
|
155
|
+
else:
|
|
156
|
+
naive_time = datetime.fromisoformat(auto_end_at)
|
|
157
|
+
end_time = naive_time.replace(tzinfo=timezone.utc)
|
|
158
|
+
|
|
159
|
+
logger.info(f" start_time (парсед): {start_time}")
|
|
160
|
+
logger.info(f" end_time (парсед): {end_time}")
|
|
161
|
+
|
|
162
|
+
# Вычисляем длительность диалога
|
|
163
|
+
planned_duration = end_time - start_time
|
|
164
|
+
planned_minutes = int(planned_duration.total_seconds() / 60)
|
|
165
|
+
logger.info(f" Запланированная длительность: {planned_minutes} минут")
|
|
166
|
+
|
|
167
|
+
# Проверяем соответствие конфигу
|
|
168
|
+
expected = config.ADMIN_SESSION_TIMEOUT_MINUTES
|
|
169
|
+
if planned_minutes == expected:
|
|
170
|
+
logger.info(f" ✅ Соответствует конфигу ({expected} мин)")
|
|
171
|
+
else:
|
|
172
|
+
logger.error(f" ❌ НЕ соответствует конфигу! Ожидалось {expected} мин, получили {planned_minutes} мин")
|
|
173
|
+
problems_found += 1
|
|
174
|
+
|
|
175
|
+
# Вычисляем текущее время до автозавершения
|
|
176
|
+
now_utc = datetime.now(timezone.utc)
|
|
177
|
+
|
|
178
|
+
# Приводим к UTC
|
|
179
|
+
if end_time.tzinfo != timezone.utc:
|
|
180
|
+
end_time_utc = end_time.astimezone(timezone.utc)
|
|
181
|
+
else:
|
|
182
|
+
end_time_utc = end_time
|
|
183
|
+
|
|
184
|
+
remaining = end_time_utc - now_utc
|
|
185
|
+
remaining_minutes = max(0, int(remaining.total_seconds() / 60))
|
|
186
|
+
|
|
187
|
+
logger.info(f" now_utc: {now_utc}")
|
|
188
|
+
logger.info(f" end_time_utc: {end_time_utc}")
|
|
189
|
+
logger.info(f" Оставшееся время: {remaining_minutes} минут")
|
|
190
|
+
|
|
191
|
+
# Вычисляем сколько уже прошло
|
|
192
|
+
if start_time.tzinfo != timezone.utc:
|
|
193
|
+
start_time_utc = start_time.astimezone(timezone.utc)
|
|
194
|
+
else:
|
|
195
|
+
start_time_utc = start_time
|
|
196
|
+
|
|
197
|
+
elapsed = now_utc - start_time_utc
|
|
198
|
+
elapsed_minutes = max(0, int(elapsed.total_seconds() / 60))
|
|
199
|
+
logger.info(f" Прошло времени: {elapsed_minutes} минут")
|
|
200
|
+
|
|
201
|
+
# Проверяем математику
|
|
202
|
+
total_check = elapsed_minutes + remaining_minutes
|
|
203
|
+
logger.info(f" Проверка: {elapsed_minutes} + {remaining_minutes} = {total_check} мин (должно быть ~{planned_minutes})")
|
|
204
|
+
|
|
205
|
+
if abs(total_check - planned_minutes) > 2:
|
|
206
|
+
logger.warning(f" ⚠️ ПРОБЛЕМА: сумма не сходится! Возможная проблема с timezone")
|
|
207
|
+
problems_found += 1
|
|
208
|
+
|
|
209
|
+
except Exception as e:
|
|
210
|
+
logger.error(f" ❌ Ошибка парсинга времени: {e}")
|
|
211
|
+
problems_found += 1
|
|
212
|
+
|
|
213
|
+
if not conversations:
|
|
214
|
+
logger.info(" Нет активных диалогов для анализа")
|
|
215
|
+
logger.info(" 💡 Создайте диалог командой /чат USER_ID для тестирования")
|
|
216
|
+
|
|
217
|
+
return problems_found == 0
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
logger.error(f"❌ Ошибка подключения к БД: {e}")
|
|
221
|
+
return False
|
|
222
|
+
|
|
223
|
+
async def test_conversation_creation(config: Config) -> bool:
|
|
224
|
+
"""
|
|
225
|
+
Тестирует создание нового диалога с правильным таймаутом
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
config: Конфигурация бота
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
bool: True если тест прошел успешно, False если найдены проблемы
|
|
232
|
+
"""
|
|
233
|
+
logger.info(f"\n{'='*50}")
|
|
234
|
+
logger.info("🧪 ТЕСТ СОЗДАНИЯ ДИАЛОГА")
|
|
235
|
+
logger.info(f"{'='*50}")
|
|
236
|
+
|
|
237
|
+
timeout_minutes = config.ADMIN_SESSION_TIMEOUT_MINUTES
|
|
238
|
+
logger.info(f"📋 Конфигурация таймаута: {timeout_minutes} минут")
|
|
239
|
+
|
|
240
|
+
# Эмулируем создание диалога
|
|
241
|
+
now_utc = datetime.now(timezone.utc)
|
|
242
|
+
auto_end_utc = now_utc + timedelta(minutes=timeout_minutes)
|
|
243
|
+
|
|
244
|
+
logger.info(f"🕐 now_utc: {now_utc}")
|
|
245
|
+
logger.info(f"⏰ auto_end_utc: {auto_end_utc}")
|
|
246
|
+
logger.info(f"📏 Разница: {int((auto_end_utc - now_utc).total_seconds() / 60)} минут")
|
|
247
|
+
|
|
248
|
+
# Проверяем ISO формат
|
|
249
|
+
auto_end_iso = auto_end_utc.isoformat()
|
|
250
|
+
logger.info(f"📝 ISO формат: {auto_end_iso}")
|
|
251
|
+
|
|
252
|
+
# Проверяем парсинг обратно
|
|
253
|
+
try:
|
|
254
|
+
if auto_end_iso.endswith('Z'):
|
|
255
|
+
parsed_back = datetime.fromisoformat(auto_end_iso.replace('Z', '+00:00'))
|
|
256
|
+
elif '+' in auto_end_iso:
|
|
257
|
+
parsed_back = datetime.fromisoformat(auto_end_iso)
|
|
258
|
+
else:
|
|
259
|
+
parsed_back = datetime.fromisoformat(auto_end_iso).replace(tzinfo=timezone.utc)
|
|
260
|
+
|
|
261
|
+
logger.info(f"🔄 Парсед обратно: {parsed_back}")
|
|
262
|
+
|
|
263
|
+
# Проверяем что время совпадает
|
|
264
|
+
if abs((parsed_back - auto_end_utc).total_seconds()) < 1:
|
|
265
|
+
logger.info("✅ Парсинг работает корректно")
|
|
266
|
+
return True
|
|
267
|
+
else:
|
|
268
|
+
logger.error("❌ Проблема с парсингом времени")
|
|
269
|
+
return False
|
|
270
|
+
|
|
271
|
+
except Exception as e:
|
|
272
|
+
logger.error(f"❌ Ошибка парсинга: {e}")
|
|
273
|
+
return False
|
|
274
|
+
|
|
275
|
+
async def check_timeouts(bot_name: str = "growthmed-october-24") -> bool:
|
|
276
|
+
"""
|
|
277
|
+
Проверяет корректность таймаутов диалогов админов
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
bot_name: Имя бота для проверки
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
bool: True если все таймауты корректны, False если найдены проблемы
|
|
284
|
+
"""
|
|
285
|
+
logger.info("🔍 Проверка таймаутов диалогов админов\n")
|
|
286
|
+
logger.info(f"🚀 Проверка для бота: {bot_name}")
|
|
287
|
+
logger.info(f"🤖 Bot ID будет автоопределен как: {bot_name}\n")
|
|
288
|
+
|
|
289
|
+
# Настраиваем окружение для бота (автоматически устанавливает BOT_ID)
|
|
290
|
+
config_dir = setup_bot_environment(bot_name)
|
|
291
|
+
if not config_dir:
|
|
292
|
+
logger.error("❌ Не удалось настроить окружение бота")
|
|
293
|
+
return False
|
|
294
|
+
|
|
295
|
+
logger.info(f"📁 Текущая рабочая директория: {os.getcwd()}")
|
|
296
|
+
logger.info(f"📂 Содержимое рабочей директории:")
|
|
297
|
+
for item in Path('.').iterdir():
|
|
298
|
+
if item.is_dir():
|
|
299
|
+
logger.info(f" 📁 {item.name}/")
|
|
300
|
+
else:
|
|
301
|
+
logger.info(f" 📄 {item.name}")
|
|
302
|
+
logger.info("")
|
|
303
|
+
|
|
304
|
+
# Инициализация
|
|
305
|
+
try:
|
|
306
|
+
logger.info("⚙️ Инициализация конфигурации...")
|
|
307
|
+
config = Config()
|
|
308
|
+
logger.info(f"✅ Конфигурация загружена")
|
|
309
|
+
|
|
310
|
+
logger.info("🔗 Подключение к Supabase...")
|
|
311
|
+
supabase_client = SupabaseClient(config.SUPABASE_URL, config.SUPABASE_KEY)
|
|
312
|
+
await supabase_client.initialize()
|
|
313
|
+
logger.info(f"✅ Supabase подключен")
|
|
314
|
+
|
|
315
|
+
logger.info("👑 Инициализация менеджеров...")
|
|
316
|
+
admin_manager = AdminManager(config, supabase_client)
|
|
317
|
+
conversation_manager = ConversationManager(supabase_client, admin_manager)
|
|
318
|
+
logger.info(f"✅ Менеджеры инициализированы\n")
|
|
319
|
+
|
|
320
|
+
except Exception as e:
|
|
321
|
+
logger.error(f"❌ Ошибка инициализации: {e}")
|
|
322
|
+
logger.exception("Стек ошибки:")
|
|
323
|
+
return False
|
|
324
|
+
|
|
325
|
+
logger.info(f"⚙️ Конфигурация:")
|
|
326
|
+
logger.info(f" BOT_ID: {config.BOT_ID}")
|
|
327
|
+
logger.info(f" ADMIN_SESSION_TIMEOUT_MINUTES: {config.ADMIN_SESSION_TIMEOUT_MINUTES}")
|
|
328
|
+
logger.info(f" PROMT_FILES_DIR: {config.PROMT_FILES_DIR}")
|
|
329
|
+
logger.info(f" Найдено промпт-файлов: {len(config.PROMPT_FILES)}")
|
|
330
|
+
logger.info(f" Админов: {len(config.ADMIN_TELEGRAM_IDS)}")
|
|
331
|
+
logger.info(f" Сейчас UTC: {datetime.now(timezone.utc)}")
|
|
332
|
+
logger.info("")
|
|
333
|
+
|
|
334
|
+
# Получаем активные диалоги
|
|
335
|
+
try:
|
|
336
|
+
logger.info("📊 Получение активных диалогов...")
|
|
337
|
+
conversations = await conversation_manager.get_active_conversations()
|
|
338
|
+
logger.info(f"✅ Получено {len(conversations)} диалогов")
|
|
339
|
+
except Exception as e:
|
|
340
|
+
logger.error(f"❌ Ошибка получения диалогов: {e}")
|
|
341
|
+
return False
|
|
342
|
+
|
|
343
|
+
if not conversations:
|
|
344
|
+
logger.info("💬 Нет активных диалогов")
|
|
345
|
+
logger.info("💡 Создайте диалог командой /чат USER_ID для тестирования")
|
|
346
|
+
|
|
347
|
+
# Показываем пример создания тестового диалога
|
|
348
|
+
logger.info(f"\n🧪 Пример создания тестового диалога:")
|
|
349
|
+
logger.info(f"1. Запустите бота: python {bot_name}.py")
|
|
350
|
+
logger.info(f"2. Как админ выполните: /чат 123456789")
|
|
351
|
+
logger.info(f"3. Затем проверьте: /чаты")
|
|
352
|
+
return True # Нет диалогов = нет проблем
|
|
353
|
+
|
|
354
|
+
logger.info(f"📊 Найдено {len(conversations)} активных диалогов:")
|
|
355
|
+
logger.info("")
|
|
356
|
+
|
|
357
|
+
problems_found = 0
|
|
358
|
+
|
|
359
|
+
for i, conv in enumerate(conversations, 1):
|
|
360
|
+
logger.info(f"{i}. Диалог ID: {conv['id']}")
|
|
361
|
+
logger.info(f" 👤 Пользователь: {conv['user_id']}")
|
|
362
|
+
logger.info(f" 👑 Админ: {conv['admin_id']}")
|
|
363
|
+
|
|
364
|
+
# Анализируем времена
|
|
365
|
+
started_at_str = conv['started_at']
|
|
366
|
+
auto_end_str = conv['auto_end_at']
|
|
367
|
+
|
|
368
|
+
logger.info(f" 🕐 started_at (сырое): {started_at_str}")
|
|
369
|
+
logger.info(f" ⏰ auto_end_at (сырое): {auto_end_str}")
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
# Парсим время начала с правильной обработкой timezone
|
|
373
|
+
if started_at_str.endswith('Z'):
|
|
374
|
+
start_time = datetime.fromisoformat(started_at_str.replace('Z', '+00:00'))
|
|
375
|
+
elif '+' in started_at_str or started_at_str.count(':') >= 3:
|
|
376
|
+
start_time = datetime.fromisoformat(started_at_str)
|
|
377
|
+
else:
|
|
378
|
+
naive_time = datetime.fromisoformat(started_at_str)
|
|
379
|
+
start_time = naive_time.replace(tzinfo=timezone.utc)
|
|
380
|
+
|
|
381
|
+
# Парсим время автозавершения с правильной обработкой timezone
|
|
382
|
+
if auto_end_str.endswith('Z'):
|
|
383
|
+
auto_end = datetime.fromisoformat(auto_end_str.replace('Z', '+00:00'))
|
|
384
|
+
elif '+' in auto_end_str or auto_end_str.count(':') >= 3:
|
|
385
|
+
auto_end = datetime.fromisoformat(auto_end_str)
|
|
386
|
+
else:
|
|
387
|
+
naive_time = datetime.fromisoformat(auto_end_str)
|
|
388
|
+
auto_end = naive_time.replace(tzinfo=timezone.utc)
|
|
389
|
+
|
|
390
|
+
logger.info(f" 📅 start_time (parsed): {start_time}")
|
|
391
|
+
logger.info(f" ⏰ auto_end (parsed): {auto_end}")
|
|
392
|
+
|
|
393
|
+
# Планируемая длительность
|
|
394
|
+
planned_duration = auto_end - start_time
|
|
395
|
+
planned_minutes = int(planned_duration.total_seconds() / 60)
|
|
396
|
+
logger.info(f" 📏 Планируемая длительность: {planned_minutes} минут")
|
|
397
|
+
|
|
398
|
+
# Текущее время в UTC
|
|
399
|
+
now_utc = datetime.now(timezone.utc)
|
|
400
|
+
|
|
401
|
+
# Приводим все к UTC для корректных расчетов
|
|
402
|
+
if start_time.tzinfo != timezone.utc:
|
|
403
|
+
start_time_utc = start_time.astimezone(timezone.utc)
|
|
404
|
+
else:
|
|
405
|
+
start_time_utc = start_time
|
|
406
|
+
|
|
407
|
+
if auto_end.tzinfo != timezone.utc:
|
|
408
|
+
auto_end_utc = auto_end.astimezone(timezone.utc)
|
|
409
|
+
else:
|
|
410
|
+
auto_end_utc = auto_end
|
|
411
|
+
|
|
412
|
+
# Прошло времени
|
|
413
|
+
elapsed = now_utc - start_time_utc
|
|
414
|
+
elapsed_minutes = max(0, int(elapsed.total_seconds() / 60))
|
|
415
|
+
logger.info(f" ⏱️ Прошло времени: {elapsed_minutes} минут")
|
|
416
|
+
|
|
417
|
+
# Оставшееся время
|
|
418
|
+
remaining = auto_end_utc - now_utc
|
|
419
|
+
remaining_minutes = max(0, int(remaining.total_seconds() / 60))
|
|
420
|
+
logger.info(f" ⏰ Осталось времени: {remaining_minutes} минут")
|
|
421
|
+
|
|
422
|
+
# Проверяем корректность конфигурации
|
|
423
|
+
expected_timeout = config.ADMIN_SESSION_TIMEOUT_MINUTES
|
|
424
|
+
if abs(planned_minutes - expected_timeout) <= 2: # допускаем погрешность 2 минуты
|
|
425
|
+
logger.info(f" ✅ Таймаут корректный (ожидался {expected_timeout} мин)")
|
|
426
|
+
else:
|
|
427
|
+
logger.error(f" ❌ ОШИБКА: ожидался {expected_timeout} мин, получили {planned_minutes} мин")
|
|
428
|
+
problems_found += 1
|
|
429
|
+
|
|
430
|
+
# Проверяем математику
|
|
431
|
+
total_check = elapsed_minutes + remaining_minutes
|
|
432
|
+
logger.info(f" 🔢 Проверка: {elapsed_minutes} + {remaining_minutes} = {total_check} мин")
|
|
433
|
+
|
|
434
|
+
if abs(total_check - planned_minutes) > 2:
|
|
435
|
+
logger.warning(f" ⚠️ ПРОБЛЕМА: сумма не сходится! Возможна проблема с timezone")
|
|
436
|
+
problems_found += 1
|
|
437
|
+
else:
|
|
438
|
+
logger.info(f" ✅ Математика сходится")
|
|
439
|
+
|
|
440
|
+
except Exception as e:
|
|
441
|
+
logger.error(f" ❌ Ошибка парсинга: {e}")
|
|
442
|
+
problems_found += 1
|
|
443
|
+
logger.exception(" Стек ошибки:")
|
|
444
|
+
|
|
445
|
+
logger.info("")
|
|
446
|
+
|
|
447
|
+
# Тестируем функцию форматирования
|
|
448
|
+
logger.info("🧪 Тестирование format_active_conversations:")
|
|
449
|
+
try:
|
|
450
|
+
formatted_text = conversation_manager.format_active_conversations(conversations)
|
|
451
|
+
logger.info(formatted_text)
|
|
452
|
+
except Exception as e:
|
|
453
|
+
logger.error(f"❌ Ошибка форматирования: {e}")
|
|
454
|
+
problems_found += 1
|
|
455
|
+
|
|
456
|
+
# Итоговый результат
|
|
457
|
+
logger.info(f"\n{'='*50}")
|
|
458
|
+
logger.info(f"📊 ИТОГОВЫЙ РЕЗУЛЬТАТ:")
|
|
459
|
+
if problems_found == 0:
|
|
460
|
+
logger.info("✅ Все таймауты корректны!")
|
|
461
|
+
else:
|
|
462
|
+
logger.error(f"❌ Найдено {problems_found} проблем")
|
|
463
|
+
logger.info("💡 Запустите fix_existing_timeouts.py для исправления")
|
|
464
|
+
logger.info(f"{'='*50}")
|
|
465
|
+
|
|
466
|
+
return problems_found == 0
|
|
467
|
+
|
|
468
|
+
def main():
|
|
469
|
+
"""Точка входа для запуска из командной строки"""
|
|
470
|
+
# Убираем лишние логи для чистого вывода
|
|
471
|
+
logging.basicConfig(level=logging.INFO, format='%(message)s')
|
|
472
|
+
|
|
473
|
+
logger.info("🔍 Утилита проверки таймаутов диалогов")
|
|
474
|
+
logger.info("Использование:")
|
|
475
|
+
logger.info(" python -m smart_bot_factory.timeout_checker [bot_name]")
|
|
476
|
+
logger.info(" python -m smart_bot_factory.timeout_checker growthmed-october-24")
|
|
477
|
+
logger.info("")
|
|
478
|
+
|
|
479
|
+
if len(sys.argv) > 1 and sys.argv[1] in ['-h', '--help', 'help']:
|
|
480
|
+
return
|
|
481
|
+
|
|
482
|
+
# Определяем какого бота проверять
|
|
483
|
+
bot_name = "growthmed-october-24" # по умолчанию
|
|
484
|
+
if len(sys.argv) > 1:
|
|
485
|
+
bot_name = sys.argv[1]
|
|
486
|
+
|
|
487
|
+
try:
|
|
488
|
+
success = asyncio.run(check_timeouts(bot_name))
|
|
489
|
+
if not success:
|
|
490
|
+
sys.exit(1)
|
|
491
|
+
except KeyboardInterrupt:
|
|
492
|
+
logger.info("\n⏹️ Прервано пользователем")
|
|
493
|
+
except Exception as e:
|
|
494
|
+
logger.error(f"\n💥 Критическая ошибка: {e}")
|
|
495
|
+
logger.exception("Стек ошибки:")
|
|
496
|
+
sys.exit(1)
|
|
497
|
+
|
|
498
|
+
if __name__ == "__main__":
|
|
499
|
+
main()
|