use std::{
sync::atomic::{AtomicBool, Ordering},
time::{Duration, UNIX_EPOCH},
};
use harmony_rust_sdk::{
api::chat::{self, stream_event},
client::{
api::{
auth::AuthStepResponse,
chat::{
guild::JoinGuildRequest,
message::{MessageExt, SendMessage},
EventSource,
},
profile::{GetProfileRequest, UpdateProfile, UserStatus},
},
error::ClientResult,
Client,
},
};
use tracing::info;
use tracing_subscriber::EnvFilter;
const EMAIL: &str = "rust_sdk_test@example.org";
const USERNAME: &str = "rust_sdk_test";
const PASSWORD: &str = "123456789Ab";
const HOMESERVER: &str = "https://chat.harmonyapp.io:2289";
const GUILD_ID_FILE: &str = "guild_id";
static DID_CTRLC: AtomicBool = AtomicBool::new(false);
#[tokio::main(flavor = "current_thread")]
async fn main() -> ClientResult<()> {
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::from("info")),
)
.init();
ctrlc::set_handler(|| {
DID_CTRLC.store(true, Ordering::Relaxed);
})
.expect("Can't set Ctrl-C handler");
let guild_invite = std::env::var("GUILD_INVITE");
let client = Client::new(HOMESERVER.parse().unwrap(), None).await?;
info!("Successfully created client.");
client.begin_auth().await?;
client.next_auth_step(AuthStepResponse::Initial).await?;
client
.next_auth_step(AuthStepResponse::login_choice())
.await?;
let login_result = client
.next_auth_step(AuthStepResponse::login_form(EMAIL, PASSWORD))
.await;
if login_result.map_or(false, |maybe_step| maybe_step.is_some()) {
info!("Login failed, let's try registering.");
client.prev_auth_step().await?;
client
.next_auth_step(AuthStepResponse::register_choice())
.await?;
client
.next_auth_step(AuthStepResponse::register_form(EMAIL, USERNAME, PASSWORD))
.await?;
info!("Successfully registered.");
} else {
info!("Successfully logon.");
}
client
.call(
UpdateProfile::default()
.with_new_status(UserStatus::Online)
.with_new_is_bot(true),
)
.await?;
let guild_id = if let Ok(invite) = guild_invite {
client.call(JoinGuildRequest::new(invite)).await?.guild_id
} else {
tokio::fs::read_to_string(GUILD_ID_FILE)
.await
.unwrap()
.trim()
.parse::<u64>()
.unwrap()
};
tokio::fs::write(GUILD_ID_FILE, guild_id.to_string())
.await
.unwrap();
info!("In guild: {}", guild_id);
let start = std::time::Instant::now();
client
.event_loop(vec![EventSource::Guild(guild_id)], {
move |client, event| async move {
if DID_CTRLC.load(Ordering::Relaxed) {
return Ok(true);
}
if let chat::Event::Chat(stream_event::Event::SentMessage(sent_message)) = event {
if let Some(message) = sent_message.message {
let text_content = message.text().unwrap_or("<empty message>");
info!("got message: {}", text_content);
if let Some(mut parts) = text_content
.strip_prefix("r!")
.map(|c| c.split_whitespace())
{
if let Some(cmd) = parts.next() {
let reply = match cmd {
"ping" => {
let created_at =
{ Duration::from_secs(message.created_at) };
let arrive_duration = (UNIX_EPOCH.elapsed().unwrap()
- created_at)
.as_secs_f32();
format!("Pong! Took {} secs.", arrive_duration)
}
"hello" => {
let user_profile = client
.call(GetProfileRequest::new(message.author_id))
.await?;
format!(
"Hello, {}!",
user_profile
.profile
.as_ref()
.map_or("unknown", |p| p.user_name.as_str())
)
}
"uptime" => {
format!(
"Been running for {} seconds.",
start.elapsed().as_secs()
)
}
_ => "No such command.".to_string(),
};
client
.call(
SendMessage::new(guild_id, sent_message.channel_id)
.with_in_reply_to(sent_message.message_id)
.text(reply),
)
.await?;
}
}
}
}
Ok(false)
}
})
.await?;
client
.call(UpdateProfile::default().with_new_status(UserStatus::OfflineUnspecified))
.await?;
Ok(())
}