v1.7.0: refactor max_bot to flat structure, add VCF+UserModel+NLP history context, portal pages and proxy fixes
- Refactored max_bot from nested packages to flat module structure - Q2: Extended BotUser model (patronymic, email, org, address, vcf_raw, contact_hash, phone_verified, email_verified, last_interaction, total_conversations, total_tickets) - Q2: VCF parser (FN, N, TEL, EMAIL, ORG, ADR), upsert on re-contact, NLP history context (_get_user_history_context -> YandexGPT) - Q1: Broadcast preview modal with 10s confirmation timer - Q3: CSS var(--white)->var(--bg-card), var(--text)->var(--text-primary) - Q4: bot_settings showNotification(), editable max_bot_id - Q5: Webhook secret passthrough via X-Max-Bot-Api-Secret - Masking sensitive keys, dialog_cleared handler, migrate via _add_column_if_not_exists() - Rate limit (asyncio.sleep 0.5 per 10), dead code removed, conv.intent context in contact.py - Portal pages: bot_consent, bot_kb (edit), bot_settings, bot_test, bot_tickets, portal_settings - Tests: 21/21 passing, added test_yandex_gpt.py, test_email_sender.py - Deploy: deploy_full.sh, schema.sql, seed_knowledge_base.sql
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
from __future__ import annotations
|
||||
import datetime
|
||||
from sqlalchemy import Column, Integer, BigInteger, String, Text, Boolean, DateTime, ForeignKey, ARRAY
|
||||
from sqlalchemy.orm import DeclarativeBase, relationship
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
class BotSetting(Base):
|
||||
__tablename__ = "bot_settings"
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
key = Column(String(255), unique=True, nullable=False)
|
||||
value = Column(Text, nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||
|
||||
|
||||
class BotFeature(Base):
|
||||
__tablename__ = "bot_features"
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
key = Column(String(255), unique=True, nullable=False)
|
||||
name = Column(String(255), nullable=False)
|
||||
description = Column(Text, nullable=True)
|
||||
is_enabled = Column(Boolean, default=False)
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
|
||||
|
||||
class BotCategory(Base):
|
||||
__tablename__ = "bot_categories"
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
name = Column(String(255), nullable=False)
|
||||
description = Column(Text, nullable=True)
|
||||
sort_order = Column(Integer, default=0)
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
|
||||
|
||||
class BotKnowledgeBase(Base):
|
||||
__tablename__ = "bot_knowledge_base"
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
category_id = Column(Integer, ForeignKey("bot_categories.id"), nullable=True)
|
||||
question = Column(Text, nullable=False)
|
||||
answer = Column(Text, nullable=True)
|
||||
keywords = Column(ARRAY(String), nullable=True)
|
||||
is_active = Column(Boolean, default=True)
|
||||
source_url = Column(String(500), nullable=True)
|
||||
sort_order = Column(Integer, default=0)
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||
|
||||
category = relationship("BotCategory", lazy="joined")
|
||||
|
||||
|
||||
class BotUser(Base):
|
||||
__tablename__ = "bot_users"
|
||||
id = Column(BigInteger, primary_key=True)
|
||||
first_name = Column(String(255), nullable=True)
|
||||
last_name = Column(String(255), nullable=True)
|
||||
patronymic = Column(String(255), nullable=True)
|
||||
username = Column(String(255), nullable=True)
|
||||
phone = Column(String(50), nullable=True)
|
||||
email = Column(String(255), nullable=True)
|
||||
organization = Column(String(255), nullable=True)
|
||||
address = Column(Text, nullable=True)
|
||||
vcf_raw = Column(Text, nullable=True)
|
||||
contact_hash = Column(String(255), nullable=True)
|
||||
phone_verified = Column(Boolean, default=False)
|
||||
email_verified = Column(Boolean, default=False)
|
||||
consent_given = Column(Boolean, default=False)
|
||||
consent_date = Column(DateTime, nullable=True)
|
||||
last_interaction = Column(DateTime, nullable=True)
|
||||
total_conversations = Column(Integer, default=0)
|
||||
total_tickets = Column(Integer, default=0)
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||
|
||||
|
||||
class BotConversation(Base):
|
||||
__tablename__ = "bot_conversations"
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
user_id = Column(BigInteger, ForeignKey("bot_users.id"), nullable=False)
|
||||
state = Column(String(50), default="greeting")
|
||||
intent = Column(String(50), nullable=True)
|
||||
inquiry_text = Column(Text, nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||
|
||||
user = relationship("BotUser", lazy="joined")
|
||||
|
||||
|
||||
class BotMessage(Base):
|
||||
__tablename__ = "bot_messages"
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
conversation_id = Column(BigInteger, ForeignKey("bot_conversations.id"), nullable=False)
|
||||
direction = Column(String(10), nullable=False)
|
||||
text = Column(Text, nullable=True)
|
||||
attachment_json = Column(Text, nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
|
||||
conversation = relationship("BotConversation", lazy="joined")
|
||||
|
||||
|
||||
class BotTicket(Base):
|
||||
__tablename__ = "bot_tickets"
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
user_id = Column(BigInteger, ForeignKey("bot_users.id"), nullable=False)
|
||||
title = Column(String(500), nullable=True)
|
||||
description = Column(Text, nullable=True)
|
||||
priority = Column(String(20), default="normal")
|
||||
status = Column(String(20), default="Новая")
|
||||
created_by = Column(String(100), default="bot")
|
||||
assigned_to = Column(BigInteger, nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||
|
||||
user = relationship("BotUser", lazy="joined")
|
||||
|
||||
|
||||
class BotTicketMessage(Base):
|
||||
__tablename__ = "bot_ticket_messages"
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
ticket_id = Column(BigInteger, ForeignKey("bot_tickets.id"), nullable=False)
|
||||
direction = Column(String(10), nullable=False)
|
||||
text = Column(Text, nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
|
||||
ticket = relationship("BotTicket", lazy="joined")
|
||||
|
||||
|
||||
class BotTicketStatus(Base):
|
||||
__tablename__ = "bot_ticket_statuses"
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
ticket_id = Column(BigInteger, ForeignKey("bot_tickets.id"), nullable=False)
|
||||
old_status = Column(String(20), nullable=True)
|
||||
new_status = Column(String(20), nullable=False)
|
||||
changed_by = Column(String(100), nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
|
||||
ticket = relationship("BotTicket", lazy="joined")
|
||||
Reference in New Issue
Block a user