Compare commits
No commits in common. "f1880424ab7990e223a2229c8b0098e2623d5e71" and "a493259f7f63af6400757c6fdc8a14916f331581" have entirely different histories.
f1880424ab
...
a493259f7f
1078
Cargo.lock
generated
1078
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -5,9 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.102"
|
||||
poise = "0.6.2"
|
||||
reqwest = {version = "0.13.3", features = ["json"]}
|
||||
serde = {version = "1.0.215", features = ["derive", "serde_derive"]}
|
||||
serde_json = "1.0.149"
|
||||
tokio = {version = "1.41.1", features = ["full"]}
|
||||
url = "2.5.3"
|
||||
|
||||
10
config.json
10
config.json
@ -1,10 +0,0 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"entries": [
|
||||
{
|
||||
"handler_type": "minecraft",
|
||||
"address": "https://dawn.shibedrill.site",
|
||||
"token": "foobar"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
use crate::types::*;
|
||||
use poise;
|
||||
|
||||
struct BotRunner {
|
||||
client: poise::serenity_prelude::Client,
|
||||
server_data: dyn ServerInfo,
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
use std::{fs::File, path::Path};
|
||||
use crate::{handlers, types::ServerInfo};
|
||||
use serde::Deserialize;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ConfigEntry {
|
||||
handler_type: String,
|
||||
address: Url,
|
||||
token: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Config {
|
||||
version: String,
|
||||
entries: Vec<ConfigEntry>,
|
||||
}
|
||||
|
||||
pub fn parse_configs(path: &Path) -> Result<Config, anyhow::Error> {
|
||||
let file_handle = File::open(path)?;
|
||||
Ok(serde_json::from_reader::<File, Config>(file_handle)?)
|
||||
}
|
||||
|
||||
pub fn build_handlers(conf: Config) -> Vec<Box<dyn ServerInfo>> {
|
||||
let mut results: Vec<Box<dyn ServerInfo>> = vec!();
|
||||
for item in conf.entries {
|
||||
match item.handler_type.as_str() {
|
||||
"minecraft" => {
|
||||
results.push(Box::new(handlers::minecraft::Server::new(item.token, item.address)))
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
results
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
use reqwest::{header::HeaderValue, Client};
|
||||
use serde::Deserialize;
|
||||
use serde_json;
|
||||
use url::Url;
|
||||
|
||||
use crate::types::{ServerInfo, ServerOnlineResponse, ServerResponse};
|
||||
use crate::types::{PlayerList, ServerInfo, ServerOnlineResponse, ServerResponse};
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct ApiResponse {
|
||||
@ -46,6 +46,34 @@ pub struct OnlineResponse {
|
||||
players: Vec<String>,
|
||||
}
|
||||
|
||||
impl ServerOnlineResponse for OnlineResponse {
|
||||
fn players_online(&self) -> u64 {
|
||||
self.players_online
|
||||
}
|
||||
|
||||
fn player_limit(&self) -> u64 {
|
||||
self.player_limit
|
||||
}
|
||||
|
||||
fn version(&self) -> &String {
|
||||
&self.version
|
||||
}
|
||||
|
||||
fn readable_name(&self) -> &String {
|
||||
&self.clean_name
|
||||
}
|
||||
|
||||
fn searchable_name(&self) -> &String {
|
||||
&self.searchable_name
|
||||
}
|
||||
}
|
||||
|
||||
impl PlayerList for OnlineResponse {
|
||||
fn players(&self) -> &Vec<String> {
|
||||
&self.players
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Server {
|
||||
#[allow(dead_code)]
|
||||
token: String,
|
||||
@ -57,42 +85,56 @@ impl ServerInfo for Server {
|
||||
Server { token, addr }
|
||||
}
|
||||
|
||||
fn addressable_name(&self) -> String {
|
||||
self.addr.clone().into()
|
||||
type OnlineResponse = OnlineResponse;
|
||||
|
||||
type AddressableName = Url;
|
||||
|
||||
fn addressable_name(&self) -> &Self::AddressableName {
|
||||
&self.addr
|
||||
}
|
||||
|
||||
fn app_token(&self) -> String {
|
||||
self.token.clone()
|
||||
}
|
||||
|
||||
fn api_address(&self) -> Url {
|
||||
async fn poll(&self) -> ServerResponse<Self::OnlineResponse> {
|
||||
use reqwest;
|
||||
let url_string = format!("https://api.mcsrvstat.us/3/{}", self.addr.host().unwrap());
|
||||
Url::parse(&url_string).unwrap()
|
||||
}
|
||||
let url = Url::try_from(url_string.as_str()).unwrap();
|
||||
let mut request = reqwest::Request::new(reqwest::Method::GET, url);
|
||||
request.headers_mut().append("User-Agent", HeaderValue::from_str("Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Mobile Safari/537.36").unwrap());
|
||||
|
||||
fn parse(&self, response: String) -> ServerResponse {
|
||||
let parsed_data: ApiResponse = serde_json::from_str(&response).unwrap();
|
||||
match parsed_data.online {
|
||||
let client = Client::new();
|
||||
|
||||
match client.execute(request).await {
|
||||
Err(error) => ServerResponse::Error(anyhow::anyhow!(error)),
|
||||
Ok(answer) => {
|
||||
let json = answer.json::<ApiResponse>().await.unwrap();
|
||||
//println!("{}", answer.text().await.unwrap());
|
||||
//ServerResponse::Offline
|
||||
|
||||
match json.online {
|
||||
true => {
|
||||
let players = parsed_data.players.unwrap();
|
||||
let motd = parsed_data.motd.as_ref().unwrap();
|
||||
self::ServerResponse::Online(ServerOnlineResponse {
|
||||
let players = json.players.unwrap();
|
||||
let motd = json.motd.as_ref().unwrap();
|
||||
self::ServerResponse::Online(OnlineResponse {
|
||||
searchable_name: self.addr.host().unwrap().to_string(),
|
||||
readable_name: motd.clean.first().unwrap().into(),
|
||||
reported_name: motd.clean.first().unwrap().into(),
|
||||
clean_name: motd.clean.first().unwrap().into(),
|
||||
players_online: players.online,
|
||||
player_limit: players.max,
|
||||
version: parsed_data.version.unwrap(),
|
||||
players: Some(players
|
||||
version: json.version.unwrap(),
|
||||
players: players
|
||||
.list
|
||||
.unwrap_or_default()
|
||||
.unwrap_or(Vec::<ApiResponsePlayerEntry>::new())
|
||||
.iter()
|
||||
.map(|e| e.name.clone())
|
||||
.collect()),
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
false => ServerResponse::Offline,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
32
src/main.rs
32
src/main.rs
@ -1,35 +1,31 @@
|
||||
mod bot_runner;
|
||||
mod config_parser;
|
||||
mod handlers;
|
||||
mod types;
|
||||
mod request;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use crate::{
|
||||
request::request, types::ServerResponse
|
||||
handlers::minecraft,
|
||||
types::{PlayerList, ServerInfo, ServerOnlineResponse, ServerResponse},
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
|
||||
let config = config_parser::parse_configs(Path::new("config.json")).unwrap();
|
||||
let handlers = config_parser::build_handlers(config);
|
||||
|
||||
let http_response = request(handlers[0].api_address()).await.unwrap();
|
||||
let results = handlers[0].parse(http_response.text().await.unwrap());
|
||||
let mc = minecraft::Server::new(
|
||||
"foo".into(),
|
||||
"http://dawn.shibedrill.site".try_into().unwrap(),
|
||||
);
|
||||
let results = mc.poll().await;
|
||||
match results {
|
||||
ServerResponse::Error(err) => println!("Error: {}", err),
|
||||
ServerResponse::Offline => println!("Offline"),
|
||||
ServerResponse::Online(online_info) => {
|
||||
println!(
|
||||
"Name: {}\nAddress: {}\nVersion: {}\nPlayers: {}/{}",
|
||||
online_info.readable_name,
|
||||
online_info.searchable_name,
|
||||
online_info.version,
|
||||
online_info.players_online,
|
||||
online_info.player_limit
|
||||
online_info.readable_name(),
|
||||
online_info.searchable_name(),
|
||||
online_info.version(),
|
||||
online_info.players_online(),
|
||||
online_info.player_limit()
|
||||
);
|
||||
println!("Players online: {}", online_info.players.unwrap().join(", "));
|
||||
println!("Players online: {}", online_info.players().join(", "));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
use url::Url;
|
||||
use reqwest::{Response, header::HeaderValue};
|
||||
|
||||
pub async fn request(url: Url) -> Result<Response, reqwest::Error> {
|
||||
let client = reqwest::Client::new();
|
||||
let mut request = reqwest::Request::new(reqwest::Method::GET, url);
|
||||
request.headers_mut().append("User-Agent", HeaderValue::from_str("Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Mobile Safari/537.36").unwrap());
|
||||
client.execute(request).await
|
||||
}
|
||||
37
src/types.rs
37
src/types.rs
@ -1,38 +1,37 @@
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ServerResponse {
|
||||
pub enum ServerResponse<T: ServerOnlineResponse> {
|
||||
Offline,
|
||||
Online(ServerOnlineResponse),
|
||||
Online(T),
|
||||
Error(anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ServerOnlineResponse {
|
||||
pub players_online: u64,
|
||||
pub player_limit: u64,
|
||||
pub version: String,
|
||||
pub searchable_name: String,
|
||||
pub readable_name: String,
|
||||
pub clean_name: String,
|
||||
pub reported_name: String,
|
||||
pub players: Option<Vec<String>>,
|
||||
pub trait ServerOnlineResponse {
|
||||
fn players_online(&self) -> u64;
|
||||
fn player_limit(&self) -> u64;
|
||||
fn version(&self) -> &String;
|
||||
fn searchable_name(&self) -> &String;
|
||||
fn readable_name(&self) -> &String;
|
||||
}
|
||||
|
||||
pub trait PlayerList {
|
||||
fn players(&self) -> &Vec<String>;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub trait ServerInfo {
|
||||
fn new(token: String, addr: Url) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
fn new(token: String, addr: Url) -> Self;
|
||||
|
||||
/// An addressable, unique identifier, used for its API.
|
||||
/// If it is a Minecraft server, this can be a fully qualified domain name or URL.
|
||||
/// If this is an SCP secret laboratory server, it can be a server ID.
|
||||
fn addressable_name(&self) -> String;
|
||||
/// The address wrapped in the API URL.
|
||||
fn api_address(&self) -> Url;
|
||||
fn parse(&self, response: String) -> ServerResponse;
|
||||
type AddressableName;
|
||||
fn addressable_name(&self) -> &Self::AddressableName;
|
||||
|
||||
/// The app token used to send status updates.
|
||||
fn app_token(&self) -> String;
|
||||
|
||||
type OnlineResponse: ServerOnlineResponse;
|
||||
async fn poll(&self) -> ServerResponse<Self::OnlineResponse>;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user