mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 03:24:20 +00:00
Add /terraria
command
This commit is contained in:
parent
dfd9c982a9
commit
261d314e83
12 changed files with 254 additions and 5 deletions
|
@ -1,5 +1,6 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run (telegram)" type="CargoCommandRunConfiguration" factoryName="Cargo Command" folderName="Run">
|
||||
<option name="buildProfileId" value="dev" />
|
||||
<option name="command" value="run --package royalnet --bin royalnet --no-default-features --features interface_database,service_telegram" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<envs>
|
||||
|
|
|
@ -103,6 +103,7 @@ optional = true
|
|||
default = [
|
||||
"interface_database",
|
||||
"interface_stratz",
|
||||
"interface_tshock",
|
||||
"service_brooch",
|
||||
"service_telegram",
|
||||
]
|
||||
|
@ -112,9 +113,13 @@ interface_database = [
|
|||
]
|
||||
interface_stratz = [
|
||||
"graphql_client"
|
||||
]
|
||||
interface_tshock = [
|
||||
|
||||
]
|
||||
service_telegram = [
|
||||
"interface_database",
|
||||
"interface_tshock",
|
||||
"teloxide",
|
||||
"rand",
|
||||
"parse_datetime",
|
||||
|
|
11
migrations/2024-10-30-232449_Add fluiddruid/down.sql
Normal file
11
migrations/2024-10-30-232449_Add fluiddruid/down.sql
Normal file
|
@ -0,0 +1,11 @@
|
|||
DELETE
|
||||
FROM telegram
|
||||
WHERE user_id = 45;
|
||||
|
||||
DELETE
|
||||
FROM steam
|
||||
WHERE user_id = 45;
|
||||
|
||||
DELETE
|
||||
FROM users
|
||||
WHERE id = 45;
|
8
migrations/2024-10-30-232449_Add fluiddruid/up.sql
Normal file
8
migrations/2024-10-30-232449_Add fluiddruid/up.sql
Normal file
|
@ -0,0 +1,8 @@
|
|||
INSERT INTO users (id, username)
|
||||
VALUES (45, 'fluiddruid');
|
||||
|
||||
INSERT INTO telegram
|
||||
VALUES (45, 524944901);
|
||||
|
||||
INSERT INTO steam
|
||||
VALUES (45, 1090217987);
|
|
@ -21,6 +21,9 @@ pub mod service_telegram {
|
|||
TELEGRAM_NOTIFICATION_CHATID?: String > i64 -> crate::instance::config::ChatIdConversionHack -> teloxide::types::ChatId,
|
||||
// TODO: Unimplemented
|
||||
TELEGRAM_MATCHMAKING_CHATID?: String > i64 -> crate::instance::config::ChatIdConversionHack -> teloxide::types::ChatId,
|
||||
|
||||
TSHOCK_BASE_URL?: String > reqwest::Url,
|
||||
TSHOCK_TOKEN?: String,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::future::Future;
|
|||
#[allow(unused_imports)]
|
||||
use crate::services::RoyalnetService;
|
||||
|
||||
pub(self) mod config;
|
||||
pub mod config;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RoyalnetInstance {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// @generated automatically by Diesel CLI.
|
||||
|
||||
pub mod sql_types {
|
||||
#[derive(diesel::query_builder::QueryId, Clone, diesel::sql_types::SqlType)]
|
||||
#[diesel(postgres_type(name = "matchmaking_choice"))]
|
||||
pub struct MatchmakingChoice;
|
||||
#[derive(diesel::query_builder::QueryId, Clone, diesel::sql_types::SqlType)]
|
||||
#[diesel(postgres_type(name = "matchmaking_choice"))]
|
||||
pub struct MatchmakingChoice;
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
|
|
|
@ -3,3 +3,6 @@ pub mod database;
|
|||
|
||||
#[cfg(feature = "interface_stratz")]
|
||||
pub mod stratz;
|
||||
|
||||
#[cfg(feature = "interface_tshock")]
|
||||
pub mod tshock;
|
||||
|
|
89
src/interfaces/tshock/mod.rs
Normal file
89
src/interfaces/tshock/mod.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
use reqwest::Url;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ServerStatus {
|
||||
// status
|
||||
#[serde(rename = "name")]
|
||||
pub server_name: String,
|
||||
|
||||
#[serde(rename = "serverversion")]
|
||||
pub server_version: String,
|
||||
|
||||
#[serde(rename = "tshockversion")]
|
||||
pub tshock_version: String,
|
||||
|
||||
pub port: u16,
|
||||
|
||||
#[serde(rename = "playercount")]
|
||||
pub current_players: u8,
|
||||
|
||||
#[serde(rename = "maxplayers")]
|
||||
pub max_players: u8,
|
||||
|
||||
#[serde(rename = "world")]
|
||||
pub world_name: String,
|
||||
|
||||
// uptime
|
||||
// serverpassword
|
||||
|
||||
pub players: Vec<PlayerStatus>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PlayerStatus {
|
||||
pub nickname: String,
|
||||
pub username: String,
|
||||
pub group: String,
|
||||
pub active: bool,
|
||||
pub state: isize, // ???
|
||||
pub team: isize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("API request failed")]
|
||||
Requesting(reqwest::Error),
|
||||
#[error("API response parsing failed")]
|
||||
Parsing(reqwest::Error),
|
||||
}
|
||||
|
||||
pub async fn get_server_status(client: &reqwest::Client, base_url: Url, token: &str) -> Result<ServerStatus, Error> {
|
||||
log::debug!("Getting TShock server status...");
|
||||
|
||||
log::trace!("Preparing request URL from base: {base_url:?}");
|
||||
let mut url = base_url;
|
||||
|
||||
log::trace!("Building path...");
|
||||
{
|
||||
let mut path = url.path_segments_mut()
|
||||
.expect("URL to have a valid path");
|
||||
|
||||
path.push("v2");
|
||||
path.push("server");
|
||||
path.push("status");
|
||||
}
|
||||
|
||||
log::trace!("Building query parameters...");
|
||||
{
|
||||
let mut query = url.query_pairs_mut();
|
||||
|
||||
query.append_pair("token", token);
|
||||
query.append_pair("players", "true");
|
||||
}
|
||||
|
||||
log::trace!("Making request with: {url:?}");
|
||||
let response = client.get(url)
|
||||
.send()
|
||||
.await
|
||||
.map_err(Error::Requesting)?;
|
||||
|
||||
log::trace!("Parsing response: {response:#?}");
|
||||
let data = response
|
||||
.json::<ServerStatus>()
|
||||
.await
|
||||
.map_err(Error::Parsing)?;
|
||||
|
||||
Ok(data)
|
||||
}
|
|
@ -27,6 +27,7 @@ pub mod roll;
|
|||
pub mod diario;
|
||||
pub mod matchmaking;
|
||||
pub mod quote;
|
||||
pub mod terraria;
|
||||
|
||||
type CommandResult = AnyResult<()>;
|
||||
|
||||
|
@ -61,6 +62,8 @@ pub enum Command {
|
|||
Matchmaking(matchmaking::MatchmakingArgs),
|
||||
#[command(description = "Invia una riga dal diario RYG.")]
|
||||
Quote(quote::QuoteArgs),
|
||||
#[command(description = "Vedi chi è online sul server attuale di Terraria.")]
|
||||
Terraria,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
|
@ -110,6 +113,7 @@ impl Command {
|
|||
Command::Diario(ref args) => diario::handler(&bot, &message, args, &database).await,
|
||||
Command::Matchmaking(ref args) => matchmaking::handler(&bot, &message, args, &database).await,
|
||||
Command::Quote(ref id) => quote::handler(&bot, &message, id, &database).await,
|
||||
Command::Terraria => terraria::handler(&bot, &message, &database).await
|
||||
};
|
||||
|
||||
log::trace!("Delegating error handling to error handler...");
|
||||
|
|
125
src/services/telegram/commands/terraria.rs
Normal file
125
src/services/telegram/commands/terraria.rs
Normal file
|
@ -0,0 +1,125 @@
|
|||
use crate::instance::config::service_telegram::{TSHOCK_BASE_URL, TSHOCK_TOKEN};
|
||||
use crate::interfaces::database::models::TelegramUserId;
|
||||
use crate::interfaces::tshock::{get_server_status, PlayerStatus, ServerStatus};
|
||||
use crate::services::telegram::commands::CommandResult;
|
||||
use crate::services::telegram::dependencies::interface_database::DatabaseInterface;
|
||||
use crate::utils::anyhow_result::AnyResult;
|
||||
use crate::utils::telegram_string::TelegramEscape;
|
||||
use anyhow::Context;
|
||||
use diesel::PgConnection;
|
||||
use teloxide::payloads::SendMessageSetters;
|
||||
use teloxide::prelude::{Message, Requester};
|
||||
use teloxide::types::{ParseMode, ReplyParameters};
|
||||
use teloxide::Bot;
|
||||
|
||||
fn stringify_team(value: isize) -> &'static str {
|
||||
match value {
|
||||
0 => "⬜️",
|
||||
1 => "🟥",
|
||||
2 => "🟩",
|
||||
3 => "🟦",
|
||||
4 => "🟨",
|
||||
5 => "🟪",
|
||||
_ => "⬛️",
|
||||
}
|
||||
}
|
||||
|
||||
fn get_player_telegram_id(database: &mut PgConnection, tshock_username: &str) -> AnyResult<Option<TelegramUserId>> {
|
||||
use diesel::prelude::*;
|
||||
use diesel::{ExpressionMethods, QueryDsl};
|
||||
use crate::interfaces::database::schema::{telegram, users};
|
||||
use crate::interfaces::database::models::TelegramUser;
|
||||
|
||||
Ok(
|
||||
users::table
|
||||
.inner_join(
|
||||
telegram::table.on(
|
||||
users::id.eq(telegram::user_id)
|
||||
)
|
||||
)
|
||||
.filter(
|
||||
users::username.eq(tshock_username)
|
||||
)
|
||||
.select(TelegramUser::as_select())
|
||||
.get_result::<TelegramUser>(database)
|
||||
.optional()
|
||||
.context("Non è stato possibile connettersi al database RYG.")?
|
||||
.map(|t| t.telegram_id)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fn stringify_player(database: &mut PgConnection, tshock: PlayerStatus) -> AnyResult<String> {
|
||||
log::debug!("Stringifying player...");
|
||||
|
||||
let telegram_id = match tshock.username.as_str() {
|
||||
"" => None,
|
||||
username => {
|
||||
get_player_telegram_id(database, username)
|
||||
.context("Non è stato possibile trovare l'username di un giocatore.")?
|
||||
}
|
||||
};
|
||||
|
||||
let string = match telegram_id {
|
||||
None => format!(
|
||||
"{} <b>{}</b>",
|
||||
stringify_team(tshock.team),
|
||||
tshock.nickname.escape_telegram_html(),
|
||||
),
|
||||
Some(telegram_id) => format!(
|
||||
"{} <a href=\"tg://user?id={}\"><b>{}</b></a>",
|
||||
stringify_team(tshock.team),
|
||||
telegram_id,
|
||||
tshock.nickname.escape_telegram_html(),
|
||||
),
|
||||
};
|
||||
|
||||
Ok(string)
|
||||
}
|
||||
|
||||
fn stringify_status(database: &mut PgConnection, tshock: ServerStatus) -> AnyResult<String> {
|
||||
let mut players_strings = Vec::new();
|
||||
|
||||
for player in tshock.players {
|
||||
players_strings.push(stringify_player(database, player)?);
|
||||
}
|
||||
|
||||
Ok(
|
||||
format!(
|
||||
"🌳 <b><u>{}</u></b>\n\
|
||||
{} giocator{} online\n\
|
||||
\n\
|
||||
{}",
|
||||
tshock.world_name,
|
||||
tshock.current_players,
|
||||
if tshock.current_players == 1 { 'e' } else { 'i' },
|
||||
players_strings.join("\n"),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn handler(bot: &Bot, message: &Message, database: &DatabaseInterface) -> CommandResult {
|
||||
let mut database = database.connect()?;
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let url = TSHOCK_BASE_URL().clone()
|
||||
.context("Il bot non è configurato per connettersi a un server di Terraria.")?;
|
||||
let token = TSHOCK_TOKEN().as_ref()
|
||||
.context("Il bot non è configurato per autenticarsi a un server di Terraria.")?;
|
||||
|
||||
let status = get_server_status(&client, url, token)
|
||||
.await
|
||||
.context("Non è stato possibile effettuare la richiesta al server. Forse il server è spento?")?;
|
||||
|
||||
let text = stringify_status(&mut database, status)
|
||||
.context("Non è stato possibile trasformare in testo lo stato corrente del server.")?;
|
||||
|
||||
let _reply = bot
|
||||
.send_message(message.chat.id, text)
|
||||
.parse_mode(ParseMode::Html)
|
||||
.reply_parameters(ReplyParameters::new(message.id))
|
||||
.await
|
||||
.context("Non è stato possibile inviare la risposta.")?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
pub mod database;
|
||||
pub mod matchmaking;
|
||||
pub mod diario;
|
||||
pub mod diario;
|
||||
|
|
Loading…
Reference in a new issue