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}