aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@bunny.garden>2025-02-18 11:39:19 +0000
committerLibravatar cel 🌸 <cel@bunny.garden>2025-02-18 11:39:19 +0000
commitc0d2aae0385aee7f1bb2ecb330e72e40b8fde6a2 (patch)
treec4ee54c64a2d8c0adf222dce5a334502453de473
parent945f1406165aa58414c0374a1ba984a1d6d896c6 (diff)
downloadluz-c0d2aae0385aee7f1bb2ecb330e72e40b8fde6a2.tar.gz
luz-c0d2aae0385aee7f1bb2ecb330e72e40b8fde6a2.tar.bz2
luz-c0d2aae0385aee7f1bb2ecb330e72e40b8fde6a2.zip
implement handle_online() and handle_offline() for CommandMessage
-rw-r--r--luz/src/connection/write.rs14
-rw-r--r--luz/src/error.rs6
-rw-r--r--luz/src/lib.rs640
-rw-r--r--luz/src/presence.rs28
-rw-r--r--luz/src/roster.rs7
-rw-r--r--stanza/src/client/message.rs20
-rw-r--r--stanza/src/client/presence.rs22
7 files changed, 653 insertions, 84 deletions
diff --git a/luz/src/connection/write.rs b/luz/src/connection/write.rs
index 70584a2..2273fac 100644
--- a/luz/src/connection/write.rs
+++ b/luz/src/connection/write.rs
@@ -139,6 +139,20 @@ pub struct WriteHandle {
sender: mpsc::Sender<WriteMessage>,
}
+impl WriteHandle {
+ pub async fn write(&self, stanza: Stanza) -> Result<(), Reason> {
+ let (send, recv) = oneshot::channel();
+ self.send(WriteMessage {
+ stanza,
+ respond_to: send,
+ })
+ .await
+ .map_err(|_| Reason::ChannelSend)?;
+ // TODO: timeout
+ recv.await?
+ }
+}
+
impl Deref for WriteHandle {
type Target = mpsc::Sender<WriteMessage>;
diff --git a/luz/src/error.rs b/luz/src/error.rs
index 7bf7bac..fbbcd2b 100644
--- a/luz/src/error.rs
+++ b/luz/src/error.rs
@@ -36,7 +36,7 @@ impl From<RosterError> for ConnectionError {
}
}
-pub struct StatusError(Reason);
+pub struct StatusError(pub Reason);
impl From<StatusError> for Error {
fn from(e: StatusError) -> Self {
@@ -55,7 +55,7 @@ pub enum Reason {
// TODO: organisastion of error into internal error thing
Timeout,
Stream(stanza::stream_error::Error),
- Stanza(stanza::stanza_error::Error),
+ Stanza(Option<stanza::client::error::Error>),
Jabber(jabber::Error),
XML(peanuts::Error),
SQL(sqlx::Error),
@@ -63,6 +63,8 @@ pub enum Reason {
LostConnection,
OneshotRecv(oneshot::error::RecvError),
UnexpectedStanza(Stanza),
+ Disconnected,
+ ChannelSend,
}
impl From<oneshot::error::RecvError> for Reason {
diff --git a/luz/src/lib.rs b/luz/src/lib.rs
index 1ed6c03..cffffb2 100644
--- a/luz/src/lib.rs
+++ b/luz/src/lib.rs
@@ -20,7 +20,7 @@ use tokio::{
sync::{mpsc, oneshot, Mutex},
task::JoinSet,
};
-use tracing::info;
+use tracing::{info, Instrument};
use user::User;
use uuid::Uuid;
@@ -286,24 +286,72 @@ impl CommandMessage {
}
}
}
- CommandMessage::GetChats(sender) => todo!(),
- CommandMessage::GetChat(jid, sender) => todo!(),
- CommandMessage::GetMessages(jid, sender) => todo!(),
- CommandMessage::DeleteChat(jid, sender) => todo!(),
- CommandMessage::DeleteMessage(uuid, sender) => todo!(),
- CommandMessage::GetUser(jid, sender) => todo!(),
- CommandMessage::AddContact(jid, sender) => todo!(),
- CommandMessage::BuddyRequest(jid, sender) => todo!(),
- CommandMessage::SubscriptionRequest(jid, sender) => todo!(),
- CommandMessage::AcceptBuddyRequest(jid, sender) => todo!(),
- CommandMessage::AcceptSubscriptionRequest(jid, sender) => todo!(),
- CommandMessage::UnsubscribeFromContact(jid, sender) => todo!(),
- CommandMessage::UnsubscribeContact(jid, sender) => todo!(),
- CommandMessage::UnfriendContact(jid, sender) => todo!(),
- CommandMessage::DeleteContact(jid, sender) => todo!(),
- CommandMessage::UpdateContact(jid, contact_update, sender) => todo!(),
- CommandMessage::SetStatus(online, sender) => todo!(),
- CommandMessage::SendMessage(jid, body, sender) => todo!(),
+ CommandMessage::GetChats(sender) => {
+ let chats = db.read_chats().await.map_err(|e| e.into());
+ sender.send(chats);
+ }
+ CommandMessage::GetChat(jid, sender) => {
+ let chats = db.read_chat(jid).await.map_err(|e| e.into());
+ sender.send(chats);
+ }
+ CommandMessage::GetMessages(jid, sender) => {
+ let messages = db.read_message_history(jid).await.map_err(|e| e.into());
+ sender.send(messages);
+ }
+ CommandMessage::DeleteChat(jid, sender) => {
+ let result = db.delete_chat(jid).await.map_err(|e| e.into());
+ sender.send(result);
+ }
+ CommandMessage::DeleteMessage(uuid, sender) => {
+ let result = db.delete_message(uuid).await.map_err(|e| e.into());
+ sender.send(result);
+ }
+ CommandMessage::GetUser(jid, sender) => {
+ let user = db.read_user(jid).await.map_err(|e| e.into());
+ sender.send(user);
+ }
+ // TODO: offline queue to modify roster
+ CommandMessage::AddContact(jid, sender) => {
+ sender.send(Err(Reason::Disconnected));
+ }
+ CommandMessage::BuddyRequest(jid, sender) => {
+ sender.send(Err(Reason::Disconnected));
+ }
+ CommandMessage::SubscriptionRequest(jid, sender) => {
+ sender.send(Err(Reason::Disconnected));
+ }
+ CommandMessage::AcceptBuddyRequest(jid, sender) => {
+ sender.send(Err(Reason::Disconnected));
+ }
+ CommandMessage::AcceptSubscriptionRequest(jid, sender) => {
+ sender.send(Err(Reason::Disconnected));
+ }
+ CommandMessage::UnsubscribeFromContact(jid, sender) => {
+ sender.send(Err(Reason::Disconnected));
+ }
+ CommandMessage::UnsubscribeContact(jid, sender) => {
+ sender.send(Err(Reason::Disconnected));
+ }
+ CommandMessage::UnfriendContact(jid, sender) => {
+ sender.send(Err(Reason::Disconnected));
+ }
+ CommandMessage::DeleteContact(jid, sender) => {
+ sender.send(Err(Reason::Disconnected));
+ }
+ CommandMessage::UpdateContact(jid, contact_update, sender) => {
+ sender.send(Err(Reason::Disconnected));
+ }
+ CommandMessage::SetStatus(online, sender) => {
+ let result = db
+ .upsert_cached_status(online)
+ .await
+ .map_err(|e| StatusError(e.into()));
+ sender.send(result);
+ }
+ // TODO: offline message queue
+ CommandMessage::SendMessage(jid, body, sender) => {
+ sender.send(Err(Reason::Disconnected));
+ }
}
}
@@ -312,7 +360,7 @@ impl CommandMessage {
write_handle: WriteHandle,
supervisor_control: SupervisorSender,
// TODO: jid could lose resource by the end
- jid: Arc<Mutex<JID>>,
+ client_jid: Arc<Mutex<JID>>,
db: Db,
update_sender: mpsc::Sender<UpdateMessage>,
pending_iqs: Arc<Mutex<HashMap<String, oneshot::Sender<Result<Stanza, Reason>>>>>,
@@ -324,7 +372,7 @@ impl CommandMessage {
// TODO: jid resource should probably be stored within the connection
let owned_jid: JID;
{
- owned_jid = jid.lock().await.clone();
+ owned_jid = client_jid.lock().await.clone();
}
let iq_id = Uuid::new_v4().to_string();
let (send, iq_recv) = oneshot::channel();
@@ -400,24 +448,502 @@ impl CommandMessage {
}
}
}
- CommandMessage::GetChats(sender) => todo!(),
- CommandMessage::GetChat(jid, sender) => todo!(),
- CommandMessage::GetMessages(jid, sender) => todo!(),
- CommandMessage::DeleteChat(jid, sender) => todo!(),
- CommandMessage::DeleteMessage(uuid, sender) => todo!(),
- CommandMessage::GetUser(jid, sender) => todo!(),
- CommandMessage::AddContact(jid, sender) => todo!(),
- CommandMessage::BuddyRequest(jid, sender) => todo!(),
- CommandMessage::SubscriptionRequest(jid, sender) => todo!(),
- CommandMessage::AcceptBuddyRequest(jid, sender) => todo!(),
- CommandMessage::AcceptSubscriptionRequest(jid, sender) => todo!(),
- CommandMessage::UnsubscribeFromContact(jid, sender) => todo!(),
- CommandMessage::UnsubscribeContact(jid, sender) => todo!(),
- CommandMessage::UnfriendContact(jid, sender) => todo!(),
- CommandMessage::DeleteContact(jid, sender) => todo!(),
- CommandMessage::UpdateContact(jid, contact_update, sender) => todo!(),
- CommandMessage::SetStatus(online, sender) => todo!(),
- CommandMessage::SendMessage(jid, body, sender) => todo!(),
+ CommandMessage::GetChats(sender) => {
+ let chats = db.read_chats().await.map_err(|e| e.into());
+ sender.send(chats);
+ }
+ CommandMessage::GetChat(jid, sender) => {
+ let chats = db.read_chat(jid).await.map_err(|e| e.into());
+ sender.send(chats);
+ }
+ CommandMessage::GetMessages(jid, sender) => {
+ let messages = db.read_message_history(jid).await.map_err(|e| e.into());
+ sender.send(messages);
+ }
+ CommandMessage::DeleteChat(jid, sender) => {
+ let result = db.delete_chat(jid).await.map_err(|e| e.into());
+ sender.send(result);
+ }
+ CommandMessage::DeleteMessage(uuid, sender) => {
+ let result = db.delete_message(uuid).await.map_err(|e| e.into());
+ sender.send(result);
+ }
+ CommandMessage::GetUser(jid, sender) => {
+ let user = db.read_user(jid).await.map_err(|e| e.into());
+ sender.send(user);
+ }
+ // TODO: offline queue to modify roster
+ CommandMessage::AddContact(jid, sender) => {
+ let owned_jid;
+ {
+ owned_jid = client_jid.lock().await.clone();
+ }
+ let iq_id = Uuid::new_v4().to_string();
+ let set_stanza = Stanza::Iq(Iq {
+ from: Some(owned_jid),
+ id: iq_id.clone(),
+ to: None,
+ r#type: IqType::Set,
+ lang: None,
+ query: Some(iq::Query::Roster(stanza::roster::Query {
+ ver: None,
+ items: vec![stanza::roster::Item {
+ approved: None,
+ ask: false,
+ jid,
+ name: None,
+ subscription: None,
+ groups: Vec::new(),
+ }],
+ })),
+ errors: Vec::new(),
+ });
+ let (send, recv) = oneshot::channel();
+ {
+ pending_iqs.lock().await.insert(iq_id.clone(), send);
+ }
+ // TODO: write_handle send helper function
+ let result = write_handle.write(set_stanza).await;
+ if let Err(_) = result {
+ sender.send(result);
+ return;
+ }
+ let iq_result = recv.await;
+ match iq_result {
+ Ok(i) => match i {
+ Ok(iq_result) => match iq_result {
+ Stanza::Iq(Iq {
+ from: _,
+ id,
+ to: _,
+ r#type,
+ lang: _,
+ query: _,
+ errors: _,
+ }) if id == iq_id && r#type == IqType::Result => {
+ sender.send(Ok(()));
+ return;
+ }
+ Stanza::Iq(Iq {
+ from: _,
+ id,
+ to: _,
+ r#type,
+ lang: _,
+ query: _,
+ errors,
+ }) if id == iq_id && r#type == IqType::Error => {
+ if let Some(error) = errors.first() {
+ sender.send(Err(Reason::Stanza(Some(error.clone()))));
+ } else {
+ sender.send(Err(Reason::Stanza(None)));
+ }
+ return;
+ }
+ s => {
+ sender.send(Err(Reason::UnexpectedStanza(s)));
+ return;
+ }
+ },
+ Err(e) => {
+ sender.send(Err(e.into()));
+ return;
+ }
+ },
+ Err(e) => {
+ sender.send(Err(e.into()));
+ return;
+ }
+ }
+ }
+ CommandMessage::BuddyRequest(jid, sender) => {
+ let presence = Stanza::Presence(stanza::client::presence::Presence {
+ from: None,
+ id: None,
+ to: Some(jid.clone()),
+ r#type: Some(stanza::client::presence::PresenceType::Subscribe),
+ lang: None,
+ show: None,
+ status: None,
+ priority: None,
+ errors: Vec::new(),
+ });
+ let result = write_handle.write(presence).await;
+ match result {
+ Err(_) => {
+ let _ = sender.send(result);
+ }
+ Ok(()) => {
+ let presence = Stanza::Presence(stanza::client::presence::Presence {
+ from: None,
+ id: None,
+ to: Some(jid),
+ r#type: Some(stanza::client::presence::PresenceType::Subscribed),
+ lang: None,
+ show: None,
+ status: None,
+ priority: None,
+ errors: Vec::new(),
+ });
+ let result = write_handle.write(presence).await;
+ let _ = sender.send(result);
+ }
+ }
+ }
+ CommandMessage::SubscriptionRequest(jid, sender) => {
+ // TODO: i should probably have builders
+ let presence = Stanza::Presence(stanza::client::presence::Presence {
+ from: None,
+ id: None,
+ to: Some(jid),
+ r#type: Some(stanza::client::presence::PresenceType::Subscribe),
+ lang: None,
+ show: None,
+ status: None,
+ priority: None,
+ errors: Vec::new(),
+ });
+ let result = write_handle.write(presence).await;
+ let _ = sender.send(result);
+ }
+ CommandMessage::AcceptBuddyRequest(jid, sender) => {
+ let presence = Stanza::Presence(stanza::client::presence::Presence {
+ from: None,
+ id: None,
+ to: Some(jid.clone()),
+ r#type: Some(stanza::client::presence::PresenceType::Subscribed),
+ lang: None,
+ show: None,
+ status: None,
+ priority: None,
+ errors: Vec::new(),
+ });
+ let result = write_handle.write(presence).await;
+ match result {
+ Err(_) => {
+ let _ = sender.send(result);
+ }
+ Ok(()) => {
+ let presence = Stanza::Presence(stanza::client::presence::Presence {
+ from: None,
+ id: None,
+ to: Some(jid),
+ r#type: Some(stanza::client::presence::PresenceType::Subscribe),
+ lang: None,
+ show: None,
+ status: None,
+ priority: None,
+ errors: Vec::new(),
+ });
+ let result = write_handle.write(presence).await;
+ let _ = sender.send(result);
+ }
+ }
+ }
+ CommandMessage::AcceptSubscriptionRequest(jid, sender) => {
+ let presence = Stanza::Presence(stanza::client::presence::Presence {
+ from: None,
+ id: None,
+ to: Some(jid),
+ r#type: Some(stanza::client::presence::PresenceType::Subscribe),
+ lang: None,
+ show: None,
+ status: None,
+ priority: None,
+ errors: Vec::new(),
+ });
+ let result = write_handle.write(presence).await;
+ let _ = sender.send(result);
+ }
+ CommandMessage::UnsubscribeFromContact(jid, sender) => {
+ let presence = Stanza::Presence(stanza::client::presence::Presence {
+ from: None,
+ id: None,
+ to: Some(jid),
+ r#type: Some(stanza::client::presence::PresenceType::Unsubscribe),
+ lang: None,
+ show: None,
+ status: None,
+ priority: None,
+ errors: Vec::new(),
+ });
+ let result = write_handle.write(presence).await;
+ let _ = sender.send(result);
+ }
+ CommandMessage::UnsubscribeContact(jid, sender) => {
+ let presence = Stanza::Presence(stanza::client::presence::Presence {
+ from: None,
+ id: None,
+ to: Some(jid),
+ r#type: Some(stanza::client::presence::PresenceType::Unsubscribed),
+ lang: None,
+ show: None,
+ status: None,
+ priority: None,
+ errors: Vec::new(),
+ });
+ let result = write_handle.write(presence).await;
+ let _ = sender.send(result);
+ }
+ CommandMessage::UnfriendContact(jid, sender) => {
+ let presence = Stanza::Presence(stanza::client::presence::Presence {
+ from: None,
+ id: None,
+ to: Some(jid.clone()),
+ r#type: Some(stanza::client::presence::PresenceType::Unsubscribe),
+ lang: None,
+ show: None,
+ status: None,
+ priority: None,
+ errors: Vec::new(),
+ });
+ let result = write_handle.write(presence).await;
+ match result {
+ Err(_) => {
+ let _ = sender.send(result);
+ }
+ Ok(()) => {
+ let presence = Stanza::Presence(stanza::client::presence::Presence {
+ from: None,
+ id: None,
+ to: Some(jid),
+ r#type: Some(stanza::client::presence::PresenceType::Unsubscribed),
+ lang: None,
+ show: None,
+ status: None,
+ priority: None,
+ errors: Vec::new(),
+ });
+ let result = write_handle.write(presence).await;
+ let _ = sender.send(result);
+ }
+ }
+ }
+ CommandMessage::DeleteContact(jid, sender) => {
+ let owned_jid;
+ {
+ owned_jid = client_jid.lock().await.clone();
+ }
+ let iq_id = Uuid::new_v4().to_string();
+ let set_stanza = Stanza::Iq(Iq {
+ from: Some(owned_jid),
+ id: iq_id.clone(),
+ to: None,
+ r#type: IqType::Set,
+ lang: None,
+ query: Some(iq::Query::Roster(stanza::roster::Query {
+ ver: None,
+ items: vec![stanza::roster::Item {
+ approved: None,
+ ask: false,
+ jid,
+ name: None,
+ subscription: Some(stanza::roster::Subscription::Remove),
+ groups: Vec::new(),
+ }],
+ })),
+ errors: Vec::new(),
+ });
+ let (send, recv) = oneshot::channel();
+ {
+ pending_iqs.lock().await.insert(iq_id.clone(), send);
+ }
+ let result = write_handle.write(set_stanza).await;
+ if let Err(_) = result {
+ sender.send(result);
+ return;
+ }
+ let iq_result = recv.await;
+ match iq_result {
+ Ok(i) => match i {
+ Ok(iq_result) => match iq_result {
+ Stanza::Iq(Iq {
+ from: _,
+ id,
+ to: _,
+ r#type,
+ lang: _,
+ query: _,
+ errors: _,
+ }) if id == iq_id && r#type == IqType::Result => {
+ sender.send(Ok(()));
+ return;
+ }
+ Stanza::Iq(Iq {
+ from: _,
+ id,
+ to: _,
+ r#type,
+ lang: _,
+ query: _,
+ errors,
+ }) if id == iq_id && r#type == IqType::Error => {
+ if let Some(error) = errors.first() {
+ sender.send(Err(Reason::Stanza(Some(error.clone()))));
+ } else {
+ sender.send(Err(Reason::Stanza(None)));
+ }
+ return;
+ }
+ s => {
+ sender.send(Err(Reason::UnexpectedStanza(s)));
+ return;
+ }
+ },
+ Err(e) => {
+ sender.send(Err(e.into()));
+ return;
+ }
+ },
+ Err(e) => {
+ sender.send(Err(e.into()));
+ return;
+ }
+ }
+ }
+ CommandMessage::UpdateContact(jid, contact_update, sender) => {
+ let owned_jid;
+ {
+ owned_jid = client_jid.lock().await.clone();
+ }
+ let iq_id = Uuid::new_v4().to_string();
+ let groups = Vec::from_iter(
+ contact_update
+ .groups
+ .into_iter()
+ .map(|group| stanza::roster::Group(Some(group))),
+ );
+ let set_stanza = Stanza::Iq(Iq {
+ from: Some(owned_jid),
+ id: iq_id.clone(),
+ to: None,
+ r#type: IqType::Set,
+ lang: None,
+ query: Some(iq::Query::Roster(stanza::roster::Query {
+ ver: None,
+ items: vec![stanza::roster::Item {
+ approved: None,
+ ask: false,
+ jid,
+ name: contact_update.name,
+ subscription: None,
+ groups,
+ }],
+ })),
+ errors: Vec::new(),
+ });
+ let (send, recv) = oneshot::channel();
+ {
+ pending_iqs.lock().await.insert(iq_id.clone(), send);
+ }
+ let result = write_handle.write(set_stanza).await;
+ if let Err(_) = result {
+ sender.send(result);
+ return;
+ }
+ let iq_result = recv.await;
+ match iq_result {
+ Ok(i) => match i {
+ Ok(iq_result) => match iq_result {
+ Stanza::Iq(Iq {
+ from: _,
+ id,
+ to: _,
+ r#type,
+ lang: _,
+ query: _,
+ errors: _,
+ }) if id == iq_id && r#type == IqType::Result => {
+ sender.send(Ok(()));
+ return;
+ }
+ Stanza::Iq(Iq {
+ from: _,
+ id,
+ to: _,
+ r#type,
+ lang: _,
+ query: _,
+ errors,
+ }) if id == iq_id && r#type == IqType::Error => {
+ if let Some(error) = errors.first() {
+ sender.send(Err(Reason::Stanza(Some(error.clone()))));
+ } else {
+ sender.send(Err(Reason::Stanza(None)));
+ }
+ return;
+ }
+ s => {
+ sender.send(Err(Reason::UnexpectedStanza(s)));
+ return;
+ }
+ },
+ Err(e) => {
+ sender.send(Err(e.into()));
+ return;
+ }
+ },
+ Err(e) => {
+ sender.send(Err(e.into()));
+ return;
+ }
+ }
+ }
+ CommandMessage::SetStatus(online, sender) => {
+ let result = db.upsert_cached_status(online.clone()).await;
+ if let Err(e) = result {
+ let _ = update_sender
+ .send(UpdateMessage::Error(Error::CacheUpdate(e.into())))
+ .await;
+ }
+ let result = write_handle
+ .write(Stanza::Presence(online.into()))
+ .await
+ .map_err(|e| StatusError(e));
+ let _ = sender.send(result);
+ }
+ // TODO: offline message queue
+ CommandMessage::SendMessage(jid, body, sender) => {
+ let id = Uuid::new_v4();
+ let owned_jid: JID;
+ {
+ // TODO: timeout
+ owned_jid = client_jid.lock().await.clone();
+ }
+ let message = Stanza::Message(stanza::client::message::Message {
+ from: Some(owned_jid.clone()),
+ id: Some(id.to_string()),
+ to: Some(jid.clone()),
+ // TODO: specify message type
+ r#type: stanza::client::message::MessageType::Chat,
+ // TODO: lang ?
+ lang: None,
+ subject: None,
+ body: Some(stanza::client::message::Body {
+ lang: None,
+ body: Some(body.body.clone()),
+ }),
+ thread: None,
+ });
+ let result = write_handle.write(message).await;
+ match result {
+ Ok(_) => {
+ let message = Message {
+ id,
+ from: owned_jid,
+ body,
+ };
+ if let Err(e) = db.create_message(message, jid).await.map_err(|e| e.into())
+ {
+ let _ = update_sender.send(UpdateMessage::Error(Error::CacheUpdate(e)));
+ }
+ let _ = sender.send(Ok(()));
+ }
+ Err(_) => {
+ let _ = sender.send(result);
+ }
+ }
+ }
}
}
}
@@ -484,46 +1010,46 @@ pub enum CommandMessage {
GetRoster(oneshot::Sender<Result<Vec<Contact>, RosterError>>),
/// get all chats. chat will include 10 messages in their message Vec (enough for chat previews)
// TODO: paging and filtering
- GetChats(oneshot::Sender<Result<Vec<Chat>, Error>>),
+ GetChats(oneshot::Sender<Result<Vec<Chat>, Reason>>),
/// get a specific chat by jid
- GetChat(JID, oneshot::Sender<Result<Chat, Error>>),
+ GetChat(JID, oneshot::Sender<Result<Chat, Reason>>),
/// get message history for chat (does appropriate mam things)
// TODO: paging and filtering
- GetMessages(JID, oneshot::Sender<Result<Vec<Message>, Error>>),
+ GetMessages(JID, oneshot::Sender<Result<Vec<Message>, Reason>>),
/// delete a chat from your chat history, along with all the corresponding messages
- DeleteChat(JID, oneshot::Sender<Result<(), Error>>),
+ DeleteChat(JID, oneshot::Sender<Result<(), Reason>>),
/// delete a message from your chat history
- DeleteMessage(Uuid, oneshot::Sender<Result<(), Error>>),
+ DeleteMessage(Uuid, oneshot::Sender<Result<(), Reason>>),
/// get a user from your users database
- GetUser(JID, oneshot::Sender<Result<User, Error>>),
+ GetUser(JID, oneshot::Sender<Result<User, Reason>>),
/// add a contact to your roster, with a status of none, no subscriptions.
// TODO: for all these, consider returning with oneshot::Sender<Result<(), Error>>
- AddContact(JID, oneshot::Sender<Result<(), Error>>),
+ AddContact(JID, oneshot::Sender<Result<(), Reason>>),
/// send a friend request i.e. a subscription request with a subscription pre-approval. if not already added to roster server adds to roster.
- BuddyRequest(JID, oneshot::Sender<Result<(), Error>>),
+ BuddyRequest(JID, oneshot::Sender<Result<(), Reason>>),
/// send a subscription request, without pre-approval. if not already added to roster server adds to roster.
- SubscriptionRequest(JID, oneshot::Sender<Result<(), Error>>),
+ SubscriptionRequest(JID, oneshot::Sender<Result<(), Reason>>),
/// accept a friend request by accepting a pending subscription and sending a subscription request back. if not already added to roster adds to roster.
- AcceptBuddyRequest(JID, oneshot::Sender<Result<(), Error>>),
+ AcceptBuddyRequest(JID, oneshot::Sender<Result<(), Reason>>),
/// accept a pending subscription and doesn't send a subscription request back. if not already added to roster adds to roster.
- AcceptSubscriptionRequest(JID, oneshot::Sender<Result<(), Error>>),
+ AcceptSubscriptionRequest(JID, oneshot::Sender<Result<(), Reason>>),
/// unsubscribe to a contact, but don't remove their subscription.
- UnsubscribeFromContact(JID, oneshot::Sender<Result<(), Error>>),
+ UnsubscribeFromContact(JID, oneshot::Sender<Result<(), Reason>>),
/// stop a contact from being subscribed, but stay subscribed to the contact.
- UnsubscribeContact(JID, oneshot::Sender<Result<(), Error>>),
+ UnsubscribeContact(JID, oneshot::Sender<Result<(), Reason>>),
/// remove subscriptions to and from contact, but keep in roster.
- UnfriendContact(JID, oneshot::Sender<Result<(), Error>>),
+ UnfriendContact(JID, oneshot::Sender<Result<(), Reason>>),
/// remove a contact from the contact list. will remove subscriptions if not already done then delete contact from roster.
- DeleteContact(JID, oneshot::Sender<Result<(), Error>>),
- /// update contact
- UpdateContact(JID, ContactUpdate, oneshot::Sender<Result<(), Error>>),
+ DeleteContact(JID, oneshot::Sender<Result<(), Reason>>),
+ /// update contact. contact details will be overwritten with the contents of the contactupdate struct.
+ UpdateContact(JID, ContactUpdate, oneshot::Sender<Result<(), Reason>>),
/// set online status. if disconnected, will be cached so when client connects, will be sent as the initial presence.
SetStatus(Online, oneshot::Sender<Result<(), StatusError>>),
/// send a directed presence (usually to a non-contact).
// TODO: should probably make it so people can add non-contact auto presence sharing in the client (most likely through setting an internal setting)
/// send a message to a jid (any kind of jid that can receive a message, e.g. a user or a
/// chatroom). if disconnected, will be cached so when client connects, message will be sent.
- SendMessage(JID, Body, oneshot::Sender<Result<(), Error>>),
+ SendMessage(JID, Body, oneshot::Sender<Result<(), Reason>>),
}
#[derive(Debug)]
diff --git a/luz/src/presence.rs b/luz/src/presence.rs
index fac1bb4..563121b 100644
--- a/luz/src/presence.rs
+++ b/luz/src/presence.rs
@@ -1,4 +1,5 @@
use sqlx::Sqlite;
+use stanza::client::presence::String1024;
#[derive(Debug, Default, sqlx::FromRow, Clone)]
pub struct Online {
@@ -62,3 +63,30 @@ pub enum Presence {
Online(Online),
Offline(Offline),
}
+
+impl From<Online> for stanza::client::presence::Presence {
+ fn from(value: Online) -> Self {
+ Self {
+ from: None,
+ id: None,
+ to: None,
+ r#type: None,
+ lang: None,
+ show: value.show.map(|show| match show {
+ Show::Away => stanza::client::presence::Show::Away,
+ Show::Chat => stanza::client::presence::Show::Chat,
+ Show::DoNotDisturb => stanza::client::presence::Show::Dnd,
+ Show::ExtendedAway => stanza::client::presence::Show::Xa,
+ }),
+ // TODO: enforce message length in status message
+ status: value.status.map(|status| stanza::client::presence::Status {
+ lang: None,
+ status: String1024(status),
+ }),
+ priority: value
+ .priority
+ .map(|priority| stanza::client::presence::Priority(priority)),
+ errors: Vec::new(),
+ }
+ }
+}
diff --git a/luz/src/roster.rs b/luz/src/roster.rs
index 90bf436..e3db00f 100644
--- a/luz/src/roster.rs
+++ b/luz/src/roster.rs
@@ -3,10 +3,9 @@ use std::collections::HashSet;
use jid::JID;
use sqlx::Sqlite;
-pub enum ContactUpdate {
- Name(Option<String>),
- AddToGroup(String),
- RemoveFromGroup(String),
+pub struct ContactUpdate {
+ pub name: Option<String>,
+ pub groups: HashSet<String>,
}
#[derive(Debug, sqlx::FromRow, Clone)]
diff --git a/stanza/src/client/message.rs b/stanza/src/client/message.rs
index 2337d7b..703b80b 100644
--- a/stanza/src/client/message.rs
+++ b/stanza/src/client/message.rs
@@ -10,16 +10,16 @@ use super::XMLNS;
#[derive(Debug)]
pub struct Message {
- from: Option<JID>,
- id: Option<String>,
- to: Option<JID>,
+ pub from: Option<JID>,
+ pub id: Option<String>,
+ pub to: Option<JID>,
// can be omitted, if so default to normal
- r#type: MessageType,
- lang: Option<String>,
+ pub r#type: MessageType,
+ pub lang: Option<String>,
// children
- subject: Option<Subject>,
- body: Option<Body>,
- thread: Option<Thread>,
+ pub subject: Option<Subject>,
+ pub body: Option<Body>,
+ pub thread: Option<Thread>,
}
impl FromElement for Message {
@@ -109,8 +109,8 @@ impl ToString for MessageType {
#[derive(Clone, Debug)]
pub struct Body {
- lang: Option<String>,
- body: Option<String>,
+ pub lang: Option<String>,
+ pub body: Option<String>,
}
impl FromElement for Body {
diff --git a/stanza/src/client/presence.rs b/stanza/src/client/presence.rs
index 877c4c6..ff7ba24 100644
--- a/stanza/src/client/presence.rs
+++ b/stanza/src/client/presence.rs
@@ -10,18 +10,18 @@ use super::{error::Error, XMLNS};
#[derive(Debug)]
pub struct Presence {
- from: Option<JID>,
- id: Option<String>,
- to: Option<JID>,
- r#type: Option<PresenceType>,
- lang: Option<String>,
+ pub from: Option<JID>,
+ pub id: Option<String>,
+ pub to: Option<JID>,
+ pub r#type: Option<PresenceType>,
+ pub lang: Option<String>,
// children
- show: Option<Show>,
- status: Option<Status>,
- priority: Option<Priority>,
+ pub show: Option<Show>,
+ pub status: Option<Status>,
+ pub priority: Option<Priority>,
// TODO: ##other
// other: Vec<Other>,
- errors: Vec<Error>,
+ pub errors: Vec<Error>,
}
impl FromElement for Presence {
@@ -165,8 +165,8 @@ impl ToString for Show {
#[derive(Clone, Debug)]
pub struct Status {
- lang: Option<String>,
- status: String1024,
+ pub lang: Option<String>,
+ pub status: String1024,
}
impl FromElement for Status {