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.

Files changed (61) hide show
  1. smart_bot_factory/__init__.py +33 -0
  2. smart_bot_factory/admin/__init__.py +16 -0
  3. smart_bot_factory/admin/admin_logic.py +430 -0
  4. smart_bot_factory/admin/admin_manager.py +141 -0
  5. smart_bot_factory/admin/admin_migration.sql +136 -0
  6. smart_bot_factory/admin/admin_tester.py +151 -0
  7. smart_bot_factory/admin/timeout_checker.py +499 -0
  8. smart_bot_factory/analytics/__init__.py +7 -0
  9. smart_bot_factory/analytics/analytics_manager.py +355 -0
  10. smart_bot_factory/cli.py +768 -0
  11. smart_bot_factory/config.py +235 -0
  12. smart_bot_factory/configs/growthmed-helper/env_example.txt +1 -0
  13. smart_bot_factory/configs/growthmed-helper/prompts/1sales_context.txt +9 -0
  14. smart_bot_factory/configs/growthmed-helper/prompts/2product_info.txt +582 -0
  15. smart_bot_factory/configs/growthmed-helper/prompts/3objection_handling.txt +66 -0
  16. smart_bot_factory/configs/growthmed-helper/prompts/final_instructions.txt +232 -0
  17. smart_bot_factory/configs/growthmed-helper/prompts/help_message.txt +28 -0
  18. smart_bot_factory/configs/growthmed-helper/prompts/welcome_message.txt +7 -0
  19. smart_bot_factory/configs/growthmed-helper/welcome_file/welcome_file_msg.txt +16 -0
  20. 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
  21. smart_bot_factory/configs/growthmed-october-24/prompts/1sales_context.txt +16 -0
  22. smart_bot_factory/configs/growthmed-october-24/prompts/2product_info.txt +582 -0
  23. smart_bot_factory/configs/growthmed-october-24/prompts/3objection_handling.txt +66 -0
  24. smart_bot_factory/configs/growthmed-october-24/prompts/final_instructions.txt +212 -0
  25. smart_bot_factory/configs/growthmed-october-24/prompts/help_message.txt +28 -0
  26. smart_bot_factory/configs/growthmed-october-24/prompts/welcome_message.txt +8 -0
  27. smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064229.txt +818 -0
  28. smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064335.txt +32 -0
  29. smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064638.txt +35 -0
  30. smart_bot_factory/configs/growthmed-october-24/tests/quick_scenarios.yaml +66 -0
  31. smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml +108 -0
  32. smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml +46 -0
  33. smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt +16 -0
  34. 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
  35. smart_bot_factory/core/__init__.py +22 -0
  36. smart_bot_factory/core/bot_utils.py +703 -0
  37. smart_bot_factory/core/conversation_manager.py +536 -0
  38. smart_bot_factory/core/decorators.py +230 -0
  39. smart_bot_factory/core/message_sender.py +249 -0
  40. smart_bot_factory/core/states.py +14 -0
  41. smart_bot_factory/creation/__init__.py +8 -0
  42. smart_bot_factory/creation/bot_builder.py +329 -0
  43. smart_bot_factory/creation/bot_testing.py +986 -0
  44. smart_bot_factory/database/database_structure.sql +57 -0
  45. smart_bot_factory/database/schema.sql +1094 -0
  46. smart_bot_factory/handlers/handlers.py +583 -0
  47. smart_bot_factory/integrations/__init__.py +9 -0
  48. smart_bot_factory/integrations/openai_client.py +435 -0
  49. smart_bot_factory/integrations/supabase_client.py +592 -0
  50. smart_bot_factory/setup_checker.py +476 -0
  51. smart_bot_factory/utils/__init__.py +9 -0
  52. smart_bot_factory/utils/debug_routing.py +103 -0
  53. smart_bot_factory/utils/prompt_loader.py +427 -0
  54. smart_bot_factory/utm_link_generator.py +106 -0
  55. smart_bot_factory-0.1.4.dist-info/METADATA +126 -0
  56. smart_bot_factory-0.1.4.dist-info/RECORD +59 -0
  57. smart_bot_factory-0.1.4.dist-info/licenses/LICENSE +24 -0
  58. smart_bot_factory-0.1.2.dist-info/METADATA +0 -31
  59. smart_bot_factory-0.1.2.dist-info/RECORD +0 -4
  60. {smart_bot_factory-0.1.2.dist-info → smart_bot_factory-0.1.4.dist-info}/WHEEL +0 -0
  61. {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()
@@ -0,0 +1,7 @@
1
+ """
2
+ Analytics модули smart_bot_factory
3
+ """
4
+
5
+ from .analytics_manager import AnalyticsManager
6
+
7
+ __all__ = ['AnalyticsManager']