diff --git a/justfile b/justfile new file mode 100644 index 0000000..01e0926 --- /dev/null +++ b/justfile @@ -0,0 +1,10 @@ +# run local instance of galileo, in dry-run mode, meaning no funds will be sent. +# assumes you've imported the galileo seed phrase locally +dev: + cargo run --release -- serve --dry-run 1penumbra --data-dir ~/.local/share/pcli-galileo + +# scan discord history for faucet drips, collect in local csv, for inclusion in allocations. +history: + cargo run --release -- history \ + --channel https://discord.com/channels/824484045370818580/915710851917439060 \ + | tee discord-history.csv diff --git a/src/handler.rs b/src/handler.rs index 0709e8a..36726b9 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -30,14 +30,19 @@ pub struct Handler { /// times we've told the user about the rate limit (so that eventually we can stop replying if /// they keep asking). send_history: Arc>, + /// Whether read-only mode is enabled. If false, Penumbra transactions will be sent, + /// and Discord replies will be posted. If true, those events will be logged, but not actually + /// performed. + dry_run: bool, } impl Handler { - pub fn new(rate_limit: Duration, reply_limit: usize) -> Self { + pub fn new(rate_limit: Duration, reply_limit: usize, dry_run: bool) -> Self { Handler { rate_limit, reply_limit, send_history: Arc::new(Mutex::new(SendHistory::new())), + dry_run, } } /// Check whether the bot can proceed with honoring this request, @@ -231,7 +236,13 @@ impl SendHistory { #[async_trait] impl EventHandler for Handler { - #[instrument(skip(self, ctx))] + // The `Message` struct is very large, and clutters up debug logging, so we'll + // extract specific fields to make spans readable. + #[instrument(skip_all, fields( + user_name = %message.author.name, + user_id = %message.author.id, + message_id = %message.id, + ))] /// Respond to faucet request. For first-time requests, will spend a bit. /// If the faucet request duplicates a previous request within the timeout /// window, then Galileo will respond instructing the user to wait longer. @@ -281,7 +292,11 @@ impl EventHandler for Handler { "Please wait for another {} before requesting more tokens. Thanks!", format_remaining_time(last_fulfilled, self.rate_limit) ); - reply(&ctx, &message, response).await; + if !self.dry_run { + reply(&ctx, &message, response).await; + } else { + tracing::debug!("dry_run: would reply with rate-limit response",); + } // Setting the notified count to zero "un-rate-limits" an entry, which we do when a // request fails, so we don't have to traverse the entire list: @@ -299,6 +314,11 @@ impl EventHandler for Handler { .unwrap() .record_request(message_info.user_id, &penumbra_addresses); + if self.dry_run { + tracing::debug!("dry_run: would send tx"); + return; + } + // Send the message to the queue, to be processed asynchronously tracing::trace!("sending message to worker queue"); ctx.data diff --git a/src/opt/serve.rs b/src/opt/serve.rs index 3cd34f5..fd16c80 100644 --- a/src/opt/serve.rs +++ b/src/opt/serve.rs @@ -60,6 +60,9 @@ pub struct Serve { /// Batch size for responding to catch-up backlog. #[clap(long, default_value = "25")] catch_up_batch_size: usize, + /// Disable transaction sending and Discord notifications. Useful for debugging. + #[clap(long)] + dry_run: bool, /// The amounts to send for each response, written as typed values 1.87penumbra, 12cubes, etc. values: Vec, } @@ -144,7 +147,7 @@ impl Serve { let (send_requests, responder) = Responder::new(service, self.max_addresses, self.values); // Create a watcher for Discord messages, which will manage spends and replies. - let handler = Handler::new(self.rate_limit, self.reply_limit); + let handler = Handler::new(self.rate_limit, self.reply_limit, self.dry_run); tracing::debug!("configuring discord client"); // Make a new client using a token set by an environment variable, with our handlers @@ -214,6 +217,9 @@ impl Serve { } /// Perform sanity checks on CLI args prior to running. async fn preflight_checks(&self) -> anyhow::Result<()> { + if self.dry_run { + tracing::info!("dry-run mode is enabled, won't send transactions or post messages"); + } if self.values.is_empty() { anyhow::bail!("at least one value must be provided"); } else if self.values.iter().any(|v| v.amount.value().is_zero()) {