diff --git a/examples/ssb-client.rs b/examples/ssb-client.rs new file mode 100644 index 0000000..2a50bff --- /dev/null +++ b/examples/ssb-client.rs @@ -0,0 +1,32 @@ +use std::process; + +use kuska_ssb::api::dto::content::Post; + +use golgi::error::GolgiError; +use golgi::sbot::Sbot; + +async fn run() -> Result<(), GolgiError> { + let mut sbot_client = Sbot::init(None, None).await?; + + let id = sbot_client.whoami().await?; + println!("{}", id); + + let post = Post::new( + "glyph from golgi".to_string(), + // mentions + None, + ); + + let msg_ref = sbot_client.publish_post(post).await?; + println!("{}", msg_ref); + + Ok(()) +} + +#[async_std::main] +async fn main() { + if let Err(e) = run().await { + eprintln!("Application error: {}", e); + process::exit(1); + } +} diff --git a/src/lib.rs b/src/lib.rs index f94841c..b96bc45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,106 +1,5 @@ -mod error; +pub mod error; +pub mod sbot; mod utils; -use async_std::net::TcpStream; -//use futures::io::{self, AsyncRead as Read, AsyncWrite as Write}; -use kuska_handshake::async_std::{BoxStream, BoxStreamRead, BoxStreamWrite}; -use kuska_handshake::HandshakeComplete; -use kuska_sodiumoxide::crypto::{auth, sign::ed25519}; -use kuska_ssb::api::{dto::CreateHistoryStreamIn, ApiCaller}; -use kuska_ssb::discovery; -use kuska_ssb::keystore; -use kuska_ssb::keystore::OwnedIdentity; -use kuska_ssb::rpc::{RpcReader, RpcWriter}; - -use crate::error::GolgiError; - -struct Sbot { - id: String, - public_key: ed25519::PublicKey, - private_key: ed25519::SecretKey, - address: String, - // aka caps key (scuttleverse identifier) - network_id: auth::Key, - client: ApiCaller, - rpc_reader: RpcReader, -} - -impl Sbot { - async fn init(ip_port: Option, net_id: Option) -> Result { - let address; - if ip_port.is_none() { - address = "127.0.0.1:8008".to_string(); - } else { - address = ip_port.unwrap(); - } - - let network_id; - if net_id.is_none() { - network_id = discovery::ssb_net_id(); - } else { - network_id = auth::Key::from_slice(&hex::decode(net_id.unwrap()).unwrap()).unwrap(); - } - - let OwnedIdentity { pk, sk, id } = keystore::from_gosbot_local() - .await - .expect("couldn't read local secret"); - - let socket = TcpStream::connect(&address) - .await - .map_err(|source| GolgiError::Io { - source, - context: "socket error; failed to initiate tcp stream connection".to_string(), - })?; - - let handshake = kuska_handshake::async_std::handshake_client( - &mut &socket, - network_id.clone(), - pk, - sk.clone(), - pk, - ) - .await - .map_err(GolgiError::Handshake)?; - - let (box_stream_read, box_stream_write) = - BoxStream::from_handshake(socket.clone(), socket, handshake, 0x8000).split_read_write(); - - let rpc_reader = RpcReader::new(box_stream_read); - let client = ApiCaller::new(RpcWriter::new(box_stream_write)); - - Ok(Self { - id, - public_key: pk, - private_key: sk, - address, - network_id, - client: client, - rpc_reader: rpc_reader, - }) - } - - async fn whoami(&mut self) -> Result { - let req_id = self.client.whoami_req_send().await?; - - utils::get_async(&mut self.rpc_reader, req_id, utils::whoami_res_parse) - .await - .map(|whoami| whoami.id) - } - - async fn create_history_stream(&mut self, id: String) -> Result<(), GolgiError> { - let args = CreateHistoryStreamIn::new(id); - let req_id = self.client.create_history_stream_req_send(&args).await?; - utils::print_source_until_eof(&mut self.rpc_reader, req_id, utils::feed_res_parse).await - } -} - -pub async fn run() -> Result<(), GolgiError> { - let mut sbot_client = Sbot::init(None, None).await?; - - let id = sbot_client.whoami().await?; - println!("{}", id); - - sbot_client.create_history_stream(id).await?; - - Ok(()) -} +pub use crate::error::GolgiError; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index ee17c18..0000000 --- a/src/main.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::process; - -#[async_std::main] -async fn main() { - if let Err(e) = golgi::run().await { - eprintln!("Application error: {}", e); - process::exit(1); - } -} diff --git a/src/sbot.rs b/src/sbot.rs new file mode 100644 index 0000000..33e6137 --- /dev/null +++ b/src/sbot.rs @@ -0,0 +1,101 @@ +use async_std::net::TcpStream; +//use futures::io::{self, AsyncRead as Read, AsyncWrite as Write}; +use kuska_handshake::async_std::BoxStream; +use kuska_sodiumoxide::crypto::{auth, sign::ed25519}; +use kuska_ssb::api::{ + dto::{content::Post, CreateHistoryStreamIn}, + ApiCaller, +}; +use kuska_ssb::discovery; +use kuska_ssb::keystore; +use kuska_ssb::keystore::OwnedIdentity; +use kuska_ssb::rpc::{RpcReader, RpcWriter}; + +use crate::error::GolgiError; +use crate::utils; + +pub struct Sbot { + id: String, + public_key: ed25519::PublicKey, + private_key: ed25519::SecretKey, + address: String, + // aka caps key (scuttleverse identifier) + network_id: auth::Key, + client: ApiCaller, + rpc_reader: RpcReader, +} + +impl Sbot { + pub async fn init(ip_port: Option, net_id: Option) -> Result { + let address; + if ip_port.is_none() { + address = "127.0.0.1:8008".to_string(); + } else { + address = ip_port.unwrap(); + } + + let network_id; + if net_id.is_none() { + network_id = discovery::ssb_net_id(); + } else { + network_id = auth::Key::from_slice(&hex::decode(net_id.unwrap()).unwrap()).unwrap(); + } + + let OwnedIdentity { pk, sk, id } = keystore::from_gosbot_local() + .await + .expect("couldn't read local secret"); + + let socket = TcpStream::connect(&address) + .await + .map_err(|source| GolgiError::Io { + source, + context: "socket error; failed to initiate tcp stream connection".to_string(), + })?; + + let handshake = kuska_handshake::async_std::handshake_client( + &mut &socket, + network_id.clone(), + pk, + sk.clone(), + pk, + ) + .await + .map_err(GolgiError::Handshake)?; + + let (box_stream_read, box_stream_write) = + BoxStream::from_handshake(socket.clone(), socket, handshake, 0x8000).split_read_write(); + + let rpc_reader = RpcReader::new(box_stream_read); + let client = ApiCaller::new(RpcWriter::new(box_stream_write)); + + Ok(Self { + id, + public_key: pk, + private_key: sk, + address, + network_id, + client, + rpc_reader, + }) + } + + pub async fn whoami(&mut self) -> Result { + let req_id = self.client.whoami_req_send().await?; + + utils::get_async(&mut self.rpc_reader, req_id, utils::whoami_res_parse) + .await + .map(|whoami| whoami.id) + } + + pub async fn publish_post(&mut self, post: Post) -> Result { + let req_id = self.client.publish_req_send(post).await?; + + utils::get_async(&mut self.rpc_reader, req_id, utils::publish_res_parse).await + } + + async fn create_history_stream(&mut self, id: String) -> Result<(), GolgiError> { + let args = CreateHistoryStreamIn::new(id); + let req_id = self.client.create_history_stream_req_send(&args).await?; + utils::print_source_until_eof(&mut self.rpc_reader, req_id, utils::feed_res_parse).await + } +} diff --git a/src/utils.rs b/src/utils.rs index 3a71d51..ba3d65b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -10,7 +10,7 @@ use std::fmt::Debug; use async_std::io::Read; -use kuska_ssb::api::dto::WhoAmIOut; +use kuska_ssb::api::dto::{PublishOut, WhoAmIOut}; use kuska_ssb::feed::Feed; use kuska_ssb::rpc::{RecvMsg, RequestNo, RpcReader}; @@ -20,6 +20,13 @@ pub fn feed_res_parse(body: &[u8]) -> Result { Ok(Feed::from_slice(body)?) } +//pub fn publish_res_parse(body: &[u8]) -> Result { +pub fn publish_res_parse(body: &[u8]) -> Result { + //Ok(serde_json::from_slice(body)?) + // TODO: cleanup with proper error handling etc. + Ok(std::str::from_utf8(body).unwrap().to_string()) +} + pub fn whoami_res_parse(body: &[u8]) -> Result { Ok(serde_json::from_slice(body)?) }