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
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.
pub fn new(app_id: impl Into<String>, secret: impl Into<String>) -> Self
Parameters
app_id
: The bot's application ID from QQ Developer Portalsecret
: The bot's secret key from QQ Developer Portal
Returns
A new Token
instance.
Example
let token = Token::new("123456789", "your_secret_key");
from_env
Creates a token from environment variables.
pub fn from_env() -> Result<Self>
Looks for the following environment variables:
QQ_BOT_APP_ID
: The bot's application IDQQ_BOT_SECRET
: The bot's secret key
Returns
A Result
containing the token if both environment variables are found.
Example
// 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.
pub fn app_id(&self) -> &str
Returns
A string slice containing the app ID.
Example
let token = Token::new("123456789", "secret");
assert_eq!(token.app_id(), "123456789");
secret
Gets the secret key.
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
let token = Token::new("app_id", "secret123");
assert_eq!(token.secret(), "secret123");
Authentication Methods
authorization_header
Generates the authorization header value for API requests.
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
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.
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
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.
pub fn validate(&self) -> Result<()>
Returns
Ok(())
if the token is valid, otherwise returns a BotError::Auth
.
Example
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.
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
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
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
// 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
// Connection timeout or network error
match token.authorization_header().await {
Err(BotError::Connection(msg)) => {
println!("Network error: {}", msg);
// Implement retry logic
}
_ => {}
}
Configuration Errors
// 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:
- Debug Safety: The
Debug
implementation redacts the secret - Safe Display: The
safe_display()
method masks sensitive parts - No Secret Exposure: Avoid calling
secret()
unless absolutely necessary
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:
# 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
// 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:
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
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
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
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
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
// 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
// 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
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 tokensContext
- Context provides token access in handlersBotApi
- API client that requires authentication- Error Types - Authentication error handling