-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Poise migration (#2) * feat: Add poise * chore: Refactor to make the migration easier * scope: bring in some placeholder info * scope: experimenting with embeds * Implement event logger (#3) * feat: add table for saving event logging targets * feat: add channel IDs as statics for now
- Loading branch information
Showing
6 changed files
with
380 additions
and
172 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
/target | ||
Secrets*.toml | ||
.idea/ | ||
.idea/ | ||
att-arma-serenity.db |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,3 +15,4 @@ reqwest = "0.11.20" | |
serde = "1.0.188" | ||
shuttle-turso = "0.25.0" | ||
libsql-client = "0.30.1" | ||
poise = "0.5.5" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
CREATE TABLE IF NOT EXISTS discord_guild ( | ||
`guild_id` INTEGER PRIMARY KEY AUTOINCREMENT, | ||
`discord_id` varchar(255) NOT NULL | ||
); | ||
|
||
CREATE TABLE IF NOT EXISTS arma_servers ( | ||
`server_id` INTEGER PRIMARY KEY AUTOINCREMENT, | ||
`guild_id` INTEGER NOT NULL, | ||
`addr` varchar(255) NOT NULL, | ||
FOREIGN KEY (guild_id) REFERENCES discord_guild(guild_id) | ||
); | ||
|
||
CREATE TABLE IF NOT EXISTS broadcast_targets ( | ||
`guild_id` INTEGER NOT NULL, | ||
`channel_id` INTEGER NOT NULL, | ||
FOREIGN KEY (guild_id) REFERENCES discord_guild(guild_id) | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
// Error handling / trace logging | ||
use tracing::{error, info}; | ||
|
||
// Serenity | ||
use serenity::{ | ||
builder::CreateApplicationCommand, model::prelude::application_command::CommandDataOption, | ||
prelude::*, utils::MessageBuilder, | ||
}; | ||
|
||
// Utility | ||
use serde::Deserialize; | ||
use std::time::Duration; | ||
use serenity::model::prelude::ChannelId; | ||
use tokio::time::sleep; | ||
|
||
pub struct ServerStatusCommand; | ||
|
||
// fn push_server_details(response: &mut MessageBuilder, server: &SteamServer) { | ||
// info!("Pushing response server details for: {:?}", server); | ||
// | ||
// response.push_bold_line(format!("Server Status for {}:", server.name)); | ||
// | ||
// // TODO: Fix "map not found" bug | ||
// response | ||
// .push_bold("Map: ") | ||
// .push_line(format!("{}", server.map)); | ||
// | ||
// response | ||
// .push_bold("Players: ") | ||
// .push_line(format!("{}/{}", server.players, server.max_players)) | ||
// .push_bold("Connect: "); | ||
// let address = server.addr.split(":").collect::<Vec<&str>>(); | ||
// response.push_line(format!( | ||
// "steam://connect/{}:{}", | ||
// address[0], server.gameport | ||
// )); | ||
// } | ||
impl ServerStatusCommand { | ||
pub async fn run( | ||
_options: &[CommandDataOption], | ||
steam_key: String, | ||
servers: Vec<String>, | ||
// response_channel_id: ChannelId, | ||
// ctx: Context, | ||
) -> String { | ||
|
||
// let _ = response_channel_id.send_message(&ctx.http, |m| { | ||
// m.content("Sure!"); | ||
// for server in servers { | ||
// m.add_embed(|e| { | ||
// e.title("Server Status") | ||
// .description("Querying the server to get status.") | ||
// .field("Server", server.name, true); | ||
// | ||
// if !server.map.is_empty() { | ||
// e.field("Map", server.map, true) | ||
// } | ||
// let address = server.addr.split(":").collect::<Vec<&str>>(); | ||
// e.field("Players", format!("{}/{}", server.players, server.max_players), true) | ||
// .field("Connect", format!("steam://connect/{}:{}", address[0], server.gameport), true) | ||
// }) | ||
// } | ||
// m | ||
// }).await; | ||
|
||
let mut response = MessageBuilder::new(); | ||
|
||
response.push_line("Sure!"); | ||
|
||
for server in servers { | ||
match Self::fetch_steam_data(&steam_key, &server).await { | ||
Ok(steam_response) => { | ||
if let Some(servers) = steam_response.response.servers { | ||
if let Some(server) = servers.first() { | ||
Self::push_server_details(&mut response, server); | ||
}; | ||
} else { | ||
response.push_line(format!( | ||
"no server found at {} or the server is down, sorry!", | ||
&server | ||
)); | ||
}; | ||
} | ||
Err(why) => { | ||
response.push_line(format!("Error grabbing details for {}: {}", server, why)); | ||
} | ||
}; | ||
let _ = sleep(Duration::from_millis(50)); | ||
} | ||
response.build() | ||
} | ||
pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { | ||
command | ||
.name("status") | ||
.description("Query the server to list all running instances.") | ||
} | ||
async fn fetch_steam_data( | ||
api_key: &str, | ||
server_details: &str, | ||
) -> Result<SteamResponse<GetServerListResponse>, reqwest::Error> { | ||
reqwest::get( | ||
format!( | ||
"https://api.steampowered.com/IGameServersService/GetServerList/v1?key={}&filter=addr\\{}", | ||
api_key, | ||
server_details)) | ||
.await? | ||
.json::<SteamResponse<GetServerListResponse>>().await | ||
} | ||
|
||
fn push_server_details(response: &mut MessageBuilder, server: &SteamServer) { | ||
info!("Pushing response server details for: {:?}", server); | ||
|
||
response.push_bold_line(format!("Server Status for {}:", server.name)); | ||
|
||
// TODO: Fix "map not found" bug | ||
response | ||
.push_bold("Map: ") | ||
.push_line(format!("{}", server.map)); | ||
|
||
response | ||
.push_bold("Players: ") | ||
.push_line(format!("{}/{}", server.players, server.max_players)) | ||
.push_bold("Connect: "); | ||
let address = server.addr.split(":").collect::<Vec<&str>>(); | ||
response.push_line(format!( | ||
"steam://connect/{}:{}", | ||
address[0], server.gameport | ||
)); | ||
} | ||
} | ||
|
||
// Steam structures | ||
/// SteamResponse is a wrapper around the actual response, representing what Steam Web API returns. | ||
#[derive(Deserialize, Debug)] | ||
pub struct SteamResponse<T: SteamApiResponse> { | ||
pub response: T, | ||
} | ||
|
||
/// SteamApiResponse is an empty trait representing the response data from the Steam Web API. | ||
pub trait SteamApiResponse {} | ||
|
||
/// GetServerListResponse is the actual response data generated when calling the Steam Web API's GetServerList endpoint. | ||
/// It contains a list of servers. | ||
#[allow(dead_code)] | ||
#[derive(Deserialize, Debug)] | ||
pub struct GetServerListResponse { | ||
servers: Option<Vec<SteamServer>>, | ||
} | ||
|
||
impl SteamApiResponse for GetServerListResponse {} | ||
|
||
#[allow(dead_code)] | ||
#[derive(Deserialize, Debug)] | ||
pub struct SteamServer { | ||
pub addr: String, | ||
pub gameport: u32, | ||
pub steamid: String, | ||
pub name: String, | ||
pub appid: u32, | ||
pub gamedir: String, | ||
pub version: String, | ||
pub product: String, | ||
pub region: i32, | ||
pub players: u32, | ||
pub max_players: u32, | ||
pub bots: u32, | ||
pub map: String, | ||
pub secure: bool, | ||
pub dedicated: bool, | ||
pub os: String, | ||
pub gametype: String, | ||
} | ||
|
||
/// GetServersAtAddressResponse is the actual response data generated when calling the Steam Web API's GetServersAtAddress endpoint. | ||
/// it has two fields, success, which is a boolean indicating whether the request was successful, and servers, which is a vector of servers. | ||
#[allow(dead_code)] | ||
#[derive(Deserialize, Debug)] | ||
pub(crate) struct GetServersAtAddressResponse { | ||
pub(crate) success: bool, | ||
pub(crate) servers: Option<Vec<ServersAtAddress>>, | ||
} | ||
|
||
impl SteamApiResponse for GetServersAtAddressResponse {} | ||
|
||
/// ServersAtAddress represents a single server as returned by the Steam Web API's GetServersAtAddress endpoint. | ||
/// It has multiple fields, including the server's IP address, port, and game ID. | ||
#[allow(dead_code)] | ||
#[derive(Deserialize, Debug)] | ||
pub(crate) struct ServersAtAddress { | ||
pub(crate) addr: String, | ||
pub(crate) gmsindex: i32, | ||
pub(crate) steamid: String, | ||
pub(crate) appid: i32, | ||
pub(crate) gamedir: String, | ||
pub(crate) region: i32, | ||
pub(crate) secure: bool, | ||
pub(crate) lan: bool, | ||
pub(crate) gameport: i32, | ||
pub(crate) specport: i32, | ||
} |
Oops, something went wrong.