xmpp-rs/xmpp/examples/hello_bot.rs
Jonas Schäfer fc8b581593 Clean up tls-related feature flags
This provides a clear and consistent interface for selecting TLS-related
features on both (xmpp and tokio-xmpp) crates. All feature checks have
been revamped. All working combinations have been tested (including a
connectivity test + --all-features docs build) using:

```
set -xeuo pipefail
features=(aws_lc_rs ring ktls,aws_lc_rs ktls,ring aws_lc_rs,ring aws_lc_rs,ring,ktls native-tls rustls-any-backend)
export RUSTFLAGS=" -D warnings"
cargo test --no-default-features
cargo test
for feature in ${features[@]}; do
  echo ">>> BUILDING with $feature" 2>&1
  # Running code or building examples cannot succeed with rustls-any-backend.
  features="starttls,$feature"
  if [ "$feature" != 'rustls-any-backend' ]; then
    if ! cargo test --no-default-features --features="$features"; then
      echo ">>> BUILD FAILED for tls feature set: $features" >&2
      exit 1
    fi
    set +e
    timeout -sINT -p -k 2 3 cargo run --no-default-features --features="$features" --example keep_connection -- test@hub.sotecware.net "$(pass xmpp/test@hub.sotecware.net)"
    status="$?"
    set -e
    if [ $status -ne 0 ]; then
      echo ">>> keep_connection did not shut down cleanly! (status: $status)" >&2
      exit 1
    fi
  else
    if ! cargo build --no-default-features --features="$feature"; then
      echo ">>> BUILD FAILED for tls feature set: $features" >&2
      exit 1
    fi
  fi
done
RUSTDOCFLAGS="--cfg docsrs" RUSTFLAGS="--cfg xmpprs_doc_build" cargo +nightly doc -Zrustdoc-map --all-features
```
2025-05-10 22:02:39 +02:00

139 lines
4.3 KiB
Rust

// Copyright (c) 2019 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#[cfg(feature = "rustls-any-backend")]
use xmpp::tokio_xmpp::rustls;
use xmpp::{
jid::BareJid,
muc::room::{JoinRoomSettings, RoomMessageSettings},
Agent, ClientBuilder, ClientFeature, ClientType, Event, RoomNick,
};
use tokio::signal::ctrl_c;
use std::env::args;
use std::str::FromStr;
#[cfg(all(
feature = "rustls-any-backend",
not(any(feature = "aws_lc_rs", feature = "ring"))
))]
compile_error!("using rustls (e.g. via the ktls feature) needs an enabled rustls backend feature (either aws_lc_rs or ring).");
#[tokio::main]
async fn main() -> Result<(), Option<()>> {
env_logger::init();
#[cfg(all(feature = "aws_lc_rs", not(feature = "ring")))]
rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.expect("failed to install rustls crypto provider");
#[cfg(all(feature = "ring"))]
rustls::crypto::ring::default_provider()
.install_default()
.expect("failed to install rustls crypto provider");
let args: Vec<String> = args().collect();
if args.len() < 3 {
println!("Usage: {} <jid> <password> [ROOM...]", args[0]);
return Err(None);
}
let jid = BareJid::from_str(&args[1]).expect(&format!("Invalid JID: {}", &args[1]));
let password = &args[2];
// Figure out which rooms to join to say hello
let mut rooms: Vec<BareJid> = Vec::new();
let mut counter = 3;
if args.len() > 3 {
while counter < args.len() {
match BareJid::from_str(&args[counter]) {
Ok(jid) => rooms.push(jid),
Err(e) => {
log::error!("Requested room {} is not a valid JID: {e}", args[counter]);
std::process::exit(1);
}
}
counter += 1;
}
}
let nick = RoomNick::from_str("bot").unwrap();
// Client instance
let mut client = ClientBuilder::new(jid, password)
.set_client(ClientType::Bot, "xmpp-rs")
.set_website("https://xmpp.rs")
.set_default_nick(nick)
.enable_feature(ClientFeature::ContactList)
.enable_feature(ClientFeature::JoinRooms)
.build();
log::info!("Connecting...");
loop {
tokio::select! {
events = client.wait_for_events() => {
for event in events {
let _ = handle_events(&mut client, event, &rooms);
}
},
_ = ctrl_c() => {
log::info!("Disconnecting...");
let _ = client.disconnect().await;
break;
},
}
}
Ok(())
}
async fn handle_events(client: &mut Agent, event: Event, rooms: &Vec<BareJid>) {
match event {
Event::Online => {
log::info!("Online.");
for room in rooms {
log::info!("Joining room {} from CLI argument…", room);
client
.join_room(JoinRoomSettings {
room: room.clone(),
nick: None,
password: None,
status: Some(("en", "Yet another bot!")),
})
.await;
}
}
Event::Disconnected(e) => {
log::info!("Disconnected: {}.", e);
}
Event::ChatMessage(_id, jid, body, time_info) => {
log::info!(
"{} {}: {}",
time_info.received.time().format("%H:%M"),
jid,
body
);
}
Event::RoomJoined(jid) => {
log::info!("Joined room {}.", jid);
client
.send_room_message(RoomMessageSettings::new(jid, "Hello world!"))
.await;
}
Event::RoomMessage(_id, jid, nick, body, time_info) => {
println!(
"Message in room {} from {} at {}: {}",
jid, nick, time_info.received, body
);
}
_ => {
log::debug!("Unimplemented event:\n{:#?}", event);
}
}
}