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    jid::BareJid,
 11    muc::room::{JoinRoomSettings, RoomMessageSettings},
 12    Agent, ClientBuilder, ClientFeature, ClientType, Event, RoomNick,
 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!("using rustls (e.g. via the ktls feature) needs an enabled rustls backend feature (either aws_lc_rs or ring).");
 25
 26#[tokio::main]
 27async fn main() -> Result<(), Option<()>> {
 28    env_logger::init();
 29
 30    #[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))]
 31    rustls::crypto::aws_lc_rs::default_provider()
 32        .install_default()
 33        .expect("failed to install rustls crypto provider");
 34
 35    #[cfg(all(feature = "ring"))]
 36    rustls::crypto::ring::default_provider()
 37        .install_default()
 38        .expect("failed to install rustls crypto provider");
 39
 40    let args: Vec<String> = args().collect();
 41    if args.len() < 3 {
 42        println!("Usage: {} <jid> <password> [ROOM...]", args[0]);
 43        return Err(None);
 44    }
 45
 46    let jid = BareJid::from_str(&args[1]).expect(&format!("Invalid JID: {}", &args[1]));
 47    let password = &args[2];
 48
 49    // Figure out which rooms to join to say hello
 50    let mut rooms: Vec<BareJid> = Vec::new();
 51    let mut counter = 3;
 52    if args.len() > 3 {
 53        while counter < args.len() {
 54            match BareJid::from_str(&args[counter]) {
 55                Ok(jid) => rooms.push(jid),
 56                Err(e) => {
 57                    log::error!("Requested room {} is not a valid JID: {e}", args[counter]);
 58                    std::process::exit(1);
 59                }
 60            }
 61            counter += 1;
 62        }
 63    }
 64
 65    let nick = RoomNick::from_str("bot").unwrap();
 66
 67    // Client instance
 68    let mut client = ClientBuilder::new(jid, password)
 69        .set_client(ClientType::Bot, "xmpp-rs")
 70        .set_website("https://xmpp.rs")
 71        .set_default_nick(nick)
 72        .enable_feature(ClientFeature::ContactList)
 73        .enable_feature(ClientFeature::JoinRooms)
 74        .build();
 75
 76    log::info!("Connecting...");
 77
 78    loop {
 79        tokio::select! {
 80            events = client.wait_for_events() => {
 81                for event in events {
 82                    let _ = handle_events(&mut client, event, &rooms).await;
 83                }
 84            },
 85            _ = ctrl_c() => {
 86                log::info!("Disconnecting...");
 87                let _ = client.disconnect().await;
 88                break;
 89            },
 90        }
 91    }
 92
 93    Ok(())
 94}
 95
 96async fn handle_events(client: &mut Agent, event: Event, rooms: &Vec<BareJid>) {
 97    match event {
 98        Event::Online => {
 99            log::info!("Online.");
100            for room in rooms {
101                log::info!("Joining room {} from CLI argument…", room);
102                client
103                    .join_room(JoinRoomSettings {
104                        room: room.clone(),
105                        nick: None,
106                        password: None,
107                        status: Some(("en", "Yet another bot!")),
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}