Start of history after removing tokens

This commit is contained in:
August 2024-10-21 10:05:34 -04:00
commit f3021fa860
Signed by: shibedrill
GPG Key ID: 5FE0CB25945EFAA2
8 changed files with 2833 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
.env

2560
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

16
Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "playerbot"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.90"
dotenvy = "0.15.7"
log = "0.4.22"
poise = "0.6.1"
pretty_env_logger = "0.5.0"
reqwest = {version = "0.12.8", features = ["json"]}
serde = {version = "1.0.210", features = ["derive", "serde_derive"]}
serde_json = {version = "1.0.132", features = []}
tokio = {version = "1.40.0", features = ["full"]}
url = "2.5.2"

21
src/funcs.rs Normal file
View File

@ -0,0 +1,21 @@
use poise::serenity_prelude::{ActivityData, OnlineStatus};
use crate::types::ServerResponse;
pub fn set_presence(ctx: &poise::serenity_prelude::Context, status: ServerResponse) {
ctx.set_presence(
Some(ActivityData::custom(match status.online() {
true => {
format!("{}/{} players online", status.players(), status.max())
}
false => "Server offline!".to_string(),
})),
match status.online() {
true => match status.is_full() {
true => OnlineStatus::Idle,
false => OnlineStatus::Online,
},
false => OnlineStatus::DoNotDisturb,
},
);
}

16
src/main.rs Normal file
View File

@ -0,0 +1,16 @@
mod minecraft;
mod scpsl;
mod types;
mod funcs;
use tokio::join;
use dotenvy::{self, dotenv};
#[macro_use]
extern crate log;
#[tokio::main]
async fn main() {
dotenv().ok();
pretty_env_logger::init();
join!(scpsl::run(), minecraft::run());
}

93
src/minecraft.rs Normal file
View File

@ -0,0 +1,93 @@
use poise::serenity_prelude as serenity;
use reqwest::{Client, Request};
use serenity::*;
use crate::{funcs, types::ServerResponse};
#[derive(serde::Deserialize)]
struct ServerSummary {
online: bool,
players: Option<Players>,
}
#[derive(serde::Deserialize)]
struct Players {
online: i32,
max: i32,
}
pub struct Data {}
async fn get_status() -> Result<ServerResponse, anyhow::Error> {
let http_client = Client::new();
let request = Request::new(
reqwest::Method::GET,
"https://api.mcstatus.io/v2/status/java/gamer.shibedrill.site"
.try_into()?,
);
let response = http_client.execute(request).await?;
let data: ServerSummary = serde_json::from_str(&response.text().await?)?;
if let Some(players) = data.players {
Ok(ServerResponse::new(
data.online,
players.online as u32,
players.max as u32,
))
} else {
Ok(ServerResponse::new(data.online, 0, 0))
}
}
pub async fn event_handler(
ctx: &serenity::Context,
event: &serenity::FullEvent,
_framework: poise::FrameworkContext<'_, Data, Error>,
_data: &Data,
) -> Result<(), Error> {
match event {
serenity::FullEvent::Ready {
data_about_bot: _data,
} => loop {
let status = get_status()
.await
.inspect_err(|e| error!("Failed to get status: {}", e))
.unwrap();
info!("Got status: {}", status.to_string());
funcs::set_presence(&ctx, status);
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
},
_ => Ok(()),
}
}
pub async fn run() {
let framework = poise::Framework::builder()
.options(poise::FrameworkOptions {
event_handler: |ctx, event, framework, data| {
Box::pin(event_handler(ctx, event, framework, data))
},
..Default::default()
})
.setup(|ctx, _ready, framework| {
Box::pin(async move {
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
Ok(Data {})
})
})
.build();
info!("Built framework successfully.");
let mut discord_client = ClientBuilder::new(
std::env::var("MINECRAFT_BOT_TOKEN").inspect_err(|e| {error!("Failed to get token: {}", e)}).unwrap(),
serenity::GatewayIntents::non_privileged(),
)
.framework(framework)
.activity(ActivityData::custom("Waiting on initial status..."))
.await
.inspect_err(|e| error!("Failed to start client: {}", e))
.unwrap();
info!("Built client successfully.");
let _ = discord_client.start().await;
}

90
src/scpsl.rs Normal file
View File

@ -0,0 +1,90 @@
use crate::{funcs, types::ServerResponse};
use poise::serenity_prelude as serenity;
use reqwest::{Client, Request};
use serenity::*;
#[derive(serde::Deserialize)]
#[allow(non_snake_case)]
struct ServerSummary {
online: bool,
players: String,
}
pub struct Data {}
async fn get_status() -> Result<ServerResponse, anyhow::Error> {
let http_client = Client::new();
let request = Request::new(
reqwest::Method::GET,
url::Url::try_from("https://api.scplist.kr/api/servers/81460")?,
);
let response = http_client.execute(request).await?;
let data: ServerSummary = serde_json::from_str(&response.text().await?)?;
let playercount: Result<Vec<u32>, _> =
data.players.split('/').map(|x| x.parse::<u32>()).collect();
let playercount_unwrapped = playercount?;
Ok(ServerResponse::new(
data.online,
playercount_unwrapped[0],
playercount_unwrapped[1],
))
}
pub async fn event_handler(
ctx: &serenity::Context,
event: &serenity::FullEvent,
_framework: poise::FrameworkContext<'_, Data, Error>,
_data: &Data,
) -> Result<(), Error> {
match event {
serenity::FullEvent::Ready {
data_about_bot: _data,
} => loop {
let status = get_status()
.await
.inspect_err(|e| error!("Failed to get status: {}", e))
.unwrap();
info!("Got status: {}", status.to_string());
funcs::set_presence(ctx, status);
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
},
_ => Ok(()),
}
}
pub async fn run() {
let framework = poise::Framework::builder()
.options(poise::FrameworkOptions {
initialize_owners: true,
event_handler: |ctx, event, framework, data| {
Box::pin(event_handler(ctx, event, framework, data))
},
..Default::default()
})
.setup(|ctx, _ready, framework| {
Box::pin(async move {
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
Ok(Data {})
})
})
.build();
info!("Built framework successfully.");
let mut discord_client = ClientBuilder::new(
std::env::var("SCPSL_BOT_TOKEN").inspect_err(|e| {error!("Failed to get token: {}", e)}).unwrap(),
serenity::GatewayIntents::non_privileged(),
)
.framework(framework)
.activity(ActivityData::custom("Waiting on initial status..."))
.await
.inspect_err(|e| error!("Failed to start client: {}", e))
.unwrap();
info!("Built client successfully.");
let _ = discord_client.start().await;
}

35
src/types.rs Normal file
View File

@ -0,0 +1,35 @@
pub struct ServerResponse {
online: bool,
players: u32,
max: u32,
}
impl ServerResponse {
pub fn new(online: bool, players: u32, max: u32) -> Self {
ServerResponse {
online,
players,
max,
}
}
pub fn online(&self) -> bool {
self.online
}
pub fn players(&self) -> u32 {
self.players
}
pub fn max(&self) -> u32 {
self.max
}
pub fn is_full(&self) -> bool {
self.players == self.max
}
pub fn to_string(&self) -> String {
format!("{}/{} ({})", self.players, self.max, self.online)
}
}