Files
site_aegisone/max_bot/app/conversation.py
T

143 lines
5.1 KiB
Python

from datetime import datetime
from typing import Optional
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.models import (
BotUser, BotConversation, BotMessage, BotConsentLog,
BotCategory, BotSetting, BotFeature, BotResponseTemplate, BotKnowledgeBase
)
from app.settings_cache import SettingsCache
from app.text_renderer import render_template
async def get_or_create_user(db: AsyncSession, max_user_id: int,
first_name: str = "", last_name: str = "",
username: str = "") -> BotUser:
result = await db.execute(select(BotUser).where(BotUser.max_user_id == max_user_id))
user = result.scalar_one_or_none()
if user:
user.last_active_at = datetime.now()
if first_name and not user.first_name:
user.first_name = first_name
if last_name and not user.last_name:
user.last_name = last_name
if username and not user.username:
user.username = username
return user
user = BotUser(
max_user_id=max_user_id, first_name=first_name,
last_name=last_name, username=username
)
db.add(user)
await db.flush()
return user
async def get_open_conversation(db: AsyncSession, user_id: int) -> Optional[BotConversation]:
result = await db.execute(
select(BotConversation).where(
BotConversation.user_id == user_id,
BotConversation.status == "open"
).order_by(BotConversation.created_at.desc()).limit(1)
)
return result.scalar_one_or_none()
async def create_conversation(db: AsyncSession, user_id: int) -> BotConversation:
conv = BotConversation(user_id=user_id, status="open")
db.add(conv)
await db.flush()
return conv
async def add_message(db: AsyncSession, conversation_id: int, direction: str,
text: str = "", has_attachment: bool = False,
attachment_type: str = "", attachment_path: str = "",
max_message_id: int = None) -> BotMessage:
msg = BotMessage(
conversation_id=conversation_id, direction=direction, text=text,
has_attachment=has_attachment, attachment_type=attachment_type,
attachment_path=attachment_path, max_message_id=max_message_id
)
db.add(msg)
await db.flush()
return msg
async def log_consent(db: AsyncSession, user_id: int, action: str, method: str = "", ip: str = ""):
log = BotConsentLog(user_id=user_id, action=action, method=method, ip_address=ip)
db.add(log)
await db.flush()
async def get_template(db: AsyncSession, key: str) -> str:
await SettingsCache.refresh(db)
return SettingsCache.get_template(key)
async def get_template_rendered(db: AsyncSession, key: str, **kwargs) -> str:
tmpl = await get_template(db, key)
return render_template(tmpl, **kwargs)
async def find_in_knowledge_base(db: AsyncSession, query: str) -> Optional[BotKnowledgeBase]:
query_lower = query.lower()
result = await db.execute(
select(BotKnowledgeBase).where(BotKnowledgeBase.active == True)
)
entries = result.scalars().all()
for entry in entries:
if query_lower in entry.question.lower():
return entry
keywords = entry.keywords or []
for kw in keywords:
if kw.lower() in query_lower:
return entry
return None
async def auto_categorize_text(db: AsyncSession, text: str) -> Optional[BotCategory]:
text_lower = text.lower()
categories = SettingsCache.get_categories()
best_match = None
best_score = 0
for cat in categories:
keywords = cat.keywords or []
score = sum(1 for kw in keywords if kw.lower() in text_lower)
if score > best_score:
best_score = score
best_match = cat
return best_match if best_score > 0 else None
async def close_conversation(db: AsyncSession, conversation_id: int):
conv = await db.execute(
select(BotConversation).where(BotConversation.id == conversation_id)
)
conv = conv.scalar_one_or_none()
if conv:
conv.status = "closed"
conv.closed_at = datetime.now()
await db.flush()
async def update_conversation_status(db: AsyncSession, conversation_id: int, status: str):
conv = await db.execute(
select(BotConversation).where(BotConversation.id == conversation_id)
)
conv = conv.scalar_one_or_none()
if conv:
conv.status = status
await db.flush()
async def detect_negative_emotion(text: str) -> bool:
negative_words = [
"ужасно", "плохо", "долго", "кошмар", "отвратительно", "невыносимо",
"бесполезно", "халтура", "безобразие", "позор", "разочарован", "злюсь",
"раздражён", "недоволен", "жалоба", "претензия", "скандал", "суд",
"верните деньги", "мошенники", "обман"
]
text_lower = text.lower()
return any(word in text_lower for word in negative_words)