Skip to content

craftserverbot/top-gg-rust

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

The (forked) Rust SDK for the Top.gg API.

Getting Started

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"

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: 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.

Examples

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
}

Packages

No packages published

Languages

  • Rust 100.0%