1use wx_bot_sdk::{
2 BotAccountOptions, MultiStartOptions, MultiWeixinBot, MultiWeixinBotOptions,
3 auth::accounts::list_weixin_account_ids, bot::handler, resolve_weixin_account,
4};
5
6#[tokio::main]
7async fn main() -> wx_bot_sdk::Result<()> {
8 let accounts = read_accounts();
9 if accounts.is_empty() {
10 eprintln!("Usage:");
11 eprintln!(" WEIXIN_BOT_TOKENS=\"token1,token2\" cargo run --example multi_echo");
12 eprintln!(" cargo run --example multi_echo -- token1 token2");
13 eprintln!("\n如果已通过扫码登录保存账号,也可以直接运行:");
14 eprintln!(" cargo run --example multi_echo");
15 std::process::exit(1);
16 }
17
18 let multi = MultiWeixinBot::new(MultiWeixinBotOptions {
19 accounts,
20 state_dir: None,
21 });
22
23 println!("starting accounts: {:?}", multi.account_ids());
24 multi
25 .start(MultiStartOptions {
26 long_poll_timeout_ms: None,
27 on_message: handler(|ctx| async move {
28 println!("[{}] 来自 {}: {}", ctx.account_id, ctx.from, ctx.body);
29 Ok(Some(format!("你说了: {}", ctx.body)))
30 }),
31 })
32 .await?;
33
34 tokio::signal::ctrl_c().await?;
35 println!("stopping all accounts...");
36 multi.stop().await?;
37 multi.join().await?;
38 Ok(())
39}
40
41fn read_accounts() -> Vec<BotAccountOptions> {
42 let tokens = read_tokens();
43 if !tokens.is_empty() {
44 return tokens
45 .into_iter()
46 .map(|token| BotAccountOptions {
47 token,
48 account_id: None,
49 base_url: None,
50 cdn_base_url: None,
51 })
52 .collect();
53 }
54
55 list_weixin_account_ids()
56 .into_iter()
57 .filter_map(|account_id| match resolve_weixin_account(&account_id) {
58 Ok(account) => account.token.map(|token| BotAccountOptions {
59 token,
60 account_id: Some(account.account_id),
61 base_url: Some(account.base_url),
62 cdn_base_url: Some(account.cdn_base_url),
63 }),
64 Err(err) => {
65 eprintln!("跳过账号 {account_id}: {err}");
66 None
67 }
68 })
69 .collect()
70}
71
72fn read_tokens() -> Vec<String> {
73 let mut tokens = std::env::args()
74 .skip(1)
75 .flat_map(|arg| split_tokens(&arg).collect::<Vec<_>>())
76 .collect::<Vec<_>>();
77
78 if tokens.is_empty()
79 && let Ok(raw) = std::env::var("WEIXIN_BOT_TOKENS")
80 {
81 tokens = split_tokens(&raw).collect();
82 }
83
84 tokens
85}
86
87fn split_tokens(raw: &str) -> impl Iterator<Item = String> + '_ {
88 raw.split(',')
89 .map(str::trim)
90 .filter(|s| !s.is_empty())
91 .map(ToOwned::to_owned)
92}