Skip to main content

topgg crates.io crates.io downloads

The official Rust SDK for the Top.gg API.

Getting Started

Make sure to have a Top.gg API token handy. If not, then view this tutorial on how to retrieve yours. After that, add the following line to the dependencies section of your Cargo.toml:

topgg = "1.4"

For more information, please read the documentation!

Features

This library provides several feature flags that can be enabled/disabled in Cargo.toml. Such as:

  • api: Interacting with the Top.gg API and accessing the top.gg/api/* endpoints. (enabled by default)
    • autoposter: Automating the process of periodically posting bot statistics to the Top.gg API.
  • webhook: Accessing the serde deserializable topgg::Vote struct.
    • actix-web: Wrapper for working with the actix-web web framework.
    • axum: Wrapper for working with the axum web framework.
    • rocket: Wrapper for working with the rocket web framework.
    • warp: Wrapper for working with the warp web framework.
  • serenity: Extra helpers for working with serenity library (with bot caching disabled).
    • serenity-cached: Extra helpers for working with serenity library (with bot caching enabled).
  • twilight: Extra helpers for working with twilight library (with bot caching disabled).
    • twilight-cached: Extra helpers for working with twilight library (with bot caching enabled).

Examples

Fetching a user from its Discord ID

use topgg::Client;

#[tokio::main]
async fn main() {
let client = Client::new(env!("TOPGG_TOKEN").to_string());
let user = client.get_user(661200758510977084).await.unwrap();

assert_eq!(user.username, "null");
assert_eq!(user.id, 661200758510977084);

println!("{:?}", user);
}

Posting your bot's statistics

use topgg::{Client, Stats};

#[tokio::main]
async fn main() {
let client = Client::new(env!("TOPGG_TOKEN").to_string());

let server_count = 12345;
client
.post_stats(Stats::from(server_count))
.await
.unwrap();
}

Checking if a user has voted your bot

use topgg::Client;

#[tokio::main]
async fn main() {
let client = Client::new(env!("TOPGG_TOKEN").to_string());

if client.has_voted(661200758510977084).await.unwrap() {
println!("checks out");
}
}

Autoposting with serenity

In your Cargo.toml:

[dependencies]
# using serenity with guild caching disabled
topgg = { version = "1.4", features = ["autoposter", "serenity"] }

# using serenity with guild caching enabled
topgg = { version = "1.4", features = ["autoposter", "serenity-cached"] }

In your code:

use core::time::Duration;
use serenity::{client::{Client, Context, EventHandler}, model::{channel::Message, gateway::Ready}};
use topgg::Autoposter;

struct Handler;

#[serenity::async_trait]
impl EventHandler for Handler {
async fn message(&self, ctx: Context, msg: Message) {
if msg.content == "!ping" {
if let Err(why) = msg.channel_id.say(&ctx.http, "Pong!").await {
println!("Error sending message: {why:?}");
}
}
}

async fn ready(&self, _: Context, ready: Ready) {
println!("{} is connected!", ready.user.name);
}
}

#[tokio::main]
async fn main() {
let topgg_client = topgg::Client::new(env!("TOPGG_TOKEN").to_string());
let autoposter = Autoposter::serenity(&topgg_client, Duration::from_secs(1800));

let bot_token = env!("DISCORD_TOKEN").to_string();
let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::GUILDS | GatewayIntents::MESSAGE_CONTENT;

let mut client = Client::builder(&bot_token, intents)
.event_handler(Handler)
.event_handler_arc(autoposter.handler())
.await
.unwrap();

if let Err(why) = client.start().await {
println!("Client error: {why:?}");
}
}

Autoposting with twilight

In your Cargo.toml:

[dependencies]
# using twilight with guild caching disabled
topgg = { version = "1.4", features = ["autoposter", "twilight"] }

# using twilight with guild caching enabled
topgg = { version = "1.4", features = ["autoposter", "twilight-cached"] }

In your code:

use core::time::Duration;
use topgg::Autoposter;
use twilight_gateway::{Event, Intents, Shard, ShardId};

#[tokio::main]
async fn main() {
let client = topgg::Client::new(env!("TOPGG_TOKEN").to_string());
let autoposter = Autoposter::twilight(&client, Duration::from_secs(1800));

let mut shard = Shard::new(
ShardId::ONE,
env!("DISCORD_TOKEN").to_string(),
Intents::GUILD_MEMBERS | Intents::GUILDS,
);

loop {
let event = match shard.next_event().await {
Ok(event) => event,
Err(source) => {
if source.is_fatal() {
break;
}

continue;
}
};

autoposter.handle(&event).await;

match event {
Event::Ready(_) => {
println!("Bot is ready!");
},

_ => {}
}
}
}

Writing an actix-web webhook for listening to votes

In your Cargo.toml:

[dependencies]
topgg = { version = "1.4", default-features = false, features = ["actix-web"] }

In your code:

use actix_web::{
error::{Error, ErrorUnauthorized},
get, post, App, HttpServer,
};
use std::io;
use topgg::IncomingVote;

#[get("/")]
async fn index() -> &'static str {
"Hello, World!"
}

#[post("/webhook")]
async fn webhook(vote: IncomingVote) -> Result<&'static str, Error> {
match vote.authenticate(env!("TOPGG_WEBHOOK_PASSWORD")) {
Some(vote) => {
println!("{:?}", vote);

Ok("ok")
}
_ => Err(ErrorUnauthorized("401")),
}
}

#[actix_web::main]
async fn main() -> io::Result<()> {
HttpServer::new(|| App::new().service(index).service(webhook))
.bind("127.0.0.1:8080")?
.run()
.await
}

Writing an axum webhook for listening to votes

In your Cargo.toml:

[dependencies]
topgg = { version = "1.4", default-features = false, features = ["axum"] }

In your code:

use axum::{routing::get, Router, Server};
use std::{net::SocketAddr, sync::Arc};
use topgg::{Vote, VoteHandler};

struct MyVoteHandler {}

#[axum::async_trait]
impl VoteHandler for MyVoteHandler {
async fn voted(&self, vote: Vote) {
println!("{:?}", vote);
}
}

async fn index() -> &'static str {
"Hello, World!"
}

#[tokio::main]
async fn main() {
let state = Arc::new(MyVoteHandler {});

let app = Router::new().route("/", get(index)).nest(
"/webhook",
topgg::axum::webhook(env!("TOPGG_WEBHOOK_PASSWORD").to_string(), Arc::clone(&state)),
);

let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();

Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}

Writing a rocket webhook for listening to votes

In your Cargo.toml:

[dependencies]
topgg = { version = "1.4", default-features = false, features = ["rocket"] }

In your code:

#![feature(decl_macro)]

use rocket::{get, http::Status, post, routes};
use topgg::IncomingVote;

#[get("/")]
fn index() -> &'static str {
"Hello, World!"
}

#[post("/webhook", data = "<vote>")]
fn webhook(vote: IncomingVote) -> Status {
match vote.authenticate(env!("TOPGG_WEBHOOK_PASSWORD")) {
Some(vote) => {
println!("{:?}", vote);

Status::Ok
},
_ => {
println!("found an unauthorized attacker.");

Status::Unauthorized
}
}
}

fn main() {
rocket::ignite()
.mount("/", routes![index, webhook])
.launch();
}

Writing a warp webhook for listening to votes

In your Cargo.toml:

[dependencies]
topgg = { version = "1.4", default-features = false, features = ["warp"] }

In your code:

use std::{net::SocketAddr, sync::Arc};
use topgg::{Vote, VoteHandler};
use warp::Filter;

struct MyVoteHandler {}

#[async_trait::async_trait]
impl VoteHandler for MyVoteHandler {
async fn voted(&self, vote: Vote) {
println!("{:?}", vote);
}
}

#[tokio::main]
async fn main() {
let state = Arc::new(MyVoteHandler {});

// POST /webhook
let webhook = topgg::warp::webhook(
"webhook",
env!("TOPGG_WEBHOOK_PASSWORD").to_string(),
Arc::clone(&state),
);

let routes = warp::get().map(|| "Hello, World!").or(webhook);

let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();

warp::serve(routes).run(addr).await
}