smart-bot-factory 0.3.2__py3-none-any.whl → 0.3.3__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.

@@ -52,15 +52,65 @@ class SupabaseClient:
52
52
  response = query.execute()
53
53
 
54
54
  if response.data:
55
- # Обновляем данные существующего пользователя
56
- update_query = self.client.table('sales_users').update({
55
+ # Получаем текущие данные пользователя для мержинга UTM и сегментов
56
+ existing_user_query = self.client.table('sales_users').select(
57
+ 'source', 'medium', 'campaign', 'content', 'term', 'segments'
58
+ ).eq('telegram_id', user_data['telegram_id'])
59
+
60
+ if self.bot_id:
61
+ existing_user_query = existing_user_query.eq('bot_id', self.bot_id)
62
+
63
+ existing_response = existing_user_query.execute()
64
+ existing_utm = existing_response.data[0] if existing_response.data else {}
65
+
66
+ # Формируем данные для обновления
67
+ update_data = {
57
68
  'username': user_data.get('username'),
58
69
  'first_name': user_data.get('first_name'),
59
70
  'last_name': user_data.get('last_name'),
60
71
  'language_code': user_data.get('language_code'),
61
72
  'updated_at': datetime.now().isoformat(),
62
73
  'is_active': True
63
- }).eq('telegram_id', user_data['telegram_id'])
74
+ }
75
+
76
+ # Мержим UTM данные: обновляем только если новое значение не None
77
+ utm_fields = ['source', 'medium', 'campaign', 'content', 'term']
78
+ for field in utm_fields:
79
+ new_value = user_data.get(field)
80
+ if new_value is not None:
81
+ # Есть новое значение - обновляем
82
+ update_data[field] = new_value
83
+ if existing_utm.get(field) != new_value:
84
+ logger.info(f"📊 UTM обновление: {field} = '{new_value}' (было: '{existing_utm.get(field)}')")
85
+ else:
86
+ # Нового значения нет - сохраняем старое
87
+ update_data[field] = existing_utm.get(field)
88
+
89
+ # Обрабатываем сегменты с накоплением через запятую
90
+ new_segment = user_data.get('segment')
91
+ if new_segment:
92
+ existing_segments = existing_utm.get('segments', '') or ''
93
+ if existing_segments:
94
+ # Разбираем существующие сегменты
95
+ segments_list = [s.strip() for s in existing_segments.split(',') if s.strip()]
96
+ # Добавляем новый сегмент, если его еще нет
97
+ if new_segment not in segments_list:
98
+ segments_list.append(new_segment)
99
+ update_data['segments'] = ', '.join(segments_list)
100
+ logger.info(f"📊 Сегмент добавлен: '{new_segment}' (было: '{existing_segments}')")
101
+ else:
102
+ update_data['segments'] = existing_segments
103
+ logger.info(f"📊 Сегмент '{new_segment}' уже существует")
104
+ else:
105
+ # Первый сегмент
106
+ update_data['segments'] = new_segment
107
+ logger.info(f"📊 Первый сегмент добавлен: '{new_segment}'")
108
+ else:
109
+ # Нового сегмента нет - сохраняем старое значение
110
+ update_data['segments'] = existing_utm.get('segments')
111
+
112
+ # Обновляем пользователя
113
+ update_query = self.client.table('sales_users').update(update_data).eq('telegram_id', user_data['telegram_id'])
64
114
 
65
115
  if self.bot_id:
66
116
  update_query = update_query.eq('bot_id', self.bot_id)
@@ -83,13 +133,17 @@ class SupabaseClient:
83
133
  'campaign': user_data.get('campaign'),
84
134
  'content': user_data.get('content'),
85
135
  'term': user_data.get('term'),
136
+ 'segments': user_data.get('segment'), # Первый сегмент при создании
86
137
  }
87
138
  if self.bot_id:
88
139
  user_insert_data['bot_id'] = self.bot_id
89
140
 
90
141
  response = self.client.table('sales_users').insert(user_insert_data).execute()
91
142
 
92
- logger.info(f"Создан новый пользователь {user_data['telegram_id']}{f' для bot_id {self.bot_id}' if self.bot_id else ''}")
143
+ if user_data.get('segment'):
144
+ logger.info(f"Создан новый пользователь {user_data['telegram_id']} с сегментом '{user_data.get('segment')}'{f' для bot_id {self.bot_id}' if self.bot_id else ''}")
145
+ else:
146
+ logger.info(f"Создан новый пользователь {user_data['telegram_id']}{f' для bot_id {self.bot_id}' if self.bot_id else ''}")
93
147
  return user_data['telegram_id']
94
148
 
95
149
  except APIError as e:
