ForcePilot/backend/package/yuxi/channels/adapters/msteams/accounts.py
Kris bd60c15df0 feat(msteams): 新增完整的 Microsoft Teams 适配器模块
实现了 Teams 机器人所需的全功能组件,包括:
- 基础命令解析与帮助卡片生成
- 租户验证与访问控制
- 自定义 UA 与媒体工具
- 消息分块、批注处理与会话管理
- 防抖、缓存与配置路由能力
- 投票、配对、审计与运行时状态管理
- TTS 语音合成与卡片构建工具
- 群组管理与权限控制逻辑
2026-05-12 00:46:44 +08:00

104 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Microsoft Teams 多账户管理。
支持 per-account 实例管理startAccount/probeAccount hooks。
"""
from __future__ import annotations
import time
from typing import Any
from yuxi.utils.logging_config import logger
TOKEN_URL = "https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token"
BOT_SCOPE = "https://api.botframework.com/.default"
class AccountManager:
def __init__(self):
self._accounts: dict[str, dict[str, Any]] = {}
def register_account(
self,
account_id: str,
app_id: str,
app_password: str,
tenant_id: str = "",
label: str = "",
) -> None:
self._accounts[account_id] = {
"account_id": account_id,
"app_id": app_id,
"app_password": app_password,
"tenant_id": tenant_id,
"label": label or account_id,
"registered_at": time.time(),
"status": "registered",
}
logger.info(f"MSTeams account registered: {account_id}")
def unregister_account(self, account_id: str) -> bool:
return self._accounts.pop(account_id, None) is not None
def get_account(self, account_id: str) -> dict[str, Any] | None:
return self._accounts.get(account_id)
def get_all_accounts(self) -> list[dict[str, Any]]:
return list(self._accounts.values())
def list_account_ids(self) -> list[str]:
return list(self._accounts.keys())
@property
def account_count(self) -> int:
return len(self._accounts)
async def probe_account(self, account_id: str) -> dict[str, Any]:
import aiohttp
account = self._accounts.get(account_id)
if not account:
return {"status": "error", "message": f"Account not found: {account_id}"}
data = {
"client_id": account["app_id"],
"client_secret": account["app_password"],
"grant_type": "client_credentials",
"scope": BOT_SCOPE,
}
try:
async with aiohttp.ClientSession() as session:
async with session.post(TOKEN_URL, data=data) as resp:
if resp.status == 200:
result = await resp.json()
token = result.get("access_token")
if token:
account["status"] = "connected"
account["last_probe_at"] = time.time()
return {
"status": "ok",
"account_id": account_id,
"connected": True,
}
except Exception as e:
logger.error(f"MSTeams account probe failed: {account_id}: {e}")
account["status"] = "error"
return {"status": "error", "account_id": account_id, "connected": False}
async def start_account(self, account_id: str, adapter: Any = None) -> bool:
account = self._accounts.get(account_id)
if not account:
return False
if adapter:
adapter._app_id = account["app_id"]
adapter._app_password = account["app_password"]
adapter.config["app_id"] = account["app_id"]
adapter.config["app_password"] = account["app_password"]
if account.get("tenant_id"):
adapter.config["tenant_id"] = account["tenant_id"]
account["status"] = "started"
account["started_at"] = time.time()
logger.info(f"MSTeams account started: {account_id}")
return True