mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 11:34:18 +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">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Run (telegram)" type="CargoCommandRunConfiguration" factoryName="Cargo Command" folderName="Run">
|
<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="command" value="run --package royalnet --bin royalnet --no-default-features --features interface_database,service_telegram" />
|
||||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||||
<envs>
|
<envs>
|
||||||
|
|
|
@ -103,6 +103,7 @@ optional = true
|
||||||
default = [
|
default = [
|
||||||
"interface_database",
|
"interface_database",
|
||||||
"interface_stratz",
|
"interface_stratz",
|
||||||
|
"interface_tshock",
|
||||||
"service_brooch",
|
"service_brooch",
|
||||||
"service_telegram",
|
"service_telegram",
|
||||||
]
|
]
|
||||||
|
@ -112,9 +113,13 @@ interface_database = [
|
||||||
]
|
]
|
||||||
interface_stratz = [
|
interface_stratz = [
|
||||||
"graphql_client"
|
"graphql_client"
|
||||||
|
]
|
||||||
|
interface_tshock = [
|
||||||
|
|
||||||
]
|
]
|
||||||
service_telegram = [
|
service_telegram = [
|
||||||
"interface_database",
|
"interface_database",
|
||||||
|
"interface_tshock",
|
||||||
"teloxide",
|
"teloxide",
|
||||||
"rand",
|
"rand",
|
||||||
"parse_datetime",
|
"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,
|
TELEGRAM_NOTIFICATION_CHATID?: String > i64 -> crate::instance::config::ChatIdConversionHack -> teloxide::types::ChatId,
|
||||||
// TODO: Unimplemented
|
// TODO: Unimplemented
|
||||||
TELEGRAM_MATCHMAKING_CHATID?: String > i64 -> crate::instance::config::ChatIdConversionHack -> teloxide::types::ChatId,
|
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)]
|
#[allow(unused_imports)]
|
||||||
use crate::services::RoyalnetService;
|
use crate::services::RoyalnetService;
|
||||||
|
|
||||||
pub(self) mod config;
|
pub mod config;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RoyalnetInstance {
|
pub struct RoyalnetInstance {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// @generated automatically by Diesel CLI.
|
// @generated automatically by Diesel CLI.
|
||||||
|
|
||||||
pub mod sql_types {
|
pub mod sql_types {
|
||||||
#[derive(diesel::query_builder::QueryId, Clone, diesel::sql_types::SqlType)]
|
#[derive(diesel::query_builder::QueryId, Clone, diesel::sql_types::SqlType)]
|
||||||
#[diesel(postgres_type(name = "matchmaking_choice"))]
|
#[diesel(postgres_type(name = "matchmaking_choice"))]
|
||||||
pub struct MatchmakingChoice;
|
pub struct MatchmakingChoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
|
|
|
@ -3,3 +3,6 @@ pub mod database;
|
||||||
|
|
||||||
#[cfg(feature = "interface_stratz")]
|
#[cfg(feature = "interface_stratz")]
|
||||||
pub mod 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 diario;
|
||||||
pub mod matchmaking;
|
pub mod matchmaking;
|
||||||
pub mod quote;
|
pub mod quote;
|
||||||
|
pub mod terraria;
|
||||||
|
|
||||||
type CommandResult = AnyResult<()>;
|
type CommandResult = AnyResult<()>;
|
||||||
|
|
||||||
|
@ -61,6 +62,8 @@ pub enum Command {
|
||||||
Matchmaking(matchmaking::MatchmakingArgs),
|
Matchmaking(matchmaking::MatchmakingArgs),
|
||||||
#[command(description = "Invia una riga dal diario RYG.")]
|
#[command(description = "Invia una riga dal diario RYG.")]
|
||||||
Quote(quote::QuoteArgs),
|
Quote(quote::QuoteArgs),
|
||||||
|
#[command(description = "Vedi chi è online sul server attuale di Terraria.")]
|
||||||
|
Terraria,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
|
@ -110,6 +113,7 @@ impl Command {
|
||||||
Command::Diario(ref args) => diario::handler(&bot, &message, args, &database).await,
|
Command::Diario(ref args) => diario::handler(&bot, &message, args, &database).await,
|
||||||
Command::Matchmaking(ref args) => matchmaking::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::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...");
|
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(())
|
||||||
|
}
|
Loading…
Reference in a new issue