Files
vinezombie/examples/msglog.rs
TheDaemoness b2f40896bf Update reg handler to use client state
The handler now, instead of returning Registration,
sets the Client's internal ClientState.
This means useful state info is more readily-available to handlers.

Updated examples to match.
2024-04-28 09:41:23 -07:00

86 lines
4.0 KiB
Rust

use vinezombie::{
client::{
self,
auth::Clear,
channel::TokioChannels,
conn::ServerAddr,
handlers::{AutoPong, YieldParsed},
register::{register_as_bot, Options},
state::ClientSource,
Client,
},
ircmsg::ClientMsg,
names::cmd::{JOIN, PRIVMSG},
string::{Arg, Bytes, Line},
};
// Let's make a simple logging bot,
// something that just dumps PRIVMSGs to standard output for muliple channels.
// Going forward, all examples are going to use tokio,
// because the sync I/O provided by std is suffering.
#[tokio::main]
async fn main() -> std::io::Result<()> {
// Let's be less verbose this time.
tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).compact().init();
// Standard vinezombie boilerplate.
let mut options: Options<Clear> = Options::new();
options.realname = Some(Line::from_str("Vinezombie Example: msglog"));
let address = ServerAddr::from_host_str("irc.libera.chat");
let sock = address.connect_tokio(|| client::tls::TlsConfigOptions::default().build()).await?;
let mut client = Client::new(sock, TokioChannels);
let (_id, reg_result) = client.add(&register_as_bot(), &options).unwrap();
client.run_tokio().await?;
reg_result.await.unwrap()?;
// The only piece of reg info we care about for this example is our nick.
let nick = client.state().get::<ClientSource>().unwrap().nick.clone();
tracing::info!("nick: {}", nick);
// Let's add a handler to auto-reply to PING messages for us. Most IRC networks need this.
// Do this before anything else.
let _ = client.add((), AutoPong);
// Let's join all the channels provided as command-line arguments.
// This is a VERY crude way of joining multiple channels, but works for illustration
// of how to construct messages that are less-trivial than a no-argument QUIT message.
// Let's start by getting an edit guard on the queue so that we can push messages to it.
let mut queue = client.queue_mut().edit();
// vinezombie's Bytes type is not like the Bytes abstraction from the bytes crate.
// It is byte string type that lazily checks UTF-8 validity and can
// optionally share ownership of its contents. It sees use throughout the codebase.
for channel in std::env::args().skip(1).map(Bytes::from) {
let mut msg = ClientMsg::new(JOIN);
// Safeguard: vinezombie ensures that you cannot easily create nonsensical messages.
// The Arg::from_bytes function checks that the string is a valid IRC message argument.
let Ok(channel) = Arg::from_bytes(channel.clone()) else {
tracing::warn!("skipping invalid channel {channel}");
continue;
};
// Absolutely nothing fancy here. No multi-target joins, just one channel per join.
msg.args.edit().add_word(channel);
queue.push(msg);
}
// We now need to receive PRIVMSGs and send them somewhere for further processing.
// `YieldParsed` exists exactly for this purpose.
let (_, mut msgs) = client.add((), YieldParsed::just(PRIVMSG)).unwrap();
// Since we are async, let's do the actual printing in another task, because we can.
tokio::spawn(async move {
while let Some(msg) = msgs.recv().await {
// If the server didn't give us a source, something's weird. Skip.
let Some(source) = msg.source else {
continue;
};
// If the message is being sent to our nick, use their nick as the context.
// An arguably better way of doing this is looking for a channel prefix in the target.
let context = if msg.target.as_bytes() == nick.as_bytes() {
source.nick.clone().into()
} else {
msg.target
};
println!("{} <{}> {}", context, source.nick, msg.value);
}
});
// Drive the client for ever and ever and ever and ever and ever and ever and ever and ever and-
loop {
client.run_tokio().await?;
}
}