Update testbot example

Packet handling is now extracted into a separate function and the bot
uses euphoxide's joined.since for its !uptime command.
This commit is contained in:
Joscha 2023-01-21 00:22:28 +01:00
parent 7357435ee1
commit 705ef90e98

View file

@ -1,8 +1,13 @@
use std::error::Error; //! A small bot that doesn't use the `bot` submodule. Meant to show how the main
use std::time::{Duration, Instant}; //! parts of the API fit together.
use std::error::Error;
use std::time::Duration;
use euphoxide::api::packet::ParsedPacket;
use euphoxide::api::{Data, Nick, Send}; use euphoxide::api::{Data, Nick, Send};
use euphoxide::conn::Conn; use euphoxide::conn::{Conn, ConnTx, State};
use time::OffsetDateTime;
const TIMEOUT: Duration = Duration::from_secs(10); const TIMEOUT: Duration = Duration::from_secs(10);
const DOMAIN: &str = "euphoria.io"; const DOMAIN: &str = "euphoria.io";
@ -10,12 +15,12 @@ const ROOM: &str = "test";
const NICK: &str = "TestBot"; const NICK: &str = "TestBot";
const HELP: &str = "I'm an example bot for https://github.com/Garmelon/euphoxide"; const HELP: &str = "I'm an example bot for https://github.com/Garmelon/euphoxide";
fn format_delta(delta: Duration) -> String { fn format_delta(delta: time::Duration) -> String {
const MINUTE: u64 = 60; const MINUTE: u64 = 60;
const HOUR: u64 = MINUTE * 60; const HOUR: u64 = MINUTE * 60;
const DAY: u64 = HOUR * 24; const DAY: u64 = HOUR * 24;
let mut seconds = delta.as_secs(); let mut seconds: u64 = delta.whole_seconds().try_into().unwrap();
let mut parts = vec![]; let mut parts = vec![];
let days = seconds / DAY; let days = seconds / DAY;
@ -43,20 +48,15 @@ fn format_delta(delta: Duration) -> String {
parts.join(" ") parts.join(" ")
} }
#[tokio::main] async fn on_packet(packet: ParsedPacket, conn_tx: &ConnTx, state: &State) -> Result<(), ()> {
async fn main() -> Result<(), Box<dyn Error>> {
let start = Instant::now();
let (mut conn, _) = Conn::connect(DOMAIN, ROOM, false, None, TIMEOUT).await?;
while let Ok(packet) = conn.recv().await {
let data = match packet.content { let data = match packet.content {
Ok(data) => data, Ok(data) => data,
Err(err) => { Err(err) => {
println!("Error for {}: {err}", packet.r#type); println!("Error for {}: {err}", packet.r#type);
continue; return Err(());
} }
}; };
match data { match data {
Data::HelloEvent(event) => println!("Connected with id {}", event.session.id), Data::HelloEvent(event) => println!("Connected with id {}", event.session.id),
Data::SnapshotEvent(event) => { Data::SnapshotEvent(event) => {
@ -71,11 +71,11 @@ async fn main() -> Result<(), Box<dyn Error>> {
// We only need to do this because we want to log the result of // We only need to do this because we want to log the result of
// the nick command. Otherwise, we could've just called // the nick command. Otherwise, we could've just called
// tx.send() synchronously and ignored the returned Future. // tx.send() synchronously and ignored the returned Future.
let tx = conn.tx().clone(); let conn_tx_clone = conn_tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
// Awaiting the future returned by the send command lets you // Awaiting the future returned by the send command lets you
// (type-safely) access the server's reply. // (type-safely) access the server's reply.
let reply = tx let reply = conn_tx_clone
.send(Nick { .send(Nick {
name: NICK.to_string(), name: NICK.to_string(),
}) })
@ -88,11 +88,11 @@ async fn main() -> Result<(), Box<dyn Error>> {
} }
Data::BounceEvent(_) => { Data::BounceEvent(_) => {
println!("Received bounce event, stopping"); println!("Received bounce event, stopping");
break; return Err(());
} }
Data::DisconnectEvent(_) => { Data::DisconnectEvent(_) => {
println!("Received disconnect event, stopping"); println!("Received disconnect event, stopping");
break; return Err(());
} }
Data::JoinEvent(event) => println!("{:?} ({}) joined", event.0.name, event.0.id), Data::JoinEvent(event) => println!("{:?} ({}) joined", event.0.name, event.0.id),
Data::PartEvent(event) => println!("{:?} ({}) left", event.0.name, event.0.id), Data::PartEvent(event) => println!("{:?} ({}) left", event.0.name, event.0.id),
@ -111,8 +111,10 @@ async fn main() -> Result<(), Box<dyn Error>> {
} else if content == format!("!help @{NICK}") { } else if content == format!("!help @{NICK}") {
reply = Some(HELP.to_string()); reply = Some(HELP.to_string());
} else if content == format!("!uptime @{NICK}") { } else if content == format!("!uptime @{NICK}") {
let delta = Instant::now().duration_since(start); if let Some(joined) = state.joined() {
let delta = OffsetDateTime::now_utc() - joined.since;
reply = Some(format!("/me has been up for {}", format_delta(delta))); reply = Some(format!("/me has been up for {}", format_delta(delta)));
}
} else if content == "!test" { } else if content == "!test" {
reply = Some("Test successful!".to_string()); reply = Some("Test successful!".to_string());
} else if content == format!("!kill @{NICK}") { } else if content == format!("!kill @{NICK}") {
@ -125,21 +127,20 @@ async fn main() -> Result<(), Box<dyn Error>> {
// would be a race between sending the message and closing // would be a race between sending the message and closing
// the connection as the send function can return before the // the connection as the send function can return before the
// message has actually been sent. // message has actually been sent.
let _ = conn let _ = conn_tx
.tx()
.send(Send { .send(Send {
content: "/me dies".to_string(), content: "/me dies".to_string(),
parent: Some(event.0.id), parent: Some(event.0.id),
}) })
.await; .await;
break; return Err(());
} }
if let Some(reply) = reply { if let Some(reply) = reply {
// If you are not interested in the result, you can just // If you are not interested in the result, you can just
// throw away the future returned by the send function. // throw away the future returned by the send function.
println!("Sending reply..."); println!("Sending reply...");
let _ = conn.tx().send(Send { let _ = conn_tx.send(Send {
content: reply, content: reply,
parent: Some(event.0.id), parent: Some(event.0.id),
}); });
@ -148,6 +149,18 @@ async fn main() -> Result<(), Box<dyn Error>> {
} }
_ => {} _ => {}
} }
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let (mut conn, _) = Conn::connect(DOMAIN, ROOM, false, None, TIMEOUT).await?;
while let Ok(packet) = conn.recv().await {
if on_packet(packet, conn.tx(), conn.state()).await.is_err() {
break;
}
} }
Ok(()) Ok(())
} }