Skip to content

Commit

Permalink
Feat/watchtower (#21)
Browse files Browse the repository at this point in the history
* feat: move from discord to watchtower

* fix: remove unused discord config part

* fix: disable warning

* fix: remove discord.rs

---------

Co-authored-by: Iris <[email protected]>
  • Loading branch information
Th0rgal and irisdv authored Sep 20, 2023
1 parent cf633f3 commit fc57a69
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 171 deletions.
2 changes: 2 additions & 0 deletions bot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ serde_json = "1.0"
lazy_static = "1.4.0"
reqwest = "0.11.18"
num-integer = "0.1.45"
serde_derive = "1.0.183"
env_logger = "0.10.0"

43 changes: 17 additions & 26 deletions bot/src/bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use starknet::{
};
use url::Url;

use crate::discord::log_msg_and_send_to_discord;
use crate::logger::Logger;
use crate::models::{
AggregateResult, AggregateResults, DomainAggregateResult, MetadataDoc, Unzip5,
};
Expand Down Expand Up @@ -68,6 +68,7 @@ fn to_uint256(n: BigInt) -> (FieldElement, FieldElement) {
pub async fn get_domains_ready_for_renewal(
config: &Config,
state: &Arc<AppState>,
logger: &Logger,
) -> Result<AggregateResults> {
let domains = state.db.collection::<Domain>("domains");
let min_expiry_date = Utc::now() + Duration::days(30);
Expand Down Expand Up @@ -136,7 +137,7 @@ pub async fn get_domains_ready_for_renewal(
// Then process the results
let futures: Vec<_> = results
.into_iter()
.map(|result| process_aggregate_result(state, result, config))
.map(|result| process_aggregate_result(state, result, config, logger))
.collect();

let processed_results: Vec<_> = futures::future::try_join_all(futures)
Expand Down Expand Up @@ -176,6 +177,7 @@ async fn process_aggregate_result(
state: &Arc<AppState>,
result: DomainAggregateResult,
config: &Config,
logger: &Logger,
) -> Result<Option<AggregateResult>> {
// Skip the rest if auto-renewal is not enabled
if !result.auto_renewal_enabled {
Expand Down Expand Up @@ -244,15 +246,10 @@ async fn process_aggregate_result(
meta_hash: FieldElement::from_str("0")?,
}))
} else {
log_msg_and_send_to_discord(
&config,
"[Renewal]",
&format!(
"Domain {} cannot be renewed because {} has not enough allowance",
result.domain, result.renewer_address
),
)
.await;
logger.warning(format!(
"Domain {} cannot be renewed because {} has not enough allowance",
result.domain, result.renewer_address
));
Ok(None)
}
}
Expand All @@ -268,6 +265,7 @@ pub async fn renew_domains(
config: &Config,
account: &SingleOwnerAccount<SequencerGatewayProvider, LocalWallet>,
mut aggregate_results: AggregateResults,
logger: &Logger,
) -> Result<()> {
// If we have more than 400 domains to renew we make multiple transactions to avoid hitting the 2M steps limit
while !aggregate_results.domains.is_empty()
Expand Down Expand Up @@ -298,23 +296,16 @@ pub async fn renew_domains(
.await
{
Ok(_) => {
log_msg_and_send_to_discord(
&config,
"[bot][renew_domains]",
&format!("Successfully renewed domains: {:?}", domains_to_renew),
)
.await
logger.info(format!(
"Sent a tx to renew {} domains",
domains_to_renew.len()
));
}
Err(e) => {
log_msg_and_send_to_discord(
&config,
"[bot][renew_domains]",
&format!(
"Error while renewing domains: {:?} for domains: {:?}",
e, domains_to_renew
),
)
.await;
logger.severe(format!(
"Error while renewing domains: {:?} for domains: {:?}",
e, domains_to_renew
));
return Err(e);
}
}
Expand Down
20 changes: 14 additions & 6 deletions bot/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,6 @@ pub_struct!(Clone, Deserialize; MyAccount {
address: FieldElement,
});

pub_struct!(Clone, Deserialize; Discord {
token: String,
channel_id: u64,
});

pub_struct!(Clone, Deserialize; Renewals {
delay: u64,
});
Expand All @@ -60,15 +55,28 @@ pub_struct!(Clone, Deserialize; IndexerServer {
server_url: String,
});

pub_struct!(Clone, Deserialize; Watchtower {
endpoint: String,
app_id: String,
token: String,
types: WatchtowerTypes,
});

pub_struct!(Clone, Deserialize; WatchtowerTypes {
info: String,
warning: String,
severe: String,
});

pub_struct!(Clone, Deserialize; Config {
apibara: Apibara,
contract: Contract,
database: Database,
devnet_provider: DevnetProvider,
account: MyAccount,
discord: Discord,
renewals : Renewals,
indexer_server: IndexerServer,
watchtower: Watchtower,
});

pub fn load() -> Config {
Expand Down
61 changes: 0 additions & 61 deletions bot/src/discord.rs

This file was deleted.

148 changes: 148 additions & 0 deletions bot/src/logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
use chrono::Utc;
use reqwest;
use serde_derive::Serialize;
use std::borrow::Cow;
use std::sync::Arc;

use crate::config::Watchtower;

// Logger structure
pub struct Logger {
config: Arc<Watchtower>,
client: Arc<reqwest::Client>,
}

// Enum for log types
#[derive(Clone)]
pub enum LogType {
Info,
Warning,
Severe,
}

#[derive(Serialize)]
struct LogData<'a> {
token: &'a str,
log: LogPayload<'a>,
}

#[derive(Serialize)]
struct LogPayload<'a> {
app_id: &'a str,
r#type: &'a str,
message: Cow<'a, str>,
timestamp: i64,
}

impl Logger {
pub fn new(config: &Watchtower) -> Self {
env_logger::init();
Logger {
config: Arc::new(config.clone()),
client: Arc::new(reqwest::Client::new()),
}
}

async fn post_log(&self, log_type: LogType, message: Cow<'static, str>) {
let config = Arc::clone(&self.config);
let client = Arc::clone(&self.client);

let message_owned = message.into_owned();

let data = LogData {
token: &config.token,
log: LogPayload {
app_id: &config.app_id,
r#type: match log_type {
LogType::Info => &config.types.info,
LogType::Warning => &config.types.warning,
LogType::Severe => &config.types.severe,
},
message: Cow::Owned(message_owned),
timestamp: Utc::now().timestamp_millis(),
},
};

let response = client.post(&config.endpoint).json(&data).send().await;

match response {
Ok(res) if res.status().is_success() => (),
Ok(res) => eprintln!(
"Failed to post log: {:?}",
res.text().await.unwrap_or_default()
),
Err(err) => eprintln!("Failed to post log: {:?}", err),
}
}

pub async fn async_info<S>(&self, message: S)
where
S: Into<Cow<'static, str>> + std::fmt::Display + Send + 'static,
{
println!("INFO: {}", &message);
self.post_log(LogType::Info, message.into()).await;
}

pub async fn async_warning<S>(&self, message: S)
where
S: Into<Cow<'static, str>> + std::fmt::Display + Send + 'static,
{
println!("WARNING: {}", &message);
self.post_log(LogType::Warning, message.into()).await;
}

pub async fn async_severe<S>(&self, message: S)
where
S: Into<Cow<'static, str>> + std::fmt::Display + Send + 'static,
{
println!("SEVERE: {}", &message);
self.post_log(LogType::Severe, message.into()).await;
}

pub fn info<S>(&self, message: S)
where
S: Into<Cow<'static, str>> + std::fmt::Display + Send + 'static,
{
let logger_clone = self.clone();
tokio::spawn(async move {
logger_clone.async_info(message).await;
});
}

pub fn warning<S>(&self, message: S)
where
S: Into<Cow<'static, str>> + std::fmt::Display + Send + 'static,
{
let logger_clone = self.clone();
tokio::spawn(async move {
logger_clone.async_warning(message).await;
});
}

pub fn severe<S>(&self, message: S)
where
S: Into<Cow<'static, str>> + std::fmt::Display + Send + 'static,
{
let logger_clone = self.clone();
tokio::spawn(async move {
logger_clone.async_severe(message).await;
});
}

#[allow(dead_code)]
pub fn local<S>(&self, message: S)
where
S: Into<Cow<'static, str>> + std::fmt::Display,
{
println!("{}", &message);
}
}

impl Clone for Logger {
fn clone(&self) -> Self {
Logger {
config: Arc::clone(&self.config),
client: Arc::clone(&self.client),
}
}
}
Loading

0 comments on commit fc57a69

Please sign in to comment.