diff --git a/src/cli.rs b/src/cli.rs index 6a13afc..3887c9b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -77,10 +77,36 @@ pub struct AccountDeleteArgs { } #[derive(Debug, Args)] +#[command(after_help = "Authentication Modes: + 1. Saved Account Mode: + --account + + 2. Explicit Credentials Mode: + --bot-token --user-id [--route-tag ] + +Usage Rules: + - Omit all auth params to use the first saved account. + - Or use exactly one of the modes above. + - --account cannot be combined with --bot-token or --user-id. + - In Explicit mode, both --bot-token and --user-id are required.")] pub struct GetContextTokenArgs { - /// Saved account user ID. If omitted, the first saved account is used. - #[arg(long)] + #[arg( + long, + help = "Saved account index from `wechat-cli account list`. Required for Saved Account Mode." + )] + pub account: Option, + #[arg(long, help = "Target user ID. Required in Explicit Credentials Mode.")] pub user_id: Option, + #[arg( + long, + help = "Explicit bot token. Required in Explicit Credentials Mode." + )] + pub bot_token: Option, + #[arg( + long, + help = "Optional route tag used only in Explicit Credentials Mode." + )] + pub route_tag: Option, } #[derive(Debug, Args)] diff --git a/src/commands/get_context_token.rs b/src/commands/get_context_token.rs index 3a24eb8..d9d9b18 100644 --- a/src/commands/get_context_token.rs +++ b/src/commands/get_context_token.rs @@ -2,17 +2,35 @@ use anyhow::{Context, Result, bail}; use crate::{ commands::account::{build_client, resolve_user_id}, - storage, + commands::send::{SendTarget, resolve_send_target}, + storage::{self}, wechat::api::is_session_expired, wechat::models::InboundMessage, }; -pub async fn run(user_id: Option<&str>) -> Result<()> { - let resolved_id = resolve_user_id(user_id)?; - let session = storage::get_account_data(&resolved_id) - .with_context(|| format!("failed to load account data for `{resolved_id}`"))?; - let client = build_client(&session); - let user_id = session.user_id; +pub async fn run( + account: Option, + user_id: Option<&str>, + bot_token: Option<&str>, + route_tag: Option<&str>, +) -> Result<()> { + let target = if account.is_none() && user_id.is_none() && bot_token.is_none() { + let resolved_id = resolve_user_id(None)?; + let session = storage::get_account_data(&resolved_id) + .with_context(|| format!("failed to load account data for `{resolved_id}`"))?; + SendTarget::Saved { + user_id: resolved_id, + client: build_client(&session), + } + } else { + resolve_send_target(account, user_id, bot_token, route_tag)? + }; + + let (user_id, client) = match target { + SendTarget::Saved { user_id, client } => (user_id, client), + SendTarget::Explicit { user_id, client } => (user_id, client), + }; + let mut consecutive_errors = 0u32; eprintln!("waiting for the bound user to send a message for `{user_id}`; press Ctrl+C to stop"); @@ -72,3 +90,56 @@ fn extract_context_token(user_id: &str, message: &InboundMessage) -> Option, user_id: Option<&str>, bot_token: Option<&str>, diff --git a/src/main.rs b/src/main.rs index 6cdea4d..e904897 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,13 @@ async fn main() -> AnyResult<()> { AccountCommand::Delete(args) => commands::account::delete_account(args.account)?, }, Command::GetContextToken(args) => { - commands::get_context_token::run(args.user_id.as_deref()).await?; + commands::get_context_token::run( + args.account, + args.user_id.as_deref(), + args.bot_token.as_deref(), + args.route_tag.as_deref(), + ) + .await?; } Command::Send(args) => { commands::send::run(