Skip to content

Token API Reference

The Token struct manages authentication credentials for QQ Guild Bot API. It handles app ID and secret storage, access token generation, and automatic token refresh for secure API communication.

Overview

rust
use botrs::{Token, Result};

// Create token with credentials
let token = Token::new("your_app_id", "your_secret");

// Create from environment variables
let token = Token::from_env()?;

// Get authorization header for API requests
let auth_header = token.authorization_header().await?;

The Token handles the OAuth 2.0 flow with QQ's API, automatically fetching and refreshing access tokens as needed.

Constructor Methods

new

Creates a new token with the provided app ID and secret.

rust
pub fn new(app_id: impl Into<String>, secret: impl Into<String>) -> Self

Parameters

  • app_id: The bot's application ID from QQ Developer Portal
  • secret: The bot's secret key from QQ Developer Portal

Returns

A new Token instance.

Example

rust
let token = Token::new("123456789", "your_secret_key");

from_env

Creates a token from environment variables.

rust
pub fn from_env() -> Result<Self>

Looks for the following environment variables:

  • QQ_BOT_APP_ID: The bot's application ID
  • QQ_BOT_SECRET: The bot's secret key

Returns

A Result containing the token if both environment variables are found.

Example

rust
// Set environment variables first:
// export QQ_BOT_APP_ID=123456789
// export QQ_BOT_SECRET=your_secret_key

let token = Token::from_env()?;

Access Methods

app_id

Gets the application ID.

rust
pub fn app_id(&self) -> &str

Returns

A string slice containing the app ID.

Example

rust
let token = Token::new("123456789", "secret");
assert_eq!(token.app_id(), "123456789");

secret

Gets the secret key.

rust
pub fn secret(&self) -> &str

Returns

A string slice containing the secret.

Warning: Be careful when using this method. Avoid logging or exposing the secret.

Example

rust
let token = Token::new("app_id", "secret123");
assert_eq!(token.secret(), "secret123");

Authentication Methods

authorization_header

Generates the authorization header value for API requests.

rust
pub async fn authorization_header(&self) -> Result<String>

This method automatically handles:

  • Fetching access tokens from QQ's API
  • Token caching and refresh
  • Error handling for authentication failures

Returns

A Result containing the authorization header value in the format "QQBot {access_token}".

Example

rust
let token = Token::new("valid_app_id", "valid_secret");
let auth_header = token.authorization_header().await?;
assert!(auth_header.starts_with("QQBot "));

// Use with HTTP client
let response = client
    .get("https://api.sgroup.qq.com/users/@me")
    .header("Authorization", auth_header)
    .send()
    .await?;

bot_token

Generates the bot token for WebSocket authentication.

rust
pub async fn bot_token(&self) -> Result<String>

This is an alias for authorization_header() that provides the same token format required for gateway connections.

Returns

A Result containing the bot token string.

Example

rust
let token = Token::new("app_id", "secret");
let bot_token = token.bot_token().await?;

// Use for WebSocket authentication
let identify = Identify {
    token: bot_token,
    intents: intents.bits(),
    // ... other fields
};

Validation Methods

validate

Validates that the token has non-empty app ID and secret.

rust
pub fn validate(&self) -> Result<()>

Returns

Ok(()) if the token is valid, otherwise returns a BotError::Auth.

Example

rust
let token = Token::new("123", "secret");
assert!(token.validate().is_ok());

let invalid_token = Token::new("", "secret");
assert!(invalid_token.validate().is_err());

Utility Methods

safe_display

Safely formats the token for logging purposes.

rust
pub fn safe_display(&self) -> String

This method masks the secret to prevent accidental exposure in logs while keeping the app ID visible.

Returns

A string representation safe for logging.

Example

rust
let token = Token::new("123456", "verylongsecret123");
let display = token.safe_display();
println!("{}", display); // "Token { app_id: 123456, secret: very****123 }"

Error Handling

Token operations can fail for several reasons:

Authentication Errors

rust
match token.authorization_header().await {
    Ok(header) => {
        // Use the header for API calls
    }
    Err(BotError::Auth(msg)) => {
        eprintln!("Authentication failed: {}", msg);
        // Check app ID and secret
    }
    Err(BotError::Connection(msg)) => {
        eprintln!("Connection error: {}", msg);
        // Check network connectivity
    }
    Err(e) => {
        eprintln!("Unexpected error: {}", e);
    }
}

Common Error Scenarios

Invalid Credentials

rust
// Wrong app ID or secret
let token = Token::new("invalid_id", "wrong_secret");
match token.authorization_header().await {
    Err(BotError::Api { code: 401, .. }) => {
        println!("Invalid credentials");
    }
    _ => {}
}

Network Issues

rust
// Connection timeout or network error
match token.authorization_header().await {
    Err(BotError::Connection(msg)) => {
        println!("Network error: {}", msg);
        // Implement retry logic
    }
    _ => {}
}

Configuration Errors

rust
// Missing environment variables
match Token::from_env() {
    Err(BotError::Config(msg)) => {
        println!("Configuration error: {}", msg);
        // Check environment variables
    }
    _ => {}
}

Security Considerations

Secret Protection

The Token struct implements several security measures:

  1. Debug Safety: The Debug implementation redacts the secret
  2. Safe Display: The safe_display() method masks sensitive parts
  3. No Secret Exposure: Avoid calling secret() unless absolutely necessary
