smart-bot-factory 0.3.9__py3-none-any.whl → 1.0.0__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/admin/admin_manager.py +2 -2
- smart_bot_factory/core/bot_utils.py +54 -24
- smart_bot_factory/core/conversation_manager.py +36 -13
- smart_bot_factory/core/decorators.py +38 -13
- smart_bot_factory/core/message_sender.py +3 -2
- smart_bot_factory/core/router.py +9 -0
- smart_bot_factory/event/__init__.py +1 -1
- smart_bot_factory/handlers/handlers.py +27 -27
- smart_bot_factory/integrations/supabase_client.py +38 -15
- {smart_bot_factory-0.3.9.dist-info → smart_bot_factory-1.0.0.dist-info}/METADATA +1 -1
- {smart_bot_factory-0.3.9.dist-info → smart_bot_factory-1.0.0.dist-info}/RECORD +14 -14
- {smart_bot_factory-0.3.9.dist-info → smart_bot_factory-1.0.0.dist-info}/WHEEL +0 -0
- {smart_bot_factory-0.3.9.dist-info → smart_bot_factory-1.0.0.dist-info}/entry_points.txt +0 -0
- {smart_bot_factory-0.3.9.dist-info → smart_bot_factory-1.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -122,8 +122,8 @@ class AdminManager:
|
|
|
122
122
|
|
|
123
123
|
async def notify_admins(self, message: str, exclude_admin: int = None):
|
|
124
124
|
"""Отправляет уведомление всем активным админам"""
|
|
125
|
-
from
|
|
126
|
-
|
|
125
|
+
from ..handlers.handlers import get_global_var
|
|
126
|
+
bot = get_global_var("bot")
|
|
127
127
|
|
|
128
128
|
active_admins = await self.get_active_admins()
|
|
129
129
|
|
|
@@ -245,25 +245,33 @@ async def process_events(session_id: str, events: list, user_id: int) -> bool:
|
|
|
245
245
|
f"🔍 Старые scheduled_tasks: {list(scheduled_tasks.keys())}"
|
|
246
246
|
)
|
|
247
247
|
|
|
248
|
-
# Сначала пробуем как обычное событие
|
|
248
|
+
# Сначала пробуем как обычное событие или scheduled task
|
|
249
|
+
handler_info = None
|
|
250
|
+
handler_type = None
|
|
251
|
+
|
|
249
252
|
if event_type in event_handlers:
|
|
253
|
+
handler_info = event_handlers.get(event_type, {})
|
|
254
|
+
handler_type = "event"
|
|
255
|
+
elif event_type in scheduled_tasks:
|
|
256
|
+
handler_info = scheduled_tasks.get(event_type, {})
|
|
257
|
+
handler_type = "task"
|
|
258
|
+
|
|
259
|
+
if handler_info:
|
|
250
260
|
from ..core.decorators import execute_event_handler
|
|
251
261
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
"send_ai_response", True
|
|
256
|
-
)
|
|
262
|
+
once_only = handler_info.get("once_only", True)
|
|
263
|
+
send_ai_response_flag = handler_info.get("send_ai_response", True)
|
|
264
|
+
should_notify = handler_info.get("notify", False) # Получаем notify из handler_info
|
|
257
265
|
|
|
258
266
|
logger.info(
|
|
259
|
-
f" 🔍
|
|
267
|
+
f" 🔍 {handler_type.title()} '{event_type}': once_only={once_only}, send_ai_response={send_ai_response_flag}, notify={should_notify}"
|
|
260
268
|
)
|
|
261
269
|
|
|
262
270
|
# Проверяем флаг send_ai_response ИЗ ДЕКОРАТОРА
|
|
263
271
|
if not send_ai_response_flag:
|
|
264
272
|
should_send_ai_response = False
|
|
265
273
|
logger.warning(
|
|
266
|
-
f" 🔇🔇🔇
|
|
274
|
+
f" 🔇🔇🔇 {handler_type.upper()} '{event_type}' ЗАПРЕТИЛ ОТПРАВКУ СООБЩЕНИЯ ОТ ИИ (send_ai_response=False) 🔇🔇🔇"
|
|
267
275
|
)
|
|
268
276
|
|
|
269
277
|
# Если once_only=True - проверяем в БД наличие выполненных событий
|
|
@@ -301,14 +309,21 @@ async def process_events(session_id: str, events: list, user_id: int) -> bool:
|
|
|
301
309
|
|
|
302
310
|
# Немедленно выполняем событие
|
|
303
311
|
logger.info(
|
|
304
|
-
f" 🎯 Немедленно выполняем
|
|
312
|
+
f" 🎯 Немедленно выполняем {handler_type}: '{event_type}'"
|
|
305
313
|
)
|
|
306
314
|
|
|
307
315
|
try:
|
|
308
|
-
# Выполняем
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
316
|
+
# Выполняем обработчик в зависимости от типа
|
|
317
|
+
if handler_type == "event":
|
|
318
|
+
result = await execute_event_handler(
|
|
319
|
+
event_type, user_id, event_info
|
|
320
|
+
)
|
|
321
|
+
elif handler_type == "task":
|
|
322
|
+
result = await execute_scheduled_task_from_event(
|
|
323
|
+
user_id, event_type, event_info, session_id
|
|
324
|
+
)
|
|
325
|
+
else:
|
|
326
|
+
raise ValueError(f"Неизвестный тип обработчика: {handler_type}")
|
|
312
327
|
|
|
313
328
|
# Проверяем наличие поля 'info' для дашборда
|
|
314
329
|
import json
|
|
@@ -353,14 +368,14 @@ async def process_events(session_id: str, events: list, user_id: int) -> bool:
|
|
|
353
368
|
)
|
|
354
369
|
event_id = response.data[0]["id"]
|
|
355
370
|
|
|
356
|
-
should_notify
|
|
357
|
-
|
|
371
|
+
# should_notify уже получен из handler_info выше
|
|
358
372
|
logger.info(
|
|
359
373
|
f" ✅ Событие {event_id} выполнено и сохранено как completed"
|
|
360
374
|
)
|
|
361
375
|
|
|
362
376
|
except Exception as e:
|
|
363
377
|
logger.error(f" ❌ Ошибка выполнения события: {e}")
|
|
378
|
+
|
|
364
379
|
# Сохраняем ошибку в БД
|
|
365
380
|
event_record = {
|
|
366
381
|
"event_type": event_type,
|
|
@@ -377,10 +392,15 @@ async def process_events(session_id: str, events: list, user_id: int) -> bool:
|
|
|
377
392
|
if supabase_client.bot_id:
|
|
378
393
|
event_record["bot_id"] = supabase_client.bot_id
|
|
379
394
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
395
|
+
try:
|
|
396
|
+
supabase_client.client.table("scheduled_events").insert(
|
|
397
|
+
event_record
|
|
398
|
+
).execute()
|
|
399
|
+
logger.info(f" 💾 Ошибка сохранена в БД")
|
|
400
|
+
except Exception as db_error:
|
|
401
|
+
logger.error(f" ❌ Не удалось сохранить ошибку в БД: {db_error}")
|
|
402
|
+
|
|
403
|
+
continue # Переходим к следующему событию после сохранения ошибки
|
|
384
404
|
|
|
385
405
|
# Если не user_event, пробуем как запланированную задачу
|
|
386
406
|
elif event_type in scheduled_tasks:
|
|
@@ -469,12 +489,22 @@ async def process_events(session_id: str, events: list, user_id: int) -> bool:
|
|
|
469
489
|
logger.error(f" ❌ Ошибка в обработчике/задаче: {e}")
|
|
470
490
|
logger.exception(" Стек ошибки:")
|
|
471
491
|
|
|
472
|
-
#
|
|
473
|
-
if
|
|
474
|
-
|
|
475
|
-
|
|
492
|
+
# Проверяем notify_time для scheduled_task
|
|
493
|
+
if handler_type == "task":
|
|
494
|
+
notify_time = handler_info.get("notify_time", "after")
|
|
495
|
+
# Для 'before' уведомляем сразу при создании
|
|
496
|
+
if notify_time == "before" and should_notify:
|
|
497
|
+
await notify_admins_about_event(user_id, event)
|
|
498
|
+
logger.info(" ✅ Админы уведомлены (notify_time=before)")
|
|
499
|
+
elif notify_time == "after":
|
|
500
|
+
logger.info(" ⏳ Уведомление будет отправлено после выполнения задачи (notify_time=after)")
|
|
476
501
|
else:
|
|
477
|
-
|
|
502
|
+
# Для обычных событий уведомляем сразу
|
|
503
|
+
if should_notify:
|
|
504
|
+
await notify_admins_about_event(user_id, event)
|
|
505
|
+
logger.info(" ✅ Админы уведомлены")
|
|
506
|
+
else:
|
|
507
|
+
logger.info(f" 🔕 Уведомления админам отключены для '{event_type}'")
|
|
478
508
|
|
|
479
509
|
except Exception as e:
|
|
480
510
|
logger.error(f"❌ Ошибка обработки события {event}: {e}")
|
|
@@ -72,15 +72,20 @@ class ConversationManager:
|
|
|
72
72
|
|
|
73
73
|
try:
|
|
74
74
|
# Получаем последние 5 сообщений (сортируем по убыванию и берем первые 5)
|
|
75
|
-
|
|
75
|
+
query = (
|
|
76
76
|
self.supabase.client.table("sales_messages")
|
|
77
77
|
.select("role", "content", "created_at")
|
|
78
78
|
.eq("session_id", session_id)
|
|
79
79
|
.order("created_at", desc=True)
|
|
80
80
|
.limit(5)
|
|
81
|
-
.execute()
|
|
82
81
|
)
|
|
83
82
|
|
|
83
|
+
# Добавляем фильтр по bot_id если он указан
|
|
84
|
+
if self.supabase.bot_id:
|
|
85
|
+
query = query.eq("bot_id", self.supabase.bot_id)
|
|
86
|
+
|
|
87
|
+
response = query.execute()
|
|
88
|
+
|
|
84
89
|
recent_messages = response.data if response.data else []
|
|
85
90
|
|
|
86
91
|
if not recent_messages:
|
|
@@ -122,12 +127,17 @@ class ConversationManager:
|
|
|
122
127
|
async def get_user_display_name(self, user_id: int) -> str:
|
|
123
128
|
"""Получает красивое отображение пользователя с username"""
|
|
124
129
|
try:
|
|
125
|
-
|
|
130
|
+
query = (
|
|
126
131
|
self.supabase.client.table("sales_users")
|
|
127
132
|
.select("first_name", "last_name", "username")
|
|
128
133
|
.eq("telegram_id", user_id)
|
|
129
|
-
.execute()
|
|
130
134
|
)
|
|
135
|
+
|
|
136
|
+
# Добавляем фильтр по bot_id если он указан
|
|
137
|
+
if self.supabase.bot_id:
|
|
138
|
+
query = query.eq("bot_id", self.supabase.bot_id)
|
|
139
|
+
|
|
140
|
+
response = query.execute()
|
|
131
141
|
|
|
132
142
|
if response.data:
|
|
133
143
|
user_info = response.data[0]
|
|
@@ -316,8 +326,7 @@ class ConversationManager:
|
|
|
316
326
|
|
|
317
327
|
try:
|
|
318
328
|
# Отправляем сообщение как от бота
|
|
319
|
-
|
|
320
|
-
await send_message_by_human(user_id, message.text, parse_mode=parse_mode)
|
|
329
|
+
await send_message_by_human(user_id, message.text)
|
|
321
330
|
|
|
322
331
|
# Сохраняем в БД как сообщение ассистента
|
|
323
332
|
session_info = await supabase_client.get_active_session(user_id)
|
|
@@ -406,27 +415,36 @@ class ConversationManager:
|
|
|
406
415
|
logger.info("🔍 Ищем активные диалоги админов...")
|
|
407
416
|
|
|
408
417
|
# Получаем все активные диалоги
|
|
409
|
-
|
|
418
|
+
query = (
|
|
410
419
|
self.supabase.client.table("admin_user_conversations")
|
|
411
420
|
.select("id", "admin_id", "user_id", "started_at", "auto_end_at")
|
|
412
421
|
.eq("status", "active")
|
|
413
|
-
.order("started_at", desc=True)
|
|
414
|
-
.execute()
|
|
415
422
|
)
|
|
416
423
|
|
|
424
|
+
# Добавляем фильтр по bot_id если он указан
|
|
425
|
+
if self.supabase.bot_id:
|
|
426
|
+
query = query.eq("bot_id", self.supabase.bot_id)
|
|
427
|
+
|
|
428
|
+
response = query.order("started_at", desc=True).execute()
|
|
429
|
+
|
|
417
430
|
logger.info(f"📊 Найдено {len(response.data)} активных диалогов в БД")
|
|
418
431
|
|
|
419
432
|
conversations = []
|
|
420
433
|
for conv in response.data:
|
|
421
434
|
# Получаем информацию о пользователе
|
|
422
435
|
try:
|
|
423
|
-
|
|
436
|
+
user_query = (
|
|
424
437
|
self.supabase.client.table("sales_users")
|
|
425
438
|
.select("first_name", "last_name", "username")
|
|
426
439
|
.eq("telegram_id", conv["user_id"])
|
|
427
|
-
.execute()
|
|
428
440
|
)
|
|
429
441
|
|
|
442
|
+
# Добавляем фильтр по bot_id если он указан
|
|
443
|
+
if self.supabase.bot_id:
|
|
444
|
+
user_query = user_query.eq("bot_id", self.supabase.bot_id)
|
|
445
|
+
|
|
446
|
+
user_response = user_query.execute()
|
|
447
|
+
|
|
430
448
|
user_info = user_response.data[0] if user_response.data else {}
|
|
431
449
|
except Exception as e:
|
|
432
450
|
logger.error(
|
|
@@ -436,13 +454,18 @@ class ConversationManager:
|
|
|
436
454
|
|
|
437
455
|
# Получаем информацию об админе
|
|
438
456
|
try:
|
|
439
|
-
|
|
457
|
+
admin_query = (
|
|
440
458
|
self.supabase.client.table("sales_admins")
|
|
441
459
|
.select("first_name", "last_name", "username")
|
|
442
460
|
.eq("telegram_id", conv["admin_id"])
|
|
443
|
-
.execute()
|
|
444
461
|
)
|
|
445
462
|
|
|
463
|
+
# Добавляем фильтр по bot_id если он указан
|
|
464
|
+
if self.supabase.bot_id:
|
|
465
|
+
admin_query = admin_query.eq("bot_id", self.supabase.bot_id)
|
|
466
|
+
|
|
467
|
+
admin_response = admin_query.execute()
|
|
468
|
+
|
|
446
469
|
admin_info = admin_response.data[0] if admin_response.data else {}
|
|
447
470
|
except Exception as e:
|
|
448
471
|
logger.error(
|
|
@@ -7,7 +7,7 @@ import logging
|
|
|
7
7
|
import re
|
|
8
8
|
from datetime import datetime, timedelta, timezone
|
|
9
9
|
from functools import wraps
|
|
10
|
-
from typing import Any, Callable, Dict, Union
|
|
10
|
+
from typing import Any, Callable, Dict, Optional, Union
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger(__name__)
|
|
13
13
|
|
|
@@ -476,6 +476,7 @@ def event_handler(
|
|
|
476
476
|
def schedule_task(
|
|
477
477
|
task_name: str,
|
|
478
478
|
notify: bool = False,
|
|
479
|
+
notify_time: str = "after", # 'after' или 'before'
|
|
479
480
|
smart_check: bool = True,
|
|
480
481
|
once_only: bool = True,
|
|
481
482
|
delay: Union[str, int] = None,
|
|
@@ -1885,7 +1886,27 @@ async def process_scheduled_event(event: Dict):
|
|
|
1885
1886
|
|
|
1886
1887
|
result = None
|
|
1887
1888
|
if event_category == "scheduled_task":
|
|
1889
|
+
# Получаем информацию о задаче
|
|
1890
|
+
router_manager = get_router_manager()
|
|
1891
|
+
if router_manager:
|
|
1892
|
+
scheduled_tasks = router_manager.get_scheduled_tasks()
|
|
1893
|
+
else:
|
|
1894
|
+
scheduled_tasks = _scheduled_tasks
|
|
1895
|
+
|
|
1896
|
+
task_info = scheduled_tasks.get(event_type, {})
|
|
1897
|
+
notify = task_info.get("notify", False)
|
|
1898
|
+
notify_time = task_info.get("notify_time", "after")
|
|
1899
|
+
|
|
1900
|
+
# Выполняем задачу
|
|
1888
1901
|
result = await execute_scheduled_task(event_type, user_id, event_data)
|
|
1902
|
+
|
|
1903
|
+
# Отправляем уведомление после выполнения если notify=True и notify_time="after"
|
|
1904
|
+
if notify and notify_time == "after":
|
|
1905
|
+
from ..core.bot_utils import notify_admins_about_event
|
|
1906
|
+
event_for_notify = {"тип": event_type, "инфо": event_data}
|
|
1907
|
+
await notify_admins_about_event(user_id, event_for_notify)
|
|
1908
|
+
logger.info(f" ✅ Админы уведомлены после выполнения задачи '{event_type}'")
|
|
1909
|
+
|
|
1889
1910
|
elif event_category == "global_handler":
|
|
1890
1911
|
result = await execute_global_handler(event_type, event_data)
|
|
1891
1912
|
elif event_category == "user_event":
|
|
@@ -2155,12 +2176,13 @@ async def check_event_already_processed(
|
|
|
2155
2176
|
return False
|
|
2156
2177
|
|
|
2157
2178
|
|
|
2158
|
-
async def process_admin_event(event: Dict):
|
|
2179
|
+
async def process_admin_event(event: Dict, single_user_id: Optional[int] = None):
|
|
2159
2180
|
"""
|
|
2160
2181
|
Обрабатывает одно админское событие - скачивает файлы из Storage и отправляет пользователям
|
|
2161
2182
|
|
|
2162
2183
|
Args:
|
|
2163
2184
|
event: Событие из БД с данными для отправки
|
|
2185
|
+
single_user_id: ID пользователя для тестовой отправки. Если указан, сообщение будет отправлено только ему
|
|
2164
2186
|
"""
|
|
2165
2187
|
import json
|
|
2166
2188
|
import shutil
|
|
@@ -2244,17 +2266,20 @@ async def process_admin_event(event: Dict):
|
|
|
2244
2266
|
raise
|
|
2245
2267
|
|
|
2246
2268
|
# 2. Получаем пользователей
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
"
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2269
|
+
if single_user_id:
|
|
2270
|
+
users = [{"telegram_id": single_user_id}]
|
|
2271
|
+
logger.info(f"🔍 Тестовая отправка для пользователя {single_user_id}")
|
|
2272
|
+
else:
|
|
2273
|
+
users = await supabase_client.get_users_by_segment(segment)
|
|
2274
|
+
if not users:
|
|
2275
|
+
logger.warning(f"⚠️ Нет пользователей для сегмента '{segment}'")
|
|
2276
|
+
return {
|
|
2277
|
+
"success_count": 0,
|
|
2278
|
+
"failed_count": 0,
|
|
2279
|
+
"total_users": 0,
|
|
2280
|
+
"segment": segment or "Все",
|
|
2281
|
+
"warning": "Нет пользователей",
|
|
2282
|
+
}
|
|
2258
2283
|
|
|
2259
2284
|
success_count = 0
|
|
2260
2285
|
failed_count = 0
|
|
@@ -7,6 +7,7 @@ import time
|
|
|
7
7
|
from datetime import datetime
|
|
8
8
|
from typing import Any, Dict, Optional
|
|
9
9
|
|
|
10
|
+
from aiogram.types import InlineKeyboardMarkup
|
|
10
11
|
import pytz
|
|
11
12
|
|
|
12
13
|
logger = logging.getLogger(__name__)
|
|
@@ -193,7 +194,7 @@ async def send_message_by_ai(
|
|
|
193
194
|
|
|
194
195
|
|
|
195
196
|
async def send_message_by_human(
|
|
196
|
-
user_id: int, message_text: str, session_id: Optional[str] = None
|
|
197
|
+
user_id: int, message_text: str, session_id: Optional[str] = None, parse_mode: str = "Markdown", reply_markup: Optional[InlineKeyboardMarkup] = None
|
|
197
198
|
) -> Dict[str, Any]:
|
|
198
199
|
"""
|
|
199
200
|
Отправляет сообщение пользователю от имени человека (готовый текст)
|
|
@@ -214,7 +215,7 @@ async def send_message_by_human(
|
|
|
214
215
|
supabase_client = get_global_var("supabase_client")
|
|
215
216
|
|
|
216
217
|
# Отправляем сообщение пользователю
|
|
217
|
-
message = await bot.send_message(chat_id=user_id, text=message_text)
|
|
218
|
+
message = await bot.send_message(chat_id=user_id, text=message_text, parse_mode=parse_mode, reply_markup=reply_markup)
|
|
218
219
|
|
|
219
220
|
# Если указана сессия, сохраняем сообщение в БД
|
|
220
221
|
if session_id:
|
smart_bot_factory/core/router.py
CHANGED
|
@@ -93,6 +93,7 @@ class EventRouter:
|
|
|
93
93
|
self,
|
|
94
94
|
task_name: str,
|
|
95
95
|
notify: bool = False,
|
|
96
|
+
notify_time: str = "after", # 'after' или 'before'
|
|
96
97
|
smart_check: bool = True,
|
|
97
98
|
once_only: bool = True,
|
|
98
99
|
delay: Union[str, int] = None,
|
|
@@ -105,6 +106,9 @@ class EventRouter:
|
|
|
105
106
|
Args:
|
|
106
107
|
task_name: Название задачи
|
|
107
108
|
notify: Уведомлять ли админов
|
|
109
|
+
notify_time: Когда отправлять уведомление админам:
|
|
110
|
+
- 'before': при создании задачи
|
|
111
|
+
- 'after': после успешного выполнения (по умолчанию)
|
|
108
112
|
smart_check: Использовать ли умную проверку
|
|
109
113
|
once_only: Выполнять ли только один раз
|
|
110
114
|
delay: Время задержки в удобном формате (например, "1h 30m", "45m", 3600) - ОБЯЗАТЕЛЬНО
|
|
@@ -141,10 +145,15 @@ class EventRouter:
|
|
|
141
145
|
)
|
|
142
146
|
raise
|
|
143
147
|
|
|
148
|
+
# Проверяем корректность notify_time
|
|
149
|
+
if notify_time not in ["before", "after"]:
|
|
150
|
+
raise ValueError(f"notify_time должен быть 'before' или 'after', получено: {notify_time}")
|
|
151
|
+
|
|
144
152
|
self._scheduled_tasks[task_name] = {
|
|
145
153
|
"handler": func,
|
|
146
154
|
"name": func.__name__,
|
|
147
155
|
"notify": notify,
|
|
156
|
+
"notify_time": notify_time, # Когда отправлять уведомление
|
|
148
157
|
"smart_check": smart_check,
|
|
149
158
|
"once_only": once_only,
|
|
150
159
|
"router": self.name,
|
|
@@ -79,7 +79,7 @@ async def timeup_handler(message: Message, state: FSMContext):
|
|
|
79
79
|
"""Обработчик команды /timeup (или /вперед) - тестирование запланированных событий"""
|
|
80
80
|
from datetime import datetime
|
|
81
81
|
|
|
82
|
-
from ..core.decorators import process_scheduled_event, update_event_result
|
|
82
|
+
from ..core.decorators import process_scheduled_event, update_event_result, process_admin_event
|
|
83
83
|
|
|
84
84
|
supabase_client = get_global_var("supabase_client")
|
|
85
85
|
|
|
@@ -95,9 +95,7 @@ async def timeup_handler(message: Message, state: FSMContext):
|
|
|
95
95
|
.in_("status", ["pending", "immediate"])
|
|
96
96
|
)
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
if supabase_client.bot_id:
|
|
100
|
-
user_events_query = user_events_query.eq("bot_id", supabase_client.bot_id)
|
|
98
|
+
user_events_query = user_events_query.eq("bot_id", supabase_client.bot_id)
|
|
101
99
|
|
|
102
100
|
user_events = user_events_query.execute()
|
|
103
101
|
|
|
@@ -107,14 +105,9 @@ async def timeup_handler(message: Message, state: FSMContext):
|
|
|
107
105
|
.select("*")
|
|
108
106
|
.is_("user_id", "null")
|
|
109
107
|
.in_("status", ["pending", "immediate"])
|
|
108
|
+
.eq("bot_id", supabase_client.bot_id)
|
|
110
109
|
)
|
|
111
110
|
|
|
112
|
-
# 🆕 Фильтруем по bot_id если указан
|
|
113
|
-
if supabase_client.bot_id:
|
|
114
|
-
global_events_query = global_events_query.eq(
|
|
115
|
-
"bot_id", supabase_client.bot_id
|
|
116
|
-
)
|
|
117
|
-
|
|
118
111
|
global_events = global_events_query.execute()
|
|
119
112
|
|
|
120
113
|
# Объединяем события
|
|
@@ -155,23 +148,30 @@ async def timeup_handler(message: Message, state: FSMContext):
|
|
|
155
148
|
)
|
|
156
149
|
|
|
157
150
|
# Выполняем событие
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
"
|
|
164
|
-
{
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
151
|
+
if event_category == "admin_event":
|
|
152
|
+
# Для админских событий используем тестовую отправку только текущему пользователю
|
|
153
|
+
await process_admin_event(event, single_user_id=message.from_user.id)
|
|
154
|
+
# Не отмечаем админское событие как выполненное при тестовой отправке
|
|
155
|
+
success_count += 1
|
|
156
|
+
results.append(f"✅ {event_label} (тестовая отправка)")
|
|
157
|
+
logger.info(f"✅ Событие {event_id} протестировано для пользователя {message.from_user.id}")
|
|
158
|
+
else:
|
|
159
|
+
await process_scheduled_event(event)
|
|
160
|
+
# Помечаем как выполненное только не-админские события
|
|
161
|
+
if event_category != "global_handler":
|
|
162
|
+
await update_event_result(
|
|
163
|
+
event_id,
|
|
164
|
+
"completed",
|
|
165
|
+
{
|
|
166
|
+
"executed": True,
|
|
167
|
+
"test_mode": True,
|
|
168
|
+
"tested_by_user": message.from_user.id,
|
|
169
|
+
"tested_at": datetime.now().isoformat(),
|
|
170
|
+
},
|
|
171
|
+
)
|
|
172
|
+
success_count += 1
|
|
173
|
+
results.append(f"✅ {event_label}")
|
|
174
|
+
logger.info(f"✅ Событие {event_id} успешно выполнено")
|
|
175
175
|
|
|
176
176
|
except Exception as e:
|
|
177
177
|
failed_count += 1
|
|
@@ -570,22 +570,37 @@ class SupabaseClient:
|
|
|
570
570
|
) -> int:
|
|
571
571
|
"""Начинает диалог между админом и пользователем"""
|
|
572
572
|
try:
|
|
573
|
+
# Проверяем существование пользователя с правильным bot_id
|
|
574
|
+
user_query = self.client.table("sales_users").select("telegram_id").eq("telegram_id", user_id)
|
|
575
|
+
if self.bot_id:
|
|
576
|
+
user_query = user_query.eq("bot_id", self.bot_id)
|
|
577
|
+
|
|
578
|
+
user_response = user_query.execute()
|
|
579
|
+
if not user_response.data:
|
|
580
|
+
logger.error(f"❌ Пользователь {user_id} не найден в sales_users{f' для bot_id {self.bot_id}' if self.bot_id else ''}")
|
|
581
|
+
raise APIError("User not found in sales_users")
|
|
582
|
+
|
|
573
583
|
# Завершаем активные диалоги этого админа
|
|
574
584
|
await self.end_admin_conversations(admin_id)
|
|
575
585
|
|
|
586
|
+
# Готовим данные для записи
|
|
587
|
+
conversation_data = {
|
|
588
|
+
"admin_id": admin_id,
|
|
589
|
+
"user_id": user_id,
|
|
590
|
+
"session_id": session_id,
|
|
591
|
+
"status": "active",
|
|
592
|
+
"auto_end_at": (
|
|
593
|
+
datetime.now(timezone.utc) + timedelta(minutes=30)
|
|
594
|
+
).isoformat(),
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
# Добавляем bot_id если он указан
|
|
598
|
+
if self.bot_id:
|
|
599
|
+
conversation_data["bot_id"] = self.bot_id
|
|
600
|
+
|
|
576
601
|
response = (
|
|
577
602
|
self.client.table("admin_user_conversations")
|
|
578
|
-
.insert(
|
|
579
|
-
{
|
|
580
|
-
"admin_id": admin_id,
|
|
581
|
-
"user_id": user_id,
|
|
582
|
-
"session_id": session_id,
|
|
583
|
-
"status": "active",
|
|
584
|
-
"auto_end_at": (
|
|
585
|
-
datetime.now(timezone.utc) + timedelta(minutes=30)
|
|
586
|
-
).isoformat(),
|
|
587
|
-
}
|
|
588
|
-
)
|
|
603
|
+
.insert(conversation_data)
|
|
589
604
|
.execute()
|
|
590
605
|
)
|
|
591
606
|
|
|
@@ -608,7 +623,7 @@ class SupabaseClient:
|
|
|
608
623
|
self.client.table("admin_user_conversations")
|
|
609
624
|
.update(
|
|
610
625
|
{
|
|
611
|
-
"status": "
|
|
626
|
+
"status": "completed", # Используем 'completed' вместо 'ended'
|
|
612
627
|
"ended_at": datetime.now(timezone.utc).isoformat(),
|
|
613
628
|
}
|
|
614
629
|
)
|
|
@@ -619,6 +634,9 @@ class SupabaseClient:
|
|
|
619
634
|
query = query.eq("admin_id", admin_id)
|
|
620
635
|
if user_id:
|
|
621
636
|
query = query.eq("user_id", user_id)
|
|
637
|
+
# Добавляем фильтр по bot_id если он указан
|
|
638
|
+
if self.bot_id:
|
|
639
|
+
query = query.eq("bot_id", self.bot_id)
|
|
622
640
|
|
|
623
641
|
response = query.execute()
|
|
624
642
|
ended_count = len(response.data)
|
|
@@ -1193,15 +1211,20 @@ class SupabaseClient:
|
|
|
1193
1211
|
"""Проверяет, изменился ли этап пользователя с момента планирования события"""
|
|
1194
1212
|
try:
|
|
1195
1213
|
# Получаем текущую информацию о сессии
|
|
1196
|
-
|
|
1214
|
+
query = (
|
|
1197
1215
|
self.client.table("sales_chat_sessions")
|
|
1198
1216
|
.select("id", "current_stage")
|
|
1199
|
-
.eq("
|
|
1217
|
+
.eq("user_id", user_id)
|
|
1200
1218
|
.order("created_at", desc=True)
|
|
1201
1219
|
.limit(1)
|
|
1202
|
-
.execute()
|
|
1203
1220
|
)
|
|
1204
1221
|
|
|
1222
|
+
# Добавляем фильтр по bot_id если он указан
|
|
1223
|
+
if self.bot_id:
|
|
1224
|
+
query = query.eq("bot_id", self.bot_id)
|
|
1225
|
+
|
|
1226
|
+
current_response = query.execute()
|
|
1227
|
+
|
|
1205
1228
|
if not current_response.data:
|
|
1206
1229
|
return False
|
|
1207
1230
|
|
|
@@ -6,7 +6,7 @@ smart_bot_factory/utm_link_generator.py,sha256=eA3Eic4Wvs0QpMdAFPJgYJyzv-_pSQWZ5
|
|
|
6
6
|
smart_bot_factory/admin/__init__.py,sha256=xHUtMLeQDDQEXZrg4WAGpt-TWp8adjCC0-mYUBuino4,489
|
|
7
7
|
smart_bot_factory/admin/admin_events.py,sha256=FkiD0FtnlgpxLCoN9KXR61VCn8OsI0JCWm9RjVVa6tk,43903
|
|
8
8
|
smart_bot_factory/admin/admin_logic.py,sha256=TiXhZIXiXK1YQkrueY_zPFYL3_sJzoAvd100AP-ZCWU,23136
|
|
9
|
-
smart_bot_factory/admin/admin_manager.py,sha256=
|
|
9
|
+
smart_bot_factory/admin/admin_manager.py,sha256=u22j1xy2zLTw0kejgzWF5m657_IyMDKnDlM9kBzbjh4,6359
|
|
10
10
|
smart_bot_factory/admin/admin_tester.py,sha256=YwovS51chPJoQ33pIiug-vS3augQzc1hHfbma48HtDk,6352
|
|
11
11
|
smart_bot_factory/admin/timeout_checker.py,sha256=afWsg39X6M5SaRKMk7ptNod8Z0StIa2o8H5Lx8y1ZE0,24438
|
|
12
12
|
smart_bot_factory/aiogram_calendar/__init__.py,sha256=UCoae3mXXDgYqqSQPotEpUxzAsZ_e62FuW9MWl1sG34,410
|
|
@@ -29,21 +29,21 @@ smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml,sh
|
|
|
29
29
|
smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml,sha256=bzDulOU4a2LyWlcHzlQU8GYhOky2WTfyizGfjX4ioMY,2436
|
|
30
30
|
smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt,sha256=Db21Mm0r8SBWFdX9EeIF2FZtLQ2cvuwVlSRJd2KEYCg,922
|
|
31
31
|
smart_bot_factory/configs/growthmed-october-24/welcome_file/Чек лист по 152ФЗ и 323ФЗ для медицины.pdf,sha256=BiAiQHNnQXJPMsks9AeL6s0beEjRFkRMJLMlAn4WorA,5284954
|
|
32
|
-
smart_bot_factory/core/bot_utils.py,sha256=
|
|
33
|
-
smart_bot_factory/core/conversation_manager.py,sha256=
|
|
34
|
-
smart_bot_factory/core/decorators.py,sha256=
|
|
35
|
-
smart_bot_factory/core/message_sender.py,sha256=
|
|
36
|
-
smart_bot_factory/core/router.py,sha256=
|
|
32
|
+
smart_bot_factory/core/bot_utils.py,sha256=vbWb4wUkG-FlQIGTQ6RLoSSVg_LRVTzpnX3tyowsbqY,52032
|
|
33
|
+
smart_bot_factory/core/conversation_manager.py,sha256=ga0ThoDpvov97x5KLcHpL8aRlEZB1XRw3JPrexIb-jk,29506
|
|
34
|
+
smart_bot_factory/core/decorators.py,sha256=9TjB9mTp2fvopSgFOJOsQ326K220QHK87ZPoNa5BV_g,108733
|
|
35
|
+
smart_bot_factory/core/message_sender.py,sha256=gK0HbjLumAu5_Vr29DnOQkwbPGGmQrtkgW1dVt_9Bb8,32518
|
|
36
|
+
smart_bot_factory/core/router.py,sha256=66sCp7Okyu1ceIQPpxFF88-NizTD1N7rR4ZAdwDYmi8,16285
|
|
37
37
|
smart_bot_factory/core/router_manager.py,sha256=x-cc4zhZUVPCTUH980GdlE_-JjHRtYnPdEBiWnf3ka8,10156
|
|
38
38
|
smart_bot_factory/core/states.py,sha256=mFyQ-oxIjTX03Wy5NHZUgYgh8fsJ5YmQU09_PhPAB04,889
|
|
39
39
|
smart_bot_factory/creation/__init__.py,sha256=lHFgYIjOLvdsDAW8eLMp8zfFUCd-6wepvUaUMeJ7feU,128
|
|
40
40
|
smart_bot_factory/creation/bot_builder.py,sha256=-gF53eCaWBRKoUbRGR-PDHpqLWlceQ9FLUdEqQRt6lw,42929
|
|
41
41
|
smart_bot_factory/creation/bot_testing.py,sha256=aMPSgj2o2zl6yWiV1a6RMmSJfiwMYp-_0ZkmJ6aFZVs,54561
|
|
42
42
|
smart_bot_factory/dashboard/__init__.py,sha256=Qie1pJgzprBlCPrZVQLhVs1JR5-YrpzfcBvHHD3OLJk,94
|
|
43
|
-
smart_bot_factory/event/__init__.py,sha256=
|
|
44
|
-
smart_bot_factory/handlers/handlers.py,sha256=
|
|
43
|
+
smart_bot_factory/event/__init__.py,sha256=eWKoiZ_DD-jTL9Tjv11ucQat8P8AhP3xWmQs29pd4ZA,190
|
|
44
|
+
smart_bot_factory/handlers/handlers.py,sha256=F7qgSrz_8kXDvolYmJPofF06PZRucsA6lI11iQCjbzk,64372
|
|
45
45
|
smart_bot_factory/integrations/openai_client.py,sha256=R04qglOWNu3yQ32QRMRMzO01T6luWTp83H3OJMvD-uM,24316
|
|
46
|
-
smart_bot_factory/integrations/supabase_client.py,sha256=
|
|
46
|
+
smart_bot_factory/integrations/supabase_client.py,sha256=7Yip9din4PDqFD1X2pkKB4KYAhmf04LUzxo9SnOrVg4,68415
|
|
47
47
|
smart_bot_factory/message/__init__.py,sha256=neUUYAs8ITz3cV1B_T0P9_3XZvvn8cZ2VDpDqrf6lrc,1940
|
|
48
48
|
smart_bot_factory/router/__init__.py,sha256=ywywG6iFBLLkBvtZ-huPVetqjXGN4UiUzoK6lw46bMg,272
|
|
49
49
|
smart_bot_factory/supabase/__init__.py,sha256=xa1xmHLaQVoghjD5cqguiL4yd25VYMvGd6_e87uc0tQ,181
|
|
@@ -52,8 +52,8 @@ smart_bot_factory/utils/__init__.py,sha256=RgP2IAMFsJF7fxhhg8JLfhbGg9mO63xQM-NEa
|
|
|
52
52
|
smart_bot_factory/utils/debug_routing.py,sha256=wV9BFwNmjBbF3rNI7UBdGsTf1bIT5XVQIGnxwIxhNYA,4707
|
|
53
53
|
smart_bot_factory/utils/prompt_loader.py,sha256=YClbQjvRgTWCD42Rr92g77zzVEVMUgrqStI6YETC0c4,20022
|
|
54
54
|
smart_bot_factory/utils/user_prompt_loader.py,sha256=lq1eQ4Hb2qN22osOjaFtkGdEc4OgpFPrzPNpPhsm5kA,2353
|
|
55
|
-
smart_bot_factory-0.
|
|
56
|
-
smart_bot_factory-0.
|
|
57
|
-
smart_bot_factory-0.
|
|
58
|
-
smart_bot_factory-0.
|
|
59
|
-
smart_bot_factory-0.
|
|
55
|
+
smart_bot_factory-1.0.0.dist-info/METADATA,sha256=9k7A5Gl3rfnCS-i9HJp2ZNXBMJCbLDOu8hlGobF0nM8,40748
|
|
56
|
+
smart_bot_factory-1.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
57
|
+
smart_bot_factory-1.0.0.dist-info/entry_points.txt,sha256=ybKEAI0WSb7WoRiey7QE-HHfn88UGV7nxLDxXq7b7SU,50
|
|
58
|
+
smart_bot_factory-1.0.0.dist-info/licenses/LICENSE,sha256=OrK3cwdUTzNzIhJvSPtJaVMoYIyC_sSx5EFE_FDMvGs,1092
|
|
59
|
+
smart_bot_factory-1.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|