A modern async Rust library for building Discord selfbot workflows with a clean API, typed models, and a resilient gateway runtime.
This project is intended for authorized and compliant use only.
- Make sure your usage complies with Discord's Terms of Service and platform policies.
- If you are using this for coursework, internal tooling, or controlled environments, ensure explicit authorization.
- Maintainers and contributors are not responsible for misuse.
- Async-first design (
tokio) - Event-driven client API (
EventHandler) - Resilient gateway loop (reconnect, resume, heartbeat ACK timeout handling)
- Typed Discord models (channels, messages, guilds, roles, permissions, overwrites)
- Configurable cache architecture (users/channels/guilds/relationships)
- Builder-based ergonomics (
ClientBuilder) - Graceful shutdown support (
Client::shutdown())
[dependencies]
diself = "0.1.1"
tokio = { version = "1", features = ["full"] }use diself::prelude::*;
struct MyHandler;
#[async_trait]
impl EventHandler for MyHandler {
async fn on_ready(&self, _ctx: &Context, user: User) {
println!("Logged in as {}", user.tag());
}
async fn on_message_create(&self, ctx: &Context, msg: Message) {
if msg.content == ".ping" {
let _ = msg.reply(&ctx.http, "pong").await;
}
}
}
#[tokio::main]
async fn main() -> Result<()> {
let token = std::env::var("DISCORD_TOKEN").expect("DISCORD_TOKEN is not set");
let client = Client::builder(token, MyHandler)
.with_cache_config(CacheConfig {
cache_users: true,
cache_channels: true,
cache_guilds: true,
cache_relationships: true,
})
.build();
client.start().await
}ClientBuilder provides a clean, scalable entrypoint:
let client = Client::builder(token, handler)
.without_cache()
.with_captcha_handler(|captcha_info| async move {
// Solve captcha and return captcha key
Ok("captcha_key".to_string())
})
.build();Run the client in a task and stop it cooperatively:
use std::sync::Arc;
let client = Arc::new(Client::builder(token, handler).build());
let runner = Arc::clone(&client);
let task = tokio::spawn(async move {
let _ = runner.start().await;
});
tokio::signal::ctrl_c().await?;
client.shutdown();
let _ = task.await;The current gateway implementation includes:
- Automatic reconnect loop
- Resume support (
RESUME) - Heartbeat + ACK timeout handling
RECONNECTandINVALID_SESSIONhandling- Backoff with jitter for reconnect attempts
Context exposes endpoint managers for ergonomic calls:
ctx.usersctx.guildsctx.channelsctx.relationships
Example:
use diself::prelude::*;
async fn example(ctx: &Context) -> Result<()> {
let me = ctx.users.me(&ctx.http).await?;
println!("connected as {}", me.tag());
// Example relationship action
ctx.relationships.send_friend_request(&ctx.http, "username").await?;
Ok(())
}examples/hello_gateway.rsexamples/cache_example.rs
Run an example:
DISCORD_TOKEN="..." cargo run --example cache_exampleRecommended local checks:
cargo check
cargo clippy --all-targets --all-features
cargo testThe project includes:
- Unit/integration tests under
tests/ - Live endpoint smoke tests under
tests/endpoints_live.rs(ignored by default)
Run regular tests:
cargo testRun live endpoint smoke tests:
DISCORD_TOKEN="..." cargo test --test endpoints_live -- --ignored --nocaptureShort-term priorities:
- Broader integration tests (gateway lifecycle, HTTP edge cases)
- Typed HTTP error surface improvements
- Documentation expansion and cookbook-style examples
- API stabilization toward a stronger
0.1.x
Contributions are welcome.
Until a dedicated CONTRIBUTING.md is added, please follow:
- Open an issue first for non-trivial changes.
- Keep pull requests focused and small.
- Include tests or rationale when changing behavior.
- Run the local checks before opening a PR.
MIT. See LICENSE.