Files
pit-router/app/services/bot_service.py

278 lines
8.1 KiB
Python
Raw Normal View History

"""
Bot 服务层
实现 Bot CRUD权限控制Token 管理Agent 绑定等功能
"""
import secrets
import hashlib
from datetime import datetime
from typing import Optional, List, Dict, Any, Tuple
from app.extensions import db
from app.models import Bot, User, Agent
class BotService:
"""Bot 服务类"""
@staticmethod
def generate_token() -> str:
"""生成 Bot API Token"""
return f"bot_{secrets.token_urlsafe(32)}"
@staticmethod
def hash_token(token: str) -> str:
"""Hash Token 用于存储"""
return hashlib.sha256(token.encode()).hexdigest()
@staticmethod
def verify_token(bot: Bot, token: str) -> bool:
"""验证 Token"""
if not bot.token_hash:
return False
return bot.token_hash == BotService.hash_token(token)
@staticmethod
def check_permission(user: User, bot: Bot, action: str) -> bool:
"""
检查用户对 Bot 的权限
Args:
user: 当前用户
bot: Bot 对象
action: 操作类型 ('view', 'use', 'edit', 'delete', 'bind')
Returns:
是否有权限
"""
# 管理员拥有所有权限
if user.role == 'admin':
return True
# 查看/使用权限
if action in ['view', 'use']:
# 系统级 Bot 所有用户可用
if bot.is_system:
return True
# 自己的 Bot
return bot.owner_id == user.id
# 编辑/删除/绑定权限
if action in ['edit', 'delete', 'bind']:
# 只有所有者可以编辑/删除/绑定
return bot.owner_id == user.id
return False
@staticmethod
def create_bot(
name: str,
owner_id: str,
display_name: Optional[str] = None,
avatar: Optional[str] = None,
description: Optional[str] = None,
is_system: bool = False,
agent_id: Optional[str] = None,
capabilities: Optional[List[str]] = None,
config: Optional[Dict[str, Any]] = None
) -> Tuple[Bot, str]:
"""
创建 Bot
Returns:
(Bot 对象, 明文 Token)
"""
# 生成 Token
token = BotService.generate_token()
token_hash = BotService.hash_token(token)
bot = Bot(
name=name,
display_name=display_name or name,
avatar=avatar,
description=description,
owner_id=owner_id,
agent_id=agent_id,
token_hash=token_hash,
is_system=is_system,
capabilities=capabilities or ['chat'],
config=config or BotService.get_default_config(),
status='offline',
created_at=datetime.utcnow()
)
db.session.add(bot)
db.session.commit()
return bot, token
@staticmethod
def get_default_config() -> Dict[str, Any]:
"""获取默认 Bot 配置"""
return {
"model": "gpt-4o",
"temperature": 0.7,
"max_tokens": 4096,
"system_prompt": "",
"rate_limit": {
"requests_per_minute": 60,
"tokens_per_day": 100000
},
"features": {
"streaming": True,
"markdown": True,
"code_highlight": True,
"file_upload": False
},
"context": {
"max_history": 20,
"summary_threshold": 10
}
}
@staticmethod
def get_bot_by_id(bot_id: str) -> Optional[Bot]:
"""根据 ID 获取 Bot"""
return Bot.query.get(bot_id)
@staticmethod
def get_bot_by_name(name: str) -> Optional[Bot]:
"""根据名称获取 Bot"""
return Bot.query.filter_by(name=name).first()
@staticmethod
def get_bots_by_owner(owner_id: str) -> List[Bot]:
"""获取用户的所有 Bot"""
return Bot.query.filter_by(owner_id=owner_id).order_by(Bot.created_at.desc()).all()
@staticmethod
def get_system_bots() -> List[Bot]:
"""获取所有系统级 Bot"""
return Bot.query.filter_by(is_system=True).order_by(Bot.created_at.desc()).all()
@staticmethod
def get_available_bots(user: User) -> List[Bot]:
"""
获取用户可用的所有 Bot
- 自己的 Bot
- 系统级 Bot
"""
if user.role == 'admin':
return Bot.query.order_by(Bot.created_at.desc()).all()
return Bot.query.filter(
(Bot.owner_id == user.id) | (Bot.is_system == True)
).order_by(Bot.created_at.desc()).all()
@staticmethod
def update_bot(
bot: Bot,
display_name: Optional[str] = None,
avatar: Optional[str] = None,
description: Optional[str] = None,
capabilities: Optional[List[str]] = None,
config: Optional[Dict[str, Any]] = None
) -> Bot:
"""更新 Bot 信息"""
if display_name is not None:
bot.display_name = display_name
if avatar is not None:
bot.avatar = avatar
if description is not None:
bot.description = description
if capabilities is not None:
bot.capabilities = capabilities
if config is not None:
bot.config = {**bot.config, **config} if bot.config else config
db.session.commit()
return bot
@staticmethod
def bind_agent(bot: Bot, agent_id: Optional[str]) -> Bot:
"""
绑定/解绑 Agent
Args:
bot: Bot 对象
agent_id: Agent IDNone 表示解绑
"""
bot.agent_id = agent_id
# 更新状态
if agent_id:
agent = Agent.query.get(agent_id)
if agent and agent.status == 'online':
bot.status = 'online'
else:
bot.status = 'offline'
else:
bot.status = 'offline'
db.session.commit()
return bot
@staticmethod
def regenerate_token(bot: Bot) -> str:
"""重新生成 Token"""
token = BotService.generate_token()
bot.token_hash = BotService.hash_token(token)
db.session.commit()
return token
@staticmethod
def delete_bot(bot: Bot) -> bool:
"""删除 Bot"""
try:
db.session.delete(bot)
db.session.commit()
return True
except Exception as e:
db.session.rollback()
return False
@staticmethod
def update_status(bot: Bot, status: str) -> Bot:
"""更新 Bot 状态"""
bot.status = status
bot.last_active_at = datetime.utcnow()
db.session.commit()
return bot
@staticmethod
def sync_agent_status(bot: Bot) -> Bot:
"""
同步 Agent 状态到 Bot
Agent 状态变化时调用
"""
if bot.agent_id:
agent = Agent.query.get(bot.agent_id)
if agent:
bot.status = agent.status
else:
bot.status = 'offline'
else:
bot.status = 'offline'
bot.last_active_at = datetime.utcnow()
db.session.commit()
return bot
@staticmethod
def get_bot_stats(bot: Bot) -> Dict[str, Any]:
"""获取 Bot 统计信息"""
from app.models import Session, Message
session_count = Session.query.filter_by(bot_id=bot.id).count()
message_count = Message.query.filter_by(bot_id=bot.id).count()
return {
'bot_id': bot.id,
'name': bot.name,
'status': bot.status,
'session_count': session_count,
'message_count': message_count,
'is_system': bot.is_system,
'created_at': bot.created_at.isoformat() if bot.created_at else None,
'last_active_at': bot.last_active_at.isoformat() if bot.last_active_at else None,
}