实现了完整的Flock渠道接入能力,包含消息收发、Webhook事件监听、账号配置、安全校验、媒体文件处理等功能,支持私聊和群组聊天,适配ForcePilot插件规范。
116 lines
3.6 KiB
Python
116 lines
3.6 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import time
|
|
|
|
import httpx
|
|
|
|
from .config import _apply_env_overrides, _dict_to_account
|
|
from .constants import BASE_URL, ENDPOINT_USER_GET_INFO, ENDPOINT_USER_PUBLIC_PROFILE, PROBE_TIMEOUT
|
|
from .types import ProbeResult
|
|
from .utils import build_auth_headers, create_http_client, flock_api_call
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def probe(config: dict, account_id: str = "default") -> ProbeResult:
|
|
account_data = config.get("accounts", {}).get(account_id, {})
|
|
account = _dict_to_account(account_data)
|
|
account = _apply_env_overrides(account)
|
|
|
|
if not account.bot_token:
|
|
return ProbeResult(ok=False, reason="bot_token not configured")
|
|
|
|
start = time.monotonic()
|
|
client = create_http_client(timeout=PROBE_TIMEOUT)
|
|
try:
|
|
headers = build_auth_headers(account.bot_token)
|
|
resp = await client.post(
|
|
f"{BASE_URL}{ENDPOINT_USER_GET_INFO}",
|
|
json={},
|
|
headers=headers,
|
|
)
|
|
latency_ms = (time.monotonic() - start) * 1000
|
|
|
|
if resp.status_code == 200:
|
|
logger.info("Flock probe OK for account %s", account_id)
|
|
return ProbeResult(ok=True, latency_ms=latency_ms)
|
|
|
|
error_code = _try_extract_error(resp)
|
|
logger.warning(
|
|
"Flock probe failed for account %s: %s (HTTP %d)",
|
|
account_id,
|
|
error_code,
|
|
resp.status_code,
|
|
)
|
|
return ProbeResult(ok=False, reason=error_code, latency_ms=latency_ms)
|
|
|
|
except Exception as e:
|
|
latency_ms = (time.monotonic() - start) * 1000
|
|
logger.warning("Flock probe error for account %s: %s", account_id, e)
|
|
return ProbeResult(ok=False, reason=str(e), latency_ms=latency_ms)
|
|
finally:
|
|
await client.aclose()
|
|
|
|
|
|
def _try_extract_error(resp: httpx.Response) -> str:
|
|
try:
|
|
body = resp.json()
|
|
return body.get("error", f"HTTP {resp.status_code}")
|
|
except Exception:
|
|
return f"HTTP {resp.status_code}"
|
|
|
|
|
|
def build_summary(config: dict) -> dict:
|
|
accounts = config.get("accounts", {})
|
|
configured = any(bool(a.get("bot_token") or a.get("incoming_webhook_url")) for a in accounts.values())
|
|
return {
|
|
"channel": "flock",
|
|
"configured": configured,
|
|
"accounts_count": len(accounts) if accounts else 1,
|
|
}
|
|
|
|
|
|
def build_account_snapshot(account: dict, config: dict) -> dict:
|
|
acct = _dict_to_account(account)
|
|
acct = _apply_env_overrides(acct)
|
|
return {
|
|
"account_id": acct.account_id,
|
|
"configured": bool(acct.bot_token or acct.incoming_webhook_url),
|
|
"enabled": acct.enabled,
|
|
"dm_policy": acct.dm_policy,
|
|
"group_policy": acct.group_policy,
|
|
}
|
|
|
|
|
|
async def get_user_info(
|
|
user_id: str | None = None,
|
|
*,
|
|
config: dict,
|
|
account_id: str = "default",
|
|
) -> dict | None:
|
|
account_data = config.get("accounts", {}).get(account_id, {})
|
|
account = _dict_to_account(account_data)
|
|
account = _apply_env_overrides(account)
|
|
|
|
if not account.bot_token:
|
|
logger.warning("Flock get_user_info: bot_token not configured")
|
|
return None
|
|
|
|
if user_id:
|
|
payload = {"userId": user_id}
|
|
endpoint = ENDPOINT_USER_PUBLIC_PROFILE
|
|
else:
|
|
payload = {}
|
|
endpoint = ENDPOINT_USER_GET_INFO
|
|
|
|
client = create_http_client()
|
|
try:
|
|
result = await flock_api_call(client, endpoint, account.bot_token, payload)
|
|
return result
|
|
except Exception as e:
|
|
logger.error("Flock get_user_info failed: %s", e)
|
|
return None
|
|
finally:
|
|
await client.aclose()
|