Token
Token carries the bot's app_id and secret, signs API requests, and caches an access token shared across Token clones. The cache is refreshed on demand and protected by an internal mutex, so cloning Token is cheap and safe across tasks.
pub struct Token { /* private fields */ }Token implements Clone, Debug, Serialize, Deserialize. The serialized form drops the cache so credentials embedded in config files round-trip cleanly.
Construction
Token::new(app_id, secret)— explicit credentials.Token::from_env()— readQQ_BOT_APP_ID/QQ_BOT_SECRET. Missing variables returnBotError::Config; present but empty values fail validation withBotError::Auth.
let token = Token::new("123456789", "abcdef...");
let token = Token::from_env()?; // env-drivenAccessors
app_id()/secret()— borrow the credential strings (returns&str).safe_display()— returns a redacted human-readable form for logs (Token { app_id: 123456789, secret: abcd****wxyz }). Use this anywhere you would otherwise be tempted to print the raw token.
Authentication
authorization_header().await—QQBot <access-token>value suitable for the HTTPAuthorizationheader; refreshes the cached token if it has expired.bot_token().await— the sameQQBot <access-token>value used by the gatewayIdentifypayload.
Both methods share the same access-token cache. Concurrent callers will block briefly on the refresh mutex when a renewal is needed and then race-free read the freshly minted token.
Validation
validate() checks that the app id and secret are non-empty and returns BotError::Auth on failure. Use it after Token::new(...) to fail fast before issuing any request.
Refresh behaviour
The framework tracks the access token's expiry and automatically renews it shortly before it lapses. If a renewal fails, the next call surfaces a BotError::Auth error with the upstream message. There is no exposed refresh() method — refresh is an implementation detail driven by authorization_header() / bot_token().
When clones share the same internal Arc<Mutex<TokenState>>, a renewal triggered on one clone is immediately visible to all others, so the framework never refreshes more than once per expiry window even under heavy concurrency.
Examples
Build a token from the environment and validate it once at startup:
let token = Token::from_env()?;
token.validate()?;Construct an HTTP client manually:
let auth = token.authorization_header().await?;
let request = client
.get("https://api.example.com/whatever")
.header("Authorization", auth)
.send()
.await?;Log a token without leaking secrets:
info!("starting bot {}", token.safe_display());Security notes
- Never
Debug-print aToken; always go throughsafe_display(). - Prefer reading credentials from env vars or a secret manager. Keep them out of source-controlled config files.
- Rotate the secret in the QQ Developer Portal if you suspect a leak; the next
authorization_header()call after rotation will fail withBotError::Auth, and you can rebuild theTokenwith fresh values.
See also
- Bot API — every route ultimately consumes a
Token. - Sessions — expose the
BotApithat owns the token used for REST calls. - Error types —
BotError::AuthandBotError::Configbehaviour referenced above.