143 lines
5.1 KiB
Python
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)
|