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