mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-22 02:54:21 +00:00
Massive amount of changes
This commit is contained in:
parent
8afbb1c421
commit
f8c77ef264
35 changed files with 1276 additions and 769 deletions
|
@ -5,7 +5,7 @@
|
|||
<envs />
|
||||
<option name="emulateTerminal" value="true" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="requiredFeatures" value="false" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run (debug)" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run --package royalnet --bin royalnet" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<envs>
|
||||
<env name="RUST_LOG" value="royalnet" />
|
||||
</envs>
|
||||
<option name="emulateTerminal" value="true" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -137,9 +137,9 @@ checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.3"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18e2d530f35b40a84124146478cd16f34225306a8441998836466a2e2961c950"
|
||||
checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -1423,7 +1423,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "royalnet"
|
||||
version = "0.3.2"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
@ -1543,9 +1543,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.11.0"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"core-foundation",
|
||||
|
@ -1556,9 +1556,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.11.0"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7"
|
||||
checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
|
@ -1625,15 +1625,6 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
|
@ -1877,9 +1868,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.38.0"
|
||||
version = "1.38.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
||||
checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
|
@ -1887,7 +1878,6 @@ dependencies = [
|
|||
"mio",
|
||||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.48.0",
|
||||
|
|
112
Cargo.toml
112
Cargo.toml
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "royalnet"
|
||||
description = "Fun software suite for the RYG community"
|
||||
version = "0.3.2"
|
||||
version = "0.4.0"
|
||||
edition = "2021"
|
||||
authors = [
|
||||
"Stefano Pigozzi <me@steffo.eu>"
|
||||
|
@ -26,23 +26,101 @@ exclude = [
|
|||
"/.env"
|
||||
]
|
||||
|
||||
#============#
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.86"
|
||||
chrono = "0.4.38"
|
||||
diesel = { version = "2.2.1", features = ["postgres"] }
|
||||
log = { version = "0.4.22", features = ["release_max_level_debug"] }
|
||||
micronfig = "0.3.0"
|
||||
pretty_env_logger = "0.5.0"
|
||||
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||
teloxide = { version = "0.12.2", features = ["ctrlc_handler", "native-tls", "macros"], default-features = false }
|
||||
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread", "time"] }
|
||||
parse_datetime = "0.6.0"
|
||||
regex = "1.10.5"
|
||||
once_cell = "1.19.0"
|
||||
reqwest = { version = "0.12.5", features = ["json"] }
|
||||
serde = { version = "1.0.204", features = ["derive"] }
|
||||
graphql_client = "0.14.0"
|
||||
thiserror = "1.0.62"
|
||||
|
||||
[dependencies.anyhow]
|
||||
version = "1.0.86"
|
||||
|
||||
[dependencies.thiserror]
|
||||
version = "1.0.62"
|
||||
|
||||
[dependencies.tokio]
|
||||
version = "1.38.0"
|
||||
features = ["macros", "rt-multi-thread", "time"]
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4.22"
|
||||
features = ["release_max_level_debug"]
|
||||
|
||||
[dependencies.pretty_env_logger]
|
||||
version = "0.5.0"
|
||||
|
||||
[dependencies.micronfig]
|
||||
version = "0.3.0"
|
||||
|
||||
[dependencies.once_cell]
|
||||
version = "1.19.0"
|
||||
|
||||
[dependencies.regex]
|
||||
version = "1.10.5"
|
||||
|
||||
[dependencies.reqwest]
|
||||
version = "0.12.5"
|
||||
features = ["json"]
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0.204"
|
||||
features = ["derive"]
|
||||
|
||||
[dependencies.diesel]
|
||||
version = "2.2.1"
|
||||
features = ["postgres"]
|
||||
optional = true
|
||||
|
||||
[dependencies.teloxide]
|
||||
version = "0.12.2"
|
||||
default-features = false
|
||||
features = ["native-tls", "macros"]
|
||||
optional = true
|
||||
|
||||
[dependencies.rand]
|
||||
version = "0.8.5"
|
||||
features = ["small_rng"]
|
||||
optional = true
|
||||
|
||||
[dependencies.chrono]
|
||||
version = "0.4.38"
|
||||
optional = true
|
||||
|
||||
[dependencies.parse_datetime]
|
||||
version = "0.6.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.graphql_client]
|
||||
version = "0.14.0"
|
||||
optional = true
|
||||
|
||||
#============#
|
||||
|
||||
[features]
|
||||
default = [
|
||||
"interface_database",
|
||||
"interface_stratz",
|
||||
"service_brooch",
|
||||
"service_telegram",
|
||||
]
|
||||
interface_database = [
|
||||
"diesel"
|
||||
]
|
||||
interface_stratz = [
|
||||
"graphql_client"
|
||||
]
|
||||
service_telegram = [
|
||||
"interface_database",
|
||||
"teloxide",
|
||||
"rand",
|
||||
"chrono",
|
||||
"parse_datetime"
|
||||
]
|
||||
service_brooch = [
|
||||
"interface_database",
|
||||
"interface_stratz",
|
||||
"graphql_client"
|
||||
]
|
||||
|
||||
#============#
|
||||
|
||||
[[bin]]
|
||||
name = "royalnet"
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
use micronfig::config;
|
||||
|
||||
config! {
|
||||
DATABASE_URL,
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
use diesel::{Connection, ConnectionResult, PgConnection};
|
||||
|
||||
mod config;
|
||||
pub mod schema;
|
||||
pub mod models;
|
||||
|
||||
pub fn connect() -> ConnectionResult<PgConnection> {
|
||||
PgConnection::establish(config::DATABASE_URL())
|
||||
}
|
60
src/instance/config.rs
Normal file
60
src/instance/config.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
#![allow(unused_attributes, unused_qualifications, clippy::needless_pub_self)]
|
||||
|
||||
|
||||
#[cfg(feature = "service_telegram")]
|
||||
pub mod service_telegram {
|
||||
use micronfig::config;
|
||||
|
||||
config! {
|
||||
TELEGRAM_DATABASE_URL: String,
|
||||
TELEGRAM_BOT_TOKEN: String,
|
||||
TELEGRAM_NOTIFICATION_CHATID?: String > i64 -> crate::instance::config::ChatIdConversionHack -> teloxide::types::ChatId,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "service_brooch")]
|
||||
pub mod brooch {
|
||||
use micronfig::config;
|
||||
|
||||
#[allow(unused_qualifications)]
|
||||
config! {
|
||||
BROOCH_DATABASE_URL: String,
|
||||
BROOCH_GRAPHQL_URL: String,
|
||||
BROOCH_STRATZ_TOKEN: String,
|
||||
BROOCH_TELEGRAM_BOT_TOKEN: String,
|
||||
BROOCH_WATCHED_GUILD_ID: String > i64,
|
||||
BROOCH_MIN_PLAYERS_TO_PROCESS: String > usize,
|
||||
BROOCH_NOTIFICATION_CHAT_ID: String > i64 -> crate::instance::config::ChatIdConversionHack -> teloxide::types::ChatId,
|
||||
BROOCH_MAX_IMP_WAIT_SECS: String > i64 -> crate::instance::config::TimeDeltaConversionHack => chrono::TimeDelta,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChatIdConversionHack(i64);
|
||||
|
||||
impl From<i64> for ChatIdConversionHack {
|
||||
fn from(value: i64) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ChatIdConversionHack> for teloxide::types::ChatId {
|
||||
fn from(value: ChatIdConversionHack) -> Self {
|
||||
Self(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TimeDeltaConversionHack(i64);
|
||||
|
||||
impl From<i64> for TimeDeltaConversionHack {
|
||||
fn from(value: i64) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<TimeDeltaConversionHack> for chrono::TimeDelta {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: TimeDeltaConversionHack) -> Result<Self, Self::Error> {
|
||||
Self::new(value.0, 0).ok_or(())
|
||||
}
|
||||
}
|
102
src/instance/mod.rs
Normal file
102
src/instance/mod.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
use std::future::Future;
|
||||
use crate::services::RoyalnetService;
|
||||
|
||||
pub(self) mod config;
|
||||
|
||||
pub struct RoyalnetInstance {
|
||||
#[cfg(feature = "service_telegram")]
|
||||
service_telegram: crate::services::telegram::TelegramService,
|
||||
|
||||
#[cfg(feature = "service_brooch")]
|
||||
service_brooch: crate::services::brooch::BroochService,
|
||||
}
|
||||
|
||||
impl RoyalnetInstance {
|
||||
pub async fn new() -> Self {
|
||||
let service_telegram = Self::setup_telegram_service().await;
|
||||
let service_brooch = Self::setup_brooch_service();
|
||||
|
||||
Self {
|
||||
service_telegram,
|
||||
service_brooch,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(mut self) {
|
||||
let future_telegram = async move {
|
||||
Self::get_telegram_future(&mut self.service_telegram).await;
|
||||
};
|
||||
let future_brooch = async move {
|
||||
Self::get_brooch_future(&mut self.service_brooch).await;
|
||||
};
|
||||
|
||||
let task_telegram = tokio::spawn(future_telegram);
|
||||
let task_brooch = tokio::spawn(future_brooch);
|
||||
|
||||
let _ = tokio::join!(
|
||||
task_telegram,
|
||||
task_brooch,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "service_telegram")]
|
||||
async fn setup_telegram_service() -> crate::services::telegram::TelegramService {
|
||||
log::debug!("Setting up Telegram service...");
|
||||
|
||||
crate::services::telegram::TelegramService::new(
|
||||
config::service_telegram::TELEGRAM_DATABASE_URL().clone(),
|
||||
config::service_telegram::TELEGRAM_BOT_TOKEN().clone(),
|
||||
config::service_telegram::TELEGRAM_NOTIFICATION_CHATID().clone(),
|
||||
).await.expect("Unable to setup Telegram service.")
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "service_telegram"))]
|
||||
async fn setup_telegram_service() -> () {
|
||||
log::warn!("Telegram service is disabled.");
|
||||
|
||||
()
|
||||
}
|
||||
|
||||
#[cfg(feature = "service_telegram")]
|
||||
fn get_telegram_future(service: &mut crate::services::telegram::TelegramService) -> impl Future<Output = ()> + '_ {
|
||||
service.run_loop()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "service_telegram"))]
|
||||
fn get_telegram_future(service: &mut crate::services::telegram::TelegramService) -> impl Future<Output = ()> + '_ {
|
||||
async {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "service_brooch")]
|
||||
fn setup_brooch_service() -> crate::services::brooch::BroochService {
|
||||
log::debug!("Setting up Brooch service...");
|
||||
|
||||
crate::services::brooch::BroochService::new(
|
||||
config::brooch::BROOCH_DATABASE_URL().clone(),
|
||||
config::brooch::BROOCH_GRAPHQL_URL(),
|
||||
config::brooch::BROOCH_STRATZ_TOKEN(),
|
||||
config::brooch::BROOCH_WATCHED_GUILD_ID().clone(),
|
||||
config::brooch::BROOCH_MIN_PLAYERS_TO_PROCESS().clone(),
|
||||
config::brooch::BROOCH_TELEGRAM_BOT_TOKEN().clone(),
|
||||
config::brooch::BROOCH_NOTIFICATION_CHAT_ID().clone(),
|
||||
config::brooch::BROOCH_MAX_IMP_WAIT_SECS().clone(),
|
||||
).expect("Unable to setup Brooch service.")
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "service_brooch"))]
|
||||
fn setup_brooch_service() -> () {
|
||||
log::warn!("Brooch service is disabled.");
|
||||
|
||||
()
|
||||
}
|
||||
|
||||
#[cfg(feature = "service_brooch")]
|
||||
fn get_brooch_future(service: &mut crate::services::brooch::BroochService) -> impl Future<Output = ()> + '_ {
|
||||
service.run_loop()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "service_brooch"))]
|
||||
fn get_brooch_future(service: &mut crate::services::brooch::BroochService) -> impl Future<Output = ()> + '_ {
|
||||
async {}
|
||||
}
|
||||
}
|
8
src/interfaces/database/mod.rs
Normal file
8
src/interfaces/database/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use diesel::{Connection, ConnectionResult, PgConnection};
|
||||
|
||||
pub mod schema;
|
||||
pub mod models;
|
||||
|
||||
pub fn connect(database_url: &str) -> ConnectionResult<PgConnection> {
|
||||
PgConnection::establish(database_url)
|
||||
}
|
|
@ -44,6 +44,7 @@ pub struct SteamUser {
|
|||
}
|
||||
|
||||
|
||||
#[cfg(feature = "service_brooch")]
|
||||
#[derive(Debug, Clone, PartialEq, Identifiable, Queryable, Selectable, Insertable)]
|
||||
#[diesel(table_name = brooch_match)]
|
||||
#[diesel(check_for_backend(Pg))]
|
|
@ -1,5 +1,6 @@
|
|||
// @generated automatically by Diesel CLI.
|
||||
|
||||
#[cfg(feature = "service_brooch")]
|
||||
diesel::table! {
|
||||
brooch_match (id) {
|
||||
id -> Int8,
|
5
src/interfaces/mod.rs
Normal file
5
src/interfaces/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
#[cfg(feature = "interface_database")]
|
||||
pub mod database;
|
||||
|
||||
#[cfg(feature = "interface_stratz")]
|
||||
pub mod stratz;
|
2
src/interfaces/stratz/graphql.config.yml
Normal file
2
src/interfaces/stratz/graphql.config.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
schema: "schema.json"
|
||||
documents: "**/*.gql"
|
54
src/interfaces/stratz/guild_matches.rs
Normal file
54
src/interfaces/stratz/guild_matches.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
#![allow(unused_imports)]
|
||||
|
||||
use graphql_client::GraphQLQuery;
|
||||
use reqwest::Url;
|
||||
pub use super::Short;
|
||||
pub use super::Long;
|
||||
pub use super::Byte;
|
||||
pub use super::QueryError as Error;
|
||||
|
||||
#[derive(graphql_client::GraphQLQuery)]
|
||||
#[graphql(
|
||||
schema_path = "src/interfaces/stratz/schema.json",
|
||||
query_path = "src/interfaces/stratz/query_guild_matches.gql",
|
||||
response_derives = "Debug, Clone"
|
||||
)]
|
||||
struct Query;
|
||||
|
||||
pub type QueryResponse = graphql_client::Response<query::ResponseData>;
|
||||
pub type QueryResult = Result<QueryResponse, Error>;
|
||||
|
||||
pub use query::LobbyTypeEnum as LobbyType;
|
||||
pub use query::GameModeEnumType as GameMode;
|
||||
pub use query::MatchLaneType as Lane;
|
||||
pub use query::MatchPlayerRoleType as Role;
|
||||
pub use query::QueryGuild as Guild;
|
||||
pub use query::QueryGuildMatches as Match;
|
||||
pub use query::QueryGuildMatchesPlayers as Player;
|
||||
pub use query::QueryGuildMatchesPlayersHero as Hero;
|
||||
pub use query::QueryGuildMatchesPlayersSteamAccount as Steam;
|
||||
pub use query::QueryGuildMatchesPlayersStatsMatchPlayerBuffEvent as Buff;
|
||||
|
||||
pub async fn query(client: &reqwest::Client, url: Url, guild_id: i64) -> QueryResult {
|
||||
log::debug!("Querying guild_matches of guild {guild_id}...");
|
||||
log::trace!("Using client: {client:?}");
|
||||
log::trace!("Using API at: {url:?}");
|
||||
|
||||
log::trace!("Configuring query variables...");
|
||||
let vars = query::Variables { guild_id };
|
||||
|
||||
log::trace!("Building query...");
|
||||
let body = Query::build_query(vars);
|
||||
|
||||
log::trace!("Making request...");
|
||||
let response = client.post(url)
|
||||
.json(&body)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|_| Error::Requesting)?
|
||||
.json::<QueryResponse>()
|
||||
.await
|
||||
.map_err(|_| Error::Parsing)?;
|
||||
|
||||
Ok(response)
|
||||
}
|
15
src/interfaces/stratz/mod.rs
Normal file
15
src/interfaces/stratz/mod.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use thiserror::Error;
|
||||
|
||||
pub type Short = i16;
|
||||
pub type Long = i64;
|
||||
pub type Byte = u8;
|
||||
|
||||
#[derive(Debug, Clone, Error)]
|
||||
pub enum QueryError {
|
||||
#[error("GraphQL request failed")]
|
||||
Requesting,
|
||||
#[error("GraphQL response parsing failed")]
|
||||
Parsing,
|
||||
}
|
||||
|
||||
pub mod guild_matches;
|
|
@ -1,4 +1,4 @@
|
|||
query GuildMatchesQuery($guild_id: Int!) {
|
||||
query Query($guild_id: Int!) {
|
||||
guild(id: $guild_id) {
|
||||
id
|
||||
matches(take: 10) {
|
||||
|
@ -7,9 +7,9 @@ query GuildMatchesQuery($guild_id: Int!) {
|
|||
gameMode
|
||||
durationSeconds
|
||||
endDateTime
|
||||
didRadiantWin
|
||||
players(steamAccountId: null) {
|
||||
isRadiant
|
||||
isVictory
|
||||
imp
|
||||
kills
|
||||
deaths
|
39
src/main.rs
39
src/main.rs
|
@ -1,41 +1,20 @@
|
|||
use anyhow::Result;
|
||||
use crate::services::RoyalnetService;
|
||||
use crate::instance::RoyalnetInstance;
|
||||
|
||||
pub(crate) mod database;
|
||||
pub(crate) mod utils;
|
||||
mod instance;
|
||||
mod interfaces;
|
||||
mod services;
|
||||
mod stratz;
|
||||
pub(crate) mod utils;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
async fn main() {
|
||||
// Logging setup
|
||||
pretty_env_logger::init();
|
||||
log::debug!("Logging initialized successfully!");
|
||||
|
||||
// Telegram setup
|
||||
log::trace!("Setting up Telegram bot service...");
|
||||
let telegram = services::telegram::BotService::from_config();
|
||||
// Create instance
|
||||
let instance = RoyalnetInstance::new().await;
|
||||
|
||||
// Brooch setup
|
||||
log::trace!("Setting up Brooch service...");
|
||||
let brooch = services::brooch::BroochService::from_config();
|
||||
instance.run().await;
|
||||
|
||||
// Run all services concurrently
|
||||
log::info!("Starting services...");
|
||||
let result = tokio::try_join![
|
||||
telegram.run(),
|
||||
brooch.run(),
|
||||
];
|
||||
|
||||
// This should never happen, but just in case...
|
||||
match result {
|
||||
Err(error) => {
|
||||
log::error!("A service has exited with an error, bailing out: {error:?}");
|
||||
anyhow::bail!("A service has exited with an error.")
|
||||
},
|
||||
_ => {
|
||||
log::error!("All service have exited successfully, bailing out...");
|
||||
anyhow::bail!("All service have exited successfully.")
|
||||
}
|
||||
}
|
||||
log::error!("No services configured.");
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
use micronfig::config;
|
||||
|
||||
config! {
|
||||
BROOCH_TELEGRAM_BOT_TOKEN,
|
||||
BROOCH_WATCHED_GUILD_ID: String > i64 -> crate::stratz::GuildId,
|
||||
BROOCH_NOTIFICATION_CHAT_ID: String > i64 -> crate::utils::hacks::ChatIdConversionHack -> teloxide::types::ChatId
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,40 @@
|
|||
use std::convert::Infallible;
|
||||
use anyhow::Result;
|
||||
|
||||
pub mod telegram;
|
||||
pub mod brooch;
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
use crate::utils::result::AnyResult;
|
||||
|
||||
pub trait RoyalnetService {
|
||||
async fn run(self) -> Result<Infallible>;
|
||||
async fn run(&mut self) -> AnyResult<()>;
|
||||
|
||||
async fn run_loop(&mut self) {
|
||||
let mut backoff = Duration::new(1, 0);
|
||||
|
||||
loop {
|
||||
let result = self.run().await;
|
||||
|
||||
match result {
|
||||
Err(e) => {
|
||||
log::error!("Service exited with error: {e:?}.")
|
||||
},
|
||||
_ => {
|
||||
log::debug!("Service exited successfully!")
|
||||
},
|
||||
}
|
||||
|
||||
let backoff_secs = backoff.as_secs();
|
||||
|
||||
log::debug!("Backing off for {backoff_secs} seconds before restarting...");
|
||||
sleep(backoff).await;
|
||||
|
||||
log::trace!("Doubling backoff value...");
|
||||
backoff *= 2;
|
||||
|
||||
log::trace!("Backoff value is now {backoff_secs} seconds.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "service_telegram")]
|
||||
pub mod telegram;
|
||||
|
||||
#[cfg(feature = "service_brooch")]
|
||||
pub mod brooch;
|
||||
|
|
|
@ -7,6 +7,7 @@ use teloxide::payloads::SendMessageSetters;
|
|||
use teloxide::requests::Requester;
|
||||
use teloxide::types::{ChatId, Message, MessageId};
|
||||
use teloxide::utils::command::BotCommands;
|
||||
use crate::services::telegram::deps::interface_database::DatabaseInterface;
|
||||
|
||||
mod start;
|
||||
mod fortune;
|
||||
|
@ -58,7 +59,7 @@ impl Command {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle(self, bot: Bot, message: Message) -> CommandResult {
|
||||
pub async fn handle(self, bot: Bot, message: Message, database: &DatabaseInterface) -> CommandResult {
|
||||
log::trace!("Handling command: {self:?}");
|
||||
|
||||
let result = match self {
|
||||
|
@ -69,7 +70,7 @@ impl Command {
|
|||
},
|
||||
Command::Fortune => fortune::handler(&bot, &message).await,
|
||||
Command::Echo(text) => echo::handler(&bot, &message, &text).await,
|
||||
Command::WhoAmI => whoami::handler(&bot, &message).await,
|
||||
Command::WhoAmI => whoami::handler(&bot, &message, &database).await,
|
||||
Command::Answer(_) => answer::handler(&bot, &message).await,
|
||||
Command::Reminder(args) => reminder::handler(&bot, &message, args).await,
|
||||
Command::Dog => dog::handler(&bot, &message).await,
|
||||
|
|
|
@ -7,7 +7,7 @@ use teloxide::types::{Message, ParseMode};
|
|||
use parse_datetime::parse_datetime_at_date;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use crate::services::telegram::escape::EscapableInTelegramHTML;
|
||||
use crate::utils::escape::EscapableInTelegramHTML;
|
||||
use super::{CommandResult};
|
||||
|
||||
|
||||
|
|
|
@ -7,39 +7,43 @@ use crate::services::telegram::commands::{CommandResult};
|
|||
use regex::Regex;
|
||||
|
||||
|
||||
|
||||
pub async fn handler(bot: &Bot, message: &Message, roll: &str) -> CommandResult {
|
||||
let mut rng = rand::rngs::SmallRng::from_entropy();
|
||||
|
||||
if rng.gen_range(1..1001) == 1 {
|
||||
let _reply = bot
|
||||
.send_message(message.chat.id, "🎶 Roll? Rick roll! https://www.youtube.com/watch?v=dQw4w9WgXcQ")
|
||||
.reply_to_message_id(message.id)
|
||||
.await
|
||||
.context("Non è stato possibile inviare la risposta.")?;
|
||||
.send_message(message.chat.id, "🎶 Roll? Rick roll! https://www.youtube.com/watch?v=dQw4w9WgXcQ")
|
||||
.reply_to_message_id(message.id)
|
||||
.await
|
||||
.context("Non è stato possibile inviare la risposta.")?;
|
||||
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let re = Regex::new(r#"(?P<qty>[0-9]*)?d(?P<die>[0-9]+)(?P<modifier>[+-]?[0-9]*)?"#).unwrap();
|
||||
let qty = captures.name("qty") // Prova a vedere se c'è il gruppo "qty"
|
||||
.map(|m| m.as_str()) // `map`: se c'è, trasforma il suo contenuto in stringa
|
||||
.map(|m| m.parse::<u32>()) // `map`: se c'è, trasforma la stringa in un u32
|
||||
.map(|m| m.context("La quantità di dadi da lanciare deve essere un numero intero positivo diverso da 0.")?) // `map`: se c'è, ma il parsing ha dato errore, restituiscilo e fai terminare la funzione qui
|
||||
.unwrap_or(1); // `unwrap_or`: se c'è, restituisci il valore, altrimenti, defaulta a 1
|
||||
|
||||
let die = captures.name("die") // Prova a vedere se c'è il gruppo "die"
|
||||
.unwrap() // `unwrap`: possiamo asserire che il gruppo "die" sia sempre presente se la regex ha matchato
|
||||
.as_str() // trasforma il suo contenuto in stringa
|
||||
.parse::<u32>() // trasforma la stringa in un u32
|
||||
.context("La dimensione del dado da lanciare deve essere un numero intero positivo.")?; // se il parsing ha dato errore, restituiscilo e fai terminare la funzione qui
|
||||
let captures = re.captures(roll)
|
||||
.context("Sintassi dei dadi non corretta.")?;
|
||||
|
||||
let qty = captures.name("qty")
|
||||
.map(|m| m.as_str())
|
||||
.map(|m| m.parse::<u32>())
|
||||
.unwrap_or(Ok(1))
|
||||
.context("La quantità di dadi da lanciare deve essere un numero intero positivo diverso da 0.")?;
|
||||
|
||||
let modifier = captures.name("modifier") // Prova a vedere se c'è il gruppo "modifier"
|
||||
.map(|m| m.as_str()) // `map`: se c'è, trasforma il suo contenuto in stringa
|
||||
.map(|m| m.parse::<i32>()) // `map`: se c'è, trasforma la stringa in un i32
|
||||
.map(|m| m.context("Il modificatore dei dadi lanciati deve essere un numero intero.")?) // `map`: se c'è, ma il parsing ha dato errore, restituiscilo e fai terminare la funzione qui
|
||||
.unwrap_or(0); // `unwrap_or`: se c'è, restituisci il valore, altrimenti, defaulta a 0
|
||||
let die = captures.name("die")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.parse::<u32>()
|
||||
.context("La dimensione del dado da lanciare deve essere un numero intero positivo.")?;
|
||||
|
||||
if die <= 0 {
|
||||
let modifier = captures.name("modifier")
|
||||
.map(|m| m.as_str())
|
||||
.map(|m| m.parse::<i32>())
|
||||
.unwrap_or(Ok(0))
|
||||
.context("Il modificatore dei dadi lanciati deve essere un numero intero.")?;
|
||||
|
||||
if die == 0 {
|
||||
anyhow::bail!("Non è stato specificato nessun dado.")
|
||||
}
|
||||
|
||||
|
@ -47,37 +51,32 @@ let qty = captures.name("qty") // Prova a vedere se c'è il gruppo "qty"
|
|||
anyhow::bail!("La quantità di dadi specificata deve essere un intero positivo.")
|
||||
}
|
||||
|
||||
|
||||
|
||||
let mut nums_rolled = Vec::<i32>::new();
|
||||
let mut nums_rolled = Vec::<u32>::new();
|
||||
for _ in 0..qty {
|
||||
nums_rolled.push(rng.gen_range(1..die+1));
|
||||
nums_rolled.push(
|
||||
rng.gen_range(1..=die)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
let mut answer = String::from("🎲 [");
|
||||
for i in 0..qty {
|
||||
if i > 0 { answer.push_str("+")}
|
||||
answer.push_str( &nums_rolled[i].to_string() );
|
||||
}
|
||||
answer.push_str("] ");
|
||||
|
||||
|
||||
let roll_string = nums_rolled
|
||||
.iter()
|
||||
.map(|n| n.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
let mut answer = format!("🎲 [{roll_string}]");
|
||||
|
||||
if modifier != 0 {
|
||||
if modifier > 0 {
|
||||
answer.push_str("+");
|
||||
}
|
||||
answer.push_str( &modifier.to_string() );
|
||||
answer.push_str(&format!("{modifier:+}"))
|
||||
}
|
||||
|
||||
answer.push_str(" = ");
|
||||
|
||||
let mut sum: i32 = nums_rolled.iter().sum();
|
||||
sum = sum + modifier;
|
||||
|
||||
|
||||
answer.push_str( &sum.to_string() );
|
||||
|
||||
|
||||
let sum: u32 = nums_rolled.iter().sum();
|
||||
let sum: i32 = sum as i32 + modifier;
|
||||
|
||||
answer.push_str(&sum.to_string());
|
||||
|
||||
let _reply = bot
|
||||
.send_message(message.chat.id, answer)
|
||||
.reply_to_message_id(message.id)
|
||||
|
|
|
@ -3,23 +3,23 @@ use teloxide::Bot;
|
|||
use teloxide::payloads::SendMessageSetters;
|
||||
use teloxide::requests::Requester;
|
||||
use teloxide::types::{Message, ParseMode};
|
||||
use crate::database::models::{RoyalnetUser};
|
||||
use crate::services::telegram::escape::EscapableInTelegramHTML;
|
||||
use crate::interfaces::database::models::{RoyalnetUser};
|
||||
use crate::services::telegram::deps::interface_database::DatabaseInterface;
|
||||
use crate::utils::escape::EscapableInTelegramHTML;
|
||||
use super::{CommandResult};
|
||||
|
||||
pub async fn handler(bot: &Bot, message: &Message) -> CommandResult {
|
||||
pub async fn handler(bot: &Bot, message: &Message, database: &DatabaseInterface) -> CommandResult {
|
||||
let author = message.from()
|
||||
.context("Non è stato possibile determinare chi ha inviato questo comando.")?;
|
||||
|
||||
let mut database = crate::database::connect().
|
||||
context("Non è stato possibile connettersi al database RYG.")?;
|
||||
let mut database = database.connect()?;
|
||||
|
||||
let royalnet_user: RoyalnetUser = {
|
||||
use diesel::prelude::*;
|
||||
use diesel::{ExpressionMethods, QueryDsl};
|
||||
use crate::database::schema::telegram::dsl::*;
|
||||
use crate::database::schema::users::dsl::*;
|
||||
use crate::database::models::RoyalnetUser;
|
||||
use crate::interfaces::database::schema::telegram::dsl::*;
|
||||
use crate::interfaces::database::schema::users::dsl::*;
|
||||
use crate::interfaces::database::models::RoyalnetUser;
|
||||
|
||||
telegram
|
||||
.filter(telegram_id.eq::<i64>(
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
use micronfig::config;
|
||||
|
||||
// Everything ok, RustRover?
|
||||
config! {
|
||||
TELEGRAM_BOT_TOKEN,
|
||||
TELEGRAM_NOTIFICATION_CHATID?: String > i64 -> crate::utils::hacks::ChatIdConversionHack -> teloxide::types::ChatId,
|
||||
}
|
19
src/services/telegram/deps/interface_database.rs
Normal file
19
src/services/telegram/deps/interface_database.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use anyhow::Context;
|
||||
use diesel::PgConnection;
|
||||
use crate::utils::result::AnyResult;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DatabaseInterface {
|
||||
database_url: String,
|
||||
}
|
||||
|
||||
impl DatabaseInterface {
|
||||
pub fn new(database_url: String) -> Self {
|
||||
Self { database_url }
|
||||
}
|
||||
|
||||
pub fn connect(&self) -> AnyResult<PgConnection> {
|
||||
crate::interfaces::database::connect(&self.database_url)
|
||||
.context("Impossibile connettersi al database RYG")
|
||||
}
|
||||
}
|
2
src/services/telegram/deps/mod.rs
Normal file
2
src/services/telegram/deps/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
#[cfg(feature = "interface_database")]
|
||||
pub mod interface_database;
|
|
@ -1,113 +1,164 @@
|
|||
use std::convert::Infallible;
|
||||
use teloxide::{Bot, dptree};
|
||||
use anyhow::{Context, Error, Result};
|
||||
use anyhow::Context;
|
||||
use teloxide::prelude::*;
|
||||
use teloxide::types::{Me, ParseMode};
|
||||
use regex::Regex;
|
||||
use teloxide::dispatching::{DefaultKey, Dispatcher, HandlerExt, UpdateFilterExt};
|
||||
use teloxide::dispatching::DefaultKey;
|
||||
use teloxide::dptree::entry;
|
||||
use teloxide::payloads::SendMessageSetters;
|
||||
use teloxide::requests::Requester;
|
||||
use teloxide::types::{Me, Message, ParseMode, Update};
|
||||
use crate::services::telegram::escape::EscapableInTelegramHTML;
|
||||
use crate::services::telegram::commands::Command;
|
||||
use crate::services::telegram::deps::interface_database::DatabaseInterface;
|
||||
use crate::utils::result::{AnyError, AnyResult};
|
||||
use crate::utils::escape::EscapableInTelegramHTML;
|
||||
use super::RoyalnetService;
|
||||
|
||||
#[allow(clippy::needless_pub_self)]
|
||||
pub(self) mod config;
|
||||
mod commands;
|
||||
pub(self) mod escape;
|
||||
|
||||
pub struct BotService {
|
||||
pub bot: Bot
|
||||
pub(self) mod deps;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TelegramService {
|
||||
database_url: String,
|
||||
bot: Bot,
|
||||
me: Me,
|
||||
notification_chat_id: Option<ChatId>,
|
||||
}
|
||||
|
||||
impl BotService {
|
||||
pub fn from_config() -> Self {
|
||||
Self {
|
||||
bot: Bot::new(config::TELEGRAM_BOT_TOKEN())
|
||||
}
|
||||
impl TelegramService {
|
||||
pub async fn new(database_url: String, token: String, notification_chat_id: Option<ChatId>) -> AnyResult<Self> {
|
||||
log::info!("Initializing a new Telegram service...");
|
||||
|
||||
let bot = Bot::new(token);
|
||||
|
||||
log::trace!("Using bot: {bot:#?}");
|
||||
|
||||
let me = Self::get_me(&bot)
|
||||
.await?;
|
||||
|
||||
log::trace!("Using self details: {me:#?}");
|
||||
|
||||
let service = Self {
|
||||
database_url,
|
||||
bot,
|
||||
me,
|
||||
notification_chat_id
|
||||
};
|
||||
|
||||
log::trace!("Created service: {service:#?}");
|
||||
|
||||
Ok(service)
|
||||
}
|
||||
|
||||
async fn send_start_notification(&mut self, me: &Me) -> Result<()> {
|
||||
let chat_id = config::TELEGRAM_NOTIFICATION_CHATID()
|
||||
.context("Variabile d'ambiente TELEGRAM_NOTIFICATION_CHATID mancante.")?;
|
||||
async fn get_me(bot: &Bot) -> AnyResult<Me> {
|
||||
log::debug!("Getting self details...");
|
||||
bot.get_me().await
|
||||
.context("Recupero dettagli sul bot non riuscito.")
|
||||
}
|
||||
|
||||
let version = crate::utils::version::VERSION;
|
||||
let username = &me.username.as_ref().unwrap();
|
||||
let id = &me.user.id;
|
||||
async fn send_start_notification(&self) -> AnyResult<Message> {
|
||||
log::debug!("Sending start notification...");
|
||||
|
||||
let notification_chat_id = self.notification_chat_id
|
||||
.context("La chat di notifica non è abilitata.")?;
|
||||
|
||||
let version = crate::utils::version::VERSION
|
||||
.escape_telegram_html();
|
||||
|
||||
let username = self.me.username
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.escape_telegram_html();
|
||||
|
||||
let id = self.me.user.id
|
||||
.to_string()
|
||||
.escape_telegram_html();
|
||||
|
||||
let text = format!(
|
||||
"💠 <b>Servizio Telegram avviato</b>\n\
|
||||
\n\
|
||||
Royalnet <a href='https://github.com/RYGhub/royalnet/releases/tag/v{}'>v{}</a>\n\
|
||||
\n\
|
||||
@{} [<code>{}</code>]",
|
||||
version.escape_telegram_html(),
|
||||
version.escape_telegram_html(),
|
||||
username.escape_telegram_html(),
|
||||
id.to_string().escape_telegram_html(),
|
||||
\n\
|
||||
Royalnet <a href='https://github.com/RYGhub/royalnet/releases/tag/v{version}'>v{version}</a>\n\
|
||||
\n\
|
||||
@{username} [<code>{id}</code>]"
|
||||
);
|
||||
|
||||
self.bot.send_message(chat_id, text)
|
||||
log::trace!("Sending start notification message...");
|
||||
let msg = self.bot.send_message(notification_chat_id, text)
|
||||
.parse_mode(ParseMode::Html)
|
||||
.await
|
||||
.context("Invio della notifica di avvio non riuscito.")?;
|
||||
|
||||
Ok(())
|
||||
log::trace!("Successfully sent start notification message!");
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
async fn set_commands(&mut self) -> AnyResult<()> {
|
||||
log::debug!("Setting self commands...");
|
||||
Command::set_commands(&mut self.bot).await
|
||||
.context("Aggiornamento dei comandi del bot non riuscito.")
|
||||
}
|
||||
|
||||
fn dispatcher(&mut self) -> Dispatcher<Bot, AnyError, DefaultKey> {
|
||||
log::debug!("Building dispatcher...");
|
||||
|
||||
let bot_name = self.me.user.username.as_ref().unwrap();
|
||||
log::trace!("Bot username is: @{bot_name:?}");
|
||||
|
||||
log::trace!("Determining pseudo-command regex...");
|
||||
let regex = Regex::new(&format!(r"^/[a-z0-9_]+(?:@{bot_name})?(?:\s+.*)?$")).unwrap();
|
||||
log::trace!("Pseudo-command regex is: {regex:?}");
|
||||
|
||||
let database = DatabaseInterface::new(self.database_url.clone());
|
||||
|
||||
log::trace!("Building dispatcher...");
|
||||
Dispatcher::builder(
|
||||
self.bot.clone(),
|
||||
// Only process message updates
|
||||
Update::filter_message()
|
||||
// Pseudo-commands
|
||||
.branch(entry()
|
||||
// Only process commands matching the pseudo-command regex
|
||||
.filter(move |message: Message| -> bool {
|
||||
message
|
||||
.text()
|
||||
.is_some_and(|text| regex.is_match(text))
|
||||
})
|
||||
// Commands
|
||||
.branch(
|
||||
entry()
|
||||
// Only process commands matching a valid command, and parse their arguments
|
||||
.filter_command::<Command>()
|
||||
// Delegate handling
|
||||
.endpoint(Command::handle)
|
||||
)
|
||||
// No valid command was found
|
||||
.endpoint(commands::unknown_command)
|
||||
)
|
||||
)
|
||||
.dependencies(
|
||||
dptree::deps![
|
||||
database
|
||||
]
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
async fn dispatch(&mut self) -> AnyResult<()> {
|
||||
log::debug!("Starting Telegram dispatcher...");
|
||||
self.dispatcher().dispatch().await;
|
||||
|
||||
anyhow::bail!("Telegram dispatcher has exited unexpectedly.")
|
||||
}
|
||||
}
|
||||
|
||||
impl RoyalnetService for BotService {
|
||||
async fn run(mut self) -> Result<Infallible> {
|
||||
impl RoyalnetService for TelegramService {
|
||||
async fn run(&mut self) -> AnyResult<()> {
|
||||
log::info!("Starting Telegram service...");
|
||||
|
||||
log::debug!("Getting bot information...");
|
||||
let me = self.bot.get_me().await
|
||||
.context("Failed to get information about self")?;
|
||||
let _ = self.set_commands()
|
||||
.await;
|
||||
|
||||
log::debug!("Setting bot commands...");
|
||||
match commands::Command::set_commands(&mut self.bot).await {
|
||||
Err(e) => log::warn!("Failed to set bot commands: {e}"),
|
||||
_ => log::trace!("Bot commands set successfully!"),
|
||||
}
|
||||
let _ = self.send_start_notification()
|
||||
.await;
|
||||
|
||||
log::debug!("Sending start notification...");
|
||||
match self.send_start_notification(&me).await {
|
||||
Err(e) => log::warn!("Failed to send start notification: {e}"),
|
||||
_ => log::trace!("Start notification sent successfully!"),
|
||||
}
|
||||
|
||||
log::debug!("Starting Telegram dispatcher...");
|
||||
dispatcher(self.bot, me).dispatch().await;
|
||||
|
||||
log::error!("Telegram dispatcher has exited, bailing out...");
|
||||
anyhow::bail!("Telegram dispatcher has exited.")
|
||||
self.dispatch()
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatcher(bot: Bot, me: Me) -> Dispatcher<Bot, Error, DefaultKey> {
|
||||
let bot_name = me.user.username.unwrap();
|
||||
log::trace!("Bot name is: {bot_name:?}");
|
||||
|
||||
let regex = Regex::new(&format!(r"^/[a-z0-9_]+(?:@{bot_name})?(?:\s+.*)?$")).unwrap();
|
||||
log::trace!("Pseudo-command regex is: {regex:?}");
|
||||
|
||||
log::trace!("Building dispatcher...");
|
||||
Dispatcher::builder(
|
||||
bot,
|
||||
Update::filter_message()
|
||||
.branch(entry()
|
||||
.filter(move |message: Message| -> bool {
|
||||
message.text().is_some_and(|text| regex.is_match(text))
|
||||
})
|
||||
.branch(
|
||||
entry()
|
||||
.filter_command::<commands::Command>()
|
||||
.endpoint(commands::Command::handle)
|
||||
)
|
||||
.endpoint(commands::unknown_command)
|
||||
)
|
||||
)
|
||||
.dependencies(
|
||||
dptree::deps![] // No deps needed at the moment.
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
use micronfig::config;
|
||||
|
||||
config! {
|
||||
STRATZ_TOKEN: String,
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
use graphql_client::GraphQLQuery;
|
||||
use reqwest::Client;
|
||||
use thiserror::Error;
|
||||
|
||||
pub(self) mod config;
|
||||
|
||||
const STRATZ_GRAPHQL_API_URL: &str = "https://api.stratz.com/graphql";
|
||||
|
||||
// Bind these weird types used in the STRATZ API
|
||||
type Short = i16;
|
||||
type Long = i64;
|
||||
type Byte = u8;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct GuildId(pub i64);
|
||||
|
||||
impl From<i64> for GuildId {
|
||||
fn from(value: i64) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(GraphQLQuery)]
|
||||
#[graphql(schema_path="src/stratz/schema.json", query_path="src/stratz/query_guild_matches.gql", response_derives="Debug, Clone")]
|
||||
pub struct GuildMatchesQuery;
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Error)]
|
||||
pub enum QueryError {
|
||||
#[error("GraphQL request failed")]
|
||||
Requesting,
|
||||
#[error("GraphQL response parsing failed")]
|
||||
Parsing,
|
||||
}
|
||||
|
||||
type GuildMatchesQueryResponse = graphql_client::Response<guild_matches_query::ResponseData>;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub use guild_matches_query::LobbyTypeEnum as LobbyType;
|
||||
#[allow(unused_imports)]
|
||||
pub use guild_matches_query::GameModeEnumType as GameMode;
|
||||
#[allow(unused_imports)]
|
||||
pub use guild_matches_query::GuildMatchesQueryGuild as Guild;
|
||||
#[allow(unused_imports)]
|
||||
pub use guild_matches_query::GuildMatchesQueryGuildMatches as Match;
|
||||
#[allow(unused_imports)]
|
||||
pub use guild_matches_query::GuildMatchesQueryGuildMatchesPlayers as Player;
|
||||
#[allow(unused_imports)]
|
||||
pub use guild_matches_query::GuildMatchesQueryGuildMatchesPlayersHero as Hero;
|
||||
#[allow(unused_imports)]
|
||||
pub use guild_matches_query::GuildMatchesQueryGuildMatchesPlayersSteamAccount as Steam;
|
||||
#[allow(unused_imports)]
|
||||
pub use guild_matches_query::MatchPlayerRoleType as Role;
|
||||
#[allow(unused_imports)]
|
||||
pub use guild_matches_query::MatchLaneType as Lane;
|
||||
#[allow(unused_imports)]
|
||||
pub use guild_matches_query::GuildMatchesQueryGuildMatchesPlayersStatsMatchPlayerBuffEvent as Buff;
|
||||
|
||||
|
||||
/// Get the latest 10 matches of a certain Dota 2 guild.
|
||||
pub async fn query_guild_matches(client: &Client, guild_id: &GuildId) -> Result<GuildMatchesQueryResponse, QueryError> {
|
||||
log::debug!("Querying guild matches with {client:?} for {guild_id:?}...");
|
||||
|
||||
log::trace!("Configuring query variables...");
|
||||
let params = guild_matches_query::Variables {
|
||||
guild_id: guild_id.0,
|
||||
};
|
||||
|
||||
log::trace!("Building query...");
|
||||
let body = GuildMatchesQuery::build_query(params);
|
||||
|
||||
log::trace!("Building API URL...");
|
||||
let url = format!("{}?jwt={}", STRATZ_GRAPHQL_API_URL, config::STRATZ_TOKEN());
|
||||
log::trace!("STRATZ API URL is: {url:?}");
|
||||
|
||||
log::trace!("Making request...");
|
||||
let response = client.post(url)
|
||||
.json(&body)
|
||||
.send().await
|
||||
.map_err(|_| QueryError::Requesting)?
|
||||
.json::<GuildMatchesQueryResponse>().await
|
||||
.map_err(|_| QueryError::Parsing)?;
|
||||
|
||||
log::trace!("Request successful!");
|
||||
Ok(response)
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
use teloxide::types::ChatId;
|
||||
|
||||
pub struct ChatIdConversionHack(i64);
|
||||
|
||||
impl From<i64> for ChatIdConversionHack {
|
||||
fn from(value: i64) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ChatIdConversionHack> for ChatId {
|
||||
fn from(value: ChatIdConversionHack) -> Self {
|
||||
Self(value.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ChatIdConversionHack> for i64 {
|
||||
fn from(value: ChatIdConversionHack) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ChatId> for ChatIdConversionHack {
|
||||
fn from(value: ChatId) -> Self {
|
||||
Self(value.0)
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod time;
|
||||
pub mod version;
|
||||
pub mod hacks;
|
||||
pub mod result;
|
||||
pub mod escape;
|
||||
|
|
2
src/utils/result.rs
Normal file
2
src/utils/result.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub use anyhow::Result as AnyResult;
|
||||
pub use anyhow::Error as AnyError;
|
Loading…
Reference in a new issue