实现了 Teams 机器人所需的全功能组件,包括: - 基础命令解析与帮助卡片生成 - 租户验证与访问控制 - 自定义 UA 与媒体工具 - 消息分块、批注处理与会话管理 - 防抖、缓存与配置路由能力 - 投票、配对、审计与运行时状态管理 - TTS 语音合成与卡片构建工具 - 群组管理与权限控制逻辑
104 lines
3.5 KiB
Python
104 lines
3.5 KiB
Python
"""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
|