Skip to content

craftserverbot/top-gg-rust

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

112 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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
}

About

Top.gg Rust SDK

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Rust 100.0%