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}