Files
wecomteam--wecom-cli/src/cmd/init.rs
T
2026-07-13 12:31:38 +08:00

111 lines
3.6 KiB
Rust

use std::io::IsTerminal;
use crate::auth;
use crate::mcp;
use crate::mcp::config::McpBindSource;
use anyhow::Result;
use clap::{ArgMatches, Args, Command, FromArgMatches};
#[derive(Args)]
pub struct InitArgs {
/// 跳过交互选择,直接使用扫码方式接入
#[arg(long)]
pub noninteractive: bool,
/// 扫码时不自动在浏览器打开二维码页面
#[arg(long = "no-open")]
pub no_open: bool,
}
pub fn build_init_cmd() -> Command {
InitArgs::augment_args(Command::new("init").about("初始化企业微信机器人配置"))
.disable_help_flag(true)
}
/// Handle the `init` subcommand: prompt for bot credentials, persist them, and verify via MCP config fetch.
pub async fn handle_init_cmd(matches: &ArgMatches) -> Result<()> {
let args = InitArgs::from_arg_matches(matches)?;
if !args.noninteractive && !std::io::stderr().is_terminal() {
anyhow::bail!(
"当前环境不支持交互式操作,请使用 --noninteractive 参数:\n {} init --noninteractive",
env!("CARGO_BIN_NAME")
);
}
cliclack::intro("企业微信机器人初始化")?;
let (bot, bind_source) = if args.noninteractive {
(init_qrcode(args.no_open).await?, McpBindSource::Qrcode)
} else {
let method: &str = cliclack::select("请选择企微机器人接入方式:")
.item("qrcode", "扫码接入(推荐)", "")
.item("manual", "手动输入 Bot ID 和 Secret", "")
.interact()?;
match method {
"qrcode" => (init_qrcode(args.no_open).await?, McpBindSource::Qrcode),
_ => (init_manual().await?, McpBindSource::Interactive),
}
};
auth::set_bot_info(&bot)?;
verify_and_finish(bind_source).await
}
/// 扫码接入流程
async fn init_qrcode(no_open: bool) -> Result<auth::Bot> {
auth::scan_qrcode_for_bot(no_open).await
}
/// 手动输入 Bot ID 和 Secret
async fn init_manual() -> Result<auth::Bot> {
let bot_id: String = cliclack::input("企业微信机器人 Bot ID")
.placeholder("请输入企业微信机器人ID")
.interact()?;
let bot_secret: String = cliclack::password("企业微信机器人 Secret")
.mask('*')
.interact()?;
Ok(auth::Bot::new(bot_id, bot_secret))
}
/// 验证凭证并完成初始化
async fn verify_and_finish(bind_source: McpBindSource) -> Result<()> {
let spinner = cliclack::spinner();
spinner.start("正在验证企业微信机器人凭证...");
if let Err(e) = mcp::config::fetch_mcp_config(bind_source).await {
spinner.stop("企业微信机器人凭证验证失败");
let mut output_errmsg: String = "验证企业微信机器人凭证失败".to_owned();
match &e {
mcp::error::FetchMcpConfigError::Api(resp) => {
if let Some(ref msg) = resp.errmsg {
if !msg.is_empty() {
output_errmsg = msg.clone();
}
}
}
mcp::error::FetchMcpConfigError::Http(http_err) => {
output_errmsg = format!("{} HTTP返回状态码 {}", output_errmsg, http_err.status);
}
mcp::error::FetchMcpConfigError::Other(other_err) => {
output_errmsg = other_err.to_string();
}
}
// Credentials invalid or server unreachable — rollback
auth::clear_bot_info();
mcp::config::clear_mcp_config();
cliclack::outro("初始化失败 ❌")?;
anyhow::bail!(output_errmsg);
}
spinner.stop("企业微信机器人凭证验证成功");
cliclack::outro("初始化完成 ✅")?;
Ok(())
}