@@ -730,47 +784,71 @@ class SupabaseClient:
730
784
  async def get_funnel_stats(self, days: int = 7) -> Dict[str, Any]:
731
785
  """Получает статистику воронки продаж"""
732
786
  try:
733
- cutoff_date = datetime.now(timezone.utc) - timedelta(days=days)
787
+ cutoff_date = datetime.now() - timedelta(days=days)
788
+
789
+ # Получаем ВСЕ уникальные пользователи из sales_users с фильтром по bot_id
790
+ users_query = self.client.table('sales_users').select('telegram_id')
734
791
 
735
- # Общее количество сессий
736
- sessions_response = self.client.table('sales_chat_sessions').select('id').gte(
737
- 'created_at', cutoff_date.isoformat()
738
- )
739
792
  if self.bot_id:
740
- sessions_response = sessions_response.eq('bot_id', self.bot_id)
793
+ users_query = users_query.eq('bot_id', self.bot_id)
741
794
 
742
- total_sessions = len(sessions_response.execute().data)
795
+ # Исключаем тестовых пользователей
796
+ users_query = users_query.neq('username', 'test_user')
743
797
 
744
- # Статистика по этапам
745
- stages_response = self.client.table('sales_chat_sessions').select(
746
- 'current_stage', 'quality_score'
747
- ).gte('created_at', cutoff_date.isoformat())
798
+ users_response = users_query.execute()
799
+ total_unique_users = len(users_response.data) if users_response.data else 0
748
800
 
801
+ # Получаем сессии с учетом bot_id за период
802
+ sessions_query = self.client.table('sales_chat_sessions').select(
803
+ 'id', 'user_id', 'current_stage', 'lead_quality_score', 'created_at'
804
+ ).gte('created_at', cutoff_date.isoformat())
805
+
749
806
  if self.bot_id:
750
- stages_response = stages_response.eq('bot_id', self.bot_id)
807
+ sessions_query = sessions_query.eq('bot_id', self.bot_id)
808
+
809
+ sessions_response = sessions_query.execute()
810
+ sessions = sessions_response.data
811
+
812
+ # Исключаем сессии тестовых пользователей
813
+ if sessions:
814
+ # Получаем telegram_id тестовых пользователей
815
+ test_users_query = self.client.table('sales_users').select('telegram_id').eq('username', 'test_user')
816
+ if self.bot_id:
817
+ test_users_query = test_users_query.eq('bot_id', self.bot_id)
818
+
819
+ test_users_response = test_users_query.execute()
820
+ test_user_ids = {user['telegram_id'] for user in test_users_response.data} if test_users_response.data else set()
821
+
822
+ # Фильтруем сессии
823
+ sessions = [s for s in sessions if s['user_id'] not in test_user_ids]
751
824
 
752
- sessions_data = stages_response.execute().data
825
+ total_sessions = len(sessions)
826
+
827
+ # Группировка по этапам
753
828
 
829
+ # Группировка по этапам
754
830
  stages = {}
755
831
  quality_scores = []
756
832
 
757
- for session in sessions_data:
833
+ for session in sessions:
758
834
  stage = session.get('current_stage', 'unknown')
759
835
  stages[stage] = stages.get(stage, 0) + 1
760
836
 
761
- if session.get('quality_score'):
762
- quality_scores.append(session['quality_score'])
837
+ score = session.get('lead_quality_score', 5)
838
+ if score:
839
+ quality_scores.append(score)
763
840
 
764
- avg_quality = sum(quality_scores) / len(quality_scores) if quality_scores else 0
841
+ avg_quality = sum(quality_scores) / len(quality_scores) if quality_scores else 5
765
842
 
766
843
  return {
767
844
  'total_sessions': total_sessions,
845
+ 'total_unique_users': total_unique_users, # ✅ ВСЕ уникальные пользователи бота
768
846
  'stages': stages,
769
- 'avg_quality': round(avg_quality, 2),
847
+ 'avg_quality': round(avg_quality, 1),
770
848
  'period_days': days
771
849
  }
772
-
773
- except Exception as e:
850
+
851
+ except APIError as e:
774
852
  logger.error(f"Ошибка получения статистики воронки: {e}")
