The (forked) Rust SDK for the Top.gg API.
Make sure to have a Top.gg API token handy, you can have an API token if you own a listed Discord bot on Top.gg (open the edit page, see in Webhooks
section) then add the following to your Cargo.toml
's dependencies:
topgg = "1.1"
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 thetop.gg/api/*
endpoints. (enabled by default)autoposter
: Automating the process of periodically posting bot statistics to the Top.gg API.
webhook
: Accessing theserde
deserializabletopgg::Vote
struct.
More things can be read in the documentation.
api
: Fetching a single Discord user from it's Discord ID
use topgg::Client;
#[tokio::main]
async fn main() {
let client = Client::new(env!("TOPGG_TOKEN"));
let user = client.get_user(661200758510977084).await.unwrap();
assert_eq!(user.username, "null");
assert_eq!(user.id, 661200758510977084);
println!("{:?}", user);
}
api
: Fetching a single Discord bot from it's Discord ID
use topgg::Client;
#[tokio::main]
async fn main() {
let client = Client::new(env!("TOPGG_TOKEN"));
let bot = client.get_bot(264811613708746752).await.unwrap();
assert_eq!(bot.username, "Luca");
assert_eq!(bot.id, 264811613708746752);
println!("{:?}", bot);
}
api
: Querying several Discord bots
use topgg::{Client, Filter, Query};
#[tokio::main]
async fn main() {
let client = Client::new(env!("TOPGG_TOKEN"));
// inputting a string searches a bot that matches that username.
for bot in client.get_bots("shiro").await.unwrap() {
println!("{:?}", bot);
}
// advanced query with filters...
let filter = Filter::new()
.username("shiro")
.certified(true);
let query = Query::new()
.limit(250)
.skip(50)
.filter(filter);
for bot in client.get_bots(query).await.unwrap() {
println!("{:?}", bot);
}
}
api
: Posting your Discord bot's statistics
use topgg::{Client, NewStats};
#[tokio::main]
async fn main() {
let client = Client::new(env!("TOPGG_TOKEN"));
let server_count = 1234; // be TRUTHFUL!
let shard_count = 10;
let stats = NewStats::count_based(server_count, Some(shard_count));
client.post_stats(stats).await.unwrap();
}
api
: Checking if a user has voted for your Discord bot
use topgg::Client;
#[tokio::main]
async fn main() {
let client = Client::new(env!("TOPGG_TOKEN"));
if client.has_voted(661200758510977084).await.unwrap() {
println!("checks out");
}
}
autoposter
: Automating the process of periodically posting your Discord bot's statistics
In your Cargo.toml
:
[dependencies]
topgg = { version = "1.1", features = ["autoposter"] }
In your code:
use core::time::Duration;
use topgg::{Autoposter, Client, NewStats};
#[tokio::main]
async fn main() {
let client = Client::new(env!("TOPGG_TOKEN"));
// creates an autoposter that posts data to Top.gg every 1800 seconds (15 minutes).
// the autopost thread will stop once it's dropped.
let autoposter = client.new_autoposter(Duration::from_secs(1800));
// ... then in some on ready/new guild event ...
let server_count = 12345;
let stats = NewStats::count_based(server_count, None);
autoposter.feed(stats).await;
}
actix
: Writing an actix-web
webhook for listening to your bot/server's vote events
In your Cargo.toml
:
[dependencies]
topgg = { version = "1.1", default-features = false, features = ["actix"] }
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
}
axum
: Writing an axum
webhook for listening to your bot/server's vote events
In your Cargo.toml
:
[dependencies]
topgg = { version = "1.1", default-features = false, features = ["axum"] }
In your code:
use axum::{routing::get, Router, Server};
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 password = env!("TOPGG_WEBHOOK_PASSWORD").to_owned();
let state = MyVoteHandler {};
let app = Router::new()
.route("/", get(index))
.nest("/webhook", topgg::axum::webhook(password, state));
// this will always be a valid SocketAddr syntax,
// therefore we can safely unwrap_unchecked this.
let addr = unsafe { "127.0.0.1:8080".parse().unwrap_unchecked() };
Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
rocket
: Writing a rocket
webhook for listening to your bot/server's vote events
In your Cargo.toml
:
[dependencies]
topgg = { version = "1.1", 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);
// 200 and 401 will always be a valid status code,
// therefore we can safely unwrap_unchecked these.
unsafe { Status::from_code(200).unwrap_unchecked() }
},
_ => {
println!("found an unauthorized attacker.");
unsafe { Status::from_code(401).unwrap_unchecked() }
},
}
}
fn main() {
rocket::ignite()
.mount("/", routes![index, webhook])
.launch();
}
warp
: Writing a warp
webhook for listening to your bot/server's vote events
In your Cargo.toml
:
[dependencies]
topgg = { version = "1.1", default-features = false, features = ["warp"] }
In your code:
use std::net::SocketAddr;
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 password = env!("TOPGG_WEBHOOK_PASSWORD").to_owned();
let state = MyVoteHandler {};
// POST /webhook
let webhook = topgg::warp::webhook("webhook", password, state);
let routes = warp::get()
.map(|| "Hello, World!")
.or(webhook);
// this will always be a valid SocketAddr syntax,
// therefore we can safely unwrap_unchecked this.
let addr: SocketAddr = unsafe { "127.0.0.1:8080".parse().unwrap_unchecked() };
warp::serve(routes)
.run(addr)
.await
}