hello_bot.rs

  1// Copyright (c) 2019 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  2//
  3// This Source Code Form is subject to the terms of the Mozilla Public
  4// License, v. 2.0. If a copy of the MPL was not distributed with this
  5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7#[cfg(feature = "rustls-any-backend")]
  8use xmpp::tokio_xmpp::rustls;
  9use xmpp::{
 10    Agent, ClientBuilder, ClientFeature, ClientType, Event, RoomNick,
 11    jid::BareJid,
 12    muc::room::{JoinRoomSettings, RoomMessageSettings},
 13};
 14
 15use tokio::signal::ctrl_c;
 16
 17use std::env::args;
 18use std::str::FromStr;
 19
 20#[cfg(all(
 21    feature = "rustls-any-backend",
 22    not(any(feature = "aws_lc_rs", feature = "ring"))
 23))]
 24compile_error!(
 25    "using rustls (e.g. via the ktls feature) needs an enabled rustls backend feature (either aws_lc_rs or ring)."
 26);
 27
 28#[tokio::main]
 29async fn main() -> Result<(), Option<()>> {
 30    env_logger::init();
 31
 32    #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))]
 33    rustls::crypto::aws_lc_rs::default_provider()
 34        .install_default()
 35        .expect("failed to install rustls crypto provider");
 36
 37    #[cfg(all(feature = "ring"))]
 38    rustls::crypto::ring::default_provider()
 39        .install_default()
 40        .expect("failed to install rustls crypto provider");
 41
 42    let args: Vec<String> = args().collect();
 43    if args.len() < 3 {
 44        println!("Usage: {} <jid> <password> [ROOM...]", args[0]);
 45        return Err(None);
 46    }
 47
 48    let jid = BareJid::from_str(&args[1]).expect(&format!("Invalid JID: {}", &args[1]));
 49    let password = &args[2];
 50
 51    // Figure out which rooms to join to say hello
 52    let mut rooms: Vec<BareJid> = Vec::new();
 53    let mut counter = 3;
 54    if args.len() > 3 {
 55        while counter < args.len() {
 56            match BareJid::from_str(&args[counter]) {
 57                Ok(jid) => rooms.push(jid),
 58                Err(e) => {
 59                    log::error!("Requested room {} is not a valid JID: {e}", args[counter]);
 60                    std::process::exit(1);
 61                }
 62            }
 63            counter += 1;
 64        }
 65    }
 66
 67    let nick = RoomNick::from_str("bot").unwrap();
 68
 69    // Client instance
 70    let mut client = ClientBuilder::new(jid, password)
 71        .set_client(ClientType::Bot, "xmpp-rs")
 72        .set_website("https://xmpp.rs")
 73        .set_default_nick(nick)
 74        .enable_feature(ClientFeature::ContactList)
 75        .enable_feature(ClientFeature::JoinRooms)
 76        .build();
 77
 78    log::info!("Connecting...");
 79
 80    loop {
 81        tokio::select! {
 82            events = client.wait_for_events() => {
 83                for event in events {
 84                    handle_events(&mut client, event, &rooms).await
 85                }
 86            },
 87            _ = ctrl_c() => {
 88                log::info!("Disconnecting...");
 89                client.disconnect().await.unwrap();
 90                break;
 91            },
 92        }
 93    }
 94
 95    Ok(())
 96}
 97
 98async fn handle_events(client: &mut Agent, event: Event, rooms: &Vec<BareJid>) {
 99    match event {
100        Event::Online => {
101            log::info!("Online.");
102            for room in rooms {
103                log::info!("Joining room {} from CLI argument…", room);
104                client
105                    .join_room(JoinRoomSettings {
106                        status: Some(("en", "Yet another bot!")),
107                        ..JoinRoomSettings::new(room.clone())
108                    })
109                    .await;
110            }
111        }
112        Event::Disconnected(e) => {
113            log::info!("Disconnected: {}.", e);
114        }
115        Event::ChatMessage(_id, jid, body, time_info) => {
116            log::info!(
117                "{} {}: {}",
118                time_info.received.time().format("%H:%M"),
119                jid,
120                body
121            );
122        }
123        Event::RoomJoined(jid) => {
124            log::info!("Joined room {}.", jid);
125            client
126                .send_room_message(RoomMessageSettings::new(jid, "Hello world!"))
127                .await;
128        }
129        Event::RoomMessage(_id, jid, nick, body, time_info) => {
130            println!(
131                "Message in room {} from {} at {}: {}",
132                jid, nick, time_info.received, body
133            );
134        }
135        _ => {
136            log::debug!("Unimplemented event:\n{:#?}", event);
137        }
138    }
139}