227 lines
6.8 KiB
Python
227 lines
6.8 KiB
Python
|
|
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
|