smart-bot-factory 0.1.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/__init__.py +30 -0
- smart_bot_factory/cli.py +624 -0
- smart_bot_factory/core/__init__.py +7 -0
- smart_bot_factory/core/bot_builder.py +337 -0
- smart_bot_factory/events/__init__.py +27 -0
- smart_bot_factory/events/decorators.py +229 -0
- smart_bot_factory/integrations/__init__.py +83 -0
- smart_bot_factory/integrations/bot_utils_integration.py +224 -0
- smart_bot_factory/services/__init__.py +12 -0
- smart_bot_factory/services/message_sender.py +235 -0
- smart_bot_factory/services/telegram_integration.py +246 -0
- smart_bot_factory/tools/__init__.py +19 -0
- smart_bot_factory/tools/decorators.py +140 -0
- smart_bot_factory-0.1.0.dist-info/METADATA +125 -0
- smart_bot_factory-0.1.0.dist-info/RECORD +18 -0
- smart_bot_factory-0.1.0.dist-info/WHEEL +4 -0
- smart_bot_factory-0.1.0.dist-info/entry_points.txt +2 -0
- smart_bot_factory-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Интеграция с bot_utils.py для обработки событий
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import List, Dict, Any
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
async def enhanced_process_events(session_id: str, events: List[Dict[str, Any]], user_id: int):
|
|
11
|
+
"""
|
|
12
|
+
Улучшенная версия process_events из bot_utils.py
|
|
13
|
+
с поддержкой декораторов обработчиков событий
|
|
14
|
+
"""
|
|
15
|
+
from ..events.decorators import (
|
|
16
|
+
execute_event_handler,
|
|
17
|
+
get_event_handlers,
|
|
18
|
+
get_scheduled_tasks,
|
|
19
|
+
execute_scheduled_task_from_event
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
logger.info(f"🔔 Обрабатываем {len(events)} событий для пользователя {user_id}")
|
|
23
|
+
|
|
24
|
+
for event in events:
|
|
25
|
+
try:
|
|
26
|
+
event_type = event.get('тип', '')
|
|
27
|
+
event_info = event.get('инфо', '')
|
|
28
|
+
|
|
29
|
+
if not event_type:
|
|
30
|
+
continue
|
|
31
|
+
|
|
32
|
+
logger.info(f"📝 Обрабатываем событие: {event_type}")
|
|
33
|
+
|
|
34
|
+
# Проверяем, есть ли зарегистрированный обработчик событий
|
|
35
|
+
event_handlers = get_event_handlers()
|
|
36
|
+
scheduled_tasks = get_scheduled_tasks()
|
|
37
|
+
|
|
38
|
+
if event_type in event_handlers:
|
|
39
|
+
# Используем зарегистрированный обработчик событий
|
|
40
|
+
logger.info(f"🔧 Используем зарегистрированный обработчик для '{event_type}'")
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
# Парсим event_info если это строка
|
|
44
|
+
if isinstance(event_info, str):
|
|
45
|
+
event_data = _parse_event_info(event_info, user_id)
|
|
46
|
+
else:
|
|
47
|
+
event_data = event_info
|
|
48
|
+
|
|
49
|
+
# Вызываем обработчик
|
|
50
|
+
result = await execute_event_handler(event_type, user_id, event_data)
|
|
51
|
+
logger.info(f"✅ Обработчик '{event_type}' выполнен: {result}")
|
|
52
|
+
|
|
53
|
+
except Exception as e:
|
|
54
|
+
logger.error(f"❌ Ошибка в обработчике '{event_type}': {e}")
|
|
55
|
+
# Fallback к стандартной обработке
|
|
56
|
+
await _fallback_event_processing(session_id, event_type, event_info, user_id)
|
|
57
|
+
|
|
58
|
+
elif event_type in scheduled_tasks:
|
|
59
|
+
# Это запланированная задача - парсим время из event_info
|
|
60
|
+
logger.info(f"⏰ Планируем задачу '{event_type}' на основе event_info: {event_info}")
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
result = await execute_scheduled_task_from_event(user_id, event_type, event_info)
|
|
64
|
+
logger.info(f"✅ Задача '{event_type}' запланирована: {result}")
|
|
65
|
+
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.error(f"❌ Ошибка в планировании задачи '{event_type}': {e}")
|
|
68
|
+
# Fallback к стандартной обработке
|
|
69
|
+
await _fallback_event_processing(session_id, event_type, event_info, user_id)
|
|
70
|
+
else:
|
|
71
|
+
# Используем стандартную обработку из bot_utils.py
|
|
72
|
+
logger.info(f"📋 Используем стандартную обработку для '{event_type}'")
|
|
73
|
+
await _fallback_event_processing(session_id, event_type, event_info, user_id)
|
|
74
|
+
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.error(f"❌ Ошибка при обработке события {event}: {e}")
|
|
77
|
+
|
|
78
|
+
def _parse_event_info(event_info: str, user_id: int) -> Dict[str, Any]:
|
|
79
|
+
"""
|
|
80
|
+
Парсит строку event_info и извлекает структурированные данные
|
|
81
|
+
"""
|
|
82
|
+
# Простой парсинг для типичных случаев
|
|
83
|
+
# Например: "Иван Петров, +7-999-123-45-67, интересуется имплантацией"
|
|
84
|
+
|
|
85
|
+
data = {
|
|
86
|
+
"user_id": user_id,
|
|
87
|
+
"raw_info": event_info
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Пытаемся извлечь телефон
|
|
91
|
+
import re
|
|
92
|
+
phone_match = re.search(r'\+?[7-8]?[\s\-]?\(?\d{3}\)?[\s\-]?\d{3}[\s\-]?\d{2}[\s\-]?\d{2}', event_info)
|
|
93
|
+
if phone_match:
|
|
94
|
+
data["phone"] = phone_match.group(0)
|
|
95
|
+
|
|
96
|
+
# Пытаемся извлечь имя (первое слово)
|
|
97
|
+
words = event_info.split(',')
|
|
98
|
+
if words:
|
|
99
|
+
data["name"] = words[0].strip()
|
|
100
|
+
|
|
101
|
+
# Пытаемся извлечь услугу/интерес
|
|
102
|
+
if len(words) > 2:
|
|
103
|
+
data["interest"] = words[2].strip()
|
|
104
|
+
|
|
105
|
+
return data
|
|
106
|
+
|
|
107
|
+
async def _fallback_event_processing(session_id: str, event_type: str, event_info: str, user_id: int):
|
|
108
|
+
"""
|
|
109
|
+
Стандартная обработка событий (из оригинального bot_utils.py)
|
|
110
|
+
"""
|
|
111
|
+
# Импортируем оригинальную функцию
|
|
112
|
+
import sys
|
|
113
|
+
from pathlib import Path
|
|
114
|
+
|
|
115
|
+
# Добавляем корень проекта в путь
|
|
116
|
+
project_root = Path(__file__).parent.parent.parent.parent
|
|
117
|
+
if str(project_root) not in sys.path:
|
|
118
|
+
sys.path.insert(0, str(project_root))
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
from supabase_client import SupabaseClient
|
|
122
|
+
from main import supabase_client
|
|
123
|
+
|
|
124
|
+
# Сохраняем в БД
|
|
125
|
+
await supabase_client.add_session_event(session_id, event_type, event_info)
|
|
126
|
+
|
|
127
|
+
# Уведомляем админов
|
|
128
|
+
await _notify_admins_about_event(user_id, {
|
|
129
|
+
'тип': event_type,
|
|
130
|
+
'инфо': event_info
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
except ImportError:
|
|
134
|
+
logger.warning("Не удалось импортировать оригинальные модули для fallback обработки")
|
|
135
|
+
|
|
136
|
+
async def _notify_admins_about_event(user_id: int, event: Dict[str, Any]):
|
|
137
|
+
"""
|
|
138
|
+
Уведомление админов о событии (из оригинального bot_utils.py)
|
|
139
|
+
"""
|
|
140
|
+
try:
|
|
141
|
+
import sys
|
|
142
|
+
from pathlib import Path
|
|
143
|
+
|
|
144
|
+
# Добавляем корень проекта в путь
|
|
145
|
+
project_root = Path(__file__).parent.parent.parent.parent
|
|
146
|
+
if str(project_root) not in sys.path:
|
|
147
|
+
sys.path.insert(0, str(project_root))
|
|
148
|
+
|
|
149
|
+
from main import supabase_client, admin_manager, bot
|
|
150
|
+
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
|
151
|
+
from datetime import datetime
|
|
152
|
+
|
|
153
|
+
event_type = event.get('тип', '')
|
|
154
|
+
event_info = event.get('инфо', '')
|
|
155
|
+
|
|
156
|
+
if not event_type:
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
# Получаем информацию о пользователе
|
|
160
|
+
try:
|
|
161
|
+
user_response = supabase_client.client.table('sales_users').select(
|
|
162
|
+
'first_name', 'last_name', 'username'
|
|
163
|
+
).eq('telegram_id', user_id).execute()
|
|
164
|
+
|
|
165
|
+
user_info = user_response.data[0] if user_response.data else {}
|
|
166
|
+
|
|
167
|
+
# Формируем имя пользователя
|
|
168
|
+
name_parts = []
|
|
169
|
+
if user_info.get('first_name'):
|
|
170
|
+
name_parts.append(user_info['first_name'])
|
|
171
|
+
if user_info.get('last_name'):
|
|
172
|
+
name_parts.append(user_info['last_name'])
|
|
173
|
+
|
|
174
|
+
user_name = " ".join(name_parts) if name_parts else "Без имени"
|
|
175
|
+
|
|
176
|
+
# Формируем отображение пользователя
|
|
177
|
+
if user_info.get('username'):
|
|
178
|
+
user_display = f"{user_name} (@{user_info['username']})"
|
|
179
|
+
else:
|
|
180
|
+
user_display = user_name
|
|
181
|
+
|
|
182
|
+
except Exception as e:
|
|
183
|
+
logger.error(f"Ошибка получения информации о пользователе {user_id}: {e}")
|
|
184
|
+
user_display = "Пользователь"
|
|
185
|
+
|
|
186
|
+
# Маппинг эмодзи
|
|
187
|
+
emoji_map = {
|
|
188
|
+
'телефон': '📱',
|
|
189
|
+
'консультация': '💬',
|
|
190
|
+
'покупка': '💰',
|
|
191
|
+
'отказ': '❌',
|
|
192
|
+
'appointment_booking': '📅',
|
|
193
|
+
'phone_collection': '📱'
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
emoji = emoji_map.get(event_type, '🔔')
|
|
197
|
+
|
|
198
|
+
# Формируем уведомление
|
|
199
|
+
notification = f"""
|
|
200
|
+
{emoji} {event_type.upper()}!
|
|
201
|
+
👤 {user_display}
|
|
202
|
+
🆔 ID: {user_id}
|
|
203
|
+
📝 {event_info}
|
|
204
|
+
🕐 {datetime.now().strftime('%H:%M')}
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
# Создаем клавиатуру
|
|
208
|
+
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
|
209
|
+
[
|
|
210
|
+
InlineKeyboardButton(text="💬 Чат", callback_data=f"admin_chat_{user_id}"),
|
|
211
|
+
InlineKeyboardButton(text="📋 История", callback_data=f"admin_history_{user_id}")
|
|
212
|
+
]
|
|
213
|
+
])
|
|
214
|
+
|
|
215
|
+
# Отправляем всем активным админам
|
|
216
|
+
active_admins = await admin_manager.get_active_admins()
|
|
217
|
+
for admin_id in active_admins:
|
|
218
|
+
try:
|
|
219
|
+
await bot.send_message(admin_id, notification.strip(), reply_markup=keyboard)
|
|
220
|
+
except Exception as e:
|
|
221
|
+
logger.error(f"Ошибка отправки уведомления админу {admin_id}: {e}")
|
|
222
|
+
|
|
223
|
+
except Exception as e:
|
|
224
|
+
logger.error(f"Ошибка отправки уведомления админам: {e}")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Сервисы для работы с внешними API
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .telegram_integration import TelegramIntegration
|
|
6
|
+
from .message_sender import send_message_by_ai, send_message_by_human
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
'TelegramIntegration',
|
|
10
|
+
'send_message_by_ai',
|
|
11
|
+
'send_message_by_human'
|
|
12
|
+
]
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Функции для отправки сообщений через ИИ и от человека
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import time
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Dict, Any, Optional
|
|
9
|
+
import pytz
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
async def send_message_by_ai(
|
|
14
|
+
user_id: int,
|
|
15
|
+
message_text: str,
|
|
16
|
+
session_id: str = None,
|
|
17
|
+
system_prompt: str = None
|
|
18
|
+
) -> Dict[str, Any]:
|
|
19
|
+
"""
|
|
20
|
+
Отправляет сообщение пользователю через ИИ (копирует логику process_user_message)
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
user_id: ID пользователя в Telegram
|
|
24
|
+
message_text: Текст сообщения для обработки ИИ
|
|
25
|
+
session_id: ID сессии чата
|
|
26
|
+
system_prompt: Системный промпт для ИИ
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Результат отправки
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
# Импортируем необходимые компоненты
|
|
33
|
+
from ..integrations import supabase_client, openai_client, config, bot, prompt_loader, parse_ai_response, process_events
|
|
34
|
+
|
|
35
|
+
# Если session_id не указан, получаем активную сессию пользователя
|
|
36
|
+
if not session_id:
|
|
37
|
+
session_info = await supabase_client.get_active_session(user_id)
|
|
38
|
+
if not session_info:
|
|
39
|
+
return {
|
|
40
|
+
"status": "error",
|
|
41
|
+
"error": "Активная сессия не найдена",
|
|
42
|
+
"user_id": user_id
|
|
43
|
+
}
|
|
44
|
+
session_id = session_info['id']
|
|
45
|
+
|
|
46
|
+
# Если system_prompt не указан, получаем из сессии
|
|
47
|
+
if not system_prompt:
|
|
48
|
+
session_info = await supabase_client.get_session_info(session_id)
|
|
49
|
+
if not session_info:
|
|
50
|
+
return {
|
|
51
|
+
"status": "error",
|
|
52
|
+
"error": "Информация о сессии не найдена",
|
|
53
|
+
"user_id": user_id
|
|
54
|
+
}
|
|
55
|
+
system_prompt = session_info['system_prompt']
|
|
56
|
+
|
|
57
|
+
# Сохраняем сообщение пользователя в БД
|
|
58
|
+
await supabase_client.add_message(
|
|
59
|
+
session_id=session_id,
|
|
60
|
+
role='user',
|
|
61
|
+
content=message_text,
|
|
62
|
+
message_type='text'
|
|
63
|
+
)
|
|
64
|
+
logger.info(f"✅ Сообщение пользователя сохранено в БД")
|
|
65
|
+
|
|
66
|
+
# Получаем историю сообщений
|
|
67
|
+
chat_history = await supabase_client.get_chat_history(session_id, limit=config.MAX_CONTEXT_MESSAGES)
|
|
68
|
+
logger.info(f"📚 Загружена история: {len(chat_history)} сообщений")
|
|
69
|
+
|
|
70
|
+
# Добавляем текущее время
|
|
71
|
+
moscow_tz = pytz.timezone('Europe/Moscow')
|
|
72
|
+
current_time = datetime.now(moscow_tz)
|
|
73
|
+
time_info = current_time.strftime('%H:%M, %d.%m.%Y, %A')
|
|
74
|
+
|
|
75
|
+
# Модифицируем системный промпт, добавляя время
|
|
76
|
+
system_prompt_with_time = f"""
|
|
77
|
+
{system_prompt}
|
|
78
|
+
|
|
79
|
+
ТЕКУЩЕЕ ВРЕМЯ: {time_info} (московское время)
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
# Формируем контекст для OpenAI
|
|
83
|
+
messages = [{"role": "system", "content": system_prompt_with_time}]
|
|
84
|
+
|
|
85
|
+
for msg in chat_history[-config.MAX_CONTEXT_MESSAGES:]:
|
|
86
|
+
messages.append({
|
|
87
|
+
"role": msg['role'],
|
|
88
|
+
"content": msg['content']
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
# Добавляем финальные инструкции
|
|
92
|
+
final_instructions = await prompt_loader.load_final_instructions()
|
|
93
|
+
if final_instructions:
|
|
94
|
+
messages.append({"role": "system", "content": final_instructions})
|
|
95
|
+
logger.info(f"🎯 Добавлены финальные инструкции")
|
|
96
|
+
|
|
97
|
+
logger.info(f"📝 Контекст сформирован: {len(messages)} сообщений")
|
|
98
|
+
|
|
99
|
+
# Отправляем действие "печатает"
|
|
100
|
+
await bot.send_chat_action(user_id, "typing")
|
|
101
|
+
|
|
102
|
+
# Получаем ответ от ИИ
|
|
103
|
+
start_time = time.time()
|
|
104
|
+
ai_response = await openai_client.get_completion(messages)
|
|
105
|
+
processing_time = int((time.time() - start_time) * 1000)
|
|
106
|
+
|
|
107
|
+
logger.info(f"🤖 OpenAI ответил за {processing_time}мс")
|
|
108
|
+
|
|
109
|
+
# Обрабатываем ответ
|
|
110
|
+
tokens_used = 0
|
|
111
|
+
ai_metadata = {}
|
|
112
|
+
response_text = ""
|
|
113
|
+
|
|
114
|
+
if not ai_response or not ai_response.strip():
|
|
115
|
+
logger.warning(f"❌ OpenAI вернул пустой ответ!")
|
|
116
|
+
fallback_message = "Извините, произошла техническая ошибка. Попробуйте переформулировать вопрос."
|
|
117
|
+
ai_response = fallback_message
|
|
118
|
+
response_text = fallback_message
|
|
119
|
+
else:
|
|
120
|
+
tokens_used = openai_client.estimate_tokens(ai_response)
|
|
121
|
+
response_text, ai_metadata = parse_ai_response(ai_response)
|
|
122
|
+
|
|
123
|
+
if not ai_metadata:
|
|
124
|
+
response_text = ai_response
|
|
125
|
+
ai_metadata = {}
|
|
126
|
+
elif not response_text.strip():
|
|
127
|
+
response_text = ai_response
|
|
128
|
+
|
|
129
|
+
# Обновляем этап сессии и качество лида
|
|
130
|
+
if ai_metadata:
|
|
131
|
+
stage = ai_metadata.get('этап')
|
|
132
|
+
quality = ai_metadata.get('качество')
|
|
133
|
+
|
|
134
|
+
if stage or quality is not None:
|
|
135
|
+
await supabase_client.update_session_stage(session_id, stage, quality)
|
|
136
|
+
logger.info(f"✅ Этап и качество обновлены в БД")
|
|
137
|
+
|
|
138
|
+
# Обрабатываем события
|
|
139
|
+
events = ai_metadata.get('события', [])
|
|
140
|
+
if events:
|
|
141
|
+
logger.info(f"🔔 Обрабатываем {len(events)} событий")
|
|
142
|
+
await process_events(session_id, events, user_id)
|
|
143
|
+
|
|
144
|
+
# Сохраняем ответ ассистента
|
|
145
|
+
await supabase_client.add_message(
|
|
146
|
+
session_id=session_id,
|
|
147
|
+
role='assistant',
|
|
148
|
+
content=response_text,
|
|
149
|
+
message_type='text',
|
|
150
|
+
tokens_used=tokens_used,
|
|
151
|
+
processing_time_ms=processing_time,
|
|
152
|
+
ai_metadata=ai_metadata
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Определяем финальный ответ
|
|
156
|
+
if config.DEBUG_MODE:
|
|
157
|
+
final_response = ai_response
|
|
158
|
+
else:
|
|
159
|
+
final_response = response_text
|
|
160
|
+
|
|
161
|
+
# Отправляем ответ пользователю напрямую через бота
|
|
162
|
+
await bot.send_message(
|
|
163
|
+
chat_id=user_id,
|
|
164
|
+
text=final_response
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
"status": "success",
|
|
169
|
+
"user_id": user_id,
|
|
170
|
+
"response_text": response_text,
|
|
171
|
+
"tokens_used": tokens_used,
|
|
172
|
+
"processing_time_ms": processing_time,
|
|
173
|
+
"events_processed": len(events) if events else 0
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
except Exception as e:
|
|
177
|
+
logger.error(f"❌ Ошибка в send_message_by_ai: {e}")
|
|
178
|
+
return {
|
|
179
|
+
"status": "error",
|
|
180
|
+
"error": str(e),
|
|
181
|
+
"user_id": user_id
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async def send_message_by_human(
|
|
185
|
+
user_id: int,
|
|
186
|
+
message_text: str,
|
|
187
|
+
session_id: Optional[str] = None
|
|
188
|
+
) -> Dict[str, Any]:
|
|
189
|
+
"""
|
|
190
|
+
Отправляет сообщение пользователю от имени человека (готовый текст)
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
user_id: ID пользователя в Telegram
|
|
194
|
+
message_text: Готовый текст сообщения
|
|
195
|
+
session_id: ID сессии (опционально, для сохранения в БД)
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Результат отправки
|
|
199
|
+
"""
|
|
200
|
+
try:
|
|
201
|
+
# Импортируем необходимые компоненты
|
|
202
|
+
from ..integrations import bot, supabase_client
|
|
203
|
+
|
|
204
|
+
# Отправляем сообщение пользователю
|
|
205
|
+
message = await bot.send_message(
|
|
206
|
+
chat_id=user_id,
|
|
207
|
+
text=message_text
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Если указана сессия, сохраняем сообщение в БД
|
|
211
|
+
if session_id:
|
|
212
|
+
await supabase_client.add_message(
|
|
213
|
+
session_id=session_id,
|
|
214
|
+
role='assistant',
|
|
215
|
+
content=message_text,
|
|
216
|
+
message_type='text',
|
|
217
|
+
metadata={'sent_by_human': True}
|
|
218
|
+
)
|
|
219
|
+
logger.info(f"💾 Сообщение от человека сохранено в БД")
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
"status": "success",
|
|
223
|
+
"user_id": user_id,
|
|
224
|
+
"message_id": message.message_id,
|
|
225
|
+
"message_text": message_text,
|
|
226
|
+
"saved_to_db": bool(session_id)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
except Exception as e:
|
|
230
|
+
logger.error(f"❌ Ошибка в send_message_by_human: {e}")
|
|
231
|
+
return {
|
|
232
|
+
"status": "error",
|
|
233
|
+
"error": str(e),
|
|
234
|
+
"user_id": user_id
|
|
235
|
+
}
|