775
853
  return {
776
854
  'total_sessions': 0,
@@ -782,25 +860,50 @@ class SupabaseClient:
782
860
  async def get_events_stats(self, days: int = 7) -> Dict[str, int]:
783
861
  """Получает статистику событий"""
784
862
  try:
785
- cutoff_date = datetime.now(timezone.utc) - timedelta(days=days)
863
+ cutoff_date = datetime.now() - timedelta(days=days)
786
864
 
787
- response = self.client.table('scheduled_events').select(
788
- 'event_type', 'status'
865
+ # Получаем события с учетом bot_id через сессии
866
+ query = self.client.table('session_events').select(
867
+ 'event_type', 'session_id'
789
868
  ).gte('created_at', cutoff_date.isoformat())
790
869
 
791
- events_data = response.execute().data
870
+ events_response = query.execute()
871
+ events = events_response.data if events_response.data else []
792
872
 
793
- stats = {}
794
- for event in events_data:
795
- event_type = event.get('event_type', 'unknown')
796
- status = event.get('status', 'unknown')
873
+ # Фильтруем события по bot_id через сессии
874
+ if self.bot_id and events:
875
+ # Получаем ID сессий этого бота
876
+ sessions_query = self.client.table('sales_chat_sessions').select('id', 'user_id').eq('bot_id', self.bot_id)
877
+ sessions_response = sessions_query.execute()
878
+
879
+ # Исключаем сессии тестовых пользователей
880
+ if sessions_response.data:
881
+ # Получаем telegram_id тестовых пользователей
882
+ test_users_query = self.client.table('sales_users').select('telegram_id').eq('username', 'test_user')
883
+ if self.bot_id:
884
+ test_users_query = test_users_query.eq('bot_id', self.bot_id)
885
+
886
+ test_users_response = test_users_query.execute()
887
+ test_user_ids = {user['telegram_id'] for user in test_users_response.data} if test_users_response.data else set()
888
+
889
+ # Фильтруем сессии: только не тестовые
890
+ bot_sessions = [s for s in sessions_response.data if s['user_id'] not in test_user_ids]
891
+ bot_session_ids = {session['id'] for session in bot_sessions}
892
+ else:
893
+ bot_session_ids = set()
797
894
 
798
- key = f"{event_type}_{status}"
799
- stats[key] = stats.get(key, 0) + 1
895
+ # Фильтруем события
896
+ events = [event for event in events if event['session_id'] in bot_session_ids]
800
897
 
801
- return stats
898
+ # Группируем по типам событий
899
+ event_counts = {}
900
+ for event in events:
901
+ event_type = event.get('event_type', 'unknown')
902
+ event_counts[event_type] = event_counts.get(event_type, 0) + 1
802
903
 
803
- except Exception as e:
904
+ return event_counts
905
+
906
+ except APIError as e:
804
907
  logger.error(f"Ошибка получения статистики событий: {e}")
805
908
  return {}
806
909
 
@@ -919,4 +1022,277 @@ class SupabaseClient:
919
1022
 
920
1023
  except Exception as e:
921
1024
  logger.error(f"Ошибка получения последнего события для пользователя {user_id}, тип '{event_type}': {e}")
922
- return None
1025
+ return None
1026
+
1027
+ async def get_all_segments(self) -> List[str]:
1028
+ """
1029
+ Получает все уникальные сегменты из таблицы sales_users
1030
+
1031
+ Returns:
1032
+ List[str]: Список уникальных сегментов
1033
+ """
1034
+ try:
1035
+ # Запрос всех непустых сегментов
1036
+ query = self.client.table('sales_users').select('segments').neq('segments', '')
1037
+
1038
+ if self.bot_id:
1039
+ query = query.eq('bot_id', self.bot_id)
1040
+
1041
+ response = query.execute()
1042
+
1043
+ # Собираем все уникальные сегменты
1044
+ all_segments = set()
1045
+ for row in response.data:
1046
+ segments_str = row.get('segments', '')
1047
+ if segments_str:
1048
+ # Разбираем сегменты через запятую
1049
+ segments = [s.strip() for s in segments_str.split(',') if s.strip()]
1050
+ all_segments.update(segments)
1051
+
1052
+ segments_list = sorted(list(all_segments))
1053
+ logger.info(f"Найдено {len(segments_list)} уникальных сегментов")
1054
+
1055
+ return segments_list
1056
+
1057
+ except Exception as e:
1058
+ logger.error(f"Ошибка получения сегментов: {e}")
1059
+ return []
1060
+
1061
+ async def get_users_by_segment(self, segment: str = None) -> List[Dict[str, Any]]:
1062
+ """
1063
+ Получает пользователей по сегменту или всех пользователей
1064
+
1065
+ Args:
1066
+ segment: Название сегмента (если None - возвращает всех)
1067
+
1068
+ Returns:
1069
+ List[Dict]: Список пользователей с telegram_id
1070
+ """
1071
+ try:
1072
+ query = self.client.table('sales_users').select('telegram_id, segments')
1073
+
1074
+ if self.bot_id:
1075
+ query = query.eq('bot_id', self.bot_id)
1076
+
1077
+ response = query.execute()
1078
+
1079
+ if segment is None:
1080
+ # Все пользователи
1081
+ logger.info(f"Получено {len(response.data)} всех пользователей")
1082
+ return response.data
1083
+
1084
+ # Фильтруем по сегменту
1085
+ users = []
1086
+ for row in response.data:
1087
+ segments_str = row.get('segments', '')
1088
+ if segments_str:
1089
+ segments = [s.strip() for s in segments_str.split(',') if s.strip()]
1090
+ if segment in segments:
1091
+ users.append(row)
1092
+
1093
+ logger.info(f"Найдено {len(users)} пользователей с сегментом '{segment}'")
1094
+ return users
1095
+
1096
+ except Exception as e:
1097
+ logger.error(f"Ошибка получения пользователей по сегменту '{segment}': {e}")
1098
+ return []
1099
+
1100
+ # =============================================================================
1101
+ # МЕТОДЫ ДЛЯ РАБОТЫ С ФАЙЛАМИ СОБЫТИЙ В SUPABASE STORAGE
1102
+ # =============================================================================
1103
+
1104
+ async def upload_event_file(self, event_name: str, file_data: bytes, file_name: str) -> Dict[str, str]:
1105
+ """
1106
+ Загружает файл события в Supabase Storage
1107
+
1108
+ Args:
1109
+ event_name: Название события (используется как папка)
1110
+ file_data: Байты файла
1111
+ file_name: Имя файла
1112
+
1113
+ Returns:
1114
+ Dict с storage_path
1115
+ """
1116
+ try:
1117
+ bucket_name = 'admin-events'
1118
+
1119
+ # Формируем путь: admin-events/{event_name}/{file_name}
1120
+ storage_path = f"{event_name}/{file_name}"
1121
+
1122
+ # Определяем MIME-type
1123
+ import mimetypes
1124
+ content_type, _ = mimetypes.guess_type(file_name)
1125
+ if not content_type:
1126
+ content_type = 'application/octet-stream'
1127
+
1128
+ # Загружаем в Storage
1129
+ self.client.storage.from_(bucket_name).upload(
1130
+ storage_path,
1131
+ file_data,
1132
+ file_options={"content-type": content_type}
1133
+ )
1134
+
1135
+ logger.info(f"✅ Файл загружен в Storage: {storage_path}")
1136
+
1137
+ return {
1138
+ 'storage_path': storage_path
1139
+ }
1140
+
1141
+ except Exception as e:
1142
+ logger.error(f"❌ Ошибка загрузки файла в Storage: {e}")
1143
+ raise
1144
+
1145
+ async def download_event_file(self, event_name: str, file_name: str) -> bytes:
1146
+ """
1147
+ Скачивает файл события из Supabase Storage
1148
+
1149
+ Args:
1150
+ event_name: Название события
1151
+ file_name: Имя файла
1152
+
1153
+ Returns:
1154
+ bytes: Содержимое файла
1155
+ """
1156
+ try:
1157
+ bucket_name = 'admin-events'
1158
+ storage_path = f"{event_name}/{file_name}"
1159
+
1160
+ # Скачиваем файл
1161
+ file_data = self.client.storage.from_(bucket_name).download(storage_path)
1162
+
1163
+ logger.info(f"✅ Файл скачан из Storage: {storage_path}")
1164
+ return file_data
1165
+
1166
+ except Exception as e:
1167
+ logger.error(f"❌ Ошибка скачивания файла из Storage: {e}")
1168
+ raise
1169
+
1170
+ async def delete_event_files(self, event_name: str):
1171
+ """
1172
+ Удаляет ВСЕ файлы события из Supabase Storage
1173
+
1174
+ Args:
1175
+ event_name: Название события (папка с файлами)
1176
+ """
1177
+ try:
1178
+ bucket_name = 'admin-events'
1179
+
1180
+ # Получаем список всех файлов в папке события
1181
+ files_list = self.client.storage.from_(bucket_name).list(event_name)
1182
+
1183
+ if not files_list:
1184
+ logger.info(f"ℹ️ Нет файлов для удаления в событии '{event_name}'")
1185
+ return
1186
+
1187
+ # Формируем пути для удаления
1188
+ file_paths = [f"{event_name}/{file['name']}" for file in files_list]
1189
+
1190
+ # Удаляем файлы
1191
+ self.client.storage.from_(bucket_name).remove(file_paths)
1192
+
1193
+ logger.info(f"✅ Удалено {len(file_paths)} файлов события '{event_name}' из Storage")
1194
+
1195
+ except Exception as e:
1196
+ logger.error(f"❌ Ошибка удаления файлов события из Storage: {e}")
1197
+ # Не прерываем выполнение, только логируем
1198
+
1199
+ async def save_admin_event(
1200
+ self,
1201
+ event_name: str,
1202
+ event_data: Dict[str, Any],
1203
+ scheduled_datetime: datetime
1204
+ ) -> str:
1205
+ """
1206
+ Сохраняет админское событие в таблицу scheduled_events
1207
+
1208
+ Args:
1209
+ event_name: Название события
1210
+ event_data: Данные события (сегмент, сообщение, файлы)
1211
+ scheduled_datetime: Дата и время отправки (должно быть в UTC с timezone info)
1212
+
1213
+ Returns:
1214
+ str: ID созданного события
1215
+ """
1216
+ try:
1217
+ import json
1218
+
1219
+ # Убеждаемся что datetime в правильном формате для PostgreSQL
1220
+ # Если есть timezone info - используем, иначе предполагаем что это UTC
1221
+ if scheduled_datetime.tzinfo is None:
1222
+ logger.warning("⚠️ scheduled_datetime без timezone info, предполагаем UTC")
1223
+ from datetime import timezone
1224
+ scheduled_datetime = scheduled_datetime.replace(tzinfo=timezone.utc)
1225
+
1226
+ event_record = {
1227
+ 'event_type': event_name,
1228
+ 'event_category': 'admin_event',
1229
+ 'user_id': None, # Для всех пользователей
1230
+ 'event_data': json.dumps(event_data, ensure_ascii=False),
1231
+ 'scheduled_at': scheduled_datetime.isoformat(),
1232
+ 'status': 'pending'
1233
+ }
1234
+
1235
+ response = self.client.table('scheduled_events').insert(event_record).execute()
1236
+ event_id = response.data[0]['id']
1237
+
1238
+ logger.info(f"💾 Админское событие '{event_name}' сохранено в БД: {event_id} на {scheduled_datetime.isoformat()}")
1239
+ return event_id
1240
+
1241
+ except Exception as e:
1242
+ logger.error(f"❌ Ошибка сохранения админского события: {e}")
1243
+ raise
1244
+
1245
+ async def get_admin_events(self, status: str = None) -> List[Dict[str, Any]]:
1246
+ """
1247
+ Получает админские события
1248
+
1249
+ Args:
1250
+ status: Фильтр по статусу (pending, completed, cancelled)
1251
+
1252
+ Returns:
1253
+ List[Dict]: Список админских событий
1254
+ """
1255
+ try:
1256
+ query = self.client.table('scheduled_events').select(
1257
+ '*'
1258
+ ).eq('event_category', 'admin_event')
1259
+
1260
+ if status:
1261
+ query = query.eq('status', status)
1262
+
1263
+ response = query.order('scheduled_at', desc=False).execute()
1264
+
1265
+ logger.info(f"Найдено {len(response.data)} админских событий")
1266
+ return response.data
1267
+
1268
+ except Exception as e:
1269
+ logger.error(f"Ошибка получения админских событий: {e}")
1270
+ return []
1271
+
1272
+ async def check_event_name_exists(self, event_name: str) -> bool:
1273
+ """
1274
+ Проверяет существует ли активное событие с таким названием
1275
+
1276
+ Args:
1277
+ event_name: Название события для проверки
1278
+
1279
+ Returns:
1280
+ bool: True если активное событие с таким именем существует
1281
+ """
1282
+ try:
1283
+ response = self.client.table('scheduled_events').select(
1284
+ 'id', 'event_type', 'status'
1285
+ ).eq('event_category', 'admin_event').eq(
1286
+ 'event_type', event_name
1287
+ ).eq('status', 'pending').execute()
1288
+
1289
+ exists = len(response.data) > 0
1290
+
1291
+ if exists:
1292
+ logger.info(f"⚠️ Найдено активное событие с названием '{event_name}'")
1293
+
1294
+ return exists
1295
+
1296
+ except Exception as e:
1297
+ logger.error(f"Ошибка проверки названия события: {e}")
1298
+ return False
@@ -24,20 +24,24 @@ def get_user_input():
24
24
  utm_content = input("utm_content (контент): ").strip()
25
25
  utm_term = input("utm_term (ключевое слово): ").strip()
26
26
 
27
+ print("\n🎯 Сегментация (нажмите Enter для пропуска):")
28
+ segment = input("seg (сегмент): ").strip()
29
+
27
30
  return {
28
31
  'bot_username': bot_username,
29
32
  'utm_source': utm_source,
30
33
  'utm_medium': utm_medium,
31
34
  'utm_campaign': utm_campaign,
32
35
  'utm_content': utm_content,
33
- 'utm_term': utm_term
36
+ 'utm_term': utm_term,
37
+ 'segment': segment
34
38
  }
35
39
 
36
40
  def create_utm_string(utm_data):
37
- """Создает строку UTM параметров в формате source-vk_campaign-summer2025"""
41
+ """Создает строку UTM параметров в формате source-vk_campaign-summer2025_seg-premium"""
38
42
  utm_parts = []
39
43
 
40
- # Маппинг полей базы данных на новый формат (без utm, в нижнем регистре)
44
+ # Маппинг полей базы данных на формат без префикса utm
41
45
  field_mapping = {
42
46
  'utm_source': 'source',
43
47
  'utm_medium': 'medium',
@@ -51,6 +55,11 @@ def create_utm_string(utm_data):
51
55
  if value:
52
56
  utm_parts.append(f"{utm_field}-{value}")
53
57
 
58
+ # Добавляем сегмент, если указан
59
+ segment = utm_data.get('segment')
60
+ if segment:
61
+ utm_parts.append(f"seg-{segment}")
62
+
54
63
  return "_".join(utm_parts)
55
64
 
56
65
  def generate_telegram_link(bot_username, utm_string):
@@ -104,3 +113,4 @@ def main():
104
113
 
105
114
  if __name__ == "__main__":
106
115
  main()
116
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: smart-bot-factory
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Библиотека для создания умных чат-ботов
5
5
  Author-email: Kopatych <eserov73@gmail.com>
6
6
  License: MIT
@@ -25,6 +25,7 @@ Requires-Dist: aiogram>=3.4.1
25
25
  Requires-Dist: click>=8.0.0
26
26
  Requires-Dist: openai>=1.12.0
27
27
  Requires-Dist: project-root-finder>=1.9
28
+ Requires-Dist: python-dateutil>=2.9.0.post0
28
29
  Requires-Dist: python-dotenv>=1.0.1
29
30
  Requires-Dist: pytz>=2023.3
30
31
  Requires-Dist: pyyaml>=6.0.2
@@ -1,15 +1,21 @@
1
1
  smart_bot_factory/__init__.py,sha256=W5k9awLVi0R_N3fSul7VNkKRdSfExZjNb_M2yNzepg8,102
2
- smart_bot_factory/cli.py,sha256=ws0tYUYDFXCq1e1PyE4Z5P3GQkDjx7xM4gvyywLJKKE,33225
2
+ smart_bot_factory/cli.py,sha256=eQ2fv0uc87nrm1b-lRRw0Tz52NquCcwkk2it__beNKU,33223
3
3
  smart_bot_factory/config.py,sha256=kB3G2hGMrrCSOAvlrddf8x43bgfgEaQqOCKOeELAzfM,11640
4
4
  smart_bot_factory/setup_checker.py,sha256=fqRzyptyMzcb2I0boUaj0rWLdarqaN6ViX6suwTEeWc,20123
5
- smart_bot_factory/utm_link_generator.py,sha256=jw7c84Y_ZvLpV4jaBWw0GK3-lCUKIIa8sZ8YNxoWIlc,4166
6
- smart_bot_factory/admin/__init__.py,sha256=aA8VMXkFn7TiWAMeHzkXCPFGEPxJvL2ECdFASSr7jtU,397
7
- smart_bot_factory/admin/admin_logic.py,sha256=guNhwm_LmliMACN_KX7z7emNwyhkN7UmAhFda7nsVMk,20736
5
+ smart_bot_factory/utm_link_generator.py,sha256=wYPdYYK555YfOP22eJXAcEv_D66_t50Dhg0-wkLVzVk,4502
6
+ smart_bot_factory/admin/__init__.py,sha256=vdsMTpt_LiXkY-awFu_X9e2Zt7CV50PwmsWkFbk6whk,488
7
+ smart_bot_factory/admin/admin_events.py,sha256=yU52QWc1GC-E-WAA_A3HAXpY3zMG96eGq97FtHC5ZB0,34804
8
+ smart_bot_factory/admin/admin_logic.py,sha256=zyaklL-s0LihPGHVbguOB9F0NMaggHAyQGukco_kTtA,22831
8
9
  smart_bot_factory/admin/admin_manager.py,sha256=xlyG9mIjPmtUhS4E9lp36T7o5Kfp5PZpJ-r1QjnSn5g,6394
9
10
  smart_bot_factory/admin/admin_migration.sql,sha256=kleMPJBSe2Z7ZZz7rNyOX_yoh4GZivGesqAX90U5PGs,5667
10
11
  smart_bot_factory/admin/admin_tester.py,sha256=PGFpf7fmD5Wxea31xR2ZM_A_QpvrB73gsbxvUrHQBkg,6463
11
12
  smart_bot_factory/admin/timeout_checker.py,sha256=TzA2FGrxwE8fuhKerGnGrt4qYMEZdIR8x3SQAnIW5YQ,24490
12
- smart_bot_factory/analytics/analytics_manager.py,sha256=JQgKGkPPt07vI6KYWpa7OAimgl5SyF5cbiM89N4LKIQ,16298
13
+ smart_bot_factory/aiogram_calendar/__init__.py,sha256=_IzB_HJIZuMs__7xBBsYVG20GbRNFw2VowvhOEyiGGc,349
14
+ smart_bot_factory/aiogram_calendar/common.py,sha256=Ik-ler6Hid11SHpphqfFs5FjvGhj8Ajz0ds-MWMjPoo,2659
15
+ smart_bot_factory/aiogram_calendar/dialog_calendar.py,sha256=AVuIWPp4D8ri7N7aSNqfZ_SicbfYGvQWeJqSmakGkuE,8289
16
+ smart_bot_factory/aiogram_calendar/schemas.py,sha256=y2jnnFuqOqLZ7PwY6WQrzmRgCE6rwFYUGTFYaQnCouM,2524
17
+ smart_bot_factory/aiogram_calendar/simple_calendar.py,sha256=XmCFjrDNJZ9LNEGkbY1vqRbU9t2EgIk69tGg5xy2sfE,7859
18
+ smart_bot_factory/analytics/analytics_manager.py,sha256=y1DI_TnAdSX5NG_mwqHD1emne4opfsyo50D_tebi_UA,17967
13
19
  smart_bot_factory/configs/growthmed-october-24/prompts/1sales_context.txt,sha256=uJ4WviYIr9xJaFdu-APrrmBgIWVwdJymcN04upIviLY,1614
14
20
  smart_bot_factory/configs/growthmed-october-24/prompts/2product_info.txt,sha256=tWP9gdn58vu9BhYpDx1lGuPNT24j5p1XPiCSD-CdXiU,24191
15
21
  smart_bot_factory/configs/growthmed-october-24/prompts/3objection_handling.txt,sha256=zwUGKp3mp1dAQUPjLQsCsczwRJJOqn9GGvVzVBCpvfY,6985
@@ -24,32 +30,30 @@ smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml,sh
24
30
  smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml,sha256=bzDulOU4a2LyWlcHzlQU8GYhOky2WTfyizGfjX4ioMY,2436
25
31
  smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt,sha256=Db21Mm0r8SBWFdX9EeIF2FZtLQ2cvuwVlSRJd2KEYCg,922
26
32
  smart_bot_factory/configs/growthmed-october-24/welcome_file/Чек лист по 152ФЗ и 323ФЗ для медицины.pdf,sha256=BiAiQHNnQXJPMsks9AeL6s0beEjRFkRMJLMlAn4WorA,5284954
27
- smart_bot_factory/core/bot_utils.py,sha256=cWCasEG_4BTTp35d3P3BIOjpn9NXOCd1b6cKhNNpNb0,44670
33
+ smart_bot_factory/core/bot_utils.py,sha256=Be334BPspBgHNO9OpEiPlrCc0Gmy7_8sox5WpV8XHic,45174
28
34
  smart_bot_factory/core/conversation_manager.py,sha256=eoHL7MCEz68DRvTVwRwZgf2PWwGv4T6J9D-I-thETi8,28289
29
- smart_bot_factory/core/decorators.py,sha256=maxoj2rdEGZmqgK6jybaTOcTXx_b32t5fWkNeJ2JdOs,83324
35
+ smart_bot_factory/core/decorators.py,sha256=gJWaJVyCoBSsy4EsaJTv4OPVK-Efv1pQLm54Odgu4Lk,93678
30
36
  smart_bot_factory/core/message_sender.py,sha256=J4b6n8nXVjqf-qzL6URRSvc-FVnQfShwujVSM6qv26w,32232
31
37
  smart_bot_factory/core/router.py,sha256=03fbysaj0LR96p4-8iiml8dTmEHCkR-AaTposSv8q8o,11898
32
38
  smart_bot_factory/core/router_manager.py,sha256=dUwesog-oHk1U2EDdS8p0e4MTSkwtx5_qXn6nrJ9l9I,9700
33
- smart_bot_factory/core/states.py,sha256=AOc19_yAsDW_8md-2oiowjBhuW3bwcsmMxszCXWZZTQ,355
39
+ smart_bot_factory/core/states.py,sha256=L8qp1UmYFuxTN5U9tY076rDuKgxtFbpSGqBpva2eWbo,895
34
40
  smart_bot_factory/creation/__init__.py,sha256=IgDk8GDS3pg7Pw_Et41J33ZmeZIU5dRwQdTmYKXfJfE,128
35
- smart_bot_factory/creation/bot_builder.py,sha256=KQmMmiL9OuGZAR3rYxMNk-2opMUR5NPRy9Sf9iVNCoU,39463
41
+ smart_bot_factory/creation/bot_builder.py,sha256=yGRmOPD7qCMbhcBiltHWISoKxWx8eqjDSnZXpwhqnUs,43115
36
42
  smart_bot_factory/creation/bot_testing.py,sha256=JDWXyJfZmbgo-DLdAPk8Sd9FiehtHHa4sLD17lBrTOc,55669
37
43
  smart_bot_factory/event/__init__.py,sha256=hPL449RULIOB-OXv1ZbGNiHctAYaOMUqhSWGPrDHYBM,212
38
- smart_bot_factory/handlers/handlers.py,sha256=urTUYSqK9zDxomFbC056gN5T4o7Gz9gN0bm84DPzvVc,60390
44
+ smart_bot_factory/handlers/handlers.py,sha256=jMKD04v81BbCNLl_00rAM_rgPDH1mMdHZlLI8ZhXpc8,60438
39
45
  smart_bot_factory/integrations/openai_client.py,sha256=fwaJpwojFdLBWChcFWpFGOHK9upG-nCIwDochkCRRlY,24291
40
- smart_bot_factory/integrations/supabase_client.py,sha256=AfALLZdDYeMWHLJw6POTGiBd-sH3i03oT6tT7m9C28I,44644
46
+ smart_bot_factory/integrations/supabase_client.py,sha256=XznvAXqcMBqHw2_-t91Exyomru-mLP6-BlYuEeoUJSY,63082
41
47
  smart_bot_factory/message/__init__.py,sha256=-ehDZweUc3uKgmLLxFVsD-KWrDtnHpHms7pCrDelWo0,1950
42
48
  smart_bot_factory/router/__init__.py,sha256=5gEbpG3eylOyow5NmidzGUy0K-AZq7RhYLVu9OaUT6c,270
43
49
  smart_bot_factory/supabase/__init__.py,sha256=XmZP6yM9ffERM5ddAWyJnrNzEhCYtMu3AcjVCi1rOf8,179
44
50
  smart_bot_factory/supabase/client.py,sha256=lWIzfOgoSvU7xPhYLoJtM5GnbWdoWsvHcRFC22sFBMU,25637
45
- smart_bot_factory/table/database_structure.sql,sha256=26gFtMC2jdQGQF7Zb_F4Br56rMd4hUDTk9FkNZYneLo,2789
46
- smart_bot_factory/table/schema.sql,sha256=-6kOmA9QnSkUtmGI2iQRbTvbdiqOhEOQcuz1lJn79mU,28059
47
51
  smart_bot_factory/utils/__init__.py,sha256=UhsJXEHfrIK8h1AHsroHSwAriijk-LvnqLyvgzi2VYs,273
48
52
  smart_bot_factory/utils/debug_routing.py,sha256=BOoDhKBg7UXe5uHQxRk3TSfPfLPOFqt0N7lAo6kjCOo,4719
49
53
  smart_bot_factory/utils/prompt_loader.py,sha256=JSn7CsWnToSbHYtURdeuZn7ectyDqQGrPGHN2ixIGkw,19930
50
54
  smart_bot_factory/utils/user_prompt_loader.py,sha256=dk6P0X_3UcNqxjRtuIvb0LcPrp03zIIsstZwdmeCPaE,2519
51
- smart_bot_factory-0.3.2.dist-info/METADATA,sha256=alPg424Y9-vgxD2LUovu3EJJNMLlxuTqEPXukWJBw0c,31905
52
- smart_bot_factory-0.3.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
53
- smart_bot_factory-0.3.2.dist-info/entry_points.txt,sha256=ybKEAI0WSb7WoRiey7QE-HHfn88UGV7nxLDxXq7b7SU,50
54
- smart_bot_factory-0.3.2.dist-info/licenses/LICENSE,sha256=OrK3cwdUTzNzIhJvSPtJaVMoYIyC_sSx5EFE_FDMvGs,1092
55
- smart_bot_factory-0.3.2.dist-info/RECORD,,
55
+ smart_bot_factory-0.3.3.dist-info/METADATA,sha256=kyf-bct-Sf1zvA6PwlUtTOGJ17kV7q8FuqtrjvNvXRM,31949
56
+ smart_bot_factory-0.3.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
57
+ smart_bot_factory-0.3.3.dist-info/entry_points.txt,sha256=ybKEAI0WSb7WoRiey7QE-HHfn88UGV7nxLDxXq7b7SU,50
58
+ smart_bot_factory-0.3.3.dist-info/licenses/LICENSE,sha256=OrK3cwdUTzNzIhJvSPtJaVMoYIyC_sSx5EFE_FDMvGs,1092
59
+ smart_bot_factory-0.3.3.dist-info/RECORD,,