aboutsummaryrefslogtreecommitdiffstats
path: root/filamento/src/lib.rs
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@bunny.garden>2025-04-08 10:38:18 +0100
committerLibravatar cel 🌸 <cel@bunny.garden>2025-04-08 10:38:18 +0100
commit5b644e2dc8712d56931b410b9c46dae1ef36e691 (patch)
tree2290b3ab2a5829c3b8406ce073a95474e146a680 /filamento/src/lib.rs
parentc24541b129a14a598afe00ed4244d49d27513310 (diff)
downloadluz-5b644e2dc8712d56931b410b9c46dae1ef36e691.tar.gz
luz-5b644e2dc8712d56931b410b9c46dae1ef36e691.tar.bz2
luz-5b644e2dc8712d56931b410b9c46dae1ef36e691.zip
feat(filamento): user avatar publishing and processing
Diffstat (limited to '')
-rw-r--r--filamento/src/lib.rs154
1 files changed, 120 insertions, 34 deletions
diff --git a/filamento/src/lib.rs b/filamento/src/lib.rs
index c44edca..42646be 100644
--- a/filamento/src/lib.rs
+++ b/filamento/src/lib.rs
@@ -11,9 +11,10 @@ use chrono::Utc;
use db::Db;
use disco::{Info, Items};
use error::{
- ConnectionJobError, DatabaseError, DiscoError, Error, IqError, MessageRecvError, NickError,
- PresenceError, PublishError, RosterError, StatusError, SubscribeError,
+ AvatarPublishError, ConnectionJobError, DatabaseError, DiscoError, Error, IqError,
+ MessageRecvError, NickError, PEPError, PresenceError, RosterError, StatusError, SubscribeError,
};
+use files::FileStore;
use futures::FutureExt;
use jid::JID;
use lampada::{
@@ -35,18 +36,20 @@ use tracing::{debug, info};
use user::User;
use uuid::Uuid;
+pub mod avatar;
pub mod caps;
pub mod chat;
pub mod db;
pub mod disco;
pub mod error;
+pub mod files;
mod logic;
pub mod pep;
pub mod presence;
pub mod roster;
pub mod user;
-pub enum Command {
+pub enum Command<Fs: FileStore> {
/// get the roster. if offline, retreive cached version from database. should be stored in application memory
GetRoster(oneshot::Sender<Result<Vec<Contact>, RosterError>>),
/// get all chats. chat will include 10 messages in their message Vec (enough for chat previews)
@@ -115,16 +118,34 @@ pub enum Command {
Option<String>,
oneshot::Sender<Result<disco::Items, DiscoError>>,
),
- /// publish item to a pep node, specified or default according to item.
- Publish {
+ /// publish item to a pep node specified.
+ PublishPEPItem {
item: pep::Item,
node: String,
- sender: oneshot::Sender<Result<(), PublishError>>,
+ sender: oneshot::Sender<Result<(), PEPError>>,
},
- /// change user nickname
- ChangeNick(String, oneshot::Sender<Result<(), NickError>>),
+ DeletePEPNode {
+ node: String,
+ sender: oneshot::Sender<Result<(), PEPError>>,
+ },
+ GetPEPItem {
+ jid: Option<JID>,
+ node: String,
+ id: String,
+ sender: oneshot::Sender<Result<pep::Item, PEPError>>,
+ },
+ /// change client user nickname
+ ChangeNick(Option<String>, oneshot::Sender<Result<(), NickError>>),
+ // // TODO
+ // GetNick(...),
+ // GetAvatar(...)
// /// get capability node
// GetCaps(String, oneshot::Sender<Result<Info, CapsError>>),
+ /// change client user avatar
+ ChangeAvatar(
+ Option<Vec<u8>>,
+ oneshot::Sender<Result<(), AvatarPublishError<Fs>>>,
+ ),
}
#[derive(Debug, Clone)]
@@ -155,18 +176,22 @@ pub enum UpdateMessage {
SubscriptionRequest(jid::JID),
NickChanged {
jid: JID,
- nick: String,
+ nick: Option<String>,
+ },
+ AvatarChanged {
+ jid: JID,
+ id: Option<String>,
},
}
/// an xmpp client that is suited for a chat client use case
#[derive(Debug)]
-pub struct Client {
- sender: mpsc::Sender<CoreClientCommand<Command>>,
+pub struct Client<Fs: FileStore> {
+ sender: mpsc::Sender<CoreClientCommand<Command<Fs>>>,
timeout: Duration,
}
-impl Clone for Client {
+impl<Fs: FileStore> Clone for Client<Fs> {
fn clone(&self) -> Self {
Self {
sender: self.sender.clone(),
@@ -175,32 +200,27 @@ impl Clone for Client {
}
}
-impl Deref for Client {
- type Target = mpsc::Sender<CoreClientCommand<Command>>;
+impl<Fs: FileStore> Deref for Client<Fs> {
+ type Target = mpsc::Sender<CoreClientCommand<Command<Fs>>>;
fn deref(&self) -> &Self::Target {
&self.sender
}
}
-impl DerefMut for Client {
+impl<Fs: FileStore> DerefMut for Client<Fs> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.sender
}
}
-impl Client {
- pub async fn connect(&self) -> Result<(), ActorError> {
- self.send(CoreClientCommand::Connect).await?;
- Ok(())
- }
-
- pub async fn disconnect(&self, offline: Offline) -> Result<(), ActorError> {
- self.send(CoreClientCommand::Disconnect).await?;
- Ok(())
- }
-
- pub fn new(jid: JID, password: String, db: Db) -> (Self, mpsc::Receiver<UpdateMessage>) {
+impl<Fs: FileStore + Clone + Send + Sync + 'static> Client<Fs> {
+ pub fn new(
+ jid: JID,
+ password: String,
+ db: Db,
+ file_store: Fs,
+ ) -> (Self, mpsc::Receiver<UpdateMessage>) {
let (command_sender, command_receiver) = mpsc::channel(20);
let (update_send, update_recv) = mpsc::channel(20);
@@ -214,14 +234,26 @@ impl Client {
timeout: Duration::from_secs(10),
};
- let logic = ClientLogic::new(client.clone(), jid.as_bare(), db, update_send);
+ let logic = ClientLogic::new(client.clone(), jid.as_bare(), db, update_send, file_store);
- let actor: CoreClient<ClientLogic> =
+ let actor: CoreClient<ClientLogic<Fs>> =
CoreClient::new(jid, password, command_receiver, None, sup_recv, logic);
tokio::spawn(async move { actor.run().await });
(client, update_recv)
}
+}
+
+impl<Fs: FileStore> Client<Fs> {
+ pub async fn connect(&self) -> Result<(), ActorError> {
+ self.send(CoreClientCommand::Connect).await?;
+ Ok(())
+ }
+
+ pub async fn disconnect(&self, offline: Offline) -> Result<(), ActorError> {
+ self.send(CoreClientCommand::Disconnect).await?;
+ Ok(())
+ }
pub async fn get_roster(&self) -> Result<Vec<Contact>, CommandError<RosterError>> {
let (send, recv) = oneshot::channel();
@@ -539,9 +571,9 @@ impl Client {
&self,
item: pep::Item,
node: String,
- ) -> Result<(), CommandError<PublishError>> {
+ ) -> Result<(), CommandError<PEPError>> {
let (send, recv) = oneshot::channel();
- self.send(CoreClientCommand::Command(Command::Publish {
+ self.send(CoreClientCommand::Command(Command::PublishPEPItem {
item,
node,
sender: send,
@@ -555,7 +587,44 @@ impl Client {
Ok(result)
}
- pub async fn change_nick(&self, nick: String) -> Result<(), CommandError<NickError>> {
+ pub async fn delete_pep_node(&self, node: String) -> Result<(), CommandError<PEPError>> {
+ let (send, recv) = oneshot::channel();
+ self.send(CoreClientCommand::Command(Command::DeletePEPNode {
+ node,
+ sender: send,
+ }))
+ .await
+ .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))?;
+ let result = timeout(self.timeout, recv)
+ .await
+ .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))?
+ .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))??;
+ Ok(result)
+ }
+
+ pub async fn get_pep_item(
+ &self,
+ jid: Option<JID>,
+ node: String,
+ id: String,
+ ) -> Result<pep::Item, CommandError<PEPError>> {
+ let (send, recv) = oneshot::channel();
+ self.send(CoreClientCommand::Command(Command::GetPEPItem {
+ jid,
+ node,
+ id,
+ sender: send,
+ }))
+ .await
+ .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))?;
+ let result = timeout(self.timeout, recv)
+ .await
+ .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))?
+ .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))??;
+ Ok(result)
+ }
+
+ pub async fn change_nick(&self, nick: Option<String>) -> Result<(), CommandError<NickError>> {
let (send, recv) = oneshot::channel();
self.send(CoreClientCommand::Command(Command::ChangeNick(nick, send)))
.await
@@ -566,10 +635,27 @@ impl Client {
.map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))??;
Ok(result)
}
+
+ pub async fn change_avatar(
+ &self,
+ avatar: Option<Vec<u8>>,
+ ) -> Result<(), CommandError<AvatarPublishError<Fs>>> {
+ let (send, recv) = oneshot::channel();
+ self.send(CoreClientCommand::Command(Command::ChangeAvatar(
+ avatar, send,
+ )))
+ .await
+ .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))?;
+ let result = timeout(self.timeout, recv)
+ .await
+ .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))?
+ .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))??;
+ Ok(result)
+ }
}
-impl From<Command> for CoreClientCommand<Command> {
- fn from(value: Command) -> Self {
+impl<Fs: FileStore> From<Command<Fs>> for CoreClientCommand<Command<Fs>> {
+ fn from(value: Command<Fs>) -> Self {
CoreClientCommand::Command(value)
}
}