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]
|
[dependencies]
|
||||||
anyhow = "1.0.102"
|
anyhow = "1.0.102"
|
||||||
poise = "0.6.2"
|
|
||||||
reqwest = {version = "0.13.3", features = ["json"]}
|
reqwest = {version = "0.13.3", features = ["json"]}
|
||||||
serde = {version = "1.0.215", features = ["derive", "serde_derive"]}
|
serde = {version = "1.0.215", features = ["derive", "serde_derive"]}
|
||||||
serde_json = "1.0.149"
|
|
||||||
tokio = {version = "1.41.1", features = ["full"]}
|
tokio = {version = "1.41.1", features = ["full"]}
|
||||||
url = "2.5.3"
|
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::Deserialize;
|
||||||
use serde_json;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::types::{ServerInfo, ServerOnlineResponse, ServerResponse};
|
use crate::types::{PlayerList, ServerInfo, ServerOnlineResponse, ServerResponse};
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct ApiResponse {
|
struct ApiResponse {
|
||||||
@ -46,6 +46,34 @@ pub struct OnlineResponse {
|
|||||||
players: Vec<String>,
|
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 {
|
pub struct Server {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
token: String,
|
token: String,
|
||||||
@ -57,42 +85,56 @@ impl ServerInfo for Server {
|
|||||||
Server { token, addr }
|
Server { token, addr }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addressable_name(&self) -> String {
|
type OnlineResponse = OnlineResponse;
|
||||||
self.addr.clone().into()
|
|
||||||
|
type AddressableName = Url;
|
||||||
|
|
||||||
|
fn addressable_name(&self) -> &Self::AddressableName {
|
||||||
|
&self.addr
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app_token(&self) -> String {
|
fn app_token(&self) -> String {
|
||||||
self.token.clone()
|
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());
|
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 client = Client::new();
|
||||||
let parsed_data: ApiResponse = serde_json::from_str(&response).unwrap();
|
|
||||||
match parsed_data.online {
|
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 => {
|
true => {
|
||||||
let players = parsed_data.players.unwrap();
|
let players = json.players.unwrap();
|
||||||
let motd = parsed_data.motd.as_ref().unwrap();
|
let motd = json.motd.as_ref().unwrap();
|
||||||
self::ServerResponse::Online(ServerOnlineResponse {
|
self::ServerResponse::Online(OnlineResponse {
|
||||||
searchable_name: self.addr.host().unwrap().to_string(),
|
searchable_name: self.addr.host().unwrap().to_string(),
|
||||||
readable_name: motd.clean.first().unwrap().into(),
|
|
||||||
reported_name: motd.clean.first().unwrap().into(),
|
reported_name: motd.clean.first().unwrap().into(),
|
||||||
clean_name: motd.clean.first().unwrap().into(),
|
clean_name: motd.clean.first().unwrap().into(),
|
||||||
players_online: players.online,
|
players_online: players.online,
|
||||||
player_limit: players.max,
|
player_limit: players.max,
|
||||||
version: parsed_data.version.unwrap(),
|
version: json.version.unwrap(),
|
||||||
players: Some(players
|
players: players
|
||||||
.list
|
.list
|
||||||
.unwrap_or_default()
|
.unwrap_or(Vec::<ApiResponsePlayerEntry>::new())
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| e.name.clone())
|
.map(|e| e.name.clone())
|
||||||
.collect()),
|
.collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
false => ServerResponse::Offline,
|
false => ServerResponse::Offline,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
32
src/main.rs
32
src/main.rs
@ -1,35 +1,31 @@
|
|||||||
mod bot_runner;
|
|
||||||
mod config_parser;
|
|
||||||
mod handlers;
|
mod handlers;
|
||||||
mod types;
|
mod types;
|
||||||
mod request;
|
|
||||||
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
request::request, types::ServerResponse
|
handlers::minecraft,
|
||||||
|
types::{PlayerList, ServerInfo, ServerOnlineResponse, ServerResponse},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
let mc = minecraft::Server::new(
|
||||||
let config = config_parser::parse_configs(Path::new("config.json")).unwrap();
|
"foo".into(),
|
||||||
let handlers = config_parser::build_handlers(config);
|
"http://dawn.shibedrill.site".try_into().unwrap(),
|
||||||
|
);
|
||||||
let http_response = request(handlers[0].api_address()).await.unwrap();
|
let results = mc.poll().await;
|
||||||
let results = handlers[0].parse(http_response.text().await.unwrap());
|
|
||||||
match results {
|
match results {
|
||||||
|
ServerResponse::Error(err) => println!("Error: {}", err),
|
||||||
ServerResponse::Offline => println!("Offline"),
|
ServerResponse::Offline => println!("Offline"),
|
||||||
ServerResponse::Online(online_info) => {
|
ServerResponse::Online(online_info) => {
|
||||||
println!(
|
println!(
|
||||||
"Name: {}\nAddress: {}\nVersion: {}\nPlayers: {}/{}",
|
"Name: {}\nAddress: {}\nVersion: {}\nPlayers: {}/{}",
|
||||||
online_info.readable_name,
|
online_info.readable_name(),
|
||||||
online_info.searchable_name,
|
online_info.searchable_name(),
|
||||||
online_info.version,
|
online_info.version(),
|
||||||
online_info.players_online,
|
online_info.players_online(),
|
||||||
online_info.player_limit
|
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;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ServerResponse {
|
pub enum ServerResponse<T: ServerOnlineResponse> {
|
||||||
Offline,
|
Offline,
|
||||||
Online(ServerOnlineResponse),
|
Online(T),
|
||||||
|
Error(anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub trait ServerOnlineResponse {
|
||||||
pub struct ServerOnlineResponse {
|
fn players_online(&self) -> u64;
|
||||||
pub players_online: u64,
|
fn player_limit(&self) -> u64;
|
||||||
pub player_limit: u64,
|
fn version(&self) -> &String;
|
||||||
pub version: String,
|
fn searchable_name(&self) -> &String;
|
||||||
pub searchable_name: String,
|
fn readable_name(&self) -> &String;
|
||||||
pub readable_name: String,
|
}
|
||||||
pub clean_name: String,
|
|
||||||
pub reported_name: String,
|
pub trait PlayerList {
|
||||||
pub players: Option<Vec<String>>,
|
fn players(&self) -> &Vec<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub trait ServerInfo {
|
pub trait ServerInfo {
|
||||||
fn new(token: String, addr: Url) -> Self
|
fn new(token: String, addr: Url) -> Self;
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
|
|
||||||
/// An addressable, unique identifier, used for its API.
|
/// 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 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.
|
/// If this is an SCP secret laboratory server, it can be a server ID.
|
||||||
fn addressable_name(&self) -> String;
|
type AddressableName;
|
||||||
/// The address wrapped in the API URL.
|
fn addressable_name(&self) -> &Self::AddressableName;
|
||||||
fn api_address(&self) -> Url;
|
|
||||||
fn parse(&self, response: String) -> ServerResponse;
|
|
||||||
|
|
||||||
/// The app token used to send status updates.
|
/// The app token used to send status updates.
|
||||||
fn app_token(&self) -> String;
|
fn app_token(&self) -> String;
|
||||||
|
|
||||||
|
type OnlineResponse: ServerOnlineResponse;
|
||||||
|
async fn poll(&self) -> ServerResponse<Self::OnlineResponse>;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user