From 696701e855319419c5c1efae961be335fa70b98a Mon Sep 17 00:00:00 2001 From: notplants Date: Wed, 29 Dec 2021 15:12:20 -0500 Subject: [PATCH] Get description --- src/messages.rs | 57 ++++++++++++++++++++++++++++++++++++++-------- src/sbot.rs | 60 +++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 96 insertions(+), 21 deletions(-) diff --git a/src/messages.rs b/src/messages.rs index ee39a07..9434e80 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -29,16 +29,55 @@ pub struct SsbMessageValue { pub signature: String, } +// Enum representing the different possible message content types +#[derive(Debug)] +pub enum SsbMessageContentType { + About, + Vote, + Post, + Contact, + None +} + impl SsbMessageValue { - /// Gets the type field of the message content if found, - /// and if not returns "none" - pub fn get_message_type(&self) -> String { - let msg_type: String = self - .content - .get("type") - .map(|msg_type| msg_type.to_string()) - .unwrap_or_else(|| "none".to_string()); - msg_type + + /// Gets the type field of the message content as an enum, if found. + /// if no type field is found, it returns a SsbMessageContentType::None + /// if a type field is found with an invalid format it returns a GolgiError::ContentTypeDecode + pub fn get_message_type(&self) -> Result { + let msg_type = self + .content + .get("type"); + let enum_type = match msg_type { + Some(mtype) => { + let mtype_str: &str = mtype.as_str().ok_or(GolgiError::ContentTypeDecode("type field with invalid format".to_string()))?; + match mtype_str { + "about" => SsbMessageContentType::About, + "post" => SsbMessageContentType::Post, + "vote" => SsbMessageContentType::Vote, + "contact" => SsbMessageContentType::Contact, + _ => return Err(GolgiError::ContentTypeDecode("type field with unknown value".to_string())) + } + }, + None => { + SsbMessageContentType::None + } + }; + Ok(enum_type) + } + + /// Helper function which returns true if this message is of the given type, + /// and false if the type does not match or is not found + pub fn is_message_type(&self, message_type: SsbMessageContentType) -> bool { + let self_message_type = self.get_message_type(); + match self_message_type { + Ok(mtype) => { + matches!(mtype, message_type) + } + Err(_err) => { + false + } + } } /// Converts the content json value into an SsbMessageContent enum, diff --git a/src/sbot.rs b/src/sbot.rs index 341ee03..56cfeda 100644 --- a/src/sbot.rs +++ b/src/sbot.rs @@ -5,7 +5,7 @@ use kuska_handshake::async_std::BoxStream; use kuska_sodiumoxide::crypto::{auth, sign::ed25519}; use kuska_ssb::{ api::{ - dto::{content::SubsetQuery, CreateHistoryStreamIn}, + dto::{CreateHistoryStreamIn}, ApiCaller, }, discovery, keystore, @@ -14,9 +14,12 @@ use kuska_ssb::{ }; use crate::error::GolgiError; -use crate::messages::{SsbKVT, SsbMessageContent, SsbMessageValue}; +use crate::messages::{SsbKVT, SsbMessageContent, SsbMessageValue, SsbMessageContentType}; use crate::utils; +// re-export types from kuska +pub use kuska_ssb::api::dto::content::SubsetQuery; + /// The Scuttlebutt identity, keys and configuration parameters for connecting to a local sbot /// instance, as well as handles for calling RPC methods and receiving responses. pub struct Sbot { @@ -154,20 +157,53 @@ impl Sbot { } - - /// Get the about messages for a particular user. - pub async fn get_about_messages(&mut self, ssb_id: String) -> Result { - let req_id = self.client.getsubset_req_send(query).await?; - - utils::get_async_until_eof(&mut self.rpc_reader, req_id, utils::kvt_res_parse).await + /// Get the about messages for a particular user in order of recency. + pub async fn get_about_messages(&mut self, ssb_id: String) -> Result, GolgiError> { + let query = SubsetQuery::Author{ + op: "author".to_string(), + feed: self.id.to_string(), + }; + let kvts: Vec = self.get_subset(query).await?; + let messages: Vec = kvts.into_iter().map(|kvt| kvt.value).collect(); + // TODO: after fixing sbot regression, + // change this subset query to filter by type about in addition to author + // and remove this filter section + // filter down to about messages + let mut about_messages: Vec = messages.into_iter().filter(|msg| { + msg.is_message_type(SsbMessageContentType::About) + }).collect(); + // TODO: use subset query to order messages instead of doing it this way + about_messages.sort_by(|a, b| { + a.timestamp.partial_cmp(&b.timestamp).unwrap() + }); + // return about messages + Ok(about_messages) } - /// Get the latest description for a particular user from their about messages. pub async fn get_description(&mut self, ssb_id: String) -> Result { - let req_id = self.client.getsubset_req_send(query).await?; - - utils::get_async_until_eof(&mut self.rpc_reader, req_id, utils::kvt_res_parse).await + // vector of about messages with most recent at the front of the vector + let about_messages = self.get_about_messages(ssb_id).await?; + // iterate through the vector looking for an about message with a description + // the first one we find is th emost recnet + for msg in about_messages { + let about_message = msg.into_ssb_message_content()?; + match about_message { + SsbMessageContent::About{description, ..} => { + match description { + Some(description_text) => { + return Ok(description_text) + }, + None => { + continue + } + } + }, + _ => {} + } + } + // if no about message with a description was found, then return the empty string + Ok("".to_string()) } /// Call the `createHistoryStream` RPC method and return a vector