lykin/src/sbot.rs

113 lines
3.5 KiB
Rust

//! Scuttlebutt functionality.
use async_std::stream::StreamExt;
use chrono::NaiveDateTime;
use golgi::{
api::friends::RelationshipQuery,
messages::{SsbMessageContentType, SsbMessageKVT},
sbot::Keystore,
GolgiError, Sbot,
};
use log::warn;
use serde_json::value::Value;
use crate::db::Post;
/// Return the public key of the local sbot instance.
pub async fn whoami() -> Result<String, String> {
let mut sbot = Sbot::init(Keystore::Patchwork, None, None)
.await
.map_err(|e| e.to_string())?;
sbot.whoami().await.map_err(|e| e.to_string())
}
/// Follow a peer.
pub async fn follow_peer(public_key: &str) -> Result<String, String> {
let mut sbot = Sbot::init(Keystore::Patchwork, None, None)
.await
.map_err(|e| e.to_string())?;
sbot.follow(public_key).await.map_err(|e| e.to_string())
}
/// Check follow status.
///
/// Is peer A (`public_key_a`) following peer B (`public_key_b`)?
pub async fn is_following(public_key_a: &str, public_key_b: &str) -> Result<String, String> {
let mut sbot = Sbot::init(Keystore::Patchwork, None, None)
.await
.map_err(|e| e.to_string())?;
let query = RelationshipQuery {
source: public_key_a.to_string(),
dest: public_key_b.to_string(),
};
sbot.friends_is_following(query)
.await
.map_err(|e| e.to_string())
}
/// Return a stream of messages authored by the given public key.
///
/// This returns all messages regardless of type.
pub async fn get_message_stream(
public_key: &str,
) -> impl futures::Stream<Item = Result<SsbMessageKVT, GolgiError>> {
let mut sbot = Sbot::init(Keystore::Patchwork, None, None)
.await
.map_err(|e| e.to_string())
.unwrap();
sbot.create_history_stream(public_key.to_string())
.await
.unwrap()
}
/// Filter a stream of messages and return a vector of root posts.
///
/// Each returned vector element includes the key of the post, the content
/// text, the date the post was published, the sequence number of the post
/// and whether it is read or unread.
pub async fn get_root_posts(
history_stream: impl futures::Stream<Item = Result<SsbMessageKVT, GolgiError>>,
) -> Vec<Post> {
let mut posts = Vec::new();
futures::pin_mut!(history_stream);
while let Some(res) = history_stream.next().await {
match res {
Ok(msg) => {
if msg.value.is_message_type(SsbMessageContentType::Post) {
let content = msg.value.content.to_owned();
if let Value::Object(content_map) = content {
if !content_map.contains_key("root") {
let timestamp_int = msg.value.timestamp.round() as i64 / 1000;
let datetime = NaiveDateTime::from_timestamp(timestamp_int, 0);
let date = datetime.format("%d %b %Y").to_string();
let text = content_map.get_key_value("text").unwrap();
posts.push(Post {
key: msg.key.to_owned(),
text: text.1.to_string(),
date,
sequence: msg.value.sequence,
read: false,
})
}
}
}
}
Err(err) => {
// Print the `GolgiError` of this element to `stderr`.
warn!("err: {:?}", err);
}
}
}
posts
}