ForcePilot/backend/package/yuxi/channels/adapters/mattermost/setup.py

227 lines
6.8 KiB
Python
Raw Normal View History

from __future__ import annotations
import os
import sys
from typing import Any
def is_mattermost_configured(account: dict) -> bool:
server_url = account.get("server_url", "")
bot_token = account.get("bot_token", "")
return bool(server_url and bot_token)
def normalize_mattermost_base_url(url: str) -> str | None:
if not url:
return None
url = url.strip().rstrip("/")
parsed = url.split("/api/v4")[0] if "/api/v4" in url else url
if parsed.startswith("http://") or parsed.startswith("https://"):
return parsed
return None
def resolve_configured(account: dict) -> bool:
return is_mattermost_configured(account)
def get_configured_score(account: dict) -> int:
score = 0
if account.get("server_url"):
score += 1
if account.get("bot_token"):
score += 1
return score
def check_env_shortcut_available(config: dict) -> bool:
token = os.getenv("MATTERMOST_BOT_TOKEN", "")
url = os.getenv("MATTERMOST_SERVER_URL", "") or os.getenv("MATTERMOST_URL", "")
has_config_token = bool(config.get("bot_token", ""))
has_config_url = bool(config.get("server_url", ""))
return bool(token and url) and not (has_config_token and has_config_url)
def get_intro_note() -> str:
return (
"要连接 Mattermost你需要\n"
"1. 一个 Mattermost 服务器实例\n"
"2. 一个 Bot Account 的 Personal Access Token\n\n"
"创建 Bot Account 步骤:\n"
"- 进入 System Console → Integrations → Bot Accounts\n"
"- 创建 Bot Account 并获取 Token"
)
def should_show_intro_note(config: dict) -> bool:
return not is_mattermost_configured(config)
def build_status_info(account: dict) -> dict:
configured = is_mattermost_configured(account)
return {
"channelLabel": "Mattermost",
"configured": configured,
"configuredLabel": "configured" if configured else None,
"unconfiguredLabel": "needs token + url" if not configured else None,
"score": get_configured_score(account),
}
def inspect_credentials(config: dict) -> dict:
return {
"accountConfigured": is_mattermost_configured(config),
"hasConfiguredValue": bool(config.get("bot_token", "")),
"hasEnvValue": bool(os.getenv("MATTERMOST_BOT_TOKEN", "")),
}
def get_keep_prompt() -> str:
return "Mattermost bot token already configured. Keep it?"
def get_env_prompt() -> str:
return "Mattermost credentials found in environment variables. Use them?"
def disable_mattermost(config: dict) -> dict:
return {**config, "mattermost": {"enabled": False}}
def interactive_setup() -> dict[str, Any]:
"""CLI 交互式 Mattermost 配置向导。
返回配置 dict可存入 config
"""
import os
print("\n=== Mattermost Bot 配置向导 ===\n")
env_url = os.getenv("MATTERMOST_SERVER_URL", "") or os.getenv("MATTERMOST_URL", "")
env_token = os.getenv("MATTERMOST_BOT_TOKEN", "")
if env_token or env_url:
print("[检测到环境变量配置]")
print(f" MATTERMOST_SERVER_URL: {'已设置' if env_url else '未设置'}")
print(f" MATTERMOST_BOT_TOKEN: {'已设置' if env_token else '未设置'}")
use_env = _prompt_yn("是否使用环境变量配置?")
if use_env:
print("\n✅ 已从环境变量加载配置")
return {
"server_url": env_url,
"bot_token": env_token,
"token_source": "env",
}
print(get_intro_note() + "\n")
server_url = _prompt(
"Mattermost 服务器 URL",
default=env_url or "https://mattermost.example.com",
validate_fn=normalize_mattermost_base_url,
normalize_fn=normalize_mattermost_base_url,
)
bot_token = _prompt(
"Bot Token (Personal Access Token)",
default=env_token or "",
secret=True,
)
dm_policy = _prompt("DM 安全策略", default="open", choices=["open", "allowlist", "pairing", "disabled"])
group_policy = _prompt("群组安全策略", default="open", choices=["open", "allowlist", "disabled"])
print("\n--- 配置摘要 ---")
print(f" Server URL: {server_url}")
print(f" Bot Token: {'[已设置]' if bot_token else '[未设置]'}")
print(f" DM Policy: {dm_policy}")
print(f" Group Policy: {group_policy}")
print()
confirm = _prompt_yn("确认配置?")
if not confirm:
print("已取消配置")
return {}
return {
"server_url": server_url,
"bot_token": bot_token,
"dm_policy": dm_policy,
"group_policy": group_policy,
}
def _prompt(
label: str,
default: str = "",
choices: list[str] | None = None,
secret: bool = False,
validate_fn: Any = None,
normalize_fn: Any = None,
) -> str:
if choices:
hint = f" [{', '.join(choices)}]"
default_hint = f" (默认: {default})" if default else ""
prompt = f"{label}{hint}{default_hint}: "
else:
default_hint = f" (默认: {default})" if default else ""
prompt = f"{label}{default_hint}: "
try:
user_input = input(prompt).strip()
except (KeyboardInterrupt, EOFError):
print()
sys.exit(0)
if not user_input:
user_input = default
if normalize_fn:
normalized = normalize_fn(user_input)
if normalized is not None:
user_input = normalized
if validate_fn:
validated = validate_fn(user_input)
if validated is None:
print(f" 输入无效: {user_input},请重试")
return _prompt(label, default, choices, secret, validate_fn, normalize_fn)
user_input = validated
return user_input
def _prompt_yn(question: str) -> bool:
try:
answer = input(f"{question} (y/n): ").strip().lower()
return answer in ("y", "yes", "")
except (KeyboardInterrupt, EOFError):
print()
return False
def validate_config(config: dict) -> list[str]:
"""验证配置是否完整并有效。返回错误信息列表。"""
errors = []
server_url = config.get("server_url", "")
if not server_url:
errors.append("server_url is required")
elif normalize_mattermost_base_url(server_url) is None:
errors.append(f"Invalid server_url format: {server_url}")
bot_token = config.get("bot_token", "")
if not bot_token:
errors.append("bot_token is required")
dm_policy = config.get("dm_policy", "")
if dm_policy and dm_policy not in ("open", "allowlist", "pairing", "disabled"):
errors.append(f"Invalid dm_policy: {dm_policy}")
group_policy = config.get("group_policy", "")
if group_policy and group_policy not in ("open", "allowlist", "disabled"):
errors.append(f"Invalid group_policy: {group_policy}")
return errors