mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-24 20:14:19 +00:00
Run "Reformat code..." IDE feature
This commit is contained in:
parent
b84b70080f
commit
ec3f5daff7
34 changed files with 67626 additions and 67621 deletions
|
@ -4,7 +4,7 @@
|
||||||
#[cfg(feature = "interface_database")]
|
#[cfg(feature = "interface_database")]
|
||||||
pub mod interface_database {
|
pub mod interface_database {
|
||||||
use micronfig::config;
|
use micronfig::config;
|
||||||
|
|
||||||
config! {
|
config! {
|
||||||
DATABASE_AUTOMIGRATE: String > bool,
|
DATABASE_AUTOMIGRATE: String > bool,
|
||||||
DATABASE_URL: String,
|
DATABASE_URL: String,
|
||||||
|
@ -14,7 +14,7 @@ pub mod interface_database {
|
||||||
#[cfg(feature = "service_telegram")]
|
#[cfg(feature = "service_telegram")]
|
||||||
pub mod service_telegram {
|
pub mod service_telegram {
|
||||||
use micronfig::config;
|
use micronfig::config;
|
||||||
|
|
||||||
config! {
|
config! {
|
||||||
TELEGRAM_DATABASE_URL: String,
|
TELEGRAM_DATABASE_URL: String,
|
||||||
TELEGRAM_BOT_TOKEN: String,
|
TELEGRAM_BOT_TOKEN: String,
|
||||||
|
@ -25,7 +25,7 @@ pub mod service_telegram {
|
||||||
#[cfg(feature = "service_brooch")]
|
#[cfg(feature = "service_brooch")]
|
||||||
pub mod brooch {
|
pub mod brooch {
|
||||||
use micronfig::config;
|
use micronfig::config;
|
||||||
|
|
||||||
#[allow(unused_qualifications)]
|
#[allow(unused_qualifications)]
|
||||||
config! {
|
config! {
|
||||||
BROOCH_DATABASE_URL: String,
|
BROOCH_DATABASE_URL: String,
|
||||||
|
@ -66,7 +66,7 @@ impl From<i64> for TimeDeltaConversionHack {
|
||||||
|
|
||||||
impl TryFrom<TimeDeltaConversionHack> for chrono::TimeDelta {
|
impl TryFrom<TimeDeltaConversionHack> for chrono::TimeDelta {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn try_from(value: TimeDeltaConversionHack) -> Result<Self, Self::Error> {
|
fn try_from(value: TimeDeltaConversionHack) -> Result<Self, Self::Error> {
|
||||||
Self::new(value.0, 0).ok_or(())
|
Self::new(value.0, 0).ok_or(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,13 @@ pub(self) mod config;
|
||||||
pub struct RoyalnetInstance {
|
pub struct RoyalnetInstance {
|
||||||
#[cfg(feature = "service_telegram")]
|
#[cfg(feature = "service_telegram")]
|
||||||
service_telegram: crate::services::telegram::TelegramService,
|
service_telegram: crate::services::telegram::TelegramService,
|
||||||
|
|
||||||
#[cfg(not(feature = "service_telegram"))]
|
#[cfg(not(feature = "service_telegram"))]
|
||||||
service_telegram: (),
|
service_telegram: (),
|
||||||
|
|
||||||
#[cfg(feature = "service_brooch")]
|
#[cfg(feature = "service_brooch")]
|
||||||
service_brooch: crate::services::brooch::BroochService,
|
service_brooch: crate::services::brooch::BroochService,
|
||||||
|
|
||||||
#[cfg(not(feature = "service_brooch"))]
|
#[cfg(not(feature = "service_brooch"))]
|
||||||
service_brooch: (),
|
service_brooch: (),
|
||||||
}
|
}
|
||||||
|
@ -27,83 +27,83 @@ impl RoyalnetInstance {
|
||||||
service_brooch: Self::setup_brooch_service().await,
|
service_brooch: Self::setup_brooch_service().await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(mut self) {
|
pub async fn run(mut self) {
|
||||||
Self::run_pending_migrations();
|
Self::run_pending_migrations();
|
||||||
|
|
||||||
let future_telegram = async move {
|
let future_telegram = async move {
|
||||||
Self::get_telegram_future(&mut self.service_telegram).await;
|
Self::get_telegram_future(&mut self.service_telegram).await;
|
||||||
};
|
};
|
||||||
let future_brooch = async move {
|
let future_brooch = async move {
|
||||||
Self::get_brooch_future(&mut self.service_brooch).await;
|
Self::get_brooch_future(&mut self.service_brooch).await;
|
||||||
};
|
};
|
||||||
|
|
||||||
let task_telegram = tokio::spawn(future_telegram);
|
let task_telegram = tokio::spawn(future_telegram);
|
||||||
let task_brooch = tokio::spawn(future_brooch);
|
let task_brooch = tokio::spawn(future_brooch);
|
||||||
|
|
||||||
let _ = tokio::join!(
|
let _ = tokio::join!(
|
||||||
task_telegram,
|
task_telegram,
|
||||||
task_brooch,
|
task_brooch,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "interface_database")]
|
#[cfg(feature = "interface_database")]
|
||||||
fn run_pending_migrations() {
|
fn run_pending_migrations() {
|
||||||
if !config::interface_database::DATABASE_AUTOMIGRATE() {
|
if !config::interface_database::DATABASE_AUTOMIGRATE() {
|
||||||
log::warn!("Database automigration is disabled.");
|
log::warn!("Database automigration is disabled.");
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::debug!("Automatically applying database migrations...");
|
log::debug!("Automatically applying database migrations...");
|
||||||
|
|
||||||
log::trace!("Connecting to the database...");
|
log::trace!("Connecting to the database...");
|
||||||
let mut db = crate::interfaces::database::connect(
|
let mut db = crate::interfaces::database::connect(
|
||||||
config::interface_database::DATABASE_URL()
|
config::interface_database::DATABASE_URL()
|
||||||
).expect("Unable to connect to the database to apply migrations.");
|
).expect("Unable to connect to the database to apply migrations.");
|
||||||
|
|
||||||
log::trace!("Applying migrations...");
|
log::trace!("Applying migrations...");
|
||||||
crate::interfaces::database::migrate(&mut db)
|
crate::interfaces::database::migrate(&mut db)
|
||||||
.expect("Failed to automatically apply migrations to the database.");
|
.expect("Failed to automatically apply migrations to the database.");
|
||||||
|
|
||||||
log::trace!("Migration successful!");
|
log::trace!("Migration successful!");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "interface_database"))]
|
#[cfg(not(feature = "interface_database"))]
|
||||||
fn run_pending_migrations() {
|
fn run_pending_migrations() {
|
||||||
log::warn!("Database automigration is not compiled in.");
|
log::warn!("Database automigration is not compiled in.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "service_telegram")]
|
#[cfg(feature = "service_telegram")]
|
||||||
async fn setup_telegram_service() -> crate::services::telegram::TelegramService {
|
async fn setup_telegram_service() -> crate::services::telegram::TelegramService {
|
||||||
log::debug!("Setting up Telegram service...");
|
log::debug!("Setting up Telegram service...");
|
||||||
|
|
||||||
crate::services::telegram::TelegramService::new(
|
crate::services::telegram::TelegramService::new(
|
||||||
config::service_telegram::TELEGRAM_DATABASE_URL().clone(),
|
config::service_telegram::TELEGRAM_DATABASE_URL().clone(),
|
||||||
config::service_telegram::TELEGRAM_BOT_TOKEN().clone(),
|
config::service_telegram::TELEGRAM_BOT_TOKEN().clone(),
|
||||||
*config::service_telegram::TELEGRAM_NOTIFICATION_CHATID(),
|
*config::service_telegram::TELEGRAM_NOTIFICATION_CHATID(),
|
||||||
).await.expect("Unable to setup Telegram service.")
|
).await.expect("Unable to setup Telegram service.")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "service_telegram"))]
|
#[cfg(not(feature = "service_telegram"))]
|
||||||
async fn setup_telegram_service() {
|
async fn setup_telegram_service() {
|
||||||
log::warn!("Telegram service is not compiled in.");
|
log::warn!("Telegram service is not compiled in.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "service_telegram")]
|
#[cfg(feature = "service_telegram")]
|
||||||
fn get_telegram_future(service: &mut crate::services::telegram::TelegramService) -> impl Future<Output = ()> + '_ {
|
fn get_telegram_future(service: &mut crate::services::telegram::TelegramService) -> impl Future<Output=()> + '_ {
|
||||||
service.run_loop()
|
service.run_loop()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "service_telegram"))]
|
#[cfg(not(feature = "service_telegram"))]
|
||||||
#[allow(clippy::manual_async_fn)]
|
#[allow(clippy::manual_async_fn)]
|
||||||
fn get_telegram_future(_service: &mut ()) -> impl Future<Output = ()> + '_ {
|
fn get_telegram_future(_service: &mut ()) -> impl Future<Output=()> + '_ {
|
||||||
async {}
|
async {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "service_brooch")]
|
#[cfg(feature = "service_brooch")]
|
||||||
async fn setup_brooch_service() -> crate::services::brooch::BroochService {
|
async fn setup_brooch_service() -> crate::services::brooch::BroochService {
|
||||||
log::debug!("Setting up Brooch service...");
|
log::debug!("Setting up Brooch service...");
|
||||||
|
|
||||||
crate::services::brooch::BroochService::new(
|
crate::services::brooch::BroochService::new(
|
||||||
config::brooch::BROOCH_DATABASE_URL().clone(),
|
config::brooch::BROOCH_DATABASE_URL().clone(),
|
||||||
config::brooch::BROOCH_GRAPHQL_URL(),
|
config::brooch::BROOCH_GRAPHQL_URL(),
|
||||||
|
@ -115,20 +115,20 @@ impl RoyalnetInstance {
|
||||||
*config::brooch::BROOCH_MAX_IMP_WAIT_SECS(),
|
*config::brooch::BROOCH_MAX_IMP_WAIT_SECS(),
|
||||||
).expect("Unable to setup Brooch service.")
|
).expect("Unable to setup Brooch service.")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "service_brooch"))]
|
#[cfg(not(feature = "service_brooch"))]
|
||||||
async fn setup_brooch_service() {
|
async fn setup_brooch_service() {
|
||||||
log::warn!("Brooch service is not compiled in.");
|
log::warn!("Brooch service is not compiled in.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "service_brooch")]
|
#[cfg(feature = "service_brooch")]
|
||||||
fn get_brooch_future(service: &mut crate::services::brooch::BroochService) -> impl Future<Output = ()> + '_ {
|
fn get_brooch_future(service: &mut crate::services::brooch::BroochService) -> impl Future<Output=()> + '_ {
|
||||||
service.run_loop()
|
service.run_loop()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "service_brooch"))]
|
#[cfg(not(feature = "service_brooch"))]
|
||||||
#[allow(clippy::manual_async_fn)]
|
#[allow(clippy::manual_async_fn)]
|
||||||
fn get_brooch_future(_service: &mut ()) -> impl Future<Output = ()> + '_ {
|
fn get_brooch_future(_service: &mut ()) -> impl Future<Output=()> + '_ {
|
||||||
async {}
|
async {}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -20,9 +20,9 @@ impl BroochMatch {
|
||||||
pub fn is_flagged(database: &mut PgConnection, match_id: DotaMatchId) -> AnyResult<bool> {
|
pub fn is_flagged(database: &mut PgConnection, match_id: DotaMatchId) -> AnyResult<bool> {
|
||||||
use crate::interfaces::database::query_prelude::*;
|
use crate::interfaces::database::query_prelude::*;
|
||||||
use schema::brooch_match;
|
use schema::brooch_match;
|
||||||
|
|
||||||
log::trace!("Checking if {match_id:?} is flagged...");
|
log::trace!("Checking if {match_id:?} is flagged...");
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
brooch_match::table
|
brooch_match::table
|
||||||
.find(match_id)
|
.find(match_id)
|
||||||
|
@ -32,13 +32,13 @@ impl BroochMatch {
|
||||||
.gt(&0usize)
|
.gt(&0usize)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flag(database: &mut PgConnection, match_id: DotaMatchId) -> AnyResult<Self> {
|
pub fn flag(database: &mut PgConnection, match_id: DotaMatchId) -> AnyResult<Self> {
|
||||||
use crate::interfaces::database::query_prelude::*;
|
use crate::interfaces::database::query_prelude::*;
|
||||||
use schema::brooch_match;
|
use schema::brooch_match;
|
||||||
|
|
||||||
log::debug!("Flagging {match_id:?} as parsed...");
|
log::debug!("Flagging {match_id:?} as parsed...");
|
||||||
|
|
||||||
diesel::insert_into(brooch_match::table)
|
diesel::insert_into(brooch_match::table)
|
||||||
.values(brooch_match::id.eq(match_id))
|
.values(brooch_match::id.eq(match_id))
|
||||||
.on_conflict_do_nothing()
|
.on_conflict_do_nothing()
|
||||||
|
|
|
@ -29,7 +29,7 @@ impl MatchmakingEvent {
|
||||||
.get_result::<Self>(database)
|
.get_result::<Self>(database)
|
||||||
.context("Non è stato possibile aggiungere il matchmaking al database RYG.")
|
.context("Non è stato possibile aggiungere il matchmaking al database RYG.")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve a [MatchmakingEvent] from the database, given its [MatchmakingId].
|
/// Retrieve a [MatchmakingEvent] from the database, given its [MatchmakingId].
|
||||||
pub fn get(database: &mut PgConnection, matchmaking_id: MatchmakingId) -> AnyResult<Self> {
|
pub fn get(database: &mut PgConnection, matchmaking_id: MatchmakingId) -> AnyResult<Self> {
|
||||||
matchmaking_events::table
|
matchmaking_events::table
|
||||||
|
@ -37,7 +37,7 @@ impl MatchmakingEvent {
|
||||||
.get_result::<Self>(database)
|
.get_result::<Self>(database)
|
||||||
.context("Non è stato possibile recuperare il matchmaking dal database RYG.")
|
.context("Non è stato possibile recuperare il matchmaking dal database RYG.")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_started(&self) -> bool {
|
pub fn has_started(&self) -> bool {
|
||||||
self.starts_at.lt(&chrono::Local::now().naive_utc())
|
self.starts_at.lt(&chrono::Local::now().naive_utc())
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,26 +51,26 @@ pub(crate) mod telegram_ext {
|
||||||
Cant,
|
Cant,
|
||||||
Wont,
|
Wont,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MatchmakingTelegramKeyboardCallback {
|
impl MatchmakingTelegramKeyboardCallback {
|
||||||
/// Create callback data representing the [MatchmakingTelegramKeyboardCallback] in the given [MatchmakingId].
|
/// Create callback data representing the [MatchmakingTelegramKeyboardCallback] in the given [MatchmakingId].
|
||||||
pub fn callback_data(self, matchmaking_id: MatchmakingId) -> String {
|
pub fn callback_data(self, matchmaking_id: MatchmakingId) -> String {
|
||||||
matchmaking_id.callback_data(self.into())
|
matchmaking_id.callback_data(self.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inline_button(self, matchmaking_id: MatchmakingId, text: &str) -> teloxide::types::InlineKeyboardButton {
|
pub fn inline_button(self, matchmaking_id: MatchmakingId, text: &str) -> teloxide::types::InlineKeyboardButton {
|
||||||
teloxide::types::InlineKeyboardButton::new(
|
teloxide::types::InlineKeyboardButton::new(
|
||||||
text,
|
text,
|
||||||
teloxide::types::InlineKeyboardButtonKind::CallbackData(
|
teloxide::types::InlineKeyboardButtonKind::CallbackData(
|
||||||
self.callback_data(matchmaking_id)
|
self.callback_data(matchmaking_id)
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for MatchmakingTelegramKeyboardCallback {
|
impl FromStr for MatchmakingTelegramKeyboardCallback {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(
|
Ok(
|
||||||
match s {
|
match s {
|
||||||
|
@ -87,7 +87,7 @@ pub(crate) mod telegram_ext {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<MatchmakingTelegramKeyboardCallback> for &'static str {
|
impl From<MatchmakingTelegramKeyboardCallback> for &'static str {
|
||||||
fn from(value: MatchmakingTelegramKeyboardCallback) -> Self {
|
fn from(value: MatchmakingTelegramKeyboardCallback) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
@ -102,22 +102,22 @@ pub(crate) mod telegram_ext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MatchmakingMessageTelegram {
|
impl MatchmakingMessageTelegram {
|
||||||
/// Get all the [MatchmakingMessageTelegram] for a specific [MatchmakingId].
|
/// Get all the [MatchmakingMessageTelegram] for a specific [MatchmakingId].
|
||||||
pub fn get_all(database: &mut PgConnection, matchmaking_id: MatchmakingId) -> AnyResult<Vec<Self>> {
|
pub fn get_all(database: &mut PgConnection, matchmaking_id: MatchmakingId) -> AnyResult<Vec<Self>> {
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use crate::interfaces::database::schema::matchmaking_messages_telegram;
|
use crate::interfaces::database::schema::matchmaking_messages_telegram;
|
||||||
|
|
||||||
matchmaking_messages_telegram::table
|
matchmaking_messages_telegram::table
|
||||||
.filter(matchmaking_messages_telegram::matchmaking_id.eq(matchmaking_id.0))
|
.filter(matchmaking_messages_telegram::matchmaking_id.eq(matchmaking_id.0))
|
||||||
.get_results::<MatchmakingMessageTelegram>(database)
|
.get_results::<MatchmakingMessageTelegram>(database)
|
||||||
.context("La query al database RYG è fallita.")
|
.context("La query al database RYG è fallita.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reply_markup(matchmaking_id: MatchmakingId) -> teloxide::types::InlineKeyboardMarkup {
|
fn reply_markup(matchmaking_id: MatchmakingId) -> teloxide::types::InlineKeyboardMarkup {
|
||||||
use MatchmakingTelegramKeyboardCallback::*;
|
use MatchmakingTelegramKeyboardCallback::*;
|
||||||
|
|
||||||
let button_yes = Yes.inline_button(matchmaking_id, "🔵 Ci sarò!");
|
let button_yes = Yes.inline_button(matchmaking_id, "🔵 Ci sarò!");
|
||||||
let button_5min = Plus5Min.inline_button(matchmaking_id, "🕐 +5 min");
|
let button_5min = Plus5Min.inline_button(matchmaking_id, "🕐 +5 min");
|
||||||
let button_15min = Plus15Min.inline_button(matchmaking_id, "🕒 +15 min");
|
let button_15min = Plus15Min.inline_button(matchmaking_id, "🕒 +15 min");
|
||||||
|
@ -126,7 +126,7 @@ pub(crate) mod telegram_ext {
|
||||||
let button_dontw = DontWait.inline_button(matchmaking_id, "❓ Non aspettatemi.");
|
let button_dontw = DontWait.inline_button(matchmaking_id, "❓ Non aspettatemi.");
|
||||||
let button_cant = Cant.inline_button(matchmaking_id, "🔺 Non posso...");
|
let button_cant = Cant.inline_button(matchmaking_id, "🔺 Non posso...");
|
||||||
let button_wont = Wont.inline_button(matchmaking_id, "🔻 Non mi interessa.");
|
let button_wont = Wont.inline_button(matchmaking_id, "🔻 Non mi interessa.");
|
||||||
|
|
||||||
teloxide::types::InlineKeyboardMarkup::new(vec![
|
teloxide::types::InlineKeyboardMarkup::new(vec![
|
||||||
vec![button_yes],
|
vec![button_yes],
|
||||||
vec![button_5min, button_15min, button_60min],
|
vec![button_5min, button_15min, button_60min],
|
||||||
|
@ -134,20 +134,20 @@ pub(crate) mod telegram_ext {
|
||||||
vec![button_cant, button_wont],
|
vec![button_cant, button_wont],
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn text(event: &MatchmakingEvent, replies: &Vec<(MatchmakingReply, RoyalnetUser, TelegramUser)>) -> String {
|
fn text(event: &MatchmakingEvent, replies: &Vec<(MatchmakingReply, RoyalnetUser, TelegramUser)>) -> String {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
|
|
||||||
let emoji = match event.has_started() {
|
let emoji = match event.has_started() {
|
||||||
false => "🚩",
|
false => "🚩",
|
||||||
true => "🔔",
|
true => "🔔",
|
||||||
};
|
};
|
||||||
|
|
||||||
let text = event.text.as_str().escape_telegram_html();
|
let text = event.text.as_str().escape_telegram_html();
|
||||||
writeln!(result, "{emoji} <b>{text}</b>").unwrap();
|
writeln!(result, "{emoji} <b>{text}</b>").unwrap();
|
||||||
|
|
||||||
let start = event.starts_at
|
let start = event.starts_at
|
||||||
.and_utc()
|
.and_utc()
|
||||||
.with_timezone(&Local)
|
.with_timezone(&Local)
|
||||||
|
@ -155,12 +155,12 @@ pub(crate) mod telegram_ext {
|
||||||
.to_string()
|
.to_string()
|
||||||
.escape_telegram_html();
|
.escape_telegram_html();
|
||||||
writeln!(result, "<i>{start}</i>").unwrap();
|
writeln!(result, "<i>{start}</i>").unwrap();
|
||||||
|
|
||||||
writeln!(result).unwrap();
|
writeln!(result).unwrap();
|
||||||
|
|
||||||
for (reply, royalnet, telegram) in replies {
|
for (reply, royalnet, telegram) in replies {
|
||||||
use MatchmakingChoice::*;
|
use MatchmakingChoice::*;
|
||||||
|
|
||||||
let emoji = match reply.choice {
|
let emoji = match reply.choice {
|
||||||
Yes => "🔵",
|
Yes => "🔵",
|
||||||
Late => match reply.late_mins {
|
Late => match reply.late_mins {
|
||||||
|
@ -182,24 +182,24 @@ pub(crate) mod telegram_ext {
|
||||||
Cant => "🔺",
|
Cant => "🔺",
|
||||||
Wont => "🔻",
|
Wont => "🔻",
|
||||||
};
|
};
|
||||||
|
|
||||||
let telegram_id = telegram.telegram_id.0;
|
let telegram_id = telegram.telegram_id.0;
|
||||||
let username = &royalnet.username;
|
let username = &royalnet.username;
|
||||||
|
|
||||||
write!(result, "{emoji} <a href=\"tg://user?id={telegram_id}\">{username}</a>").unwrap();
|
write!(result, "{emoji} <a href=\"tg://user?id={telegram_id}\">{username}</a>").unwrap();
|
||||||
|
|
||||||
if reply.choice == Late {
|
if reply.choice == Late {
|
||||||
let late_mins = reply.late_mins;
|
let late_mins = reply.late_mins;
|
||||||
|
|
||||||
write!(result, " (+{late_mins} mins)").unwrap();
|
write!(result, " (+{late_mins} mins)").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
writeln!(result).unwrap();
|
writeln!(result).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_new(
|
async fn send_new(
|
||||||
database: &mut PgConnection,
|
database: &mut PgConnection,
|
||||||
matchmaking_id: MatchmakingId,
|
matchmaking_id: MatchmakingId,
|
||||||
|
@ -209,32 +209,32 @@ pub(crate) mod telegram_ext {
|
||||||
) -> AnyResult<teloxide::types::Message> {
|
) -> AnyResult<teloxide::types::Message> {
|
||||||
let event = MatchmakingEvent::get(database, matchmaking_id)
|
let event = MatchmakingEvent::get(database, matchmaking_id)
|
||||||
.context("Non è stato possibile recuperare il matchmaking dal database RYG.")?;
|
.context("Non è stato possibile recuperare il matchmaking dal database RYG.")?;
|
||||||
|
|
||||||
let replies = MatchmakingReply::get_all_telegram(database, matchmaking_id)
|
let replies = MatchmakingReply::get_all_telegram(database, matchmaking_id)
|
||||||
.context("Non è stato possibile recuperare le risposte al matchmaking dal database RYG.")?;
|
.context("Non è stato possibile recuperare le risposte al matchmaking dal database RYG.")?;
|
||||||
|
|
||||||
let text = Self::text(&event, &replies);
|
let text = Self::text(&event, &replies);
|
||||||
|
|
||||||
let mut request = bot.send_message(chat_id, text)
|
let mut request = bot.send_message(chat_id, text)
|
||||||
.parse_mode(ParseMode::Html);
|
.parse_mode(ParseMode::Html);
|
||||||
|
|
||||||
if !event.has_started() {
|
if !event.has_started() {
|
||||||
request = request.reply_markup(
|
request = request.reply_markup(
|
||||||
Self::reply_markup(matchmaking_id)
|
Self::reply_markup(matchmaking_id)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(reply_to) = reply_to {
|
if let Some(reply_to) = reply_to {
|
||||||
request = request.reply_parameters(
|
request = request.reply_parameters(
|
||||||
teloxide::types::ReplyParameters::new(reply_to)
|
teloxide::types::ReplyParameters::new(reply_to)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
request
|
request
|
||||||
.await
|
.await
|
||||||
.context("La richiesta di invio messaggio alla Bot API di Telegram è fallita.")
|
.context("La richiesta di invio messaggio alla Bot API di Telegram è fallita.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create(
|
fn create(
|
||||||
database: &mut PgConnection,
|
database: &mut PgConnection,
|
||||||
matchmaking_id: MatchmakingId,
|
matchmaking_id: MatchmakingId,
|
||||||
|
@ -245,7 +245,7 @@ pub(crate) mod telegram_ext {
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::dsl::*;
|
use diesel::dsl::*;
|
||||||
use crate::interfaces::database::schema::matchmaking_messages_telegram;
|
use crate::interfaces::database::schema::matchmaking_messages_telegram;
|
||||||
|
|
||||||
insert_into(matchmaking_messages_telegram::table)
|
insert_into(matchmaking_messages_telegram::table)
|
||||||
.values(&MatchmakingMessageTelegram {
|
.values(&MatchmakingMessageTelegram {
|
||||||
matchmaking_id,
|
matchmaking_id,
|
||||||
|
@ -256,7 +256,7 @@ pub(crate) mod telegram_ext {
|
||||||
.get_result::<MatchmakingMessageTelegram>(database)
|
.get_result::<MatchmakingMessageTelegram>(database)
|
||||||
.context("L'inserimento nel database RYG è fallito.")
|
.context("L'inserimento nel database RYG è fallito.")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_new_and_create(
|
pub async fn send_new_and_create(
|
||||||
database: &mut PgConnection,
|
database: &mut PgConnection,
|
||||||
matchmaking_id: MatchmakingId,
|
matchmaking_id: MatchmakingId,
|
||||||
|
@ -269,13 +269,13 @@ pub(crate) mod telegram_ext {
|
||||||
let reply = Self::send_new(database, matchmaking_id, bot, chat_id, reply_to)
|
let reply = Self::send_new(database, matchmaking_id, bot, chat_id, reply_to)
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare il messaggio Telegram del matchmaking.")?;
|
.context("Non è stato possibile inviare il messaggio Telegram del matchmaking.")?;
|
||||||
|
|
||||||
let this = Self::create(database, matchmaking_id, &reply)
|
let this = Self::create(database, matchmaking_id, &reply)
|
||||||
.context("Non è stato possibile aggiungere il messaggio Telegram al database RYG.")?;
|
.context("Non è stato possibile aggiungere il messaggio Telegram al database RYG.")?;
|
||||||
|
|
||||||
Ok(this)
|
Ok(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_edit(
|
async fn send_edit(
|
||||||
&self,
|
&self,
|
||||||
bot: &teloxide::Bot,
|
bot: &teloxide::Bot,
|
||||||
|
@ -285,21 +285,21 @@ pub(crate) mod telegram_ext {
|
||||||
-> AnyResult<teloxide::types::Message>
|
-> AnyResult<teloxide::types::Message>
|
||||||
{
|
{
|
||||||
let telegram_chat_id: teloxide::types::ChatId = self.telegram_chat_id.into();
|
let telegram_chat_id: teloxide::types::ChatId = self.telegram_chat_id.into();
|
||||||
|
|
||||||
let mut request = bot.edit_message_text(telegram_chat_id, self.telegram_message_id.into(), text)
|
let mut request = bot.edit_message_text(telegram_chat_id, self.telegram_message_id.into(), text)
|
||||||
.parse_mode(ParseMode::Html);
|
.parse_mode(ParseMode::Html);
|
||||||
|
|
||||||
if with_keyboard {
|
if with_keyboard {
|
||||||
request = request.reply_markup(
|
request = request.reply_markup(
|
||||||
Self::reply_markup(self.matchmaking_id)
|
Self::reply_markup(self.matchmaking_id)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
request
|
request
|
||||||
.await
|
.await
|
||||||
.context("La richiesta di modifica messaggio alla Bot API di Telegram è fallita.")
|
.context("La richiesta di modifica messaggio alla Bot API di Telegram è fallita.")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn make_text_and_send_edit(
|
pub async fn make_text_and_send_edit(
|
||||||
&self,
|
&self,
|
||||||
database: &mut PgConnection,
|
database: &mut PgConnection,
|
||||||
|
@ -309,19 +309,19 @@ pub(crate) mod telegram_ext {
|
||||||
{
|
{
|
||||||
let event = MatchmakingEvent::get(database, self.matchmaking_id)
|
let event = MatchmakingEvent::get(database, self.matchmaking_id)
|
||||||
.context("Non è stato possibile recuperare il matchmaking dal database RYG.")?;
|
.context("Non è stato possibile recuperare il matchmaking dal database RYG.")?;
|
||||||
|
|
||||||
let replies = MatchmakingReply::get_all_telegram(database, self.matchmaking_id)
|
let replies = MatchmakingReply::get_all_telegram(database, self.matchmaking_id)
|
||||||
.context("Non è stato possibile recuperare le risposte al matchmaking dal database RYG.")?;
|
.context("Non è stato possibile recuperare le risposte al matchmaking dal database RYG.")?;
|
||||||
|
|
||||||
let text = Self::text(&event, &replies);
|
let text = Self::text(&event, &replies);
|
||||||
|
|
||||||
self.send_edit(bot, &text, !event.has_started())
|
self.send_edit(bot, &text, !event.has_started())
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile modificare il messaggio Telegram del matchmaking.")?;
|
.context("Non è stato possibile modificare il messaggio Telegram del matchmaking.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_delete(
|
async fn send_delete(
|
||||||
&self,
|
&self,
|
||||||
bot: &teloxide::Bot,
|
bot: &teloxide::Bot,
|
||||||
|
@ -333,7 +333,7 @@ pub(crate) mod telegram_ext {
|
||||||
.await
|
.await
|
||||||
.context("La richiesta di eliminazione messaggio alla Bot API di Telegram è fallita.")
|
.context("La richiesta di eliminazione messaggio alla Bot API di Telegram è fallita.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destroy(
|
fn destroy(
|
||||||
&self,
|
&self,
|
||||||
database: &mut PgConnection,
|
database: &mut PgConnection,
|
||||||
|
@ -343,7 +343,7 @@ pub(crate) mod telegram_ext {
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::dsl::*;
|
use diesel::dsl::*;
|
||||||
use crate::interfaces::database::schema::matchmaking_messages_telegram;
|
use crate::interfaces::database::schema::matchmaking_messages_telegram;
|
||||||
|
|
||||||
delete(matchmaking_messages_telegram::table)
|
delete(matchmaking_messages_telegram::table)
|
||||||
.filter(matchmaking_messages_telegram::matchmaking_id.eq(self.matchmaking_id))
|
.filter(matchmaking_messages_telegram::matchmaking_id.eq(self.matchmaking_id))
|
||||||
.filter(matchmaking_messages_telegram::telegram_chat_id.eq(self.telegram_chat_id))
|
.filter(matchmaking_messages_telegram::telegram_chat_id.eq(self.telegram_chat_id))
|
||||||
|
@ -351,21 +351,21 @@ pub(crate) mod telegram_ext {
|
||||||
.execute(database)
|
.execute(database)
|
||||||
.context("La rimozione dal database RYG è fallita.")
|
.context("La rimozione dal database RYG è fallita.")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn destroy_and_send_delete(
|
pub async fn destroy_and_send_delete(
|
||||||
self,
|
self,
|
||||||
database: &mut PgConnection,
|
database: &mut PgConnection,
|
||||||
bot: &teloxide::Bot
|
bot: &teloxide::Bot,
|
||||||
)
|
)
|
||||||
-> AnyResult<()>
|
-> AnyResult<()>
|
||||||
{
|
{
|
||||||
self.destroy(database)
|
self.destroy(database)
|
||||||
.context("Non è stato possibile eliminare il messaggio Telegram dal database RYG.")?;
|
.context("Non è stato possibile eliminare il messaggio Telegram dal database RYG.")?;
|
||||||
|
|
||||||
self.send_delete(bot)
|
self.send_delete(bot)
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile eliminare il messaggio Telegram del matchmaking.")?;
|
.context("Non è stato possibile eliminare il messaggio Telegram del matchmaking.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub struct MatchmakingReply {
|
||||||
impl MatchmakingReply {
|
impl MatchmakingReply {
|
||||||
pub fn get_all_telegram(database: &mut PgConnection, matchmaking_id: MatchmakingId) -> AnyResult<Vec<(Self, RoyalnetUser, TelegramUser)>> {
|
pub fn get_all_telegram(database: &mut PgConnection, matchmaking_id: MatchmakingId) -> AnyResult<Vec<(Self, RoyalnetUser, TelegramUser)>> {
|
||||||
use schema::{matchmaking_replies, telegram, users};
|
use schema::{matchmaking_replies, telegram, users};
|
||||||
|
|
||||||
matchmaking_replies::table
|
matchmaking_replies::table
|
||||||
.filter(matchmaking_replies::matchmaking_id.eq(matchmaking_id))
|
.filter(matchmaking_replies::matchmaking_id.eq(matchmaking_id))
|
||||||
.inner_join(users::table.on(matchmaking_replies::user_id.eq(users::id)))
|
.inner_join(users::table.on(matchmaking_replies::user_id.eq(users::id)))
|
||||||
|
@ -36,10 +36,10 @@ impl MatchmakingReply {
|
||||||
.get_results::<(Self, RoyalnetUser, TelegramUser)>(database)
|
.get_results::<(Self, RoyalnetUser, TelegramUser)>(database)
|
||||||
.context("Non è stato possibile recuperare le risposte al matchmaking dal database RYG.")
|
.context("Non è stato possibile recuperare le risposte al matchmaking dal database RYG.")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(database: &mut PgConnection, matchmaking_id: MatchmakingId, user_id: RoyalnetUserId, choice: MatchmakingChoice) -> AnyResult<Self> {
|
pub fn set(database: &mut PgConnection, matchmaking_id: MatchmakingId, user_id: RoyalnetUserId, choice: MatchmakingChoice) -> AnyResult<Self> {
|
||||||
use schema::matchmaking_replies;
|
use schema::matchmaking_replies;
|
||||||
|
|
||||||
insert_into(matchmaking_replies::table)
|
insert_into(matchmaking_replies::table)
|
||||||
.values(&Self {
|
.values(&Self {
|
||||||
matchmaking_id,
|
matchmaking_id,
|
||||||
|
@ -56,10 +56,10 @@ impl MatchmakingReply {
|
||||||
.get_result::<Self>(database)
|
.get_result::<Self>(database)
|
||||||
.context("Non è stato possibile inserire la risposta al matchmaking nel database RYG.")
|
.context("Non è stato possibile inserire la risposta al matchmaking nel database RYG.")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_late_minutes(database: &mut PgConnection, matchmaking_id: MatchmakingId, user_id: RoyalnetUserId, increase_by: i32) -> AnyResult<Self> {
|
pub fn add_late_minutes(database: &mut PgConnection, matchmaking_id: MatchmakingId, user_id: RoyalnetUserId, increase_by: i32) -> AnyResult<Self> {
|
||||||
use schema::matchmaking_replies;
|
use schema::matchmaking_replies;
|
||||||
|
|
||||||
insert_into(matchmaking_replies::table)
|
insert_into(matchmaking_replies::table)
|
||||||
.values(&Self {
|
.values(&Self {
|
||||||
matchmaking_id,
|
matchmaking_id,
|
||||||
|
|
|
@ -29,33 +29,33 @@ mod telegram_ext {
|
||||||
Self(value.0)
|
Self(value.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TelegramChatId> for teloxide::types::ChatId {
|
impl From<TelegramChatId> for teloxide::types::ChatId {
|
||||||
fn from(value: TelegramChatId) -> Self {
|
fn from(value: TelegramChatId) -> Self {
|
||||||
Self(value.0)
|
Self(value.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<teloxide::types::UserId> for TelegramUserId {
|
impl From<teloxide::types::UserId> for TelegramUserId {
|
||||||
fn from(value: teloxide::types::UserId) -> Self {
|
fn from(value: teloxide::types::UserId) -> Self {
|
||||||
// FIXME: this surely seems like a great idea
|
// FIXME: this surely seems like a great idea
|
||||||
Self(value.0 as i64)
|
Self(value.0 as i64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TelegramUserId> for teloxide::types::UserId {
|
impl From<TelegramUserId> for teloxide::types::UserId {
|
||||||
fn from(value: TelegramUserId) -> Self {
|
fn from(value: TelegramUserId) -> Self {
|
||||||
// FIXME: this surely seems like a great idea
|
// FIXME: this surely seems like a great idea
|
||||||
Self(value.0 as u64)
|
Self(value.0 as u64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<teloxide::types::MessageId> for TelegramMessageId {
|
impl From<teloxide::types::MessageId> for TelegramMessageId {
|
||||||
fn from(value: teloxide::types::MessageId) -> Self {
|
fn from(value: teloxide::types::MessageId) -> Self {
|
||||||
Self(value.0)
|
Self(value.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TelegramMessageId> for teloxide::types::MessageId {
|
impl From<TelegramMessageId> for teloxide::types::MessageId {
|
||||||
fn from(value: TelegramMessageId) -> Self {
|
fn from(value: TelegramMessageId) -> Self {
|
||||||
Self(value.0)
|
Self(value.0)
|
||||||
|
|
|
@ -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! {
|
||||||
|
|
|
@ -34,13 +34,13 @@ pub async fn query(client: &reqwest::Client, url: Url, guild_id: i64) -> QueryRe
|
||||||
log::debug!("Querying guild_matches of guild {guild_id}...");
|
log::debug!("Querying guild_matches of guild {guild_id}...");
|
||||||
log::trace!("Using client: {client:?}");
|
log::trace!("Using client: {client:?}");
|
||||||
log::trace!("Using API at: {url:?}");
|
log::trace!("Using API at: {url:?}");
|
||||||
|
|
||||||
log::trace!("Configuring query variables...");
|
log::trace!("Configuring query variables...");
|
||||||
let vars = query::Variables { guild_id };
|
let vars = query::Variables { guild_id };
|
||||||
|
|
||||||
log::trace!("Building query...");
|
log::trace!("Building query...");
|
||||||
let body = Query::build_query(vars);
|
let body = Query::build_query(vars);
|
||||||
|
|
||||||
log::trace!("Making request...");
|
log::trace!("Making request...");
|
||||||
let response = client.post(url)
|
let response = client.post(url)
|
||||||
.json(&body)
|
.json(&body)
|
||||||
|
@ -50,6 +50,6 @@ pub async fn query(client: &reqwest::Client, url: Url, guild_id: i64) -> QueryRe
|
||||||
.json::<QueryResponse>()
|
.json::<QueryResponse>()
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::Parsing)?;
|
.map_err(|_| Error::Parsing)?;
|
||||||
|
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,37 @@
|
||||||
query Query($guild_id: Int!) {
|
query Query($guild_id: Int!) {
|
||||||
guild(id: $guild_id) {
|
guild(id: $guild_id) {
|
||||||
id
|
id
|
||||||
matches(take: 10) {
|
matches(take: 10) {
|
||||||
id
|
id
|
||||||
lobbyType
|
lobbyType
|
||||||
gameMode
|
gameMode
|
||||||
durationSeconds
|
durationSeconds
|
||||||
endDateTime
|
endDateTime
|
||||||
players(steamAccountId: null) {
|
players(steamAccountId: null) {
|
||||||
isRadiant
|
isRadiant
|
||||||
isVictory
|
isVictory
|
||||||
imp
|
imp
|
||||||
kills
|
kills
|
||||||
deaths
|
deaths
|
||||||
assists
|
assists
|
||||||
lane
|
lane
|
||||||
role
|
role
|
||||||
hero {
|
hero {
|
||||||
displayName
|
displayName
|
||||||
}
|
}
|
||||||
steamAccount {
|
steamAccount {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
stats {
|
stats {
|
||||||
matchPlayerBuffEvent {
|
matchPlayerBuffEvent {
|
||||||
time
|
time
|
||||||
itemId
|
itemId
|
||||||
abilityId
|
abilityId
|
||||||
stackCount
|
stackCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
22
src/main.rs
22
src/main.rs
|
@ -7,15 +7,15 @@ pub(crate) mod utils;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
// Logging setup
|
// Logging setup
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
log::debug!("Logging initialized successfully!");
|
log::debug!("Logging initialized successfully!");
|
||||||
|
|
||||||
// Create instance
|
// Create instance
|
||||||
let instance = RoyalnetInstance::new().await;
|
let instance = RoyalnetInstance::new().await;
|
||||||
|
|
||||||
log::trace!("Starting {instance:?}!");
|
log::trace!("Starting {instance:?}!");
|
||||||
instance.run().await;
|
instance.run().await;
|
||||||
|
|
||||||
log::error!("No services configured.");
|
log::error!("No services configured.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,33 +40,33 @@ impl BroochService {
|
||||||
min_players_to_process: usize,
|
min_players_to_process: usize,
|
||||||
telegram_bot_token: String,
|
telegram_bot_token: String,
|
||||||
notification_chat_id: ChatId,
|
notification_chat_id: ChatId,
|
||||||
max_imp_wait: TimeDelta
|
max_imp_wait: TimeDelta,
|
||||||
)
|
)
|
||||||
-> AnyResult<Self>
|
-> AnyResult<Self>
|
||||||
{
|
{
|
||||||
log::info!("Initializing a new Brooch service...");
|
log::info!("Initializing a new Brooch service...");
|
||||||
|
|
||||||
let mut graphql_url = Url::parse(graphql_base_url)
|
let mut graphql_url = Url::parse(graphql_base_url)
|
||||||
.context("URL GraphQL non valido.")?;
|
.context("URL GraphQL non valido.")?;
|
||||||
{
|
{
|
||||||
let mut graphql_url_params = graphql_url.query_pairs_mut();
|
let mut graphql_url_params = graphql_url.query_pairs_mut();
|
||||||
graphql_url_params.append_pair("jwt", stratz_token);
|
graphql_url_params.append_pair("jwt", stratz_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::trace!("Using GraphQL API URL: {graphql_url:?}");
|
log::trace!("Using GraphQL API URL: {graphql_url:?}");
|
||||||
|
|
||||||
if min_players_to_process == 0 {
|
if min_players_to_process == 0 {
|
||||||
anyhow::bail!("min_players_to_progress devono essere almeno 1.");
|
anyhow::bail!("min_players_to_progress devono essere almeno 1.");
|
||||||
}
|
}
|
||||||
|
|
||||||
log::trace!("Processing only matches with at least {min_players_to_process} players.");
|
log::trace!("Processing only matches with at least {min_players_to_process} players.");
|
||||||
|
|
||||||
let telegram_bot = Bot::new(telegram_bot_token);
|
let telegram_bot = Bot::new(telegram_bot_token);
|
||||||
|
|
||||||
log::trace!("Using bot: {telegram_bot:#?}");
|
log::trace!("Using bot: {telegram_bot:#?}");
|
||||||
|
|
||||||
log::trace!("Max IMP wait is: {max_imp_wait:?}");
|
log::trace!("Max IMP wait is: {max_imp_wait:?}");
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
BroochService {
|
BroochService {
|
||||||
database_url,
|
database_url,
|
||||||
|
@ -79,143 +79,143 @@ impl BroochService {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_http_client(&self) -> AnyResult<reqwest::Client> {
|
fn create_http_client(&self) -> AnyResult<reqwest::Client> {
|
||||||
log::debug!("Creating HTTP client...");
|
log::debug!("Creating HTTP client...");
|
||||||
|
|
||||||
reqwest::Client::builder()
|
reqwest::Client::builder()
|
||||||
.build()
|
.build()
|
||||||
.context("Impossibile creare un client HTTP appropriato a fare richieste all'API.")
|
.context("Impossibile creare un client HTTP appropriato a fare richieste all'API.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_postgres_connection(&self) -> AnyResult<PgConnection> {
|
fn create_postgres_connection(&self) -> AnyResult<PgConnection> {
|
||||||
log::debug!("Creating PostgreSQL connection...");
|
log::debug!("Creating PostgreSQL connection...");
|
||||||
|
|
||||||
database::connect(&self.database_url)
|
database::connect(&self.database_url)
|
||||||
.context("Non è stato possibile connettersi al database RYG.")
|
.context("Non è stato possibile connettersi al database RYG.")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn query_guild_matches(&self, client: &reqwest::Client) -> AnyResult<guild_matches::QueryResponse> {
|
async fn query_guild_matches(&self, client: &reqwest::Client) -> AnyResult<guild_matches::QueryResponse> {
|
||||||
log::debug!("Querying for guild matches...");
|
log::debug!("Querying for guild matches...");
|
||||||
|
|
||||||
guild_matches::query(client, self.graphql_url.clone(), self.watched_guild_id)
|
guild_matches::query(client, self.graphql_url.clone(), self.watched_guild_id)
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile recuperare le ultime partite di Dota da STRATZ.")
|
.context("Non è stato possibile recuperare le ultime partite di Dota da STRATZ.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_guild_data(&self, data: guild_matches::QueryResponse) -> AnyResult<guild_matches::Guild> {
|
fn process_guild_data(&self, data: guild_matches::QueryResponse) -> AnyResult<guild_matches::Guild> {
|
||||||
log::debug!("Processing guild data...");
|
log::debug!("Processing guild data...");
|
||||||
|
|
||||||
let data = data.data
|
let data = data.data
|
||||||
.context("La richiesta è riuscita, ma la risposta ricevuta da STRATZ era vuota.")?;
|
.context("La richiesta è riuscita, ma la risposta ricevuta da STRATZ era vuota.")?;
|
||||||
|
|
||||||
let guild = data.guild
|
let guild = data.guild
|
||||||
.context("La richiesta è riuscita, ma non sono state ricevute gilde da STRATZ.")?;
|
.context("La richiesta è riuscita, ma non sono state ricevute gilde da STRATZ.")?;
|
||||||
|
|
||||||
let guild_id: i64 = guild.id
|
let guild_id: i64 = guild.id
|
||||||
.context("La richiesta è riuscita, ma non è stato ricevuto l'ID della gilda da STRATZ.")?;
|
.context("La richiesta è riuscita, ma non è stato ricevuto l'ID della gilda da STRATZ.")?;
|
||||||
|
|
||||||
log::trace!("Guild id is: {guild_id}");
|
log::trace!("Guild id is: {guild_id}");
|
||||||
|
|
||||||
if guild_id != self.watched_guild_id {
|
if guild_id != self.watched_guild_id {
|
||||||
anyhow::bail!("La richiesta è riuscita, ma STRATZ ha risposto con le informazioni della gilda sbagliata.");
|
anyhow::bail!("La richiesta è riuscita, ma STRATZ ha risposto con le informazioni della gilda sbagliata.");
|
||||||
}
|
}
|
||||||
|
|
||||||
log::trace!("Guild id matches watched guild.");
|
log::trace!("Guild id matches watched guild.");
|
||||||
|
|
||||||
Ok(guild)
|
Ok(guild)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_matches_data(&self, guild: guild_matches::Guild) -> AnyResult<Vec<Match>> {
|
fn process_matches_data(&self, guild: guild_matches::Guild) -> AnyResult<Vec<Match>> {
|
||||||
log::debug!("Processing matches data...");
|
log::debug!("Processing matches data...");
|
||||||
|
|
||||||
let mut matches = guild.matches
|
let mut matches = guild.matches
|
||||||
.context("La richiesta è riuscita, ma non sono state ricevute informazioni sulle partite della gilda da STRATZ.")?
|
.context("La richiesta è riuscita, ma non sono state ricevute informazioni sulle partite della gilda da STRATZ.")?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect::<Vec<Match>>();
|
.collect::<Vec<Match>>();
|
||||||
|
|
||||||
log::trace!("Received {} matches.", matches.len());
|
log::trace!("Received {} matches.", matches.len());
|
||||||
|
|
||||||
log::trace!("Sorting matches by datetime...");
|
log::trace!("Sorting matches by datetime...");
|
||||||
|
|
||||||
// Sort matches chronologically
|
// Sort matches chronologically
|
||||||
matches.sort_unstable_by_key(|o| o
|
matches.sort_unstable_by_key(|o| o
|
||||||
.end_date_time
|
.end_date_time
|
||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
);
|
);
|
||||||
|
|
||||||
log::trace!("Sorted matches by datetime!");
|
log::trace!("Sorted matches by datetime!");
|
||||||
|
|
||||||
Ok(matches)
|
Ok(matches)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_match_id(&self, r#match: &Match) -> AnyResult<DotaMatchId> {
|
fn get_match_id(&self, r#match: &Match) -> AnyResult<DotaMatchId> {
|
||||||
log::trace!("Getting match id...");
|
log::trace!("Getting match id...");
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
r#match.id
|
r#match.id
|
||||||
.context("La richiesta è riuscita, ma non è stato ricevuto da STRATZ l'ID della partita.")?
|
.context("La richiesta è riuscita, ma non è stato ricevuto da STRATZ l'ID della partita.")?
|
||||||
.into()
|
.into()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_database_match(&self, database: &mut PgConnection, match_id: DotaMatchId) -> AnyResult<Option<BroochMatch>> {
|
fn get_database_match(&self, database: &mut PgConnection, match_id: DotaMatchId) -> AnyResult<Option<BroochMatch>> {
|
||||||
use crate::interfaces::database::query_prelude::*;
|
use crate::interfaces::database::query_prelude::*;
|
||||||
use crate::interfaces::database::schema::brooch_match;
|
use crate::interfaces::database::schema::brooch_match;
|
||||||
|
|
||||||
log::trace!("Getting {match_id:?} from the database...");
|
log::trace!("Getting {match_id:?} from the database...");
|
||||||
|
|
||||||
brooch_match::table
|
brooch_match::table
|
||||||
.filter(brooch_match::id.eq(match_id))
|
.filter(brooch_match::id.eq(match_id))
|
||||||
.get_result::<BroochMatch>(database)
|
.get_result::<BroochMatch>(database)
|
||||||
.optional()
|
.optional()
|
||||||
.context("Non è stato possibile recuperare la partita restituita da STRATZ dal database RYG.")
|
.context("Non è stato possibile recuperare la partita restituita da STRATZ dal database RYG.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_process_match_exists(&self, database: &mut PgConnection, match_id: DotaMatchId) -> AnyResult<bool> {
|
fn should_process_match_exists(&self, database: &mut PgConnection, match_id: DotaMatchId) -> AnyResult<bool> {
|
||||||
log::trace!("Determining whether {match_id:?} should be processed...");
|
log::trace!("Determining whether {match_id:?} should be processed...");
|
||||||
|
|
||||||
self.get_database_match(database, match_id)
|
self.get_database_match(database, match_id)
|
||||||
.map(|m| m.is_none())
|
.map(|m| m.is_none())
|
||||||
.context("Non è stato possibile determinare se la partita restituita da STRATZ fosse stata già processata.")
|
.context("Non è stato possibile determinare se la partita restituita da STRATZ fosse stata già processata.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_match_datetime(&self, r#match: &Match) -> AnyResult<DateTime<Local>> {
|
fn get_match_datetime(&self, r#match: &Match) -> AnyResult<DateTime<Local>> {
|
||||||
log::trace!("Getting match datetime...");
|
log::trace!("Getting match datetime...");
|
||||||
|
|
||||||
let match_date = r#match.end_date_time
|
let match_date = r#match.end_date_time
|
||||||
.context("Non è stato ricevuto da STRATZ il momento di termine della partita.")?;
|
.context("Non è stato ricevuto da STRATZ il momento di termine della partita.")?;
|
||||||
|
|
||||||
log::trace!("Converting match datetime to local datetime...");
|
log::trace!("Converting match datetime to local datetime...");
|
||||||
|
|
||||||
Local.timestamp_opt(match_date, 0)
|
Local.timestamp_opt(match_date, 0)
|
||||||
.earliest()
|
.earliest()
|
||||||
.context("È stato ricevuto da STRATZ un momento di termine della partita non valido.")
|
.context("È stato ricevuto da STRATZ un momento di termine della partita non valido.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_match_timedelta(&self, datetime: &DateTime<Local>) -> TimeDelta {
|
fn get_match_timedelta(&self, datetime: &DateTime<Local>) -> TimeDelta {
|
||||||
log::trace!("Getting current time...");
|
log::trace!("Getting current time...");
|
||||||
|
|
||||||
let now = Local::now();
|
let now = Local::now();
|
||||||
|
|
||||||
log::trace!("Getting match timedelta...");
|
log::trace!("Getting match timedelta...");
|
||||||
|
|
||||||
now - *datetime
|
now - *datetime
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_match_type(&self, r#match: &Match) -> AnyResult<LobbyType> {
|
fn get_match_type(&self, r#match: &Match) -> AnyResult<LobbyType> {
|
||||||
log::trace!("Getting match type...");
|
log::trace!("Getting match type...");
|
||||||
|
|
||||||
r#match.lobby_type.clone()
|
r#match.lobby_type.clone()
|
||||||
.context("Non è stato ricevuta da STRATZ il tipo della partita.")
|
.context("Non è stato ricevuta da STRATZ il tipo della partita.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stringify_type(&self, r#type: LobbyType) -> String {
|
fn stringify_type(&self, r#type: LobbyType) -> String {
|
||||||
use LobbyType::*;
|
use LobbyType::*;
|
||||||
|
|
||||||
log::trace!("Stringifying match type: {:?}", r#type);
|
log::trace!("Stringifying match type: {:?}", r#type);
|
||||||
|
|
||||||
match r#type {
|
match r#type {
|
||||||
UNRANKED => String::from("Normale"),
|
UNRANKED => String::from("Normale"),
|
||||||
PRACTICE => String::from("Torneo"),
|
PRACTICE => String::from("Torneo"),
|
||||||
|
@ -232,19 +232,19 @@ impl BroochService {
|
||||||
Other(t) => t.clone(),
|
Other(t) => t.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_match_mode(&self, r#match: &Match) -> AnyResult<GameMode> {
|
fn get_match_mode(&self, r#match: &Match) -> AnyResult<GameMode> {
|
||||||
log::trace!("Getting match mode...");
|
log::trace!("Getting match mode...");
|
||||||
|
|
||||||
r#match.game_mode.clone()
|
r#match.game_mode.clone()
|
||||||
.context("Non è stata ricevuta da STRATZ la modalità della partita.")
|
.context("Non è stata ricevuta da STRATZ la modalità della partita.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stringify_mode(&self, mode: GameMode) -> String {
|
fn stringify_mode(&self, mode: GameMode) -> String {
|
||||||
use GameMode::*;
|
use GameMode::*;
|
||||||
|
|
||||||
log::trace!("Stringifying match mode: {:?}", mode);
|
log::trace!("Stringifying match mode: {:?}", mode);
|
||||||
|
|
||||||
match mode {
|
match mode {
|
||||||
NONE => String::from("Sandbox"),
|
NONE => String::from("Sandbox"),
|
||||||
ALL_PICK => String::from("All Pick"),
|
ALL_PICK => String::from("All Pick"),
|
||||||
|
@ -275,185 +275,185 @@ impl BroochService {
|
||||||
Other(t) => t.clone(),
|
Other(t) => t.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stringify_duration(&self, duration: TimeDelta) -> String {
|
fn stringify_duration(&self, duration: TimeDelta) -> String {
|
||||||
let minutes = duration.num_minutes();
|
let minutes = duration.num_minutes();
|
||||||
let seconds = duration.num_seconds() % 60;
|
let seconds = duration.num_seconds() % 60;
|
||||||
|
|
||||||
format!("{minutes:02}:{seconds:02}")
|
format!("{minutes:02}:{seconds:02}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_match_duration(&self, r#match: &Match) -> AnyResult<TimeDelta> {
|
fn get_match_duration(&self, r#match: &Match) -> AnyResult<TimeDelta> {
|
||||||
log::trace!("Getting match duration...");
|
log::trace!("Getting match duration...");
|
||||||
|
|
||||||
let secs = r#match.duration_seconds
|
let secs = r#match.duration_seconds
|
||||||
.context("Non è stata ricevuta da STRATZ la durata della partita.")?;
|
.context("Non è stata ricevuta da STRATZ la durata della partita.")?;
|
||||||
|
|
||||||
log::trace!("Getting match duration timedelta...");
|
log::trace!("Getting match duration timedelta...");
|
||||||
|
|
||||||
let delta = TimeDelta::new(secs, 0)
|
let delta = TimeDelta::new(secs, 0)
|
||||||
.context("Non è stato possibile rappresentare la durata della partita ricevuta da STRATZ.")?;
|
.context("Non è stato possibile rappresentare la durata della partita ricevuta da STRATZ.")?;
|
||||||
|
|
||||||
Ok(delta)
|
Ok(delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_match_players(&self, r#match: Match) -> AnyResult<Vec<Player>> {
|
fn get_match_players(&self, r#match: Match) -> AnyResult<Vec<Player>> {
|
||||||
log::debug!("Getting match players...");
|
log::debug!("Getting match players...");
|
||||||
|
|
||||||
let mut players: Vec<Player> = r#match.players
|
let mut players: Vec<Player> = r#match.players
|
||||||
.context("Non è stato ricevuto da STRATZ l'elenco dei giocatori della partita.")?
|
.context("Non è stato ricevuto da STRATZ l'elenco dei giocatori della partita.")?
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|o| o.to_owned())
|
.filter_map(|o| o.to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
log::trace!("Sorting match players...");
|
log::trace!("Sorting match players...");
|
||||||
|
|
||||||
players.sort_unstable_by_key(|o| match o.is_radiant.unwrap() {
|
players.sort_unstable_by_key(|o| match o.is_radiant.unwrap() {
|
||||||
true => 1,
|
true => 1,
|
||||||
false => 2,
|
false => 2,
|
||||||
});
|
});
|
||||||
|
|
||||||
log::trace!("Sorted match players!");
|
log::trace!("Sorted match players!");
|
||||||
|
|
||||||
Ok(players)
|
Ok(players)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_process_match_players(&self, players: &[Player]) -> bool {
|
fn should_process_match_players(&self, players: &[Player]) -> bool {
|
||||||
let players_len = players.len();
|
let players_len = players.len();
|
||||||
|
|
||||||
log::trace!("Determining whether {players_len} are enough for the match to be processed...");
|
log::trace!("Determining whether {players_len} are enough for the match to be processed...");
|
||||||
|
|
||||||
players_len >= self.min_players_to_process
|
players_len >= self.min_players_to_process
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_process_match_imp(&self, players: &[Player], timedelta: &TimeDelta) -> bool {
|
fn should_process_match_imp(&self, players: &[Player], timedelta: &TimeDelta) -> bool {
|
||||||
log::trace!("Determining whether IMP is available for all players...");
|
log::trace!("Determining whether IMP is available for all players...");
|
||||||
|
|
||||||
let imp_available_for_everyone = players.iter()
|
let imp_available_for_everyone = players.iter()
|
||||||
.map(|o| o.imp)
|
.map(|o| o.imp)
|
||||||
.map(|o| o.is_some())
|
.map(|o| o.is_some())
|
||||||
.all(|o| o);
|
.all(|o| o);
|
||||||
|
|
||||||
log::trace!("Determining whether enough time has passed for IMP to be ignored...");
|
log::trace!("Determining whether enough time has passed for IMP to be ignored...");
|
||||||
|
|
||||||
let imp_waited_too_long = *timedelta > self.max_imp_wait;
|
let imp_waited_too_long = *timedelta > self.max_imp_wait;
|
||||||
|
|
||||||
imp_available_for_everyone || imp_waited_too_long
|
imp_available_for_everyone || imp_waited_too_long
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_match_side(&self, players: &[Player]) -> AnyResult<MatchSide> {
|
fn get_match_side(&self, players: &[Player]) -> AnyResult<MatchSide> {
|
||||||
use MatchSide::*;
|
use MatchSide::*;
|
||||||
|
|
||||||
log::debug!("Getting match side...");
|
log::debug!("Getting match side...");
|
||||||
|
|
||||||
let mut side = None;
|
let mut side = None;
|
||||||
|
|
||||||
for player in players.iter() {
|
for player in players.iter() {
|
||||||
side = match (side, player.is_radiant) {
|
side = match (side, player.is_radiant) {
|
||||||
(_, None) => {
|
(_, None) => {
|
||||||
anyhow::bail!("Non è stata ricevuta da STRATZ la squadra di almeno uno dei giocatori.")
|
anyhow::bail!("Non è stata ricevuta da STRATZ la squadra di almeno uno dei giocatori.")
|
||||||
},
|
}
|
||||||
(None, Some(true)) => {
|
(None, Some(true)) => {
|
||||||
Some(Radiant)
|
Some(Radiant)
|
||||||
},
|
}
|
||||||
(None, Some(false)) => {
|
(None, Some(false)) => {
|
||||||
Some(Dire)
|
Some(Dire)
|
||||||
},
|
}
|
||||||
(Some(Radiant), Some(true)) |
|
(Some(Radiant), Some(true)) |
|
||||||
(Some(Dire), Some(false)) => {
|
(Some(Dire), Some(false)) => {
|
||||||
side
|
side
|
||||||
},
|
}
|
||||||
(Some(Radiant), Some(false)) |
|
(Some(Radiant), Some(false)) |
|
||||||
(Some(Dire), Some(true)) => {
|
(Some(Dire), Some(true)) => {
|
||||||
Some(Both)
|
Some(Both)
|
||||||
},
|
}
|
||||||
(Some(Both), _) => {
|
(Some(Both), _) => {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let side = side.unwrap();
|
let side = side.unwrap();
|
||||||
|
|
||||||
log::trace!("Match side is: {side:?}");
|
log::trace!("Match side is: {side:?}");
|
||||||
|
|
||||||
Ok(side)
|
Ok(side)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_match_outcome(&self, players: &[Player]) -> AnyResult<MatchOutcome> {
|
fn get_match_outcome(&self, players: &[Player]) -> AnyResult<MatchOutcome> {
|
||||||
use MatchOutcome::*;
|
use MatchOutcome::*;
|
||||||
|
|
||||||
log::debug!("Getting match outcome...");
|
log::debug!("Getting match outcome...");
|
||||||
|
|
||||||
let mut outcome = None;
|
let mut outcome = None;
|
||||||
|
|
||||||
for player in players.iter() {
|
for player in players.iter() {
|
||||||
outcome = match (outcome, player.is_victory) {
|
outcome = match (outcome, player.is_victory) {
|
||||||
(_, None) => {
|
(_, None) => {
|
||||||
anyhow::bail!("Non è stata ricevuta da STRATZ la squadra di almeno uno dei giocatori.")
|
anyhow::bail!("Non è stata ricevuta da STRATZ la squadra di almeno uno dei giocatori.")
|
||||||
},
|
}
|
||||||
(None, Some(true)) => {
|
(None, Some(true)) => {
|
||||||
Some(Victory)
|
Some(Victory)
|
||||||
},
|
}
|
||||||
(None, Some(false)) => {
|
(None, Some(false)) => {
|
||||||
Some(Defeat)
|
Some(Defeat)
|
||||||
},
|
}
|
||||||
(Some(Victory), Some(true)) |
|
(Some(Victory), Some(true)) |
|
||||||
(Some(Defeat), Some(false)) => {
|
(Some(Defeat), Some(false)) => {
|
||||||
outcome
|
outcome
|
||||||
},
|
}
|
||||||
(Some(Victory), Some(false)) |
|
(Some(Victory), Some(false)) |
|
||||||
(Some(Defeat), Some(true)) => {
|
(Some(Defeat), Some(true)) => {
|
||||||
Some(Clash)
|
Some(Clash)
|
||||||
},
|
}
|
||||||
(Some(Clash), _) => {
|
(Some(Clash), _) => {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let outcome = outcome.unwrap();
|
let outcome = outcome.unwrap();
|
||||||
|
|
||||||
log::trace!("Match outcome is: {outcome:?}");
|
log::trace!("Match outcome is: {outcome:?}");
|
||||||
|
|
||||||
Ok(outcome)
|
Ok(outcome)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_player_steam(&self, player: &Player) -> AnyResult<Steam> {
|
fn get_player_steam(&self, player: &Player) -> AnyResult<Steam> {
|
||||||
log::trace!("Getting player's Steam account...");
|
log::trace!("Getting player's Steam account...");
|
||||||
|
|
||||||
player.steam_account.clone()
|
player.steam_account.clone()
|
||||||
.context("Non è stato ricevuto da STRATZ l'account Steam di almeno uno dei giocatori della partita.")
|
.context("Non è stato ricevuto da STRATZ l'account Steam di almeno uno dei giocatori della partita.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_player_name(&self, steam: &Steam) -> AnyResult<String> {
|
fn get_player_name(&self, steam: &Steam) -> AnyResult<String> {
|
||||||
log::trace!("Getting player's Steam name...");
|
log::trace!("Getting player's Steam name...");
|
||||||
|
|
||||||
steam.name.clone()
|
steam.name.clone()
|
||||||
.context("Non è stato ricevuto da STRATZ il display name di almeno uno dei giocatori della partita.")
|
.context("Non è stato ricevuto da STRATZ il display name di almeno uno dei giocatori della partita.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_player_telegram_id(&self, database: &mut PgConnection, player_steam: Steam) -> AnyResult<Option<TelegramUserId>> {
|
fn get_player_telegram_id(&self, database: &mut PgConnection, player_steam: Steam) -> AnyResult<Option<TelegramUserId>> {
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::{ExpressionMethods, QueryDsl};
|
use diesel::{ExpressionMethods, QueryDsl};
|
||||||
use crate::interfaces::database::schema::{steam, telegram, users};
|
use crate::interfaces::database::schema::{steam, telegram, users};
|
||||||
use crate::interfaces::database::models::TelegramUser;
|
use crate::interfaces::database::models::TelegramUser;
|
||||||
|
|
||||||
log::trace!("Getting player's Steam name...");
|
log::trace!("Getting player's Steam name...");
|
||||||
|
|
||||||
let player_steam_id = player_steam.id
|
let player_steam_id = player_steam.id
|
||||||
.context("Non è stato ricevuto da STRATZ lo SteamID di almeno uno dei giocatori della partita.")?;
|
.context("Non è stato ricevuto da STRATZ lo SteamID di almeno uno dei giocatori della partita.")?;
|
||||||
|
|
||||||
log::trace!("Computing the two possible SteamIDs...");
|
log::trace!("Computing the two possible SteamIDs...");
|
||||||
|
|
||||||
let player_steam_id_y0 = 0x_0110_0001_0000_0000 + player_steam_id;
|
let player_steam_id_y0 = 0x_0110_0001_0000_0000 + player_steam_id;
|
||||||
|
|
||||||
log::trace!("SteamID Y0 is: {player_steam_id_y0}");
|
log::trace!("SteamID Y0 is: {player_steam_id_y0}");
|
||||||
|
|
||||||
let player_steam_id_y1 = 0x_0110_0001_0000_0001 + player_steam_id;
|
let player_steam_id_y1 = 0x_0110_0001_0000_0001 + player_steam_id;
|
||||||
|
|
||||||
log::trace!("SteamID Y1 is: {player_steam_id_y1}");
|
log::trace!("SteamID Y1 is: {player_steam_id_y1}");
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
steam::table
|
steam::table
|
||||||
.inner_join(
|
.inner_join(
|
||||||
|
@ -479,24 +479,24 @@ impl BroochService {
|
||||||
.map(|t| t.telegram_id)
|
.map(|t| t.telegram_id)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_player_hero_name(&self, player: &Player) -> AnyResult<String> {
|
fn get_player_hero_name(&self, player: &Player) -> AnyResult<String> {
|
||||||
log::trace!("Getting player's hero name...");
|
log::trace!("Getting player's hero name...");
|
||||||
|
|
||||||
player.hero.clone()
|
player.hero.clone()
|
||||||
.context("Non è stato ricevuto da STRATZ l'eroe giocato da almeno uno dei giocatori della partita.")?
|
.context("Non è stato ricevuto da STRATZ l'eroe giocato da almeno uno dei giocatori della partita.")?
|
||||||
.display_name
|
.display_name
|
||||||
.context("Non è stato ricevuto da STRATZ il nome dell'eroe giocato da almeno uno dei giocatori della partita.")
|
.context("Non è stato ricevuto da STRATZ il nome dell'eroe giocato da almeno uno dei giocatori della partita.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_player_outcome(&self, player: &Player) -> AnyResult<MatchOutcome> {
|
fn get_player_outcome(&self, player: &Player) -> AnyResult<MatchOutcome> {
|
||||||
use MatchOutcome::*;
|
use MatchOutcome::*;
|
||||||
|
|
||||||
log::trace!("Getting player's match outcome...");
|
log::trace!("Getting player's match outcome...");
|
||||||
|
|
||||||
let is_victory = &player.is_victory
|
let is_victory = &player.is_victory
|
||||||
.context("Non è stato ricevuto da STRATZ il risultato della partita per almeno uno dei giocatori.")?;
|
.context("Non è stato ricevuto da STRATZ il risultato della partita per almeno uno dei giocatori.")?;
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
match is_victory {
|
match is_victory {
|
||||||
true => Victory,
|
true => Victory,
|
||||||
|
@ -504,68 +504,68 @@ impl BroochService {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emojify_outcome(&self, outcome: MatchOutcome) -> &'static str {
|
fn emojify_outcome(&self, outcome: MatchOutcome) -> &'static str {
|
||||||
use MatchOutcome::*;
|
use MatchOutcome::*;
|
||||||
|
|
||||||
log::trace!("Emojifying match outcome...");
|
log::trace!("Emojifying match outcome...");
|
||||||
|
|
||||||
match outcome {
|
match outcome {
|
||||||
Victory => "🟩",
|
Victory => "🟩",
|
||||||
Defeat => "🔴",
|
Defeat => "🔴",
|
||||||
Clash => "💛",
|
Clash => "💛",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stringify_outcome(&self, outcome: MatchOutcome) -> &'static str {
|
fn stringify_outcome(&self, outcome: MatchOutcome) -> &'static str {
|
||||||
use MatchOutcome::*;
|
use MatchOutcome::*;
|
||||||
|
|
||||||
log::trace!("Stringifying match outcome...");
|
log::trace!("Stringifying match outcome...");
|
||||||
|
|
||||||
match outcome {
|
match outcome {
|
||||||
Victory => "Vittoria!",
|
Victory => "Vittoria!",
|
||||||
Defeat => "Sconfitta...",
|
Defeat => "Sconfitta...",
|
||||||
Clash => "Derby",
|
Clash => "Derby",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn numberify_role_lane(role: &Option<Role>, lane: &Option<Lane>) -> u8 {
|
fn numberify_role_lane(role: &Option<Role>, lane: &Option<Lane>) -> u8 {
|
||||||
use Role::*;
|
use Role::*;
|
||||||
use Lane::*;
|
use Lane::*;
|
||||||
|
|
||||||
match (role, lane) {
|
match (role, lane) {
|
||||||
( Some(CORE), Some(SAFE_LANE)) => 1,
|
(Some(CORE), Some(SAFE_LANE)) => 1,
|
||||||
( Some(CORE), Some(MID_LANE)) => 2,
|
(Some(CORE), Some(MID_LANE)) => 2,
|
||||||
( Some(CORE), Some(OFF_LANE)) => 3,
|
(Some(CORE), Some(OFF_LANE)) => 3,
|
||||||
( _, Some(ROAMING)) => 4,
|
(_, Some(ROAMING)) => 4,
|
||||||
( _, Some(JUNGLE)) => 5,
|
(_, Some(JUNGLE)) => 5,
|
||||||
(Some(LIGHT_SUPPORT), _) => 6,
|
(Some(LIGHT_SUPPORT), _) => 6,
|
||||||
( Some(HARD_SUPPORT), _) => 7,
|
(Some(HARD_SUPPORT), _) => 7,
|
||||||
( _, _) => 8,
|
(_, _) => 8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stringify_role_lane(&self, role: Role, lane: Lane) -> &'static str {
|
fn stringify_role_lane(&self, role: Role, lane: Lane) -> &'static str {
|
||||||
use Role::*;
|
use Role::*;
|
||||||
use Lane::*;
|
use Lane::*;
|
||||||
|
|
||||||
log::trace!("Stringifying role and lane...");
|
log::trace!("Stringifying role and lane...");
|
||||||
|
|
||||||
match (role, lane) {
|
match (role, lane) {
|
||||||
( CORE, SAFE_LANE) => "1️⃣ Safe Carry",
|
(CORE, SAFE_LANE) => "1️⃣ Safe Carry",
|
||||||
( CORE, MID_LANE) => "2️⃣ Mid Carry",
|
(CORE, MID_LANE) => "2️⃣ Mid Carry",
|
||||||
( CORE, OFF_LANE) => "3️⃣ Off Tank",
|
(CORE, OFF_LANE) => "3️⃣ Off Tank",
|
||||||
( _, ROAMING) => "🔀 Roaming",
|
(_, ROAMING) => "🔀 Roaming",
|
||||||
( _, JUNGLE) => "⏫ Jungle",
|
(_, JUNGLE) => "⏫ Jungle",
|
||||||
(LIGHT_SUPPORT, _) => "4️⃣ Soft Support",
|
(LIGHT_SUPPORT, _) => "4️⃣ Soft Support",
|
||||||
( HARD_SUPPORT, _) => "5️⃣ Hard Support",
|
(HARD_SUPPORT, _) => "5️⃣ Hard Support",
|
||||||
( _, _) => "🆕 Sconosciuto",
|
(_, _) => "🆕 Sconosciuto",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emojify_imp(&self, imp: Short) -> &'static str {
|
fn emojify_imp(&self, imp: Short) -> &'static str {
|
||||||
log::trace!("Emojifying IMP...");
|
log::trace!("Emojifying IMP...");
|
||||||
|
|
||||||
match imp {
|
match imp {
|
||||||
Short::MIN..=-49 => "🟧",
|
Short::MIN..=-49 => "🟧",
|
||||||
-48..=-25 => "🔶",
|
-48..=-25 => "🔶",
|
||||||
|
@ -575,44 +575,44 @@ impl BroochService {
|
||||||
49..=Short::MAX => "🟦",
|
49..=Short::MAX => "🟦",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emojify_kills_deaths_assists(&self, kills: Byte, deaths: Byte, assists: Byte) -> &'static str {
|
fn emojify_kills_deaths_assists(&self, kills: Byte, deaths: Byte, assists: Byte) -> &'static str {
|
||||||
log::trace!("Emojifying KDA...");
|
log::trace!("Emojifying KDA...");
|
||||||
|
|
||||||
let kills = kills as i16;
|
let kills = kills as i16;
|
||||||
let deaths = deaths as i16;
|
let deaths = deaths as i16;
|
||||||
let assists = assists as i16;
|
let assists = assists as i16;
|
||||||
|
|
||||||
let kda_score = kills + (assists / 2) - deaths;
|
let kda_score = kills + (assists / 2) - deaths;
|
||||||
|
|
||||||
match kda_score {
|
match kda_score {
|
||||||
i16::MIN..=-1 => "➖",
|
i16::MIN..=-1 => "➖",
|
||||||
0..=i16::MAX => "➕",
|
0..=i16::MAX => "➕",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stringify_player(&self, database: &mut PgConnection, player: Player, show_outcome: bool) -> AnyResult<String> {
|
fn stringify_player(&self, database: &mut PgConnection, player: Player, show_outcome: bool) -> AnyResult<String> {
|
||||||
log::debug!("Stringifying player...");
|
log::debug!("Stringifying player...");
|
||||||
log::trace!("Showing outcome: {show_outcome:?}");
|
log::trace!("Showing outcome: {show_outcome:?}");
|
||||||
|
|
||||||
let steam = self.get_player_steam(&player)?;
|
let steam = self.get_player_steam(&player)?;
|
||||||
let name = self.get_player_name(&steam)?;
|
let name = self.get_player_name(&steam)?;
|
||||||
let telegram_id = self.get_player_telegram_id(database, steam)?;
|
let telegram_id = self.get_player_telegram_id(database, steam)?;
|
||||||
let hero_name = self.get_player_hero_name(&player)?;
|
let hero_name = self.get_player_hero_name(&player)?;
|
||||||
let outcome = self.get_player_outcome(&player)?;
|
let outcome = self.get_player_outcome(&player)?;
|
||||||
|
|
||||||
let role = player.role.clone();
|
let role = player.role.clone();
|
||||||
let lane = player.lane.clone();
|
let lane = player.lane.clone();
|
||||||
let imp = player.imp;
|
let imp = player.imp;
|
||||||
|
|
||||||
let kills = player.kills;
|
let kills = player.kills;
|
||||||
let deaths = player.deaths;
|
let deaths = player.deaths;
|
||||||
let assists = player.assists;
|
let assists = player.assists;
|
||||||
|
|
||||||
// TODO: Buffs
|
// TODO: Buffs
|
||||||
|
|
||||||
let mut lines = Vec::<String>::new();
|
let mut lines = Vec::<String>::new();
|
||||||
|
|
||||||
match telegram_id {
|
match telegram_id {
|
||||||
None => lines.push(format!(
|
None => lines.push(format!(
|
||||||
"<u><b>{}</b> ({})</u>",
|
"<u><b>{}</b> ({})</u>",
|
||||||
|
@ -626,102 +626,102 @@ impl BroochService {
|
||||||
hero_name.to_string().escape_telegram_html(),
|
hero_name.to_string().escape_telegram_html(),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
|
|
||||||
if show_outcome {
|
if show_outcome {
|
||||||
lines.push(format!(
|
lines.push(format!(
|
||||||
"— {}", self.stringify_outcome(outcome)
|
"— {}", self.stringify_outcome(outcome)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Some(role), Some(lane)) = (role, lane) {
|
if let (Some(role), Some(lane)) = (role, lane) {
|
||||||
lines.push(format!(
|
lines.push(format!(
|
||||||
"— {}", self.stringify_role_lane(role, lane)
|
"— {}", self.stringify_role_lane(role, lane)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Some(kills), Some(deaths), Some(assists)) = (kills, deaths, assists) {
|
if let (Some(kills), Some(deaths), Some(assists)) = (kills, deaths, assists) {
|
||||||
lines.push(format!(
|
lines.push(format!(
|
||||||
"— {} {kills} K / {deaths} D / {assists} A", self.emojify_kills_deaths_assists(kills, deaths, assists)
|
"— {} {kills} K / {deaths} D / {assists} A", self.emojify_kills_deaths_assists(kills, deaths, assists)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(imp) = imp {
|
if let Some(imp) = imp {
|
||||||
lines.push(format!(
|
lines.push(format!(
|
||||||
"— {} {imp} IMP", self.emojify_imp(imp)
|
"— {} {imp} IMP", self.emojify_imp(imp)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(lines.join("\n"))
|
Ok(lines.join("\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stringify_match(&self, database: &mut PgConnection, r#match: Match) -> AnyResult<(DotaMatchId, Option<String>)> {
|
fn stringify_match(&self, database: &mut PgConnection, r#match: Match) -> AnyResult<(DotaMatchId, Option<String>)> {
|
||||||
log::debug!("Stringifying match...");
|
log::debug!("Stringifying match...");
|
||||||
|
|
||||||
let match_id = self.get_match_id(&r#match)?;
|
let match_id = self.get_match_id(&r#match)?;
|
||||||
|
|
||||||
if !self.should_process_match_exists(database, match_id)? {
|
if !self.should_process_match_exists(database, match_id)? {
|
||||||
log::trace!("Skipping match, already parsed.");
|
log::trace!("Skipping match, already parsed.");
|
||||||
return Ok((match_id, None))
|
return Ok((match_id, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
let datetime = self.get_match_datetime(&r#match)?;
|
let datetime = self.get_match_datetime(&r#match)?;
|
||||||
let timedelta = self.get_match_timedelta(&datetime);
|
let timedelta = self.get_match_timedelta(&datetime);
|
||||||
|
|
||||||
let r#type = self.get_match_type(&r#match)?;
|
let r#type = self.get_match_type(&r#match)?;
|
||||||
let mode = self.get_match_mode(&r#match)?;
|
let mode = self.get_match_mode(&r#match)?;
|
||||||
let duration = self.get_match_duration(&r#match)?;
|
let duration = self.get_match_duration(&r#match)?;
|
||||||
|
|
||||||
let mut players = self.get_match_players(r#match)?;
|
let mut players = self.get_match_players(r#match)?;
|
||||||
|
|
||||||
if !self.should_process_match_players(&players) {
|
if !self.should_process_match_players(&players) {
|
||||||
log::trace!("Skipping match, not enough players.");
|
log::trace!("Skipping match, not enough players.");
|
||||||
return Ok((match_id, None))
|
return Ok((match_id, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.should_process_match_imp(&players, &timedelta) {
|
if !self.should_process_match_imp(&players, &timedelta) {
|
||||||
log::trace!("Skipping match, IMP is not ready.");
|
log::trace!("Skipping match, IMP is not ready.");
|
||||||
return Ok((match_id, None))
|
return Ok((match_id, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
players.sort_unstable_by_key(|a| Self::numberify_role_lane(&a.role, &a.lane));
|
players.sort_unstable_by_key(|a| Self::numberify_role_lane(&a.role, &a.lane));
|
||||||
|
|
||||||
let _side = self.get_match_side(&players)?;
|
let _side = self.get_match_side(&players)?;
|
||||||
let outcome = self.get_match_outcome(&players)?;
|
let outcome = self.get_match_outcome(&players)?;
|
||||||
|
|
||||||
let mut lines = Vec::<String>::new();
|
let mut lines = Vec::<String>::new();
|
||||||
|
|
||||||
lines.push(format!(
|
lines.push(format!(
|
||||||
"{} <u><b>{}</b></u>",
|
"{} <u><b>{}</b></u>",
|
||||||
self.emojify_outcome(outcome),
|
self.emojify_outcome(outcome),
|
||||||
self.stringify_outcome(outcome),
|
self.stringify_outcome(outcome),
|
||||||
));
|
));
|
||||||
|
|
||||||
lines.push(format!(
|
lines.push(format!(
|
||||||
"<b>{}</b> · {} · <i>{}</i>",
|
"<b>{}</b> · {} · <i>{}</i>",
|
||||||
self.stringify_type(r#type),
|
self.stringify_type(r#type),
|
||||||
self.stringify_mode(mode),
|
self.stringify_mode(mode),
|
||||||
self.stringify_duration(duration),
|
self.stringify_duration(duration),
|
||||||
));
|
));
|
||||||
|
|
||||||
lines.push("".to_string());
|
lines.push("".to_string());
|
||||||
|
|
||||||
for player in players.into_iter() {
|
for player in players.into_iter() {
|
||||||
let string = self.stringify_player(database, player, outcome == MatchOutcome::Clash)?;
|
let string = self.stringify_player(database, player, outcome == MatchOutcome::Clash)?;
|
||||||
lines.push(string);
|
lines.push(string);
|
||||||
lines.push("".to_string());
|
lines.push("".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.push(format!(
|
lines.push(format!(
|
||||||
"Partita <code>{}</code>",
|
"Partita <code>{}</code>",
|
||||||
match_id,
|
match_id,
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok((match_id, Some(lines.join("\n"))))
|
Ok((match_id, Some(lines.join("\n"))))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_notification(&self, match_id: DotaMatchId, text: &str) -> AnyResult<Message> {
|
async fn send_notification(&self, match_id: DotaMatchId, text: &str) -> AnyResult<Message> {
|
||||||
log::debug!("Sending notification...");
|
log::debug!("Sending notification...");
|
||||||
|
|
||||||
self.telegram_bot.send_message(self.notification_chat_id, text)
|
self.telegram_bot.send_message(self.notification_chat_id, text)
|
||||||
.parse_mode(teloxide::types::ParseMode::Html)
|
.parse_mode(teloxide::types::ParseMode::Html)
|
||||||
.disable_notification(true)
|
.disable_notification(true)
|
||||||
|
@ -735,26 +735,26 @@ impl BroochService {
|
||||||
.await
|
.await
|
||||||
.context("Impossibile inviare la notifica di una partita.")
|
.context("Impossibile inviare la notifica di una partita.")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn iteration(&self) -> AnyResult<()> {
|
async fn iteration(&self) -> AnyResult<()> {
|
||||||
log::debug!("Now running an iteration of brooch!");
|
log::debug!("Now running an iteration of brooch!");
|
||||||
|
|
||||||
let client = self.create_http_client()?;
|
let client = self.create_http_client()?;
|
||||||
let mut database = self.create_postgres_connection()?;
|
let mut database = self.create_postgres_connection()?;
|
||||||
|
|
||||||
let data = self.query_guild_matches(&client).await?;
|
let data = self.query_guild_matches(&client).await?;
|
||||||
|
|
||||||
let guild = self.process_guild_data(data)?;
|
let guild = self.process_guild_data(data)?;
|
||||||
let matches = self.process_matches_data(guild)?;
|
let matches = self.process_matches_data(guild)?;
|
||||||
|
|
||||||
let results = matches
|
let results = matches
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|r#match| self.stringify_match(&mut database, r#match))
|
.map(|r#match| self.stringify_match(&mut database, r#match))
|
||||||
.collect::<Vec<AnyResult<(DotaMatchId, Option<String>)>>>();
|
.collect::<Vec<AnyResult<(DotaMatchId, Option<String>)>>>();
|
||||||
|
|
||||||
for result in results {
|
for result in results {
|
||||||
let (match_id, message) = result?;
|
let (match_id, message) = result?;
|
||||||
|
|
||||||
if let Some(message) = message {
|
if let Some(message) = message {
|
||||||
self.send_notification(match_id, &message).await?;
|
self.send_notification(match_id, &message).await?;
|
||||||
BroochMatch::flag(&mut database, match_id)?;
|
BroochMatch::flag(&mut database, match_id)?;
|
||||||
|
@ -769,7 +769,7 @@ impl RoyalnetService for BroochService {
|
||||||
async fn run(&mut self) -> AnyResult<()> {
|
async fn run(&mut self) -> AnyResult<()> {
|
||||||
loop {
|
loop {
|
||||||
self.iteration().await?;
|
self.iteration().await?;
|
||||||
|
|
||||||
sleep(Duration::new(60 * 15, 0)).await;
|
sleep(Duration::new(60 * 15, 0)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,30 +7,30 @@ use crate::utils::anyhow_result::AnyResult;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub trait RoyalnetService {
|
pub trait RoyalnetService {
|
||||||
async fn run(&mut self) -> AnyResult<()>;
|
async fn run(&mut self) -> AnyResult<()>;
|
||||||
|
|
||||||
async fn run_loop(&mut self) {
|
async fn run_loop(&mut self) {
|
||||||
let mut backoff = Duration::new(1, 0);
|
let mut backoff = Duration::new(1, 0);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let result = self.run().await;
|
let result = self.run().await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Service exited with error: {e:?}.")
|
log::error!("Service exited with error: {e:?}.")
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::debug!("Service exited successfully!")
|
log::debug!("Service exited successfully!")
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let backoff_secs = backoff.as_secs();
|
let backoff_secs = backoff.as_secs();
|
||||||
|
|
||||||
log::debug!("Backing off for {backoff_secs} seconds before restarting...");
|
log::debug!("Backing off for {backoff_secs} seconds before restarting...");
|
||||||
sleep(backoff).await;
|
sleep(backoff).await;
|
||||||
|
|
||||||
log::trace!("Doubling backoff value...");
|
log::trace!("Doubling backoff value...");
|
||||||
backoff *= 2;
|
backoff *= 2;
|
||||||
|
|
||||||
log::trace!("Backoff value is now {backoff_secs} seconds.");
|
log::trace!("Backoff value is now {backoff_secs} seconds.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,83 +12,83 @@ use crate::services::telegram::commands::CommandResult;
|
||||||
// Se avete un'idea ma metterebbe troppe opzioni in un'unica categoria, mettetela sotto commento.
|
// Se avete un'idea ma metterebbe troppe opzioni in un'unica categoria, mettetela sotto commento.
|
||||||
const ANSWERS: [&str; 60] = [
|
const ANSWERS: [&str; 60] = [
|
||||||
// risposte "sì": 20
|
// risposte "sì": 20
|
||||||
"🔵 Sì.",
|
"🔵 Sì.",
|
||||||
"🔵 Decisamente sì!",
|
"🔵 Decisamente sì!",
|
||||||
"🔵 Uhm, secondo me sì.",
|
"🔵 Uhm, secondo me sì.",
|
||||||
"🔵 Sì! Sì! SÌ!",
|
"🔵 Sì! Sì! SÌ!",
|
||||||
"🔵 Yup.",
|
"🔵 Yup.",
|
||||||
"🔵 Direi proprio di sì.",
|
"🔵 Direi proprio di sì.",
|
||||||
"🔵 Assolutamente sì.",
|
"🔵 Assolutamente sì.",
|
||||||
"🔵 Ma certo!",
|
"🔵 Ma certo!",
|
||||||
"🔵 Esatto!",
|
"🔵 Esatto!",
|
||||||
"🔵 Senz'altro!",
|
"🔵 Senz'altro!",
|
||||||
"🔵 Ovviamente.",
|
"🔵 Ovviamente.",
|
||||||
"🔵 Questa domanda ha risposta affermativa.",
|
"🔵 Questa domanda ha risposta affermativa.",
|
||||||
"🔵 Hell yeah.",
|
"🔵 Hell yeah.",
|
||||||
"🔵 YES! YES! YES!",
|
"🔵 YES! YES! YES!",
|
||||||
"🔵 yusssssss",
|
"🔵 yusssssss",
|
||||||
"🔵 Non vedo perchè no",
|
"🔵 Non vedo perchè no",
|
||||||
"🔵 Ha senso, ha perfettamente senso, nulla da obiettare, ha senso.",
|
"🔵 Ha senso, ha perfettamente senso, nulla da obiettare, ha senso.",
|
||||||
"🔵 Yos!",
|
"🔵 Yos!",
|
||||||
"🔵 Sì, ma tienilo segreto...",
|
"🔵 Sì, ma tienilo segreto...",
|
||||||
"🔵 [RADIO] Affermativo.",
|
"🔵 [RADIO] Affermativo.",
|
||||||
|
|
||||||
// risposte "no": 20
|
// risposte "no": 20
|
||||||
"❌ No.",
|
"❌ No.",
|
||||||
"❌ Decisamente no!",
|
"❌ Decisamente no!",
|
||||||
"❌ Uhm, secondo me sì. No, aspetta, ci ho ripensato. È un no.",
|
"❌ Uhm, secondo me sì. No, aspetta, ci ho ripensato. È un no.",
|
||||||
"❌ No, no, e ancora NO!",
|
"❌ No, no, e ancora NO!",
|
||||||
"❌ Nope.",
|
"❌ Nope.",
|
||||||
"❌ Direi proprio di no.",
|
"❌ Direi proprio di no.",
|
||||||
"❌ Assolutamente no.",
|
"❌ Assolutamente no.",
|
||||||
"❌ Certo che no!",
|
"❌ Certo che no!",
|
||||||
"❌ Neanche per idea!",
|
"❌ Neanche per idea!",
|
||||||
"❌ Neanche per sogno!",
|
"❌ Neanche per sogno!",
|
||||||
"❌ Niente affatto!",
|
"❌ Niente affatto!",
|
||||||
"❌ Questa domanda ha risposta negativa.",
|
"❌ Questa domanda ha risposta negativa.",
|
||||||
"❌ Hell no.",
|
"❌ Hell no.",
|
||||||
"❌ NO! NO! NO!",
|
"❌ NO! NO! NO!",
|
||||||
"❌ lolno",
|
"❌ lolno",
|
||||||
"❌ NEIN NEIN NEIN NEIN",
|
"❌ NEIN NEIN NEIN NEIN",
|
||||||
"❌ Delet dis",
|
"❌ Delet dis",
|
||||||
"❌ Nopety nope!",
|
"❌ Nopety nope!",
|
||||||
"❌ No, ma tienilo segreto.",
|
"❌ No, ma tienilo segreto.",
|
||||||
"❌ [RADIO] Negativo.",
|
"❌ [RADIO] Negativo.",
|
||||||
|
|
||||||
// risposte "boh": 20
|
// risposte "boh": 20
|
||||||
"❔ Boh.",
|
"❔ Boh.",
|
||||||
"❔ E io che ne so?!",
|
"❔ E io che ne so?!",
|
||||||
"❔ Non so proprio rispondere.",
|
"❔ Non so proprio rispondere.",
|
||||||
"❔ Non lo so...",
|
"❔ Non lo so...",
|
||||||
"❔ Mi avvalgo della facoltà di non rispondere.",
|
"❔ Mi avvalgo della facoltà di non rispondere.",
|
||||||
"❔ Non parlerò senza il mio avvocato!",
|
"❔ Non parlerò senza il mio avvocato!",
|
||||||
"❔ Dunno.",
|
"❔ Dunno.",
|
||||||
"❔ Perché lo chiedi a me?",
|
"❔ Perché lo chiedi a me?",
|
||||||
"❔ Ah, non lo so io!",
|
"❔ Ah, non lo so io!",
|
||||||
r#"❔ ¯\_(ツ)_/¯"#,
|
r#"❔ ¯\_(ツ)_/¯"#,
|
||||||
"❔ No idea.",
|
"❔ No idea.",
|
||||||
"❔ Dunno.",
|
"❔ Dunno.",
|
||||||
"❔ Boooooh!",
|
"❔ Boooooh!",
|
||||||
"❔ Non ne ho la più pallida idea.",
|
"❔ Non ne ho la più pallida idea.",
|
||||||
"❔ No comment.",
|
"❔ No comment.",
|
||||||
"❔ maibi",
|
"❔ maibi",
|
||||||
"❔ maibi not",
|
"❔ maibi not",
|
||||||
"❔ idk dude",
|
"❔ idk dude",
|
||||||
"❔ Non mi è permesso condividere questa informazione.",
|
"❔ Non mi è permesso condividere questa informazione.",
|
||||||
"❔ [RADIO] Mantengo la posizione.",
|
"❔ [RADIO] Mantengo la posizione.",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub async fn handler(bot: &Bot, message: &Message) -> CommandResult {
|
pub async fn handler(bot: &Bot, message: &Message) -> CommandResult {
|
||||||
let mut rng = rand::rngs::SmallRng::from_entropy();
|
let mut rng = rand::rngs::SmallRng::from_entropy();
|
||||||
|
|
||||||
let answer = ANSWERS.choose(&mut rng)
|
let answer = ANSWERS.choose(&mut rng)
|
||||||
.context("Non è stato possibile selezionare una risposta.")?;
|
.context("Non è stato possibile selezionare una risposta.")?;
|
||||||
|
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_message(message.chat.id, answer.to_string())
|
.send_message(message.chat.id, answer.to_string())
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare la risposta.")?;
|
.context("Non è stato possibile inviare la risposta.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
|
@ -26,17 +26,17 @@ pub async fn handler(bot: &Bot, message: &Message) -> CommandResult {
|
||||||
.context("Il gatto ricevuto in risposta dall'API è indecifrabile, quindi non è stato possibile riceverlo.")?
|
.context("Il gatto ricevuto in risposta dall'API è indecifrabile, quindi non è stato possibile riceverlo.")?
|
||||||
.pop()
|
.pop()
|
||||||
.context("Il gatto ricevuto in risposta dall'API non esiste, quindi non è stato possibile riceverlo.")?;
|
.context("Il gatto ricevuto in risposta dall'API non esiste, quindi non è stato possibile riceverlo.")?;
|
||||||
|
|
||||||
let url: Url = response.url.parse()
|
let url: Url = response.url.parse()
|
||||||
.context("L'URL del gatto ricevuto in risposta dall'API è malformato, quindi non è stato possibile riceverlo.")?;
|
.context("L'URL del gatto ricevuto in risposta dall'API è malformato, quindi non è stato possibile riceverlo.")?;
|
||||||
|
|
||||||
let input = InputFile::url(url);
|
let input = InputFile::url(url);
|
||||||
|
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_photo(message.chat.id, input)
|
.send_photo(message.chat.id, input)
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare un gatto in risposta a questo messaggio.")?;
|
.context("Non è stato possibile inviare un gatto in risposta a questo messaggio.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,68 +25,69 @@ pub struct DiarioArgs {
|
||||||
|
|
||||||
impl FromStr for DiarioArgs {
|
impl FromStr for DiarioArgs {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r#" *(?:\[(?<warning>.+)])? *"(?<quote>.+)"[, ]*(?:[-–—]+(?<quoted>\w+)(?:, *(?<context>.+))?)?"#).unwrap());
|
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r#" *(?:\[(?<warning>.+)])? *"(?<quote>.+)"[, ]*(?:[-–—]+(?<quoted>\w+)(?:, *(?<context>.+))?)?"#).unwrap());
|
||||||
|
|
||||||
let captures = REGEX.captures(s);
|
let captures = REGEX.captures(s);
|
||||||
|
|
||||||
let args = match captures {
|
let args = match captures {
|
||||||
Some(captures) => {
|
Some(captures) => {
|
||||||
let warning = captures.name("warning")
|
let warning = captures.name("warning")
|
||||||
.map(|s| s.as_str().to_owned());
|
.map(|s| s.as_str().to_owned());
|
||||||
|
|
||||||
let quote = captures.name("quote")
|
let quote = captures.name("quote")
|
||||||
.context("Citazione non specificata nel comando.")?
|
.context("Citazione non specificata nel comando.")?
|
||||||
.as_str()
|
.as_str()
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
let quoted = captures.name("quoted")
|
let quoted = captures.name("quoted")
|
||||||
.map(|s| s.as_str().to_owned());
|
.map(|s| s.as_str().to_owned());
|
||||||
|
|
||||||
let context = captures.name("context")
|
let context = captures.name("context")
|
||||||
.map(|s| s.as_str().to_owned());
|
.map(|s| s.as_str().to_owned());
|
||||||
|
|
||||||
DiarioArgs {warning, quote, quoted, context}
|
DiarioArgs { warning, quote, quoted, context }
|
||||||
},
|
}
|
||||||
None => {
|
None => {
|
||||||
let warning = None;
|
let warning = None;
|
||||||
let quote = s.to_string();
|
let quote = s.to_string();
|
||||||
let quoted = None;
|
let quoted = None;
|
||||||
let context = None;
|
let context = None;
|
||||||
|
|
||||||
DiarioArgs {warning, quote, quoted, context}
|
DiarioArgs { warning, quote, quoted, context }
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(args)
|
Ok(args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TelegramWrite for Diario {
|
impl TelegramWrite for Diario {
|
||||||
fn write_telegram<T>(&self, f: &mut T) -> Result<(), Error>
|
fn write_telegram<T>(&self, f: &mut T) -> Result<(), Error>
|
||||||
where T: Write
|
where
|
||||||
|
T: Write,
|
||||||
{
|
{
|
||||||
// Diario ID
|
// Diario ID
|
||||||
write!(f, "<code>#{}</code>", self.id)?;
|
write!(f, "<code>#{}</code>", self.id)?;
|
||||||
|
|
||||||
// Optional content warning
|
// Optional content warning
|
||||||
if let Some(warning) = self.to_owned().warning {
|
if let Some(warning) = self.to_owned().warning {
|
||||||
write!(f, ", <b>{}</b>", warning.escape_telegram_html())?;
|
write!(f, ", <b>{}</b>", warning.escape_telegram_html())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Newline
|
// Newline
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
|
|
||||||
// Quote optionally covered by a spoiler tag
|
// Quote optionally covered by a spoiler tag
|
||||||
match self.warning.to_owned() {
|
match self.warning.to_owned() {
|
||||||
None => write!(f, "<blockquote expandable>{}</blockquote>", self.clone().quote.escape_telegram_html())?,
|
None => write!(f, "<blockquote expandable>{}</blockquote>", self.clone().quote.escape_telegram_html())?,
|
||||||
Some(_) => write!(f, "<blockquote expandable><tg-spoiler>{}</tg-spoiler></blockquote>", self.clone().quote.escape_telegram_html())?,
|
Some(_) => write!(f, "<blockquote expandable><tg-spoiler>{}</tg-spoiler></blockquote>", self.clone().quote.escape_telegram_html())?,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Newline
|
// Newline
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
|
|
||||||
// Optional citation with optional context
|
// Optional citation with optional context
|
||||||
match (self.quoted_name.to_owned(), self.context.to_owned()) {
|
match (self.quoted_name.to_owned(), self.context.to_owned()) {
|
||||||
(Some(name), Some(context)) => write!(f, "—{}, <i>{}</i>", name.escape_telegram_html(), context.escape_telegram_html())?,
|
(Some(name), Some(context)) => write!(f, "—{}, <i>{}</i>", name.escape_telegram_html(), context.escape_telegram_html())?,
|
||||||
|
@ -94,7 +95,7 @@ impl TelegramWrite for Diario {
|
||||||
(None, Some(context)) => write!(f, "...<i>{}</i>", context.escape_telegram_html())?,
|
(None, Some(context)) => write!(f, "...<i>{}</i>", context.escape_telegram_html())?,
|
||||||
(None, None) => write!(f, "")?,
|
(None, None) => write!(f, "")?,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,16 +103,16 @@ impl TelegramWrite for Diario {
|
||||||
pub async fn handler(bot: &Bot, message: &Message, args: &DiarioArgs, database: &DatabaseInterface) -> CommandResult {
|
pub async fn handler(bot: &Bot, message: &Message, args: &DiarioArgs, database: &DatabaseInterface) -> CommandResult {
|
||||||
let author = message.from.as_ref()
|
let author = message.from.as_ref()
|
||||||
.context("Non è stato possibile determinare chi ha inviato questo comando.")?;
|
.context("Non è stato possibile determinare chi ha inviato questo comando.")?;
|
||||||
|
|
||||||
let mut database = database.connect()?;
|
let mut database = database.connect()?;
|
||||||
|
|
||||||
let royalnet_user: RoyalnetUser = {
|
let royalnet_user: RoyalnetUser = {
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::{ExpressionMethods, QueryDsl};
|
use diesel::{ExpressionMethods, QueryDsl};
|
||||||
use crate::interfaces::database::schema::telegram::dsl::*;
|
use crate::interfaces::database::schema::telegram::dsl::*;
|
||||||
use crate::interfaces::database::schema::users::dsl::*;
|
use crate::interfaces::database::schema::users::dsl::*;
|
||||||
use crate::interfaces::database::models::RoyalnetUser;
|
use crate::interfaces::database::models::RoyalnetUser;
|
||||||
|
|
||||||
telegram
|
telegram
|
||||||
.filter(telegram_id.eq::<i64>(
|
.filter(telegram_id.eq::<i64>(
|
||||||
author.id.0.try_into()
|
author.id.0.try_into()
|
||||||
|
@ -122,10 +123,10 @@ pub async fn handler(bot: &Bot, message: &Message, args: &DiarioArgs, database:
|
||||||
.get_result(&mut database)
|
.get_result(&mut database)
|
||||||
.context("Non è stato possibile recuperare il tuo utente Telegram dal database RYG.")?
|
.context("Non è stato possibile recuperare il tuo utente Telegram dal database RYG.")?
|
||||||
};
|
};
|
||||||
|
|
||||||
let entry = {
|
let entry = {
|
||||||
use schema::diario;
|
use schema::diario;
|
||||||
|
|
||||||
insert_into(diario::table)
|
insert_into(diario::table)
|
||||||
.values(&(
|
.values(&(
|
||||||
diario::saver_id.eq(Some(royalnet_user.id)),
|
diario::saver_id.eq(Some(royalnet_user.id)),
|
||||||
|
@ -137,14 +138,14 @@ pub async fn handler(bot: &Bot, message: &Message, args: &DiarioArgs, database:
|
||||||
.get_result::<Diario>(&mut database)
|
.get_result::<Diario>(&mut database)
|
||||||
.context("Non è stato possibile aggiungere la riga di diario al database RYG.")?
|
.context("Non è stato possibile aggiungere la riga di diario al database RYG.")?
|
||||||
};
|
};
|
||||||
|
|
||||||
let text = format!(
|
let text = format!(
|
||||||
"🖋 Riga aggiunta al diario!\n\
|
"🖋 Riga aggiunta al diario!\n\
|
||||||
\n\
|
\n\
|
||||||
{}",
|
{}",
|
||||||
entry.to_string_telegram(),
|
entry.to_string_telegram(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_message(message.chat.id, text)
|
.send_message(message.chat.id, text)
|
||||||
.parse_mode(ParseMode::Html)
|
.parse_mode(ParseMode::Html)
|
||||||
|
@ -153,6 +154,6 @@ pub async fn handler(bot: &Bot, message: &Message, args: &DiarioArgs, database:
|
||||||
// teloxide does not support blockquotes yet and errors out on parsing the response
|
// teloxide does not support blockquotes yet and errors out on parsing the response
|
||||||
// .context("Non è stato possibile inviare la risposta.")?
|
// .context("Non è stato possibile inviare la risposta.")?
|
||||||
;
|
;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
|
@ -22,17 +22,17 @@ pub async fn handler(bot: &Bot, message: &Message) -> CommandResult {
|
||||||
.context("Non è stato possibile richiedere un cane all'API.")?
|
.context("Non è stato possibile richiedere un cane all'API.")?
|
||||||
.json::<DogQueryResponse>().await
|
.json::<DogQueryResponse>().await
|
||||||
.context("Il cane ricevuto in risposta dall'API è indecifrabile, quindi non è stato possibile riceverlo.")?;
|
.context("Il cane ricevuto in risposta dall'API è indecifrabile, quindi non è stato possibile riceverlo.")?;
|
||||||
|
|
||||||
let url: Url = response.message.parse()
|
let url: Url = response.message.parse()
|
||||||
.context("L'URL del cane ricevuto in risposta dall'API è malformato, quindi non è stato possibile riceverlo.")?;
|
.context("L'URL del cane ricevuto in risposta dall'API è malformato, quindi non è stato possibile riceverlo.")?;
|
||||||
|
|
||||||
let input = InputFile::url(url);
|
let input = InputFile::url(url);
|
||||||
|
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_photo(message.chat.id, input)
|
.send_photo(message.chat.id, input)
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare un cane in risposta a questo messaggio.")?;
|
.context("Non è stato possibile inviare un cane in risposta a questo messaggio.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
|
@ -10,12 +10,12 @@ pub async fn handler(bot: &Bot, message: &Message, text: &str) -> CommandResult
|
||||||
let text = format!(
|
let text = format!(
|
||||||
"💬 {text}"
|
"💬 {text}"
|
||||||
);
|
);
|
||||||
|
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_message(message.chat.id, text)
|
.send_message(message.chat.id, text)
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare la risposta.")?;
|
.context("Non è stato possibile inviare la risposta.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,21 +174,21 @@ const FORTUNES: [&str; 164] = [
|
||||||
"🍻 Oggi una birra ti ridarà una vita!",
|
"🍻 Oggi una birra ti ridarà una vita!",
|
||||||
"🎶 Oggi Hatsune Miku si nasconderà nella tua Wi-Fi!",
|
"🎶 Oggi Hatsune Miku si nasconderà nella tua Wi-Fi!",
|
||||||
"🚽 Oggi delle telecamere combatteranno contro dei gabinetti!",
|
"🚽 Oggi delle telecamere combatteranno contro dei gabinetti!",
|
||||||
"🌟 Oggi verrà scoperta una galassia grande quanto qualcuno della tua famiglia!",
|
"🌟 Oggi verrà scoperta una galassia grande quanto qualcuno della tua famiglia!",
|
||||||
"🎶 Oggi Rick non rinuncerà mai a te!",
|
"🎶 Oggi Rick non rinuncerà mai a te!",
|
||||||
"🏚 Oggi ristrutturerai una villa completando dei minigiochi match-3!",
|
"🏚 Oggi ristrutturerai una villa completando dei minigiochi match-3!",
|
||||||
];
|
];
|
||||||
|
|
||||||
struct FortuneKey {
|
struct FortuneKey {
|
||||||
today: chrono::NaiveDate,
|
today: chrono::NaiveDate,
|
||||||
author_id: teloxide::types::UserId
|
author_id: teloxide::types::UserId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for FortuneKey {
|
impl Hash for FortuneKey {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
let days: i32 = self.today.num_days_from_ce();
|
let days: i32 = self.today.num_days_from_ce();
|
||||||
let id: u64 = self.author_id.0;
|
let id: u64 = self.author_id.0;
|
||||||
|
|
||||||
state.write_i32(days);
|
state.write_i32(days);
|
||||||
state.write_u64(id);
|
state.write_u64(id);
|
||||||
}
|
}
|
||||||
|
@ -196,13 +196,13 @@ impl Hash for FortuneKey {
|
||||||
|
|
||||||
pub async fn handler(bot: &Bot, message: &Message) -> CommandResult {
|
pub async fn handler(bot: &Bot, message: &Message) -> CommandResult {
|
||||||
let today = chrono::Local::now().date_naive();
|
let today = chrono::Local::now().date_naive();
|
||||||
|
|
||||||
let author = message.from.as_ref()
|
let author = message.from.as_ref()
|
||||||
.context("Non è stato possibile determinare chi ha inviato questo comando.")?;
|
.context("Non è stato possibile determinare chi ha inviato questo comando.")?;
|
||||||
let author_id = author.id;
|
let author_id = author.id;
|
||||||
|
|
||||||
let key = FortuneKey {today, author_id};
|
let key = FortuneKey { today, author_id };
|
||||||
|
|
||||||
let mut hasher = std::hash::DefaultHasher::new();
|
let mut hasher = std::hash::DefaultHasher::new();
|
||||||
key.hash(&mut hasher);
|
key.hash(&mut hasher);
|
||||||
let hash = hasher.finish()
|
let hash = hasher.finish()
|
||||||
|
@ -216,17 +216,17 @@ pub async fn handler(bot: &Bot, message: &Message) -> CommandResult {
|
||||||
anyhow::bail!("Non è stato possibile determinare il tuo oroscopo.");
|
anyhow::bail!("Non è stato possibile determinare il tuo oroscopo.");
|
||||||
}
|
}
|
||||||
let hash = hash.unwrap();
|
let hash = hash.unwrap();
|
||||||
|
|
||||||
let mut rng = rand::rngs::SmallRng::from_seed(hash);
|
let mut rng = rand::rngs::SmallRng::from_seed(hash);
|
||||||
|
|
||||||
let fortune = FORTUNES.choose(&mut rng)
|
let fortune = FORTUNES.choose(&mut rng)
|
||||||
.context("Non è stato possibile selezionare il tuo oroscopo.")?;
|
.context("Non è stato possibile selezionare il tuo oroscopo.")?;
|
||||||
|
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_message(message.chat.id, fortune.to_string())
|
.send_message(message.chat.id, fortune.to_string())
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare la risposta.")?;
|
.context("Non è stato possibile inviare la risposta.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
|
@ -9,16 +9,16 @@ use super::CommandResult;
|
||||||
|
|
||||||
pub async fn handler_all(bot: &Bot, message: &Message) -> CommandResult {
|
pub async fn handler_all(bot: &Bot, message: &Message) -> CommandResult {
|
||||||
let descriptions = super::Command::descriptions().to_string();
|
let descriptions = super::Command::descriptions().to_string();
|
||||||
|
|
||||||
let text = format!("❔ <b>Comandi disponibili</b>\n\n{descriptions}");
|
let text = format!("❔ <b>Comandi disponibili</b>\n\n{descriptions}");
|
||||||
|
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_message(message.chat.id, text)
|
.send_message(message.chat.id, text)
|
||||||
.parse_mode(ParseMode::Html)
|
.parse_mode(ParseMode::Html)
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare la risposta.")?;
|
.context("Non è stato possibile inviare la risposta.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,51 +27,51 @@ pub async fn handler_specific(bot: &Bot, message: &Message, target: &str) -> Com
|
||||||
.get_me()
|
.get_me()
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile recuperare informazioni sul bot.")?;
|
.context("Non è stato possibile recuperare informazioni sul bot.")?;
|
||||||
|
|
||||||
let me_username = me.username.as_ref()
|
let me_username = me.username.as_ref()
|
||||||
.context("Non è stato possibile determinare l'username del bot.")?;
|
.context("Non è stato possibile determinare l'username del bot.")?;
|
||||||
|
|
||||||
let suffix = format!("@{me_username}");
|
let suffix = format!("@{me_username}");
|
||||||
|
|
||||||
let target = target.strip_prefix('/')
|
let target = target.strip_prefix('/')
|
||||||
.unwrap_or(target);
|
.unwrap_or(target);
|
||||||
|
|
||||||
let target = target.strip_suffix(&suffix)
|
let target = target.strip_suffix(&suffix)
|
||||||
.unwrap_or(target);
|
.unwrap_or(target);
|
||||||
|
|
||||||
log::trace!("Stripped target command: {target:?}");
|
log::trace!("Stripped target command: {target:?}");
|
||||||
|
|
||||||
let all_commands: Vec<BotCommand> = super::Command::bot_commands();
|
let all_commands: Vec<BotCommand> = super::Command::bot_commands();
|
||||||
|
|
||||||
log::trace!("All commands are: {all_commands:?}");
|
log::trace!("All commands are: {all_commands:?}");
|
||||||
|
|
||||||
let identify_command = |cmd: &&BotCommand| -> bool {
|
let identify_command = |cmd: &&BotCommand| -> bool {
|
||||||
let command = &cmd.command;
|
let command = &cmd.command;
|
||||||
|
|
||||||
let command = command.strip_prefix('/')
|
let command = command.strip_prefix('/')
|
||||||
.unwrap_or_else(|| command);
|
.unwrap_or_else(|| command);
|
||||||
|
|
||||||
target == command
|
target == command
|
||||||
};
|
};
|
||||||
|
|
||||||
let target = match all_commands.iter().find(identify_command) {
|
let target = match all_commands.iter().find(identify_command) {
|
||||||
Some(bot_command) => bot_command,
|
Some(bot_command) => bot_command,
|
||||||
None => anyhow::bail!("Non è stato possibile trovare il comando specificato."),
|
None => anyhow::bail!("Non è stato possibile trovare il comando specificato."),
|
||||||
};
|
};
|
||||||
|
|
||||||
let display_suffix = match message.chat.is_private() {
|
let display_suffix = match message.chat.is_private() {
|
||||||
false => &suffix,
|
false => &suffix,
|
||||||
true => "",
|
true => "",
|
||||||
};
|
};
|
||||||
|
|
||||||
let text = format!("❔ <b>Comando {}{}</b>\n\n{}", target.command, display_suffix, target.description);
|
let text = format!("❔ <b>Comando {}{}</b>\n\n{}", target.command, display_suffix, target.description);
|
||||||
|
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_message(message.chat.id, text)
|
.send_message(message.chat.id, text)
|
||||||
.parse_mode(ParseMode::Html)
|
.parse_mode(ParseMode::Html)
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare la risposta.")?;
|
.context("Non è stato possibile inviare la risposta.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
|
@ -21,26 +21,26 @@ pub struct MatchmakingArgs {
|
||||||
|
|
||||||
impl FromStr for MatchmakingArgs {
|
impl FromStr for MatchmakingArgs {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\[(?<start>.*)]\s*(?<text>.+)$").unwrap());
|
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\[(?<start>.*)]\s*(?<text>.+)$").unwrap());
|
||||||
|
|
||||||
let captures = REGEX.captures(s)
|
let captures = REGEX.captures(s)
|
||||||
.context("Sintassi del comando incorretta.")?;
|
.context("Sintassi del comando incorretta.")?;
|
||||||
|
|
||||||
let start = captures.name("start")
|
let start = captures.name("start")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_str();
|
.as_str();
|
||||||
|
|
||||||
let text = captures.name("text")
|
let text = captures.name("text")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_str()
|
.as_str()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let start = parse_datetime_at_date(chrono::Local::now(), start)
|
let start = parse_datetime_at_date(chrono::Local::now(), start)
|
||||||
.context("Impossibile determinare la data in cui l'attesa avrà termine.")?
|
.context("Impossibile determinare la data in cui l'attesa avrà termine.")?
|
||||||
.with_timezone(&chrono::Local);
|
.with_timezone(&chrono::Local);
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
Self { start, text }
|
Self { start, text }
|
||||||
)
|
)
|
||||||
|
@ -49,23 +49,23 @@ impl FromStr for MatchmakingArgs {
|
||||||
|
|
||||||
pub async fn handler(bot: &Bot, message: &Message, args: &MatchmakingArgs, database: &DatabaseInterface) -> CommandResult {
|
pub async fn handler(bot: &Bot, message: &Message, args: &MatchmakingArgs, database: &DatabaseInterface) -> CommandResult {
|
||||||
let mut database = database.connect()?;
|
let mut database = database.connect()?;
|
||||||
|
|
||||||
let event = MatchmakingEvent::create(&mut database, &args.text, &args.start)
|
let event = MatchmakingEvent::create(&mut database, &args.text, &args.start)
|
||||||
.context("Non è stato possibile creare un nuovo matchmaking.")?;
|
.context("Non è stato possibile creare un nuovo matchmaking.")?;
|
||||||
|
|
||||||
let mm1 = MatchmakingMessageTelegram::send_new_and_create(&mut database, event.id, bot, message.chat.id, Some(message.id))
|
let mm1 = MatchmakingMessageTelegram::send_new_and_create(&mut database, event.id, bot, message.chat.id, Some(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile postare il matchmaking.")?;
|
.context("Non è stato possibile postare il matchmaking.")?;
|
||||||
|
|
||||||
sleep_chrono(&args.start).await;
|
sleep_chrono(&args.start).await;
|
||||||
|
|
||||||
let _mm2 = MatchmakingMessageTelegram::send_new_and_create(&mut database, event.id, bot, message.chat.id, Some(message.id))
|
let _mm2 = MatchmakingMessageTelegram::send_new_and_create(&mut database, event.id, bot, message.chat.id, Some(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile confermare il matchmaking.")?;
|
.context("Non è stato possibile confermare il matchmaking.")?;
|
||||||
|
|
||||||
mm1.destroy_and_send_delete(&mut database, bot)
|
mm1.destroy_and_send_delete(&mut database, bot)
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile eliminare il matchmaking.")?;
|
.context("Non è stato possibile eliminare il matchmaking.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,9 @@ type CommandResult = AnyResult<()>;
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
#[command(description = "Invia messaggio di introduzione.")]
|
#[command(description = "Invia messaggio di introduzione.")]
|
||||||
Start,
|
Start,
|
||||||
#[command(description = "Visualizza l'elenco dei comandi disponibili, o mostra informazioni su uno specifico comando.")]
|
#[command(
|
||||||
|
description = "Visualizza l'elenco dei comandi disponibili, o mostra informazioni su uno specifico comando."
|
||||||
|
)]
|
||||||
Help(String),
|
Help(String),
|
||||||
#[command(description = "Mostra il tuo oroscopo di oggi.")]
|
#[command(description = "Mostra il tuo oroscopo di oggi.")]
|
||||||
Fortune,
|
Fortune,
|
||||||
|
@ -62,28 +64,28 @@ impl Command {
|
||||||
/// Update the [commands menu](https://core.telegram.org/bots/features#commands) of the bot.
|
/// Update the [commands menu](https://core.telegram.org/bots/features#commands) of the bot.
|
||||||
pub async fn set_commands(bot: &mut Bot) -> AnyResult<()> {
|
pub async fn set_commands(bot: &mut Bot) -> AnyResult<()> {
|
||||||
log::debug!("Setting bot commands...");
|
log::debug!("Setting bot commands...");
|
||||||
|
|
||||||
log::trace!("Determining bot commands...");
|
log::trace!("Determining bot commands...");
|
||||||
let commands = Self::bot_commands();
|
let commands = Self::bot_commands();
|
||||||
|
|
||||||
log::trace!("Setting commands: {commands:#?}");
|
log::trace!("Setting commands: {commands:#?}");
|
||||||
bot.set_my_commands(commands).await
|
bot.set_my_commands(commands).await
|
||||||
.context("Non è stato possibile aggiornare la lista comandi del bot.")?;
|
.context("Non è stato possibile aggiornare la lista comandi del bot.")?;
|
||||||
|
|
||||||
log::trace!("Setting commands successful!");
|
log::trace!("Setting commands successful!");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_self(self, bot: Bot, message: Message, database: Arc<DatabaseInterface>) -> CommandResult {
|
pub async fn handle_self(self, bot: Bot, message: Message, database: Arc<DatabaseInterface>) -> CommandResult {
|
||||||
log::debug!("Handling command...");
|
log::debug!("Handling command...");
|
||||||
|
|
||||||
log::trace!(
|
log::trace!(
|
||||||
"Handling {:?} in {:?} with {:?}...",
|
"Handling {:?} in {:?} with {:?}...",
|
||||||
self,
|
self,
|
||||||
&message.chat.id,
|
&message.chat.id,
|
||||||
&message.id,
|
&message.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
// FIXME: Quick hack to fix single thread
|
// FIXME: Quick hack to fix single thread
|
||||||
log::trace!("Spawning task for future...");
|
log::trace!("Spawning task for future...");
|
||||||
let _task = tokio::spawn(async move {
|
let _task = tokio::spawn(async move {
|
||||||
|
@ -105,28 +107,28 @@ 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,
|
||||||
};
|
};
|
||||||
|
|
||||||
log::trace!("Delegating error handling to error handler...");
|
log::trace!("Delegating error handling to error handler...");
|
||||||
let result2 = match result1.as_ref() {
|
let result2 = match result1.as_ref() {
|
||||||
Ok(_) => return,
|
Ok(_) => return,
|
||||||
Err(e1) => self.handle_error(&bot, &message, e1).await
|
Err(e1) => self.handle_error(&bot, &message, e1).await
|
||||||
};
|
};
|
||||||
|
|
||||||
let e1 = result1.unwrap_err();
|
let e1 = result1.unwrap_err();
|
||||||
|
|
||||||
log::trace!("Delegating fatal error handling to fatal error handler...");
|
log::trace!("Delegating fatal error handling to fatal error handler...");
|
||||||
let _result3 = match result2 {
|
let _result3 = match result2 {
|
||||||
Ok(_) => return,
|
Ok(_) => return,
|
||||||
Err(e2) => self.handle_fatal(&bot, &message, &e1, &e2).await
|
Err(e2) => self.handle_fatal(&bot, &message, &e1, &e2).await
|
||||||
};
|
};
|
||||||
|
|
||||||
log::trace!("Successfully handled command!");
|
log::trace!("Successfully handled command!");
|
||||||
});
|
});
|
||||||
|
|
||||||
log::trace!("Successfully spawned task!");
|
log::trace!("Successfully spawned task!");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_unknown(bot: Bot, message: Message) -> CommandResult {
|
pub async fn handle_unknown(bot: Bot, message: Message) -> CommandResult {
|
||||||
log::debug!("Received an unknown command or an invalid syntax: {:?}", message.text());
|
log::debug!("Received an unknown command or an invalid syntax: {:?}", message.text());
|
||||||
|
|
||||||
|
@ -136,11 +138,11 @@ impl Command {
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare il messaggio di errore.")?;
|
.context("Non è stato possibile inviare il messaggio di errore.")?;
|
||||||
|
|
||||||
log::trace!("Successfully handled unknown command!");
|
log::trace!("Successfully handled unknown command!");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_error(&self, bot: &Bot, message: &Message, error: &Error) -> CommandResult {
|
async fn handle_error(&self, bot: &Bot, message: &Message, error: &Error) -> CommandResult {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Command message in {:?} with id {:?} and contents {:?} errored out with `{:?}`",
|
"Command message in {:?} with id {:?} and contents {:?} errored out with `{:?}`",
|
||||||
|
@ -156,11 +158,11 @@ impl Command {
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare il messaggio di errore.")?;
|
.context("Non è stato possibile inviare il messaggio di errore.")?;
|
||||||
|
|
||||||
log::trace!("Successfully handled errored command!");
|
log::trace!("Successfully handled errored command!");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_fatal(&self, _bot: &Bot, message: &Message, error1: &Error, error2: &Error) -> CommandResult {
|
async fn handle_fatal(&self, _bot: &Bot, message: &Message, error1: &Error, error2: &Error) -> CommandResult {
|
||||||
log::error!(
|
log::error!(
|
||||||
"Command message in {:?} with id {:?} and contents {:?} errored out with `{:?}`, and it was impossible to handle the error because of `{:?}`",
|
"Command message in {:?} with id {:?} and contents {:?} errored out with `{:?}`, and it was impossible to handle the error because of `{:?}`",
|
||||||
|
@ -170,7 +172,7 @@ impl Command {
|
||||||
error1,
|
error1,
|
||||||
error2,
|
error2,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,26 +22,26 @@ pub struct ReminderArgs {
|
||||||
|
|
||||||
impl FromStr for ReminderArgs {
|
impl FromStr for ReminderArgs {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\[(?<target>.*)]\s*(?<reminder>.+)$").unwrap());
|
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\[(?<target>.*)]\s*(?<reminder>.+)$").unwrap());
|
||||||
|
|
||||||
let captures = REGEX.captures(s)
|
let captures = REGEX.captures(s)
|
||||||
.context("Sintassi del comando incorretta.")?;
|
.context("Sintassi del comando incorretta.")?;
|
||||||
|
|
||||||
let target = captures.name("target")
|
let target = captures.name("target")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_str();
|
.as_str();
|
||||||
|
|
||||||
let reminder = captures.name("reminder")
|
let reminder = captures.name("reminder")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_str()
|
.as_str()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let target = parse_datetime_at_date(chrono::Local::now(), target)
|
let target = parse_datetime_at_date(chrono::Local::now(), target)
|
||||||
.context("Impossibile determinare la data in cui l'attesa avrà termine.")?
|
.context("Impossibile determinare la data in cui l'attesa avrà termine.")?
|
||||||
.with_timezone(&chrono::Local);
|
.with_timezone(&chrono::Local);
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
ReminderArgs { target, reminder }
|
ReminderArgs { target, reminder }
|
||||||
)
|
)
|
||||||
|
@ -57,16 +57,16 @@ pub async fn handler(bot: &Bot, message: &Message, ReminderArgs { target, remind
|
||||||
target.format("%c").to_string().escape_telegram_html(),
|
target.format("%c").to_string().escape_telegram_html(),
|
||||||
reminder.clone().escape_telegram_html()
|
reminder.clone().escape_telegram_html()
|
||||||
);
|
);
|
||||||
|
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_message(message.chat.id, text)
|
.send_message(message.chat.id, text)
|
||||||
.parse_mode(ParseMode::Html)
|
.parse_mode(ParseMode::Html)
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare la conferma del promemoria.")?;
|
.context("Non è stato possibile inviare la conferma del promemoria.")?;
|
||||||
|
|
||||||
sleep_chrono(target).await;
|
sleep_chrono(target).await;
|
||||||
|
|
||||||
let text = format!(
|
let text = format!(
|
||||||
"🕒 <b>Promemoria attivato</b>\n\
|
"🕒 <b>Promemoria attivato</b>\n\
|
||||||
<i>{}</i>\n\
|
<i>{}</i>\n\
|
||||||
|
@ -75,13 +75,13 @@ pub async fn handler(bot: &Bot, message: &Message, ReminderArgs { target, remind
|
||||||
target.format("%c").to_string().escape_telegram_html(),
|
target.format("%c").to_string().escape_telegram_html(),
|
||||||
reminder.escape_telegram_html()
|
reminder.escape_telegram_html()
|
||||||
);
|
);
|
||||||
|
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_message(message.chat.id, text)
|
.send_message(message.chat.id, text)
|
||||||
.parse_mode(ParseMode::Html)
|
.parse_mode(ParseMode::Html)
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare il promemoria.")?;
|
.context("Non è stato possibile inviare il promemoria.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,20 +9,20 @@ use teloxide::types::ReplyParameters;
|
||||||
use crate::services::telegram::commands::CommandResult;
|
use crate::services::telegram::commands::CommandResult;
|
||||||
|
|
||||||
pub async fn handler(bot: &Bot, message: &Message, roll: &str) -> CommandResult {
|
pub async fn handler(bot: &Bot, message: &Message, roll: &str) -> CommandResult {
|
||||||
let mut rng = rand::rngs::SmallRng::from_entropy();
|
let mut rng = rand::rngs::SmallRng::from_entropy();
|
||||||
|
|
||||||
if rng.gen_range(1..1001) == 1 {
|
if rng.gen_range(1..1001) == 1 {
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_message(message.chat.id, "🎶 Roll? Rick roll! https://www.youtube.com/watch?v=dQw4w9WgXcQ")
|
.send_message(message.chat.id, "🎶 Roll? Rick roll! https://www.youtube.com/watch?v=dQw4w9WgXcQ")
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare la risposta.")?;
|
.context("Non è stato possibile inviare la risposta.")?;
|
||||||
|
|
||||||
return Ok(())
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let re = Regex::new(r#"(?P<qty>[0-9]*)?d(?P<die>[0-9]+)(?P<modifier>[+-]+[0-9]+)?"#).unwrap();
|
let re = Regex::new(r#"(?P<qty>[0-9]*)?d(?P<die>[0-9]+)(?P<modifier>[+-]+[0-9]+)?"#).unwrap();
|
||||||
|
|
||||||
let captures = re.captures(roll)
|
let captures = re.captures(roll)
|
||||||
.context("Sintassi dei dadi non corretta.")?;
|
.context("Sintassi dei dadi non corretta.")?;
|
||||||
|
|
||||||
|
@ -31,58 +31,58 @@ pub async fn handler(bot: &Bot, message: &Message, roll: &str) -> CommandResult
|
||||||
.map(|m| m.parse::<u32>())
|
.map(|m| m.parse::<u32>())
|
||||||
.unwrap_or(Ok(1))
|
.unwrap_or(Ok(1))
|
||||||
.context("La quantità di dadi da lanciare deve essere un numero intero positivo diverso da 0.")?;
|
.context("La quantità di dadi da lanciare deve essere un numero intero positivo diverso da 0.")?;
|
||||||
|
|
||||||
let die = captures.name("die")
|
let die = captures.name("die")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_str()
|
.as_str()
|
||||||
.parse::<u32>()
|
.parse::<u32>()
|
||||||
.context("La dimensione del dado da lanciare deve essere un numero intero positivo.")?;
|
.context("La dimensione del dado da lanciare deve essere un numero intero positivo.")?;
|
||||||
|
|
||||||
let modifier = captures.name("modifier")
|
let modifier = captures.name("modifier")
|
||||||
.map(|m| m.as_str())
|
.map(|m| m.as_str())
|
||||||
.map(|m| m.parse::<i32>())
|
.map(|m| m.parse::<i32>())
|
||||||
.unwrap_or(Ok(0))
|
.unwrap_or(Ok(0))
|
||||||
.context("Il modificatore dei dadi lanciati deve essere un numero intero.")?;
|
.context("Il modificatore dei dadi lanciati deve essere un numero intero.")?;
|
||||||
|
|
||||||
if die == 0 {
|
if die == 0 {
|
||||||
anyhow::bail!("Non è stato specificato nessun dado.")
|
anyhow::bail!("Non è stato specificato nessun dado.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if qty < 1 {
|
if qty < 1 {
|
||||||
anyhow::bail!("La quantità di dadi specificata deve essere un intero positivo.")
|
anyhow::bail!("La quantità di dadi specificata deve essere un intero positivo.")
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut nums_rolled = Vec::<u32>::new();
|
let mut nums_rolled = Vec::<u32>::new();
|
||||||
for _ in 0..qty {
|
for _ in 0..qty {
|
||||||
nums_rolled.push(
|
nums_rolled.push(
|
||||||
rng.gen_range(1..=die)
|
rng.gen_range(1..=die)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let roll_string = nums_rolled
|
let roll_string = nums_rolled
|
||||||
.iter()
|
.iter()
|
||||||
.map(|n| n.to_string())
|
.map(|n| n.to_string())
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
let mut answer = format!("🎲 [{roll_string}]");
|
|
||||||
|
|
||||||
if modifier != 0 {
|
|
||||||
answer.push_str(&format!("{modifier:+}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
answer.push_str(" = ");
|
|
||||||
|
|
||||||
let sum: u32 = nums_rolled.iter().sum();
|
|
||||||
let sum: i32 = sum as i32 + modifier;
|
|
||||||
|
|
||||||
answer.push_str(&sum.to_string());
|
let mut answer = format!("🎲 [{roll_string}]");
|
||||||
|
|
||||||
|
if modifier != 0 {
|
||||||
|
answer.push_str(&format!("{modifier:+}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
answer.push_str(" = ");
|
||||||
|
|
||||||
|
let sum: u32 = nums_rolled.iter().sum();
|
||||||
|
let sum: i32 = sum as i32 + modifier;
|
||||||
|
|
||||||
|
answer.push_str(&sum.to_string());
|
||||||
|
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_message(message.chat.id, answer)
|
.send_message(message.chat.id, answer)
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare la risposta.")?;
|
.context("Non è stato possibile inviare la risposta.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
|
@ -9,38 +9,38 @@ use super::CommandResult;
|
||||||
pub async fn handler(bot: &Bot, message: &Message) -> CommandResult {
|
pub async fn handler(bot: &Bot, message: &Message) -> CommandResult {
|
||||||
let author = message.from.as_ref()
|
let author = message.from.as_ref()
|
||||||
.context("Non è stato possibile determinare chi ha inviato questo comando.")?;
|
.context("Non è stato possibile determinare chi ha inviato questo comando.")?;
|
||||||
|
|
||||||
let author_username = match author.username.as_ref() {
|
let author_username = match author.username.as_ref() {
|
||||||
None => {
|
None => {
|
||||||
author.first_name.clone()
|
author.first_name.clone()
|
||||||
},
|
}
|
||||||
Some(username) => {
|
Some(username) => {
|
||||||
format!("@{}", &username)
|
format!("@{}", &username)
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let me = bot
|
let me = bot
|
||||||
.get_me()
|
.get_me()
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile recuperare informazioni sul bot.")?;
|
.context("Non è stato possibile recuperare informazioni sul bot.")?;
|
||||||
|
|
||||||
let me_username = me.username.as_ref()
|
let me_username = me.username.as_ref()
|
||||||
.context("Non è stato possibile determinare l'username del bot.")?;
|
.context("Non è stato possibile determinare l'username del bot.")?;
|
||||||
|
|
||||||
let version = crate::utils::version::VERSION;
|
let version = crate::utils::version::VERSION;
|
||||||
|
|
||||||
let text = format!(
|
let text = format!(
|
||||||
"👋 Ciao {author_username}! Sono @{me_username}, il robot tuttofare della RYG!\n\n\
|
"👋 Ciao {author_username}! Sono @{me_username}, il robot tuttofare della RYG!\n\n\
|
||||||
Sto eseguendo la versione {version}.\n\n\
|
Sto eseguendo la versione {version}.\n\n\
|
||||||
Puoi vedere l'elenco delle mie funzionalità dal menu in basso.\n\n\
|
Puoi vedere l'elenco delle mie funzionalità dal menu in basso.\n\n\
|
||||||
Cosa posso fare per te oggi?",
|
Cosa posso fare per te oggi?",
|
||||||
);
|
);
|
||||||
|
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_message(message.chat.id, text)
|
.send_message(message.chat.id, text)
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare la risposta.")?;
|
.context("Non è stato possibile inviare la risposta.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,15 +13,15 @@ use super::CommandResult;
|
||||||
pub async fn handler(bot: &Bot, message: &Message, database: &DatabaseInterface) -> CommandResult {
|
pub async fn handler(bot: &Bot, message: &Message, database: &DatabaseInterface) -> CommandResult {
|
||||||
let author = message.from.as_ref()
|
let author = message.from.as_ref()
|
||||||
.context("Non è stato possibile determinare chi ha inviato questo comando.")?;
|
.context("Non è stato possibile determinare chi ha inviato questo comando.")?;
|
||||||
|
|
||||||
let mut database = database.connect()?;
|
let mut database = database.connect()?;
|
||||||
|
|
||||||
let royalnet_user: RoyalnetUser = {
|
let royalnet_user: RoyalnetUser = {
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::{ExpressionMethods, QueryDsl};
|
use diesel::{ExpressionMethods, QueryDsl};
|
||||||
use crate::interfaces::database::schema::telegram::dsl::*;
|
use crate::interfaces::database::schema::telegram::dsl::*;
|
||||||
use crate::interfaces::database::schema::users::dsl::*;
|
use crate::interfaces::database::schema::users::dsl::*;
|
||||||
|
|
||||||
telegram
|
telegram
|
||||||
.filter(telegram_id.eq::<i64>(
|
.filter(telegram_id.eq::<i64>(
|
||||||
author.id.0.try_into()
|
author.id.0.try_into()
|
||||||
|
@ -32,20 +32,20 @@ pub async fn handler(bot: &Bot, message: &Message, database: &DatabaseInterface)
|
||||||
.get_result(&mut database)
|
.get_result(&mut database)
|
||||||
.context("Non è stato possibile recuperare il tuo utente Telegram dal database RYG.")?
|
.context("Non è stato possibile recuperare il tuo utente Telegram dal database RYG.")?
|
||||||
};
|
};
|
||||||
|
|
||||||
let username = &royalnet_user.username;
|
let username = &royalnet_user.username;
|
||||||
|
|
||||||
let text = format!(
|
let text = format!(
|
||||||
"👤 Nel database RYG, tu hai l'username <code>{}</code>.",
|
"👤 Nel database RYG, tu hai l'username <code>{}</code>.",
|
||||||
username.escape_telegram_html(),
|
username.escape_telegram_html(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let _reply = bot
|
let _reply = bot
|
||||||
.send_message(message.chat.id, text)
|
.send_message(message.chat.id, text)
|
||||||
.parse_mode(ParseMode::Html)
|
.parse_mode(ParseMode::Html)
|
||||||
.reply_parameters(ReplyParameters::new(message.id))
|
.reply_parameters(ReplyParameters::new(message.id))
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile inviare la risposta.")?;
|
.context("Non è stato possibile inviare la risposta.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ impl DatabaseInterface {
|
||||||
pub fn new(database_url: String) -> Self {
|
pub fn new(database_url: String) -> Self {
|
||||||
Self { database_url }
|
Self { database_url }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn connect(&self) -> AnyResult<PgConnection> {
|
pub fn connect(&self) -> AnyResult<PgConnection> {
|
||||||
crate::interfaces::database::connect(&self.database_url)
|
crate::interfaces::database::connect(&self.database_url)
|
||||||
.context("Impossibile connettersi al database RYG.")
|
.context("Impossibile connettersi al database RYG.")
|
||||||
|
|
|
@ -10,13 +10,13 @@ use crate::services::telegram::keyboard_callbacks::KeyboardCallbackResult;
|
||||||
|
|
||||||
pub async fn handler(bot: &Bot, query: CallbackQuery, matchmaking_id: MatchmakingId, callback: MatchmakingTelegramKeyboardCallback, database: &DatabaseInterface) -> KeyboardCallbackResult {
|
pub async fn handler(bot: &Bot, query: CallbackQuery, matchmaking_id: MatchmakingId, callback: MatchmakingTelegramKeyboardCallback, database: &DatabaseInterface) -> KeyboardCallbackResult {
|
||||||
use MatchmakingTelegramKeyboardCallback::*;
|
use MatchmakingTelegramKeyboardCallback::*;
|
||||||
|
|
||||||
let mut database = database.connect()
|
let mut database = database.connect()
|
||||||
.context("Non è stato possibile connettersi al database RYG.")?;
|
.context("Non è stato possibile connettersi al database RYG.")?;
|
||||||
|
|
||||||
let royalnet_user = RoyalnetUser::from_telegram_userid(&mut database, query.from.id)
|
let royalnet_user = RoyalnetUser::from_telegram_userid(&mut database, query.from.id)
|
||||||
.context("Non è stato possibile recuperare il tuo utente Telegram dal database RYG.")?;
|
.context("Non è stato possibile recuperare il tuo utente Telegram dal database RYG.")?;
|
||||||
|
|
||||||
match callback {
|
match callback {
|
||||||
Yes => MatchmakingReply::set(&mut database, matchmaking_id, royalnet_user.id, MatchmakingChoice::Yes)?,
|
Yes => MatchmakingReply::set(&mut database, matchmaking_id, royalnet_user.id, MatchmakingChoice::Yes)?,
|
||||||
Plus5Min => MatchmakingReply::add_late_minutes(&mut database, matchmaking_id, royalnet_user.id, 5)?,
|
Plus5Min => MatchmakingReply::add_late_minutes(&mut database, matchmaking_id, royalnet_user.id, 5)?,
|
||||||
|
@ -27,20 +27,20 @@ pub async fn handler(bot: &Bot, query: CallbackQuery, matchmaking_id: Matchmakin
|
||||||
Cant => MatchmakingReply::set(&mut database, matchmaking_id, royalnet_user.id, MatchmakingChoice::Cant)?,
|
Cant => MatchmakingReply::set(&mut database, matchmaking_id, royalnet_user.id, MatchmakingChoice::Cant)?,
|
||||||
Wont => MatchmakingReply::set(&mut database, matchmaking_id, royalnet_user.id, MatchmakingChoice::Wont)?,
|
Wont => MatchmakingReply::set(&mut database, matchmaking_id, royalnet_user.id, MatchmakingChoice::Wont)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let messages_telegram = MatchmakingMessageTelegram::get_all(&mut database, matchmaking_id)
|
let messages_telegram = MatchmakingMessageTelegram::get_all(&mut database, matchmaking_id)
|
||||||
.context("Non è stato possibile recuperare i messaggi di matchmaking inviati su Telegram.")?;
|
.context("Non è stato possibile recuperare i messaggi di matchmaking inviati su Telegram.")?;
|
||||||
|
|
||||||
for message_telegram in messages_telegram {
|
for message_telegram in messages_telegram {
|
||||||
message_telegram.make_text_and_send_edit(&mut database, bot)
|
message_telegram.make_text_and_send_edit(&mut database, bot)
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile aggiornare un messaggio di matchmaking su Telegram.")?;
|
.context("Non è stato possibile aggiornare un messaggio di matchmaking su Telegram.")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = bot.answer_callback_query(query.id)
|
let _ = bot.answer_callback_query(query.id)
|
||||||
.text("Ricevuto!")
|
.text("Ricevuto!")
|
||||||
.await
|
.await
|
||||||
.context("Non è stato possibile rispondere alla pressione del bottone su Telegram.")?;
|
.context("Non è stato possibile rispondere alla pressione del bottone su Telegram.")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,24 +19,24 @@ pub enum KeyboardCallback {
|
||||||
|
|
||||||
impl FromStr for KeyboardCallback {
|
impl FromStr for KeyboardCallback {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let (keyword, data) = s.split_once(":")
|
let (keyword, data) = s.split_once(":")
|
||||||
.context("Impossibile dividere il payload in keyword e dati.")?;
|
.context("Impossibile dividere il payload in keyword e dati.")?;
|
||||||
|
|
||||||
let (id, data) = data.split_once(":")
|
let (id, data) = data.split_once(":")
|
||||||
.context("Impossibile dividere il payload in id e dati.")?;
|
.context("Impossibile dividere il payload in id e dati.")?;
|
||||||
|
|
||||||
let id: MatchmakingId = id.parse()
|
let id: MatchmakingId = id.parse()
|
||||||
.context("Impossibile convertire l'id a un numero.")?;
|
.context("Impossibile convertire l'id a un numero.")?;
|
||||||
|
|
||||||
match keyword {
|
match keyword {
|
||||||
"matchmaking" => {
|
"matchmaking" => {
|
||||||
data
|
data
|
||||||
.parse()
|
.parse()
|
||||||
.map(|c| Self::Matchmaking(id, c))
|
.map(|c| Self::Matchmaking(id, c))
|
||||||
.context("Impossibile processare i dati.")
|
.context("Impossibile processare i dati.")
|
||||||
},
|
}
|
||||||
x => {
|
x => {
|
||||||
anyhow::bail!("Keyword sconosciuta: {x:?}")
|
anyhow::bail!("Keyword sconosciuta: {x:?}")
|
||||||
}
|
}
|
||||||
|
@ -47,33 +47,33 @@ impl FromStr for KeyboardCallback {
|
||||||
impl KeyboardCallback {
|
impl KeyboardCallback {
|
||||||
pub async fn handle_self(self, bot: Bot, query: CallbackQuery, database: Arc<DatabaseInterface>) -> KeyboardCallbackResult {
|
pub async fn handle_self(self, bot: Bot, query: CallbackQuery, database: Arc<DatabaseInterface>) -> KeyboardCallbackResult {
|
||||||
log::debug!("Handling keyboard callback...");
|
log::debug!("Handling keyboard callback...");
|
||||||
|
|
||||||
log::trace!(
|
log::trace!(
|
||||||
"Handling {:?} in {:?} with {:?}...",
|
"Handling {:?} in {:?} with {:?}...",
|
||||||
self,
|
self,
|
||||||
&query.message.as_ref().map(|q| q.chat().id),
|
&query.message.as_ref().map(|q| q.chat().id),
|
||||||
&query.id,
|
&query.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Matchmaking(matchmaking_id, callback) => {
|
Self::Matchmaking(matchmaking_id, callback) => {
|
||||||
matchmaking::handler(&bot, query, matchmaking_id, callback, &database).await?
|
matchmaking::handler(&bot, query, matchmaking_id, callback, &database).await?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log::trace!("Successfully handled keyboard callback!");
|
log::trace!("Successfully handled keyboard callback!");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_unknown(bot: Bot, query: CallbackQuery) -> KeyboardCallbackResult {
|
pub async fn handle_unknown(bot: Bot, query: CallbackQuery) -> KeyboardCallbackResult {
|
||||||
log::warn!("Received an unknown keyboard callback: {:#?}", &query.data);
|
log::warn!("Received an unknown keyboard callback: {:#?}", &query.data);
|
||||||
|
|
||||||
bot
|
bot
|
||||||
.answer_callback_query(query.id)
|
.answer_callback_query(query.id)
|
||||||
.show_alert(true)
|
.show_alert(true)
|
||||||
.text("⚠️ Il tasto che hai premuto non è più valido.")
|
.text("⚠️ Il tasto che hai premuto non è più valido.")
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
log::trace!("Successfully handled unknown keyboard callback!");
|
log::trace!("Successfully handled unknown keyboard callback!");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,52 +32,52 @@ pub struct TelegramService {
|
||||||
impl TelegramService {
|
impl TelegramService {
|
||||||
pub async fn new(database_url: String, token: String, notification_chat_id: Option<ChatId>) -> AnyResult<Self> {
|
pub async fn new(database_url: String, token: String, notification_chat_id: Option<ChatId>) -> AnyResult<Self> {
|
||||||
log::info!("Initializing a new Telegram service...");
|
log::info!("Initializing a new Telegram service...");
|
||||||
|
|
||||||
let bot = Bot::new(token);
|
let bot = Bot::new(token);
|
||||||
|
|
||||||
log::trace!("Using bot: {bot:#?}");
|
log::trace!("Using bot: {bot:#?}");
|
||||||
|
|
||||||
let me = Self::get_me(&bot)
|
let me = Self::get_me(&bot)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
log::trace!("Using self details: {me:#?}");
|
log::trace!("Using self details: {me:#?}");
|
||||||
|
|
||||||
let service = Self {
|
let service = Self {
|
||||||
database_url,
|
database_url,
|
||||||
bot,
|
bot,
|
||||||
me,
|
me,
|
||||||
notification_chat_id
|
notification_chat_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
log::trace!("Created service: {service:#?}");
|
log::trace!("Created service: {service:#?}");
|
||||||
|
|
||||||
Ok(service)
|
Ok(service)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_me(bot: &Bot) -> AnyResult<Me> {
|
async fn get_me(bot: &Bot) -> AnyResult<Me> {
|
||||||
log::debug!("Getting self details...");
|
log::debug!("Getting self details...");
|
||||||
bot.get_me().await
|
bot.get_me().await
|
||||||
.context("Recupero dettagli sul bot non riuscito.")
|
.context("Recupero dettagli sul bot non riuscito.")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_start_notification(&self) -> AnyResult<Message> {
|
async fn send_start_notification(&self) -> AnyResult<Message> {
|
||||||
log::debug!("Sending start notification...");
|
log::debug!("Sending start notification...");
|
||||||
|
|
||||||
let notification_chat_id = self.notification_chat_id
|
let notification_chat_id = self.notification_chat_id
|
||||||
.context("La chat di notifica non è abilitata.")?;
|
.context("La chat di notifica non è abilitata.")?;
|
||||||
|
|
||||||
let version = crate::utils::version::VERSION
|
let version = crate::utils::version::VERSION
|
||||||
.escape_telegram_html();
|
.escape_telegram_html();
|
||||||
|
|
||||||
let username = self.me.username
|
let username = self.me.username
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.escape_telegram_html();
|
.escape_telegram_html();
|
||||||
|
|
||||||
let id = self.me.user.id
|
let id = self.me.user.id
|
||||||
.to_string()
|
.to_string()
|
||||||
.escape_telegram_html();
|
.escape_telegram_html();
|
||||||
|
|
||||||
let text = format!(
|
let text = format!(
|
||||||
"💠 <b>Servizio Telegram avviato</b>\n\
|
"💠 <b>Servizio Telegram avviato</b>\n\
|
||||||
\n\
|
\n\
|
||||||
|
@ -85,35 +85,35 @@ impl TelegramService {
|
||||||
\n\
|
\n\
|
||||||
@{username} [<code>{id}</code>]"
|
@{username} [<code>{id}</code>]"
|
||||||
);
|
);
|
||||||
|
|
||||||
log::trace!("Sending start notification message...");
|
log::trace!("Sending start notification message...");
|
||||||
let msg = self.bot.send_message(notification_chat_id, text)
|
let msg = self.bot.send_message(notification_chat_id, text)
|
||||||
.parse_mode(ParseMode::Html)
|
.parse_mode(ParseMode::Html)
|
||||||
.await
|
.await
|
||||||
.context("Invio della notifica di avvio non riuscito.")?;
|
.context("Invio della notifica di avvio non riuscito.")?;
|
||||||
|
|
||||||
log::trace!("Successfully sent start notification message!");
|
log::trace!("Successfully sent start notification message!");
|
||||||
Ok(msg)
|
Ok(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_commands(&mut self) -> AnyResult<()> {
|
async fn set_commands(&mut self) -> AnyResult<()> {
|
||||||
log::debug!("Setting self commands...");
|
log::debug!("Setting self commands...");
|
||||||
Command::set_commands(&mut self.bot).await
|
Command::set_commands(&mut self.bot).await
|
||||||
.context("Aggiornamento dei comandi del bot non riuscito.")
|
.context("Aggiornamento dei comandi del bot non riuscito.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatcher(&mut self) -> Dispatcher<Bot, AnyError, DefaultKey> {
|
fn dispatcher(&mut self) -> Dispatcher<Bot, AnyError, DefaultKey> {
|
||||||
log::debug!("Building dispatcher...");
|
log::debug!("Building dispatcher...");
|
||||||
|
|
||||||
let bot_name = self.me.user.username.as_ref().unwrap();
|
let bot_name = self.me.user.username.as_ref().unwrap();
|
||||||
log::trace!("Bot username is: @{bot_name:?}");
|
log::trace!("Bot username is: @{bot_name:?}");
|
||||||
|
|
||||||
log::trace!("Determining pseudo-command regex...");
|
log::trace!("Determining pseudo-command regex...");
|
||||||
let regex = Regex::new(&format!(r"^/[a-z0-9_]+(?:@{bot_name})?(?:\s+.*)?$")).unwrap();
|
let regex = Regex::new(&format!(r"^/[a-z0-9_]+(?:@{bot_name})?(?:\s+.*)?$")).unwrap();
|
||||||
log::trace!("Pseudo-command regex is: {regex:?}");
|
log::trace!("Pseudo-command regex is: {regex:?}");
|
||||||
|
|
||||||
let database = Arc::new(DatabaseInterface::new(self.database_url.clone()));
|
let database = Arc::new(DatabaseInterface::new(self.database_url.clone()));
|
||||||
|
|
||||||
log::trace!("Building dispatcher...");
|
log::trace!("Building dispatcher...");
|
||||||
Dispatcher::builder(
|
Dispatcher::builder(
|
||||||
self.bot.clone(),
|
self.bot.clone(),
|
||||||
|
@ -152,8 +152,8 @@ impl TelegramService {
|
||||||
.endpoint(KeyboardCallback::handle_self)
|
.endpoint(KeyboardCallback::handle_self)
|
||||||
)
|
)
|
||||||
.endpoint(KeyboardCallback::handle_unknown)
|
.endpoint(KeyboardCallback::handle_unknown)
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.dependencies(
|
.dependencies(
|
||||||
dptree::deps![
|
dptree::deps![
|
||||||
database
|
database
|
||||||
|
@ -164,11 +164,11 @@ impl TelegramService {
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn dispatch(&mut self) -> AnyResult<()> {
|
async fn dispatch(&mut self) -> AnyResult<()> {
|
||||||
log::debug!("Starting Telegram dispatcher...");
|
log::debug!("Starting Telegram dispatcher...");
|
||||||
self.dispatcher().dispatch().await;
|
self.dispatcher().dispatch().await;
|
||||||
|
|
||||||
anyhow::bail!("Telegram dispatcher has exited unexpectedly.")
|
anyhow::bail!("Telegram dispatcher has exited unexpectedly.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,13 +176,13 @@ impl TelegramService {
|
||||||
impl RoyalnetService for TelegramService {
|
impl RoyalnetService for TelegramService {
|
||||||
async fn run(&mut self) -> AnyResult<()> {
|
async fn run(&mut self) -> AnyResult<()> {
|
||||||
log::info!("Starting Telegram service...");
|
log::info!("Starting Telegram service...");
|
||||||
|
|
||||||
let _ = self.set_commands()
|
let _ = self.set_commands()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let _ = self.send_start_notification()
|
let _ = self.send_start_notification()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
self.dispatch()
|
self.dispatch()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ impl RoyalnetUser {
|
||||||
pub fn from_telegram_userid(database: &mut PgConnection, user_id: UserId) -> AnyResult<Self> {
|
pub fn from_telegram_userid(database: &mut PgConnection, user_id: UserId) -> AnyResult<Self> {
|
||||||
use crate::interfaces::database::query_prelude::*;
|
use crate::interfaces::database::query_prelude::*;
|
||||||
use schema::{telegram, users};
|
use schema::{telegram, users};
|
||||||
|
|
||||||
log::trace!("Retrieving RoyalnetUser with {user_id:?}");
|
log::trace!("Retrieving RoyalnetUser with {user_id:?}");
|
||||||
|
|
||||||
telegram::table
|
telegram::table
|
||||||
.filter(telegram::telegram_id.eq::<i64>(
|
.filter(telegram::telegram_id.eq::<i64>(
|
||||||
user_id.0.try_into()
|
user_id.0.try_into()
|
||||||
|
|
|
@ -2,8 +2,9 @@ use std::fmt::{Error, Write};
|
||||||
|
|
||||||
pub trait TelegramWrite {
|
pub trait TelegramWrite {
|
||||||
fn write_telegram<T>(&self, f: &mut T) -> Result<(), Error>
|
fn write_telegram<T>(&self, f: &mut T) -> Result<(), Error>
|
||||||
where T: Write;
|
where
|
||||||
|
T: Write;
|
||||||
|
|
||||||
fn to_string_telegram(&self) -> String {
|
fn to_string_telegram(&self) -> String {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
self.write_telegram(&mut result).unwrap();
|
self.write_telegram(&mut result).unwrap();
|
||||||
|
@ -16,7 +17,8 @@ pub trait TelegramEscape {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TelegramEscape for T
|
impl<T> TelegramEscape for T
|
||||||
where String: From<T>
|
where
|
||||||
|
String: From<T>,
|
||||||
{
|
{
|
||||||
fn escape_telegram_html(self) -> String {
|
fn escape_telegram_html(self) -> String {
|
||||||
String::from(self)
|
String::from(self)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
pub fn chrono_to_tokio_duration(duration: chrono::TimeDelta) -> Option<tokio::time::Duration> {
|
pub fn chrono_to_tokio_duration(duration: chrono::TimeDelta) -> Option<tokio::time::Duration> {
|
||||||
let nanos = duration.num_nanoseconds()?;
|
let nanos = duration.num_nanoseconds()?;
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
tokio::time::Duration::from_nanos(nanos as u64)
|
tokio::time::Duration::from_nanos(nanos as u64)
|
||||||
)
|
)
|
||||||
|
@ -8,11 +8,11 @@ pub fn chrono_to_tokio_duration(duration: chrono::TimeDelta) -> Option<tokio::ti
|
||||||
|
|
||||||
pub async fn sleep_chrono(until: &chrono::DateTime<chrono::Local>) {
|
pub async fn sleep_chrono(until: &chrono::DateTime<chrono::Local>) {
|
||||||
let now = chrono::Local::now();
|
let now = chrono::Local::now();
|
||||||
|
|
||||||
let duration = until.signed_duration_since(now);
|
let duration = until.signed_duration_since(now);
|
||||||
|
|
||||||
let duration = chrono_to_tokio_duration(duration)
|
let duration = chrono_to_tokio_duration(duration)
|
||||||
.expect("Nanoseconds to not overflow u64");
|
.expect("Nanoseconds to not overflow u64");
|
||||||
|
|
||||||
tokio::time::sleep(duration).await;
|
tokio::time::sleep(duration).await;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue