maxbot 0.7.6

Автоматизация работы с чат-ботами на платформе MAX (max.ru)
Documentation
// Стресс-тест ограничения RPS: отправляет сообщения на максимальной скорости до первой ошибки 429.
//
// Использование:
//   cargo run --example stress_test -- <rps> [token] [chat_id]
//
//   rps     - желаемое количество запросов в секунду (целое число, >0) – обязательный
//   token   - необязательно, иначе берётся из MAXBOT_TOKEN
//   chat_id - необязательно, иначе берётся из CHAT_ID
//
// Пример:
//   cargo run --example stress_test -- 50 "Basic dXNlcjpwYXNz" 123456789
//   или с переменными окружения:
//   export MAXBOT_TOKEN="Basic dXNlcjpwYXNz"
//   export CHAT_ID=123456789
//   cargo run --example stress-test -- 50

use maxbot::{MaxClient, SendMessageParamsBuilder, set_global_max_rps, set_global_base_url};
use std::time::{Duration, Instant};
use std::env;

#[tokio::main]
async fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        eprintln!("Usage: {} <rps> [token] [chat_id]", args[0]);
        eprintln!("  rps     - requests per second (positive integer)");
        eprintln!("  token   - optional, fallback to MAXBOT_TOKEN env var");
        eprintln!("  chat_id - optional, fallback to CHAT_ID env var");
        std::process::exit(1);
    }

    let rps: usize = match args[1].parse() {
        Ok(v) if v > 0 => v,
        _ => {
            eprintln!("Invalid RPS, must be positive integer");
            std::process::exit(1);
        }
    };

    // Токен: аргумент или переменная окружения
    let token = if args.len() >= 3 {
        args[2].clone()
    } else {
        match env::var("MAXBOT_TOKEN") {
            Ok(v) => v,
            Err(_) => {
                eprintln!("Missing MAXBOT_TOKEN (neither argument nor env)");
                std::process::exit(1);
            }
        }
    };

    // Chat ID: аргумент или переменная окружения
    let chat_id: i64 = if args.len() >= 4 {
        match args[3].parse() {
            Ok(v) => v,
            Err(_) => {
                eprintln!("Invalid CHAT_ID argument");
                std::process::exit(1);
            }
        }
    } else {
        match env::var("CHAT_ID") {
            Ok(v) => match v.parse() {
                Ok(id) => id,
                Err(_) => {
                    eprintln!("Invalid CHAT_ID in environment");
                    std::process::exit(1);
                }
            },
            Err(_) => {
                eprintln!("Missing CHAT_ID (neither argument nor env)");
                std::process::exit(1);
            }
        }
    };

    // Базовый URL (прокси) – опционально через переменную окружения
    if let Ok(proxy_url) = env::var("MAXBOT_PROXY") {
        println!("Using proxy base URL: {}", proxy_url);
        set_global_base_url(proxy_url);
    }

    // Устанавливаем глобальное ограничение RPS
    set_global_max_rps(rps);
    println!("Starting stress test with {} requests per second.", rps);
    println!("Sending messages to chat {}...", chat_id);

    let client = MaxClient::new(token);
    let interval = Duration::from_millis(1000 / rps as u64);
    let mut msg_counter = 0;
    let start_time = Instant::now();

    loop {
        msg_counter += 1;
        let text = format!("Stress test message #{}", msg_counter);
        let builder = SendMessageParamsBuilder::new()
            .text(text)
            .chat_id(chat_id)
            .silent();

        let before = Instant::now();
        let result = client.send_message_builder(builder).await;
        let elapsed = before.elapsed();

        match result {
            Ok(ids) => {
                let mid = ids.first().unwrap();
                print!("\rSent #{} (mid={}, took {} ms)", msg_counter, mid, elapsed.as_millis());
                // Небольшая пауза, чтобы не перегружать CPU.
                tokio::time::sleep(interval).await;
            }
            Err(e) => {
                let err_str = e.to_string();
                if err_str.contains("429") || err_str.contains("too.many.requests") {
                    let total_sec = start_time.elapsed().as_secs_f64();
                    let successful = msg_counter - 1;
                    println!("\n\nRate limit hit after {} successful messages.", successful);
                    println!("Error: {}", err_str);
                    println!("Test finished. Sent {} messages in {:.2} seconds (avg {:.2} msg/sec).",
                             successful, total_sec, successful as f64 / total_sec);
                    return;
                } else {
                    eprintln!("\nUnexpected error: {}", err_str);
                    std::process::exit(1);
                }
            }
        }
    }
}