Внедрение яндекса

main
pilot639 8 hours ago
parent 5792993ca5
commit 5b7ad6da89

@ -4,3 +4,4 @@ mysqlclient
qrcode qrcode
django-environ django-environ
pillow pillow
openai

@ -1,23 +1,43 @@
# api_common.py # api_common.py
import os import os
import json import json
import re
from datetime import datetime from datetime import datetime
from typing import Dict, Any, List from typing import Dict, Any, List, Optional
from max_bot.models import Client, ProductCategory, Product, Qr from django.core.cache import cache
from max_bot.models import Client, ProductCategory, Product, Qr, Partner, Integration
from restoran_max_bot import settings
# Кеш для базового промта
_BASE_PROMPT_CACHE = None _BASE_PROMPT_CACHE = None
GLOBAL_ERROR_MSG = '⚠️ К сожалению, я не могу ответить на этот вопрос.'
AI_AGENT_REGISTRY = {
'yandexai': {
'slug': 'yandexai',
'module': 'api_yandex_ai',
'function': 'yandex_chat_structured',
'required_settings': ['yandex_folder_id'],
'default_model': 'yandexgpt',
},
'deepseek': {
'slug': 'deepseek',
'module': 'api_deepseek_ai',
'function': 'deepseek_chat_structured',
'required_settings': [],
'default_model': 'deepseek-chat',
},
}
def _log_dialogue(session_id: str, role: str, content: str, log_file: str = None) -> None: def _log_dialogue(session_id: str, role: str, content: str, log_file: str = None) -> None:
""" if role.upper() == 'SYSTEM' and not getattr(settings, 'LOG_SYSTEM_PROMPT', False):
Записывает сообщение диалога в файл лога. return
"""
if log_file is None: if log_file is None:
base_dir = os.path.dirname(os.path.abspath(__file__)) base_dir = os.path.dirname(os.path.abspath(__file__))
log_dir = os.path.join(base_dir, 'logs') log_dir = os.path.join(base_dir, 'logs')
os.makedirs(log_dir, exist_ok=True) os.makedirs(log_dir, exist_ok=True)
log_file = os.path.join(log_dir, 'ai_dialogs.log') # общий лог для всех AI log_file = os.path.join(log_dir, 'ai_dialogs.log')
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
log_entry = f"[{timestamp}] [{role.upper()}] [{session_id}]: {content}\n" log_entry = f"[{timestamp}] [{role.upper()}] [{session_id}]: {content}\n"
@ -31,7 +51,6 @@ def _log_dialogue(session_id: str, role: str, content: str, log_file: str = None
def _load_base_prompt() -> str: def _load_base_prompt() -> str:
"""Загружает базовый текст системного промта из файла."""
global _BASE_PROMPT_CACHE global _BASE_PROMPT_CACHE
if _BASE_PROMPT_CACHE is not None: if _BASE_PROMPT_CACHE is not None:
return _BASE_PROMPT_CACHE return _BASE_PROMPT_CACHE
@ -53,7 +72,11 @@ def _load_base_prompt() -> str:
def get_system_prompt(client: Client) -> str: def get_system_prompt(client: Client) -> str:
"""Генерирует системный промпт на основе данных клиента.""" cache_key = f"system_prompt_{client.id}"
cached = cache.get(cache_key)
if cached:
return cached
prompt = _load_base_prompt() prompt = _load_base_prompt()
qr = Qr.objects.filter(client=client).first() qr = Qr.objects.filter(client=client).first()
@ -89,34 +112,157 @@ def get_system_prompt(client: Client) -> str:
prompt += "\nМеню не загружено.\n" prompt += "\nМеню не загружено.\n"
prompt += """ prompt += """
При ответах на вопросы о меню используй только те блюда, которые перечислены выше. При ответах на вопросы о меню используй только те блюда, которые перечислены выше.
Если клиент спрашивает о блюде, которого нет в списке, честно скажи об этом и предложи посмотреть другие позиции. Если клиент спрашивает о блюде, которого нет в списке, честно скажи об этом и предложи посмотреть другие позиции.
Всегда помни, что ты должен возвращать только JSON, без лишнего текста вне JSON. Всегда помни, что ты должен возвращать только JSON, без лишнего текста вне JSON.
""" """
cache.set(cache_key, prompt, 300)
return prompt return prompt
def parse_ai_response(assistant_content: str) -> Dict[str, Any]: def parse_ai_response(assistant_content: str) -> Dict[str, Any]:
""" content = re.sub(r'```(?:json)?\s*', '', assistant_content)
Универсальный парсер для ответа AI (ожидается JSON с полями intent, message, entities, status). content = re.sub(r'\s*```', '', content)
Возвращает словарь с гарантированными ключами. content = content.strip()
"""
try: try:
parsed = json.loads(assistant_content) parsed = json.loads(content)
required = ["intent", "message", "entities", "status"] required = ["intent", "message", "entities", "status"]
if all(k in parsed for k in required): if all(k in parsed for k in required):
return parsed return parsed
else: else:
return { return {
"intent": "general", "intent": "general",
"message": assistant_content, "message": content,
"entities": {}, "entities": {},
"status": "complete" "status": "complete"
} }
except json.JSONDecodeError: except json.JSONDecodeError:
return { return {
"intent": "general", "intent": "general",
"message": assistant_content, "message": content,
"entities": {},
"status": "complete"
}
def get_ai_agent(client: Client) -> Dict[str, Optional[str]]:
for agent_slug, config in AI_AGENT_REGISTRY.items():
partner = Partner.objects.filter(slug=agent_slug).first()
if not partner:
continue
integration = Integration.objects.filter(
client=client,
partner=partner,
status=True
).first()
if integration:
try:
settings = json.loads(integration.setting) if integration.setting else {}
except json.JSONDecodeError:
settings = {}
required_settings = config.get('required_settings', [])
missing = [f for f in required_settings if f not in settings]
if missing:
_log_dialogue(str(client.id), 'ERROR',
f"AI agent {agent_slug} missing settings: {missing}")
continue
if not integration.token:
_log_dialogue(str(client.id), 'ERROR',
f"AI agent {agent_slug} missing token")
continue
result = {
'ai_agent': agent_slug,
'token': integration.token,
'client_prompt': settings.get('promt'),
'settings': settings,
}
if agent_slug == 'yandexai' and 'yandex_folder_id' in settings:
result['folder_id'] = settings['yandex_folder_id']
return result
return {
'ai_agent': None,
'token': None,
'client_prompt': None,
'settings': None,
'folder_id': None,
}
def call_ai_agent(client: Client, session_id: str, user_query: str) -> Dict[str, Any]:
agent_info = get_ai_agent(client)
ai_agent = agent_info.get('ai_agent')
if not ai_agent:
return {
"intent": "general",
"message": GLOBAL_ERROR_MSG,
"entities": {},
"status": "complete"
}
config = AI_AGENT_REGISTRY.get(ai_agent)
if not config:
return {
"intent": "general",
"message": f"Агент {ai_agent} не зарегистрирован.",
"entities": {},
"status": "complete"
}
try:
module = __import__(f"ai_agent.{config['module']}", fromlist=[config['function']])
func = getattr(module, config['function'])
except (ImportError, AttributeError) as e:
error_msg = f"Не удалось загрузить функцию для агента {ai_agent}: {e}"
_log_dialogue(session_id, 'ERROR', error_msg)
return {
"intent": "general",
"message": GLOBAL_ERROR_MSG,
"entities": {},
"status": "complete"
}
system_prompt = get_system_prompt(client)
client_prompt = agent_info.get('client_prompt')
if client_prompt:
system_prompt += f"\n\nДополнительная информация о ресторане:\n{client_prompt}"
kwargs = {
'session_id': session_id,
'system_prompt': system_prompt,
'user_query': user_query,
'api_token': agent_info.get('token'),
}
if ai_agent == 'yandexai':
folder_id = agent_info.get('folder_id')
if not folder_id:
return {
"intent": "general",
"message": "Ошибка конфигурации Yandex: не указан folder_id.",
"entities": {},
"status": "complete"
}
kwargs['folder_id'] = folder_id
# Можно задать модель из настроек или использовать default
model = agent_info.get('settings', {}).get('model', config.get('default_model', 'yandexgpt'))
kwargs['model'] = model
# Логируем вызов
_log_dialogue(session_id, 'DEBUG', f"Calling {ai_agent} with kwargs: {kwargs}")
try:
response = func(**kwargs)
return response
except Exception as e:
_log_dialogue(session_id, 'ERROR', f"Error calling {ai_agent}: {e}")
return {
"intent": "general",
"message": GLOBAL_ERROR_MSG,
"entities": {}, "entities": {},
"status": "complete" "status": "complete"
} }

@ -1,7 +1,7 @@
# api_deepseek_ai.py # api_deepseek_ai.py
import requests import requests
from typing import Dict, Any, List from typing import Dict, Any, List
from api_common import _log_dialogue, get_system_prompt, parse_ai_response from ai_agent.api_common import _log_dialogue, parse_ai_response, GLOBAL_ERROR_MSG
# Хранилище контекстов для DeepSeek # Хранилище контекстов для DeepSeek
_contexts: Dict[str, List[Dict[str, str]]] = {} _contexts: Dict[str, List[Dict[str, str]]] = {}
@ -55,7 +55,7 @@ def deepseek_chat_structured(
except requests.exceptions.HTTPError as e: except requests.exceptions.HTTPError as e:
if e.response.status_code == 402: if e.response.status_code == 402:
user_message = "⚠️ К сожалению, я сейчас не могу отвечать на эти вопросы, попробуйте позже." user_message = GLOBAL_ERROR_MSG
_log_dialogue(session_id, 'ERROR', f"Payment Required (402): {e}") _log_dialogue(session_id, 'ERROR', f"Payment Required (402): {e}")
return { return {
"intent": "general", "intent": "general",
@ -64,7 +64,7 @@ def deepseek_chat_structured(
"status": "complete" "status": "complete"
} }
else: else:
error_msg = "⚠️ Произошла ошибка при обращении к серверу. Попробуйте позже." error_msg = GLOBAL_ERROR_MSG
_log_dialogue(session_id, 'ERROR', f"HTTP error: {e}") _log_dialogue(session_id, 'ERROR', f"HTTP error: {e}")
return { return {
"intent": "general", "intent": "general",
@ -73,7 +73,7 @@ def deepseek_chat_structured(
"status": "complete" "status": "complete"
} }
except Exception as e: except Exception as e:
error_msg = "⚠️ Извините, произошла ошибка. Попробуйте позже." error_msg = GLOBAL_ERROR_MSG
_log_dialogue(session_id, 'ERROR', f"DeepSeek error: {e}") _log_dialogue(session_id, 'ERROR', f"DeepSeek error: {e}")
return { return {
"intent": "general", "intent": "general",

@ -1,20 +1,19 @@
# api_yandex_ai.py # api_yandex_ai.py
from typing import Dict, Any, List from typing import Dict, Any, List
from openai import OpenAI from openai import OpenAI
from api_common import _log_dialogue, get_system_prompt, parse_ai_response from ai_agent.api_common import _log_dialogue, parse_ai_response, GLOBAL_ERROR_MSG
# Хранилище контекстов для Yandex
_contexts: Dict[str, List[Dict[str, str]]] = {} _contexts: Dict[str, List[Dict[str, str]]] = {}
def yandex_chat_structured( def yandex_chat_structured(
session_id: str, session_id: str,
api_key: str, api_token: str,
folder_id: str, folder_id: str,
system_prompt: str, system_prompt: str,
user_query: str, user_query: str,
model: str = "yandexgpt", model: str = "yandexgpt", # можно "yandexgpt-lite"
max_tokens: int = 1000, max_tokens: int = 500,
temperature: float = 0.3, temperature: float = 0.3,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
@ -30,18 +29,21 @@ def yandex_chat_structured(
_contexts[session_id].append({"role": "user", "content": user_query}) _contexts[session_id].append({"role": "user", "content": user_query})
try: try:
if not folder_id:
raise ValueError("folder_id is empty")
client = OpenAI( client = OpenAI(
api_key=api_key, api_key=api_token,
base_url="https://ai.api.cloud.yandex.net/v1", base_url="https://ai.api.cloud.yandex.net/v1",
project=folder_id, project=folder_id,
) )
# Убираем response_format он может вызывать ошибку парсинга URI модели
response = client.chat.completions.create( response = client.chat.completions.create(
model=model, model=model,
messages=_contexts[session_id], messages=_contexts[session_id],
max_tokens=max_tokens, max_tokens=max_tokens,
temperature=temperature, temperature=temperature,
response_format={"type": "json_object"}
) )
assistant_content = response.choices[0].message.content assistant_content = response.choices[0].message.content
@ -52,7 +54,7 @@ def yandex_chat_structured(
return parse_ai_response(assistant_content) return parse_ai_response(assistant_content)
except Exception as e: except Exception as e:
error_msg = "⚠️ Извините, произошла ошибка при обращении к Yandex GPT. Попробуйте позже." error_msg = GLOBAL_ERROR_MSG
_log_dialogue(session_id, 'ERROR', f"Yandex GPT error: {e}") _log_dialogue(session_id, 'ERROR', f"Yandex GPT error: {e}")
return { return {
"intent": "general", "intent": "general",

@ -50,21 +50,9 @@ def api_decorator(func):
settings_max = json.loads(integration.setting) settings_max = json.loads(integration.setting)
settings_max['token'] = integration.token settings_max['token'] = integration.token
# проверка на интеграцию с максом
partner = Partner.objects.filter(slug='deepseek').first()
integration = Integration.objects.filter(client=client, partner=partner, status=True).first()
if not integration:
settings_deepseek = {'token': None, 'deepseek_system_prompt': None}
else:
deepseek_system_prompt = json.loads(integration.setting).get('system_prompt', None)
settings_deepseek = {'token': integration.token, 'deepseek_system_prompt': deepseek_system_prompt}
kwargs['client'] = client kwargs['client'] = client
kwargs['integration'] = integration kwargs['integration'] = integration
kwargs['settings_max'] = settings_max kwargs['settings_max'] = settings_max
kwargs['settings_deepseek'] = settings_deepseek
return func(*args, **kwargs) return func(*args, **kwargs)
return wrapper_api_decorator return wrapper_api_decorator
@ -76,7 +64,6 @@ def api_start_max_v1(request, token, **kwargs):
data = json.loads(request.body.decode('utf-8')) data = json.loads(request.body.decode('utf-8'))
client = kwargs['client'] client = kwargs['client']
settings_max = kwargs['settings_max'] settings_max = kwargs['settings_max']
settings_deepseek = kwargs['settings_deepseek']
if DEBUG: if DEBUG:
print(data) print(data)
@ -87,12 +74,10 @@ def api_start_max_v1(request, token, **kwargs):
# получение сообщения из бота # получение сообщения из бота
if data['update_type'] == 'message_created': if data['update_type'] == 'message_created':
bot_message(client=client, message=data, settings_max=settings_max, settings_deepseek=settings_deepseek, bot_message(client=client, message=data, settings_max=settings_max, message_type='message_created')
message_type='message_created')
if data['update_type'] == 'message_callback': if data['update_type'] == 'message_callback':
bot_message(client=client, message=data, settings_max=settings_max, settings_deepseek=settings_deepseek, bot_message(client=client, message=data, settings_max=settings_max, message_type='message_callback')
message_type='message_callback')
# start - запуск бота # start - запуск бота
if data['update_type'] == 'bot_started': if data['update_type'] == 'bot_started':
@ -106,7 +91,7 @@ def api_start_max_v1(request, token, **kwargs):
return JsonResponse(rt, status=200) return JsonResponse(rt, status=200)
#https://maxbot.telefon-ip.ru/max/iiko/webhook/71232b71-dacc-4586-95d0-0f352d9d09c3 # https://maxbot.telefon-ip.ru/max/iiko/webhook/71232b71-dacc-4586-95d0-0f352d9d09c3
@csrf_exempt @csrf_exempt
def iiko_webhook(request: WSGIRequest, token): def iiko_webhook(request: WSGIRequest, token):
rt = {'success': False, 'data': ''} rt = {'success': False, 'data': ''}

@ -1,8 +1,9 @@
# bot_message.py
import re import re
import json
from ai_agent.api_deepseek_ai import get_system_prompt, deepseek_chat_structured
from max_bot.max_api import *
import qrcode import qrcode
from ai_agent.api_common import get_system_prompt, get_ai_agent, call_ai_agent
from max_bot.max_api import *
from restoran_max_bot.bot_started import check_registration from restoran_max_bot.bot_started import check_registration
from restoran_max_bot.common import * from restoran_max_bot.common import *
from restoran_max_bot.iiko_api import customer_info, customer_create, customer_wallet from restoran_max_bot.iiko_api import customer_info, customer_create, customer_wallet
@ -11,24 +12,24 @@ from restoran_max_bot.settings import STATICFILES_DIRS
from restoran_max_bot.utils import has_key, is_json from restoran_max_bot.utils import has_key, is_json
# отправка сообщения в группу администраторв по бронированию # ---------- вспомогательные функции ----------
def send_booking_to_crm(chat_id, date, time, people, contact): def send_booking_to_crm(chat_id, date, time, people, phone):
# TODO: реализовать отправку в CRM
return True return True
def send_contact_to_admin(chat_id, phone, contact_method, question, preferred_time, contact): def send_contact_to_admin(chat_id, phone, contact_method, question, preferred_time, contact):
# TODO: реализовать отправку администратору
return True return True
# получение информации по интеграции с терминалом ikko, rkeeper # ---------- получение бонусов ----------
def get_bonus(client: Client, phone: str, default_bonus: float, contact: Contact): def get_bonus(client: Client, phone: str, default_bonus: float, contact: Contact):
integration = common_get_integration(client=client) integration = common_get_integration(client=client)
bonus = 0 bonus = 0
date = contact.field1 date = contact.field1
if len(date) < 2: if len(date) < 2:
date = '0' + date date = '0' + date
month = contact.field2 month = contact.field2
if len(month) < 2: if len(month) < 2:
month = '0' + month month = '0' + month
@ -39,23 +40,16 @@ def get_bonus(client: Client, phone: str, default_bonus: float, contact: Contact
customerinfo = customer_info(config['token'], phone, config['id_org']) customerinfo = customer_info(config['token'], phone, config['id_org'])
if not customerinfo: if not customerinfo:
birthday = f"1990-{month}-{date} 00:00:00.000" birthday = f"1990-{month}-{date} 00:00:00.000"
dt = customer_create(api_key=config['token'], phone=phone, name=contact.name, dt = customer_create(api_key=config['token'], phone=phone, name=contact.name,
organization_id=config['id_org'], birthday=birthday) organization_id=config['id_org'], birthday=birthday)
dt = customer_info(config['token'], phone, config['id_org']) dt = customer_info(config['token'], phone, config['id_org'])
customer_wallet(api_key=config['token'], iiko_wallet_id=dt['walletBalances'][0]['id'], customer_wallet(api_key=config['token'], iiko_wallet_id=dt['walletBalances'][0]['id'],
customer_id=dt['id'], organization_id=config['id_org'], customer_id=dt['id'], organization_id=config['id_org'],
bonus=default_bonus) bonus=default_bonus)
bonus = default_bonus bonus = default_bonus
else: else:
bonus = customerinfo['walletBalances'] bonus = customerinfo['walletBalances'][0]['balance']
bonus = bonus[0]['balance']
# http://192.168.9.186:8890/docs
if integration.partner.slug == 'rkeeper': if integration.partner.slug == 'rkeeper':
config = json.loads(integration.setting) config = json.loads(integration.setting)
rs = rkiper_customer_info(rkiper_server=config['url'], rkiper_token=config['token'], phone=phone) rs = rkiper_customer_info(rkiper_server=config['url'], rkiper_token=config['token'], phone=phone)
@ -72,26 +66,24 @@ def get_bonus(client: Client, phone: str, default_bonus: float, contact: Contact
return bonus return bonus
# получение вложения в сообщениях для их обработки (регистрация по номеру) # ---------- получение вложений ----------
def get_attachments(message: dict): def get_attachments(message: dict):
chat_id = message['message']['recipient']['chat_id']
if 'attachments' in message['message']['body']: if 'attachments' in message['message']['body']:
if 'vcf_info' in message['message']['body']['attachments'][0]['payload']: if 'vcf_info' in message['message']['body']['attachments'][0]['payload']:
# Регулярное выражение: ищем всё после 'TEL;TYPE=cell:' до \r или конца строки
match = re.search(r'TEL;TYPE=cell:(\d+)', match = re.search(r'TEL;TYPE=cell:(\d+)',
message['message']['body']['attachments'][0]['payload']['vcf_info']) message['message']['body']['attachments'][0]['payload']['vcf_info'])
if match: if match:
phone_number = match.group(1) phone_number = match.group(1)
return {'attachments_type': 'vcf_info', 'data': phone_number} return {'attachments_type': 'vcf_info', 'data': phone_number}
return {'attachments_type': False, 'data': ''} return {'attachments_type': False, 'data': ''}
def bot_message(client: Client, message: dict, settings_max: dict, settings_deepseek: dict, message_type): # ---------- основной обработчик ----------
def bot_message(client: Client, message: dict, settings_max: dict, message_type):
chat_id = message['message']['recipient']['chat_id'] chat_id = message['message']['recipient']['chat_id']
attachments = get_attachments(message=message) attachments = get_attachments(message=message)
# регистрируем по номеру телефона выдаем соответствующий контент # регистрация по vcf
if attachments['attachments_type'] == 'vcf_info': if attachments['attachments_type'] == 'vcf_info':
common_set_contact(client=client, uid_client=chat_id, name=message['message']['sender']['name'], common_set_contact(client=client, uid_client=chat_id, name=message['message']['sender']['name'],
phone=attachments['data']) phone=attachments['data'])
@ -104,10 +96,11 @@ def bot_message(client: Client, message: dict, settings_max: dict, settings_deep
data = None data = None
if message_type == 'message_created': if message_type == 'message_created':
data = common_get_data(client=client, key=str(message['message']['body']['text']).lower()) data = common_get_data(client=client, key=str(message['message']['body']['text']).lower())
elif message_type == 'message_callback':
if message_type == 'message_callback':
payload = json.loads(message['callback']['payload']) payload = json.loads(message['callback']['payload'])
data = common_get_data(client=client, key=str(payload['data']).lower()) data = common_get_data(client=client, key=str(payload['data']).lower())
# ---------- обработка стандартных команд (меню, бонусы и т.п.) ----------
if data: if data:
for dt in data: for dt in data:
if dt.title == '##bonus##': if dt.title == '##bonus##':
@ -116,28 +109,26 @@ def bot_message(client: Client, message: dict, settings_max: dict, settings_deep
url = f"https://maxbot.telefon-ip.ru/static/client_qr/{contact.phone}.png" url = f"https://maxbot.telefon-ip.ru/static/client_qr/{contact.phone}.png"
maxbot_send_img_message(chat_id=chat_id, max_token=settings_max['token'], img=url, maxbot_send_img_message(chat_id=chat_id, max_token=settings_max['token'], img=url,
message='QR-код для начисления, списания бонусов.') message='QR-код для начисления, списания бонусов.')
bonus = get_bonus(client=client, phone=contact.phone, default_bonus=float(dt.price), contact=contact) bonus = get_bonus(client=client, phone=contact.phone, default_bonus=float(dt.price), contact=contact)
maxbot_send_text_message(chat_id=chat_id, max_token=settings_max['token'], maxbot_send_text_message(chat_id=chat_id, max_token=settings_max['token'],
message=f"💰 Ваш бонусный баланс: {bonus} руб") message=f"💰 Ваш бонусный баланс: {bonus} руб")
if dt.title == '##feedback##': elif dt.title == '##feedback##':
maxbot_send_feedback_button(client=client, message='👇', chat_id=chat_id, max_token=settings_max['token']) maxbot_send_feedback_button(client=client, message='👇', chat_id=chat_id,
max_token=settings_max['token'])
return True return True
if dt.title == '##booking##': elif dt.title == '##booking##':
maxbot_send_booking_count_people_button(client=client, message='👇', maxbot_send_booking_count_people_button(client=client, message='👇',
chat_id=chat_id, max_token=settings_max['token']) chat_id=chat_id, max_token=settings_max['token'])
return True return True
# для более сложных функций # сложные кнопки
if is_json(dt.title): if is_json(dt.title):
data = json.loads(dt.title) data_json = json.loads(dt.title)
if 'type' in dt.title: if 'type' in dt.title and data_json.get('type') == 'linkbutton':
if data['type'] == 'linkbutton': maxbot_send_link_button(text=dt.descr, title_button=data_json['button'], link=dt.url,
maxbot_send_link_button(text=dt.descr, title_button=data['button'], link=dt.url, chat_id=chat_id, max_token=settings_max['token'])
chat_id=chat_id,
max_token=settings_max['token'])
if dt.img: if dt.img:
url = f"https://cdn.telefon-ip.ru/{dt.img}?thumb=600" url = f"https://cdn.telefon-ip.ru/{dt.img}?thumb=600"
@ -149,86 +140,79 @@ def bot_message(client: Client, message: dict, settings_max: dict, settings_deep
if dt.url and not is_json(dt.title): if dt.url and not is_json(dt.title):
maxbot_send_text_message(chat_id=chat_id, max_token=settings_max['token'], message=dt.url) maxbot_send_text_message(chat_id=chat_id, max_token=settings_max['token'], message=dt.url)
# отправка меню
maxbot_send_menu_button(chat_id=chat_id, max_token=settings_max['token'], message='👇 Выберите раздел', maxbot_send_menu_button(chat_id=chat_id, max_token=settings_max['token'], message='👇 Выберите раздел',
client=client) client=client)
else: return True
# =========================== обработка сообщения через ИИ ===========================
if settings_deepseek.get('token', False): # ---------- обработка через AI ----------
text = message['message']['body']['text'] # Проверяем, есть ли AI-агент у клиента
# Получаем системный промт из настроек ai_agent = get_ai_agent(client)
system_prompt = get_system_prompt(client) if not ai_agent.get('ai_agent'):
if settings_deepseek.get('deepseek_system_prompt', False): maxbot_send_menu_button(chat_id=chat_id, max_token=settings_max['token'],
system_prompt = (system_prompt + "\n Дополнительная информация:\n" + message='👇 Выберите раздел', client=client)
settings_deepseek.get('deepseek_system_prompt')) return True
# Отправляем запрос в DeepSeek # Вызываем AI-агента через универсальную функцию
response = deepseek_chat_structured( response = call_ai_agent(
session_id=str(chat_id), client=client,
api_token=settings_deepseek.get('token'), session_id=str(chat_id),
system_prompt=system_prompt, user_query=message['message']['body']['text']
user_query=text )
)
# ---------- обработка ответа AI ----------
intent = response.get('intent') intent = response.get('intent')
message_text = response.get('message', '') message_text = response.get('message', '')
entities = response.get('entities', {}) entities = response.get('entities', {})
status = response.get('status', 'complete') status = response.get('status', 'complete')
# Если общий вопрос — просто отвечаем if intent == 'general':
if intent == 'general': maxbot_send_text_message(chat_id, settings_max['token'], message_text)
maxbot_send_text_message(chat_id, settings_max['token'], message_text)
elif intent == 'booking': elif intent == 'booking':
if status == 'need_more_info': if status == 'need_more_info':
# Отправляем уточняющий вопрос maxbot_send_text_message(chat_id, settings_max['token'], message_text)
maxbot_send_text_message(chat_id, settings_max['token'], message_text) elif status == 'complete':
elif status == 'complete': date = entities.get('date')
# Все данные собраны — передаём в CRM time = entities.get('time')
date = entities.get('date') people = entities.get('people')
time = entities.get('time') phone = entities.get('phone')
people = entities.get('people') if date and time and people:
phone = entities.get('phone') success = send_booking_to_crm(chat_id, date, time, people, phone)
if date and time and people: if success:
# Вызываем функцию для отправки в CRM confirm_msg = f"✅ Бронирование на {date} в {time} на {people} чел. принято! Администратор свяжется с вами."
success = send_booking_to_crm(chat_id, date, time, people, phone)
if success:
confirm_msg = f"✅ Бронирование на {date} в {time} на {people} чел. принято! " \
f"Администратор свяжется с вами для уточнения вопросов"
else:
confirm_msg = "Не удалось забронировать. Попробуйте позже."
maxbot_send_text_message(chat_id, settings_max['token'], confirm_msg)
else:
maxbot_send_text_message(chat_id, settings_max['token'], "⚠️ Не хватает данных для бронирования.")
else: else:
maxbot_send_text_message(chat_id, settings_max['token'], message_text) confirm_msg = "Не удалось забронировать. Попробуйте позже."
elif intent == 'contact_admin': maxbot_send_text_message(chat_id, settings_max['token'], confirm_msg)
if status == 'need_more_info': else:
# Отправляем уточняющий вопрос maxbot_send_text_message(chat_id, settings_max['token'], "⚠️ Не хватает данных для бронирования.")
maxbot_send_text_message(chat_id, settings_max['token'], message_text) else:
elif status == 'complete': maxbot_send_text_message(chat_id, settings_max['token'], message_text)
# Все данные собраны — передаём администратору
phone = entities.get('phone') elif intent == 'contact_admin':
contact_method = entities.get('contact_method') if status == 'need_more_info':
question = entities.get('question', '') maxbot_send_text_message(chat_id, settings_max['token'], message_text)
preferred_time = entities.get('preferred_time', '') elif status == 'complete':
if phone and contact_method: phone = entities.get('phone')
# Вызываем функцию отправки уведомления администратору contact_method = entities.get('contact_method')
success = send_contact_to_admin(chat_id, phone, contact_method, question, preferred_time, contact) question = entities.get('question', '')
if success: preferred_time = entities.get('preferred_time', '')
confirm_msg = f"✅ Ваш запрос передан администратору. Способ связи: {contact_method}. Скоро с вами свяжутся." if phone and contact_method:
else: success = send_contact_to_admin(chat_id, phone, contact_method, question, preferred_time, contact)
confirm_msg = "Не удалось отправить запрос. Попробуйте позже." if success:
maxbot_send_text_message(chat_id, settings_max['token'], confirm_msg) confirm_msg = f"✅ Ваш запрос передан администратору. Способ связи: {contact_method}. Скоро с вами свяжутся."
else:
maxbot_send_text_message(chat_id, settings_max['token'],
"⚠️ Не хватает данных для связи с администратором.")
else: else:
maxbot_send_text_message(chat_id, settings_max['token'], message_text) confirm_msg = "Не удалось отправить запрос. Попробуйте позже."
maxbot_send_text_message(chat_id, settings_max['token'], confirm_msg)
else: else:
maxbot_send_text_message(chat_id, settings_max['token'], message_text) maxbot_send_text_message(chat_id, settings_max['token'],
"⚠️ Не хватает данных для связи с администратором.")
else:
maxbot_send_text_message(chat_id, settings_max['token'], message_text)
# После ответа отправляем меню (если нужно) else:
maxbot_send_menu_button(chat_id=chat_id, max_token=settings_max['token'], maxbot_send_text_message(chat_id, settings_max['token'], message_text)
message='👇 Выберите раздел', client=client)
# всегда отправляем меню после ответа
maxbot_send_menu_button(chat_id=chat_id, max_token=settings_max['token'],
message='👇 Выберите раздел', client=client)
return True return True

@ -125,3 +125,5 @@ STATICFILES_DIRS = [(os.path.join(BASE_DIR, "static"))]
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOG_SYSTEM_PROMPT = False # False - не логировать системный промт

Loading…
Cancel
Save