rust
let token = Token::new("app_id", "secret123");

// Safe for logging
println!("{:?}", token); // Shows [REDACTED] for secret
println!("{}", token.safe_display()); // Shows masked secret

// Avoid this in production logs
println!("Secret: {}", token.secret()); // Exposes full secret

Environment Variables

When using from_env(), ensure environment variables are secure:

bash
# Good: Set in secure environment
export QQ_BOT_APP_ID=123456789
export QQ_BOT_SECRET=your_secret_key

# Bad: Don't put in shell history or scripts
echo "QQ_BOT_SECRET=secret123" >> ~/.bashrc

Token Storage

rust
// Good: Create token when needed
async fn create_bot() -> Result<()> {
    let token = Token::from_env()?;
    let client = Client::new(app_id, handler)
        .token(token)
        .build()
        .await?;
    Ok(())
}

// Avoid: Storing tokens in plain text files
// Don't serialize tokens to JSON/YAML configuration files

Advanced Usage

Custom Token Refresh

The token automatically refreshes access tokens, but you can observe the process:

rust
use tracing::{info, warn};

let token = Token::new("app_id", "secret");

// This will trigger token fetch on first call
match token.authorization_header().await {
    Ok(header) => {
        info!("Successfully obtained access token");
        // Token is cached for subsequent calls
    }
    Err(e) => {
        warn!("Token fetch failed: {}", e);
    }
}

// Subsequent calls use cached token (if not expired)
let header2 = token.authorization_header().await?;

Token Validation in Production

rust
async fn validate_bot_config() -> Result<()> {
    let token = Token::from_env()?;
    
    // Validate credentials format
    token.validate()?;
    
    // Test actual authentication
    match token.authorization_header().await {
        Ok(_) => {
            println!("Bot credentials verified");
            Ok(())
        }
        Err(e) => {
            eprintln!("Credential verification failed: {}", e);
            Err(e)
        }
    }
}

Multiple Bot Support

rust
struct MultiBotManager {
    bots: HashMap<String, Token>,
}

impl MultiBotManager {
    fn new() -> Self {
        Self {
            bots: HashMap::new(),
        }
    }
    
    fn add_bot(&mut self, name: String, app_id: String, secret: String) {
        let token = Token::new(app_id, secret);
        self.bots.insert(name, token);
    }
    
    async fn get_auth_header(&self, bot_name: &str) -> Result<String> {
        let token = self.bots.get(bot_name)
            .ok_or_else(|| BotError::config("Bot not found"))?;
        token.authorization_header().await
    }
}

Integration Examples

With HTTP Client

rust
use reqwest::Client;

async fn make_api_call(token: &Token) -> Result<serde_json::Value> {
    let client = Client::new();
    let auth_header = token.authorization_header().await?;
    
    let response = client
        .get("https://api.sgroup.qq.com/users/@me")
        .header("Authorization", auth_header)
        .send()
        .await?;
    
    if response.status().is_success() {
        Ok(response.json().await?)
    } else {
        Err(BotError::api(
            response.status().as_u16() as u32,
            "API call failed".to_string()
        ))
    }
}

With BotRS Client

rust
use botrs::{Client, EventHandler};

struct MyBot;

#[async_trait::async_trait]
impl EventHandler for MyBot {
    async fn ready(&self, ctx: Context, ready: Ready) {
        println!("Bot {} is ready!", ready.user.username);
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let token = Token::from_env()?;
    let bot = MyBot;
    
    let mut client = Client::new(token.app_id(), bot)
        .token(token)
        .build()
        .await?;
    
    client.start().await?;
    Ok(())
}

Best Practices

Development Environment

rust
// Use environment variables for development
let token = match Token::from_env() {
    Ok(token) => token,
    Err(_) => {
        eprintln!("Please set QQ_BOT_APP_ID and QQ_BOT_SECRET");
        std::process::exit(1);
    }
};

Production Deployment

rust
// Validate configuration at startup
async fn initialize_bot() -> Result<()> {
    let token = Token::from_env()
        .map_err(|e| {
            eprintln!("Configuration error: {}", e);
            e
        })?;
    
    // Test credentials before starting
    token.authorization_header().await
        .map_err(|e| {
            eprintln!("Authentication test failed: {}", e);
            e
        })?;
    
    println!("Bot configuration validated");
    Ok(())
}

Error Recovery

rust
async fn robust_api_call(token: &Token) -> Result<serde_json::Value> {
    const MAX_RETRIES: u32 = 3;
    
    for attempt in 1..=MAX_RETRIES {
        match token.authorization_header().await {
            Ok(auth_header) => {
                // Make API call with header
                return make_request_with_auth(auth_header).await;
            }
            Err(BotError::Auth(_)) => {
                // Authentication failed, don't retry
                return Err(BotError::auth("Authentication failed permanently"));
            }
            Err(e) if attempt < MAX_RETRIES => {
                eprintln!("Attempt {} failed: {}, retrying...", attempt, e);
                tokio::time::sleep(Duration::from_secs(1)).await;
            }
            Err(e) => {
                return Err(e);
            }
        }
    }
    
    unreachable!()
}

See Also

  • Client - Bot client that uses tokens
  • Context - Context provides token access in handlers
  • BotApi - API client that requires authentication
  • Error Types - Authentication error handling

Released under the MIT License.