improvements for use with imp; migrate echobot to

This commit is contained in:
Dan Ballard 2022-07-21 01:04:21 -07:00
parent 752d2967e0
commit 52b799ef7c
7 changed files with 262 additions and 195 deletions

12
Cargo.lock generated
View File

@ -149,6 +149,7 @@ dependencies = [
"libc", "libc",
"serde", "serde",
"serde_json", "serde_json",
"serde_repr",
"serde_with", "serde_with",
"sha2", "sha2",
] ]
@ -233,6 +234,17 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_repr"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2ad84e47328a31223de7fed7a4f5087f2d6ddfe586cf3ca25b7a165bc0a5aed"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "serde_with" name = "serde_with"
version = "1.11.0" version = "1.11.0"

View File

@ -18,4 +18,5 @@ libc = "0.2"
serde_json = "1.0" serde_json = "1.0"
serde = { version = "1.0.127", features = ["derive"] } serde = { version = "1.0.127", features = ["derive"] }
serde_with = { version = "1.10.0" } serde_with = { version = "1.10.0" }
serde_repr = "0.1"
chrono = "0.4.19" chrono = "0.4.19"

View File

@ -3,6 +3,7 @@ use std::thread;
use libcwtch; use libcwtch;
use libcwtch::structs::*; use libcwtch::structs::*;
use libcwtch::CwtchLib; use libcwtch::CwtchLib;
use libcwtch::event::Event;
fn main() { fn main() {
let bot_home = "example_cwtch_dir"; let bot_home = "example_cwtch_dir";
@ -19,46 +20,30 @@ fn main() {
let event_loop_handle = thread::spawn(move || { let event_loop_handle = thread::spawn(move || {
loop { loop {
let event_str = cwtch.get_appbus_event(); let event = cwtch.get_appbus_event();
println!("event: {}", event_str); println!("event: {:?}", event);
let event: CwtchEvent = match event {
serde_json::from_str(&event_str).expect("Error parsing Cwtch event"); Event::CwtchStarted => {
match event.event_type.as_str() {
"CwtchStarted" => {
println!("event CwtchStarted!"); println!("event CwtchStarted!");
println!("Creating bot"); println!("Creating bot");
cwtch.create_profile("Echobot", "be gay do crime"); cwtch.create_profile("Echobot", "be gay do crime");
} }
"NewPeer" => { Event::NewPeer { profile_id, tag, created, name, default_picture, picture, online, profile_data } => {
println!( println!(
"\n***** {} at {} *****\n", "\n***** {} at {} *****\n",
event.data["name"], event.data["Identity"] name, profile_id.as_str()
); );
// process json for profile, contacts and servers...else { // process json for profile, contacts and servers...else {
let profile = match Profile::new( let profile = profile_data;
&event.data["Identity"],
&event.data["name"],
&event.data["picture"],
&event.data["ContactsJson"],
&event.data["ServerList"],
) {
Ok(p) => p,
Err(e) => panic!("error parsing profile: {}", e),
};
print!("profile: {:?}", profile); print!("profile: {:?}", profile);
} }
"NewMessageFromPeer" => { Event::NewMessageFromPeer { profile_id, conversation_id, contact_id: contact, nick, timestamp_received, message, notification, picture } => {
let to = &event.data["ProfileOnion"]; let response = Message { o: message.o.into(), d: message.d };
let conversation_id = event.data["ConversationID"].parse::<i32>().unwrap();
let message: Message =
serde_json::from_str(&event.data["Data"]).expect("Error parsing message");
let response = Message { o: 1, d: message.d };
let response_json = let response_json =
serde_json::to_string(&response).expect("Error parsing json response"); serde_json::to_string(&response).expect("Error parsing json response");
cwtch.send_message(&to, conversation_id, &response_json); cwtch.send_message( &profile_id, conversation_id, &response_json);
} }
_ => eprintln!("unhandled event!"), _ => eprintln!("unhandled event!"),
}; };

View File

@ -122,48 +122,49 @@ impl CwtchLib for CwtchLibGo {
c_bind!(start_cwtch(app_dir: &str, tor_path: &str;;) c_StartCwtch -> i32); c_bind!(start_cwtch(app_dir: &str, tor_path: &str;;) c_StartCwtch -> i32);
c_bind!(started(;;) c_Started -> i32); c_bind!(started(;;) c_Started -> i32);
c_bind!(send_app_event(event_json: &str;;) c_SendAppEvent); c_bind!(send_app_event(event_json: &str;;) c_SendAppEvent);
fn send_profile_event(&self, profile: ProfileIdentity, event_jason: &str) { fn send_profile_event(&self, profile: &ProfileIdentity, event_jason: &str) {
self._send_profile_event(String::from(profile).as_str(), event_jason) self._send_profile_event(profile.as_str(), event_jason)
} }
c_bind!(create_profile(nick: &str, pass: &str;;) c_CreateProfile); c_bind!(create_profile(nick: &str, pass: &str;;) c_CreateProfile);
c_bind!(load_profiles(pass: &str;;) c_LoadProfiles); c_bind!(load_profiles(pass: &str;;) c_LoadProfiles);
fn accept_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID) { fn accept_conversation(&self, profile: &ProfileIdentity, conversation_id: ConversationID) {
self._accept_conversation(String::from(profile).as_str(), conversation_id.into()) self._accept_conversation(profile.as_str(), conversation_id.into())
} }
fn block_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID) { fn block_contact(&self, profile: &ProfileIdentity, conversation_id: ConversationID) {
self._block_contact(String::from(profile).as_str(), conversation_id.into()) self._block_contact(String::from(profile).as_str(), conversation_id.into())
} }
fn unblock_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID) { fn unblock_contact(&self, profile: &ProfileIdentity, conversation_id: ConversationID) {
self._unblock_contact(String::from(profile).as_str(), conversation_id.into()) self._unblock_contact(String::from(profile).as_str(), conversation_id.into())
} }
fn get_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32) -> String { fn get_message(&self, profile: &ProfileIdentity, conversation_id: ConversationID, message_index: i32) -> String {
self._get_message(String::from(profile).as_str(), conversation_id.into(), message_index) self._get_message(String::from(profile).as_str(), conversation_id.into(), message_index)
} }
fn get_message_by_id(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_id: i32) -> String { fn get_message_by_id(&self, profile: &ProfileIdentity, conversation_id: ConversationID, message_id: i32) -> String {
self._get_message_by_id(String::from(profile).as_str(), conversation_id.into(), message_id) self._get_message_by_id(String::from(profile).as_str(), conversation_id.into(), message_id)
} }
fn get_message_by_content_hash(&self, profile: ProfileIdentity, conversation_id: ConversationID, hash: &str) -> String { fn get_message_by_content_hash(&self, profile: &ProfileIdentity, conversation_id: ConversationID, hash: &str) -> String {
self._get_message_by_content_hash(String::from(profile).as_str(), conversation_id.into(), hash) self._get_message_by_content_hash(String::from(profile).as_str(), conversation_id.into(), hash)
} }
fn get_messages(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32, count: i32) -> String { fn get_messages(&self, profile: &ProfileIdentity, conversation_id: ConversationID, message_index: i32, count: i32) -> String {
self._get_messages(String::from(profile).as_str(), conversation_id.into(), message_index, count) self._get_messages(String::from(profile).as_str(), conversation_id.into(), message_index, count)
} }
fn send_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, msg: &str) -> String { fn send_message(&self, profile: &ProfileIdentity, conversation_id: ConversationID, msg: &str) -> String {
self._send_message(String::from(profile).as_str(), conversation_id.into(), msg) self._send_message(String::from(profile).as_str(), conversation_id.into(), msg)
} }
fn send_invitation(&self, profile: ProfileIdentity, conversation_id: ConversationID, target_id: i32) -> String { fn send_invitation(&self, profile: &ProfileIdentity, conversation_id: ConversationID, target_id: i32) -> String {
self._send_invitation(String::from(profile).as_str(), conversation_id.into(), target_id) self._send_invitation(String::from(profile).as_str(), conversation_id.into(), target_id)
} }
fn share_file(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_path: &str) -> String { fn share_file(&self, profile: &ProfileIdentity, conversation_id: ConversationID, file_path: &str) -> String {
self._share_file(String::from(profile).as_str(), conversation_id.into(), file_path) self._share_file(String::from(profile).as_str(), conversation_id.into(), file_path)
} }
fn download_file(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_path: &str, manifest_path: &str, file_key: FileKey) { fn download_file(&self, profile: &ProfileIdentity, conversation_id: ConversationID, file_path: &str, manifest_path: &str, file_key: FileKey) {
self._download_file(String::from(profile).as_str(), conversation_id.into(), file_path, manifest_path, String::from(file_key).as_str()) self._download_file(String::from(profile).as_str(), conversation_id.into(), file_path, manifest_path, String::from(file_key).as_str())
} }
fn check_download_status(&self, profile: ProfileIdentity, file_key: FileKey) { fn check_download_status(&self, profile: &ProfileIdentity, file_key: FileKey) {
self._check_download_status(String::from(profile).as_str(), String::from(file_key).as_str()) self._check_download_status(String::from(profile).as_str(), String::from(file_key).as_str())
} }
fn verify_or_resume_download(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_key: FileKey) { fn verify_or_resume_download(&self, profile: &ProfileIdentity, conversation_id: ConversationID, file_key: FileKey) {
self._verify_or_resume_download(String::from(profile).as_str(), conversation_id.into(), String::from(file_key).as_str()) self._verify_or_resume_download(String::from(profile).as_str(), conversation_id.into(), String::from(file_key).as_str())
} }
@ -172,26 +173,26 @@ impl CwtchLib for CwtchLibGo {
bindings::c_ResetTor(); bindings::c_ResetTor();
} }
} }
fn create_group(&self, profile: ProfileIdentity, server: &str, name: &str) { fn create_group(&self, profile: &ProfileIdentity, server: &str, name: &str) {
self._create_group(String::from(profile).as_str(), server, name) self._create_group(String::from(profile).as_str(), server, name)
} }
fn delete_profile(&self, profile: ProfileIdentity, pass: &str) { fn delete_profile(&self, profile: &ProfileIdentity, pass: &str) {
self._delete_profile(String::from(profile).as_str(), pass) self._delete_profile(String::from(profile).as_str(), pass)
} }
fn archive_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID) { fn archive_conversation(&self, profile: &ProfileIdentity, conversation_id: ConversationID) {
self._archive_conversation(String::from(profile).as_str(), conversation_id.into()) self._archive_conversation(String::from(profile).as_str(), conversation_id.into())
} }
fn delete_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID) { fn delete_contact(&self, profile: &ProfileIdentity, conversation_id: ConversationID) {
self._delete_contact(String::from(profile).as_str(), conversation_id.into()) self._delete_contact(profile.as_str(), conversation_id.into())
} }
fn import_bundle(&self, profile: ProfileIdentity, bundle: &str) { fn import_bundle(&self, profile: &ProfileIdentity, bundle: &str) {
self._import_bundle(String::from(profile).as_str(), bundle) self._import_bundle(String::from(profile).as_str(), bundle)
} }
fn set_profile_attribute(&self, profile: ProfileIdentity, key: &str, val: &str) { fn set_profile_attribute(&self, profile: &ProfileIdentity, key: &str, val: &str) {
self._set_profile_attribute(String::from(profile).as_str(), key, val) self._set_profile_attribute(String::from(profile).as_str(), key, val)
} }
fn get_profile_attribute(&self, profile: ProfileIdentity, key: &str) -> Result<Option<String>, CwtchError> { fn get_profile_attribute(&self, profile: &ProfileIdentity, key: &str) -> Result<Option<String>, CwtchError> {
let resp = self._get_profile_attribute(String::from(profile).as_str(), key); let resp = self._get_profile_attribute(String::from(profile).as_str(), key);
let attr: Attribute = match serde_json::from_str(&resp) { let attr: Attribute = match serde_json::from_str(&resp) {
Ok(attr) => attr, Ok(attr) => attr,
@ -202,10 +203,10 @@ impl CwtchLib for CwtchLibGo {
false => Ok(None), false => Ok(None),
} }
} }
fn set_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str, val: &str) { fn set_conversation_attribute(&self, profile: &ProfileIdentity, conversation_id: ConversationID, key: &str, val: &str) {
self._set_conversation_attribute(String::from(profile).as_str(), conversation_id.into(), key, val) self._set_conversation_attribute(String::from(profile).as_str(), conversation_id.into(), key, val)
} }
fn get_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str) -> Result<Option<String>, CwtchError> { fn get_conversation_attribute(&self, profile: &ProfileIdentity, conversation_id: ConversationID, key: &str) -> Result<Option<String>, CwtchError> {
let resp = self._get_conversation_attribute(String::from(profile).as_str(), conversation_id.into(), key); let resp = self._get_conversation_attribute(String::from(profile).as_str(), conversation_id.into(), key);
let attr: Attribute = match serde_json::from_str(&resp) { let attr: Attribute = match serde_json::from_str(&resp) {
Ok(attr) => attr, Ok(attr) => attr,
@ -216,13 +217,13 @@ impl CwtchLib for CwtchLibGo {
false => Ok(None), false => Ok(None),
} }
} }
fn set_message_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, channel_id: i32, message_id: i32, key: &str, val: &str) { fn set_message_attribute(&self, profile: &ProfileIdentity, conversation_id: ConversationID, channel_id: i32, message_id: i32, key: &str, val: &str) {
self._set_message_attribute(String::from(profile).as_str(), conversation_id.into(), channel_id, message_id, key, val) self._set_message_attribute(String::from(profile).as_str(), conversation_id.into(), channel_id, message_id, key, val)
} }
fn change_password(&self, profile: ProfileIdentity, old_pass: &str, new_pass: &str, new_pass_again: &str) { fn change_password(&self, profile: &ProfileIdentity, old_pass: &str, new_pass: &str, new_pass_again: &str) {
self._change_password(String::from(profile).as_str(), old_pass, new_pass, new_pass_again) self._change_password(String::from(profile).as_str(), old_pass, new_pass, new_pass_again)
} }
fn export_profile(&self, profile: ProfileIdentity, filename: &str) { fn export_profile(&self, profile: &ProfileIdentity, filename: &str) {
self._export_profile(String::from(profile).as_str(), filename) self._export_profile(String::from(profile).as_str(), filename)
} }

View File

@ -1,12 +1,14 @@
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use chrono::prelude::*; use chrono::prelude::*;
use std::convert::From; use std::convert::From;
use crate::structs::{ACL, ConnectionState, CwtchEvent, Message, Profile}; use crate::structs::{ACL, ConnectionState, CwtchEvent, Message, Profile, Settings};
#[derive(Debug)]
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
/// Profile ID used to refer to profiles in Cwtch /// Profile ID used to refer to profiles in Cwtch
pub struct ProfileIdentity(String); pub struct ProfileIdentity(String);
@ -28,7 +30,21 @@ impl From<&str> for ProfileIdentity {
} }
} }
#[derive(Debug)] impl From<&ProfileIdentity> for String {
fn from(x: &ProfileIdentity) -> Self {
x.0.clone()
}
}
impl ProfileIdentity {
/// Get &str of ProfileIdentity String
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
/// Contact ID used to refer to contacts in Cwtch /// Contact ID used to refer to contacts in Cwtch
pub struct ContactIdentity(String); pub struct ContactIdentity(String);
@ -38,9 +54,16 @@ impl From<String> for ContactIdentity {
} }
} }
#[derive(Debug)] impl ContactIdentity {
/// Get &str of ContactIdentity String
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Hash, Eq, PartialEq)]
/// Conversation ID user to refer to a conversation with a Contact or Group in Cwtch /// Conversation ID user to refer to a conversation with a Contact or Group in Cwtch
pub struct ConversationID(i32) ; pub struct ConversationID(pub i32) ;
impl From<i32> for ConversationID { impl From<i32> for ConversationID {
fn from(x: i32) -> Self { fn from(x: i32) -> Self {
@ -54,7 +77,8 @@ impl From<ConversationID> for i32 {
} }
} }
#[derive(Debug)]
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
/// Group ID used to refer to a Group in Cwtch /// Group ID used to refer to a Group in Cwtch
pub struct GroupID(String) ; pub struct GroupID(String) ;
@ -64,7 +88,8 @@ impl From<String> for GroupID {
} }
} }
#[derive(Debug)]
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
/// Server ID user to refer to a server in Cwtch /// Server ID user to refer to a server in Cwtch
pub struct ServerIdentity(String); pub struct ServerIdentity(String);
@ -86,7 +111,7 @@ impl From<ServerIdentity> for String {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
/// FileKey ID user to refer to a file share in Cwtch /// FileKey ID user to refer to a file share in Cwtch
pub struct FileKey(String); pub struct FileKey(String);
@ -102,7 +127,7 @@ impl From<FileKey> for String {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
/// Enum for type of notification a UI/client should emit for a new message /// Enum for type of notification a UI/client should emit for a new message
pub enum MessageNotification { pub enum MessageNotification {
/// means no notification /// means no notification
@ -124,7 +149,7 @@ impl From<String> for MessageNotification {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
/// Enum for results from NetworkCheck plugin on profiles /// Enum for results from NetworkCheck plugin on profiles
pub enum NetworkCheckStatus { pub enum NetworkCheckStatus {
/// There was an error, this profile cannot connect to itself /// There was an error, this profile cannot connect to itself
@ -143,7 +168,7 @@ impl From<String> for NetworkCheckStatus {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
/// Enum denoting server storage mode /// Enum denoting server storage mode
pub enum ServerStorageType { pub enum ServerStorageType {
/// indicates some app supplied default password is being used on this server for storage encryption /// indicates some app supplied default password is being used on this server for storage encryption
@ -165,7 +190,7 @@ impl From<String> for ServerStorageType {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
/// Enum used by servers to declare their intent to be running or stopped /// Enum used by servers to declare their intent to be running or stopped
pub enum ServerIntent { pub enum ServerIntent {
/// a server intends to be running /// a server intends to be running
@ -184,7 +209,7 @@ impl From<String> for ServerIntent {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
/// Enum for events from Cwtch appbus /// Enum for events from Cwtch appbus
pub enum Event { pub enum Event {
@ -195,7 +220,7 @@ pub enum Event {
/// A new peer has been loaded, details of peer. Identity should be stored so further API calls can be made /// A new peer has been loaded, details of peer. Identity should be stored so further API calls can be made
NewPeer { NewPeer {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// optional client specified tag /// optional client specified tag
tag: String, tag: String,
/// is this a newly created profile event, or a load /// is this a newly created profile event, or a load
@ -219,7 +244,7 @@ pub enum Event {
/// Global settings being emited from lcg, usually in response to them being sent to be saved by client /// Global settings being emited from lcg, usually in response to them being sent to be saved by client
UpdateGlobalSettings { UpdateGlobalSettings {
/// map of setting names to values (https://git.openprivacy.ca/cwtch.im/libcwtch-go/src/branch/trunk/utils/settings.go) /// map of setting names to values (https://git.openprivacy.ca/cwtch.im/libcwtch-go/src/branch/trunk/utils/settings.go)
settings: HashMap<String, String>, settings: Settings,
}, },
/// A profile has an error, usually emited in response to an API call /// A profile has an error, usually emited in response to an API call
PeerError { PeerError {
@ -229,7 +254,7 @@ pub enum Event {
/// A profile was successfully deleted, it is no longer usable /// A profile was successfully deleted, it is no longer usable
PeerDeleted { PeerDeleted {
/// identity of deleted peer /// identity of deleted peer
profile: ProfileIdentity profile_id: ProfileIdentity
}, },
/// Cwtch is shutting down, stop making API calls /// Cwtch is shutting down, stop making API calls
Shutdown, Shutdown,
@ -298,17 +323,17 @@ pub enum Event {
/// A new message was received /// A new message was received
NewMessageFromPeer { NewMessageFromPeer {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// conversation id /// conversation id
conversation_id: ConversationID, conversation_id: ConversationID,
/// contact id /// contact id
contact: ContactIdentity, contact_id: ContactIdentity,
/// name of contact /// name of contact
nick: String, nick: String,
/// time message was received /// time message was received
timestamp_received: DateTime<FixedOffset>, timestamp_received: DateTime<FixedOffset>,
/// the message /// the message
message: Result<Message, serde_json::Error>, message: Message,
/// notification instructions (based on settings) /// notification instructions (based on settings)
notification: MessageNotification, notification: MessageNotification,
/// path to picture for the contact /// path to picture for the contact
@ -317,11 +342,11 @@ pub enum Event {
/// A new contact has been created (imported, added, or contacted by) /// A new contact has been created (imported, added, or contacted by)
ContactCreated { ContactCreated {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// conversation id /// conversation id
conversation_id: ConversationID, conversation_id: ConversationID,
/// contact id /// contact id
contact: ContactIdentity, contact_id: ContactIdentity,
/// name of group /// name of group
nick: String, nick: String,
/// connection status to group server /// connection status to group server
@ -349,16 +374,16 @@ pub enum Event {
/// A peer has changed state /// A peer has changed state
PeerStateChange { PeerStateChange {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// contact id /// contact id
contact: ContactIdentity, contact_id: ContactIdentity,
/// connection status to contact /// connection status to contact
connection_state: ConnectionState, connection_state: ConnectionState,
}, },
/// Notice from the network check plugin, a profile self check test by attempting to connecting to itself /// Notice from the network check plugin, a profile self check test by attempting to connecting to itself
NetworkStatus { NetworkStatus {
/// profile the check was performed on /// profile the check was performed on
profile: ProfileIdentity, profile_id: ProfileIdentity,
// it's technically a profile self check // it's technically a profile self check
/// error if there was one (can be empty) /// error if there was one (can be empty)
error: String, error: String,
@ -368,9 +393,9 @@ pub enum Event {
/// Information from the ACN about a peer /// Information from the ACN about a peer
ACNInfo { ACNInfo {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// contact id /// contact id
contact: ContactIdentity, contact_id: ContactIdentity,
/// key of info /// key of info
key: String, key: String,
/// data of info /// data of info
@ -379,7 +404,7 @@ pub enum Event {
/// a profile attribute has been updated with a new value /// a profile attribute has been updated with a new value
UpdatedProfileAttribute { UpdatedProfileAttribute {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// attribute key /// attribute key
key: String, key: String,
/// attribute new value /// attribute new value
@ -388,7 +413,7 @@ pub enum Event {
/// emited to confirm ack of a message succeeded /// emited to confirm ack of a message succeeded
IndexedAcknowledgement { IndexedAcknowledgement {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// conversation id /// conversation id
conversation_id: i32, conversation_id: i32,
/// index of message acked /// index of message acked
@ -397,41 +422,41 @@ pub enum Event {
/// emited to signal failure to ack a message /// emited to signal failure to ack a message
IndexedFailure { IndexedFailure {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// conversation id /// conversation id
conversation_id: ConversationID, conversation_id: ConversationID,
/// index of failure of message to ack /// index of failure of message to ack
index: i32, index: i32,
/// contact id /// contact id
contact: ContactIdentity, contact_id: ContactIdentity,
/// error string /// error string
error: String, error: String,
}, },
/// a peer has acked a message /// a peer has acked a message
PeerAcknowledgement { PeerAcknowledgement {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// message id this is an ack to /// message id this is an ack to
event_id: String, event_id: String,
/// contact id /// contact id
contact: ContactIdentity, contact_id: ContactIdentity,
/// conversation id /// conversation id
conversation_id: i32, conversation_id: i32,
}, },
/// New message received on a group /// New message received on a group
NewMessageFromGroup { NewMessageFromGroup {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// conversation id /// conversation id
conversation_id: ConversationID, conversation_id: ConversationID,
/// time of message /// time of message
timestamp_sent: DateTime<FixedOffset>, timestamp_sent: DateTime<FixedOffset>,
/// contact id /// contact id
contact: ContactIdentity, contact_id: ContactIdentity,
/// message index /// message index
index: i32, index: i32,
/// the message /// the message
message: Result<Message, serde_json::Error>, message: Message,
/// hash of the message /// hash of the message
content_hash: String, content_hash: String,
/// path to picture for sender /// path to picture for sender
@ -444,7 +469,7 @@ pub enum Event {
/// notice a group has been created /// notice a group has been created
GroupCreated { GroupCreated {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// conversation id /// conversation id
conversation_id: ConversationID, conversation_id: ConversationID,
/// group id /// group id
@ -461,7 +486,7 @@ pub enum Event {
/// notice a new group exists /// notice a new group exists
NewGroup { NewGroup {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// conversation id /// conversation id
conversation_id: ConversationID, conversation_id: ConversationID,
/// group id /// group id
@ -480,7 +505,7 @@ pub enum Event {
/// a server connection state has changed /// a server connection state has changed
ServerStateChange { ServerStateChange {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// server the group is on /// server the group is on
group_server: String, group_server: String,
/// state of connection to server /// state of connection to server
@ -489,9 +514,9 @@ pub enum Event {
/// A getval call to a peer has returned a response /// A getval call to a peer has returned a response
NewRetValMessageFromPeer { NewRetValMessageFromPeer {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// conversation id /// conversation id
contact: ContactIdentity, contact_id: ContactIdentity,
/// scope of the val /// scope of the val
scope: String, scope: String,
/// path of the val (zone.key) /// path of the val (zone.key)
@ -506,7 +531,7 @@ pub enum Event {
/// result of a call to share a file, the filekey and manifest to operate on it /// result of a call to share a file, the filekey and manifest to operate on it
ShareManifest { ShareManifest {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// filekey /// filekey
filekey: FileKey, filekey: FileKey,
/// serialized manifest of the share /// serialized manifest of the share
@ -515,29 +540,29 @@ pub enum Event {
/// Information on a peer fileshare has been received /// Information on a peer fileshare has been received
ManifestSizeReceived { ManifestSizeReceived {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// filekey /// filekey
filekey: FileKey, filekey: FileKey,
/// size of manifest received for a share /// size of manifest received for a share
manifest_size: i32, manifest_size: i32,
/// contact id /// contact id
contact: ContactIdentity, contact_id: ContactIdentity,
}, },
/// An error has occured while trying to parse a peer sharefile /// An error has occured while trying to parse a peer sharefile
ManifestError { ManifestError {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// contact id /// contact id
contact: ContactIdentity, contact_id: ContactIdentity,
/// filekey /// filekey
filekey: FileKey, filekey: FileKey,
}, },
/// A peer message about a shared file has been received /// A peer message about a shared file has been received
ManifestReceived { ManifestReceived {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// contact id /// contact id
contact: ContactIdentity, contact_id: ContactIdentity,
/// filekey /// filekey
filekey: FileKey, filekey: FileKey,
/// serialized manifest /// serialized manifest
@ -546,9 +571,9 @@ pub enum Event {
/// a received manfiest has been saved /// a received manfiest has been saved
ManifestSaved { ManifestSaved {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// contact id /// contact id
contact: ContactIdentity, contact_id: ContactIdentity,
/// filekey /// filekey
filekey: FileKey, filekey: FileKey,
/// serialized manifest /// serialized manifest
@ -561,7 +586,7 @@ pub enum Event {
/// periodically emited status updates about an active download of a shared file /// periodically emited status updates about an active download of a shared file
FileDownloadProgressUpdate { FileDownloadProgressUpdate {
/// identity field /// identity field
profile: ProfileIdentity, profile_id: ProfileIdentity,
/// filekey /// filekey
filekey: FileKey, filekey: FileKey,
/// progress of download of share in chunks /// progress of download of share in chunks
@ -585,10 +610,11 @@ pub enum Event {
impl From<&CwtchEvent> for Event { impl From<&CwtchEvent> for Event {
fn from(cwtch_event: &CwtchEvent) -> Self { fn from(cwtch_event: &CwtchEvent) -> Self {
println!("EVENT: {:?}", cwtch_event);
match cwtch_event.event_type.as_str() { match cwtch_event.event_type.as_str() {
"CwtchStarted" => Event::CwtchStarted, "CwtchStarted" => Event::CwtchStarted,
"NewPeer" => Event::NewPeer { "NewPeer" => Event::NewPeer {
profile: cwtch_event.data["Identity"].clone().into(), profile_id: cwtch_event.data["Identity"].clone().into(),
tag: cwtch_event.data["tag"].clone(), tag: cwtch_event.data["tag"].clone(),
created: cwtch_event.data["Created"] == "true", created: cwtch_event.data["Created"] == "true",
name: cwtch_event.data["name"].clone(), name: cwtch_event.data["name"].clone(),
@ -596,7 +622,7 @@ impl From<&CwtchEvent> for Event {
picture: cwtch_event.data["picture"].clone(), picture: cwtch_event.data["picture"].clone(),
online: cwtch_event.data["Online"].clone(), online: cwtch_event.data["Online"].clone(),
profile_data: Profile::new( profile_data: Profile::new(
&cwtch_event.data["Identity"], cwtch_event.data["Identity"].clone().into(),
&cwtch_event.data["name"], &cwtch_event.data["name"],
&cwtch_event.data["picture"], &cwtch_event.data["picture"],
&cwtch_event.data["ContactsJson"], &cwtch_event.data["ContactsJson"],
@ -605,23 +631,23 @@ impl From<&CwtchEvent> for Event {
}, },
"NewMessageFromPeer" => Event::NewMessageFromPeer { "NewMessageFromPeer" => Event::NewMessageFromPeer {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(), conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(),
contact: cwtch_event.data["RemotePeer"].clone().into(), contact_id: cwtch_event.data["RemotePeer"].clone().into(),
nick: cwtch_event.data["Nick"].clone(), nick: cwtch_event.data["Nick"].clone(),
timestamp_received: DateTime::parse_from_rfc3339(cwtch_event.data["TimestampReceived"].as_str()).unwrap_or( DateTime::from(Utc::now())), timestamp_received: DateTime::parse_from_rfc3339(cwtch_event.data["TimestampReceived"].as_str()).unwrap_or( DateTime::from(Utc::now())),
message: serde_json::from_str(&cwtch_event.data["Data"]), message: Message::from_json(&cwtch_event.data["Data"]),
notification: MessageNotification::from(cwtch_event.data["notification"].clone()), notification: MessageNotification::from(cwtch_event.data["notification"].clone()),
picture: cwtch_event.data["Picture"].clone(), picture: cwtch_event.data["picture"].clone(),
}, },
"PeerError" => Event::PeerError { error: cwtch_event.data["Error"].clone() }, "PeerError" => Event::PeerError { error: cwtch_event.data["Error"].clone() },
"AppError" => Event::AppError { "AppError" => Event::AppError {
error: cwtch_event.data["Error"].clone(), error: cwtch_event.data["Error"].clone(),
}, },
"ContactCreated" => Event::ContactCreated { "ContactCreated" => Event::ContactCreated {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
conversation_id: cwtch_event.data["ConversationID"].clone().parse().unwrap_or(-2).into(), conversation_id: cwtch_event.data["ConversationID"].clone().parse().unwrap_or(-2).into(),
contact: cwtch_event.data["RemotePeer"].clone().into(), contact_id: cwtch_event.data["RemotePeer"].clone().into(),
unread: cwtch_event.data["unread"].clone().parse().unwrap_or(0), unread: cwtch_event.data["unread"].clone().parse().unwrap_or(0),
picture: cwtch_event.data["picture"].clone(), picture: cwtch_event.data["picture"].clone(),
default_picture: cwtch_event.data["defaultPicture"].clone(), default_picture: cwtch_event.data["defaultPicture"].clone(),
@ -635,14 +661,14 @@ impl From<&CwtchEvent> for Event {
last_msg_time: DateTime::parse_from_rfc3339(cwtch_event.data["lastMsgTime"].as_str()).unwrap_or(DateTime::from(Utc::now())), last_msg_time: DateTime::parse_from_rfc3339(cwtch_event.data["lastMsgTime"].as_str()).unwrap_or(DateTime::from(Utc::now())),
}, },
"PeerStateChange" => Event::PeerStateChange { "PeerStateChange" => Event::PeerStateChange {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
contact: cwtch_event.data["RemotePeer"].clone().into(), contact_id: cwtch_event.data["RemotePeer"].clone().into(),
connection_state: ConnectionState::from(cwtch_event.data["ConnectionState"].as_str()), connection_state: ConnectionState::from(cwtch_event.data["ConnectionState"].as_str()),
}, },
"UpdateGlobalSettings" => Event::UpdateGlobalSettings { "UpdateGlobalSettings" => Event::UpdateGlobalSettings {
settings: serde_json::from_str(cwtch_event.data["Data"].as_str()).unwrap_or(HashMap::new()), settings: serde_json::from_str(cwtch_event.data["Data"].as_str()).expect("could not parse settings from libCwtch-go"),
}, },
"PeerDeleted" => Event::PeerDeleted { profile: cwtch_event.data["Identity"].clone().into() }, "PeerDeleted" => Event::PeerDeleted { profile_id: cwtch_event.data["Identity"].clone().into() },
"ACNStatus" => Event::ACNStatus { "ACNStatus" => Event::ACNStatus {
progress: cwtch_event.data["Progress"].parse().unwrap_or(0), progress: cwtch_event.data["Progress"].parse().unwrap_or(0),
@ -650,30 +676,28 @@ impl From<&CwtchEvent> for Event {
}, },
"ACNVersion" => Event::ACNVersion { version: cwtch_event.data["Data"].clone()}, "ACNVersion" => Event::ACNVersion { version: cwtch_event.data["Data"].clone()},
"NetworkError" => Event::NetworkStatus { "NetworkError" => Event::NetworkStatus {
profile: cwtch_event.data["Onion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
error: cwtch_event.data["Error"].clone(), error: cwtch_event.data["Error"].clone(),
status: NetworkCheckStatus::from(cwtch_event.data["Status"].clone()) status: NetworkCheckStatus::from(cwtch_event.data["Status"].clone())
}, },
"ACNInfo" => Event::ACNInfo { "ACNInfo" => Event::ACNInfo {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
contact: cwtch_event.data["Handle"].clone().into(), contact_id: cwtch_event.data["Handle"].clone().into(),
key: cwtch_event.data["Key"].clone(), key: cwtch_event.data["Key"].clone(),
data: cwtch_event.data["Data"].clone(), data: cwtch_event.data["Data"].clone(),
}, },
"UpdatedProfileAttribute" => Event::UpdatedProfileAttribute { "UpdatedProfileAttribute" => Event::UpdatedProfileAttribute {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
key: cwtch_event.data["Key"].clone(), key: cwtch_event.data["Key"].clone(),
value: cwtch_event.data["Data"].clone(), value: cwtch_event.data["Data"].clone(),
}, },
"IndexedAcknowledgement" => Event::IndexedFailure { "IndexedAcknowledgement" => Event::IndexedAcknowledgement {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-1).into(), conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-1).into(),
index: cwtch_event.data["Index"].parse().unwrap_or(-1), index: cwtch_event.data["Index"].parse().unwrap_or(-1),
contact: cwtch_event.data["Handle"].clone().into(),
error: cwtch_event.data["Error"].clone(),
}, },
"ShareManifest" => Event::ShareManifest { "ShareManifest" => Event::ShareManifest {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
filekey: cwtch_event.data["FileKey"].clone().into(), filekey: cwtch_event.data["FileKey"].clone().into(),
serialized_manifest: cwtch_event.data["SerializedManifest"].clone(), serialized_manifest: cwtch_event.data["SerializedManifest"].clone(),
}, },
@ -703,57 +727,57 @@ impl From<&CwtchEvent> for Event {
connections: cwtch_event.data["Connections"].parse().unwrap_or(0), connections: cwtch_event.data["Connections"].parse().unwrap_or(0),
}, },
"ManifestSizeReceived" => Event::ManifestSizeReceived { "ManifestSizeReceived" => Event::ManifestSizeReceived {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
filekey: cwtch_event.data["FileKey"].clone().into(), filekey: cwtch_event.data["FileKey"].clone().into(),
manifest_size: cwtch_event.data["ManifestSize"].parse().unwrap_or(0), manifest_size: cwtch_event.data["ManifestSize"].parse().unwrap_or(0),
contact: cwtch_event.data["Handle"].clone().into(), contact_id: cwtch_event.data["Handle"].clone().into(),
}, },
"ManifestError" => Event::ManifestError { "ManifestError" => Event::ManifestError {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
contact: cwtch_event.data["Handle"].clone().into(), contact_id: cwtch_event.data["Handle"].clone().into(),
filekey: cwtch_event.data["FileKey"].clone().into(), filekey: cwtch_event.data["FileKey"].clone().into(),
}, },
"ManifestReceived" => Event::ManifestReceived { "ManifestReceived" => Event::ManifestReceived {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
contact: cwtch_event.data["Handle"].clone().into(), contact_id: cwtch_event.data["Handle"].clone().into(),
filekey: cwtch_event.data["FileKey"].clone().into(), filekey: cwtch_event.data["FileKey"].clone().into(),
serialized_manifest: cwtch_event.data["SerializedManifest"].clone(), serialized_manifest: cwtch_event.data["SerializedManifest"].clone(),
}, },
"ManifestSaved" => Event::ManifestSaved { "ManifestSaved" => Event::ManifestSaved {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
contact: cwtch_event.data["Handle"].clone().into(), contact_id: cwtch_event.data["Handle"].clone().into(),
filekey: cwtch_event.data["FileKey"].clone().into(), filekey: cwtch_event.data["FileKey"].clone().into(),
serialized_manifest: cwtch_event.data["SerializedManifest"].clone(), serialized_manifest: cwtch_event.data["SerializedManifest"].clone(),
temp_file: cwtch_event.data["TempFile"].clone(), temp_file: cwtch_event.data["TempFile"].clone(),
name_suggestion: cwtch_event.data["NameSuggestion"].clone(), name_suggestion: cwtch_event.data["NameSuggestion"].clone(),
}, },
"FileDownloadProgressUpdate" => Event::FileDownloadProgressUpdate { "FileDownloadProgressUpdate" => Event::FileDownloadProgressUpdate {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
filekey: cwtch_event.data["FileKey"].clone().into(), filekey: cwtch_event.data["FileKey"].clone().into(),
progress: cwtch_event.data["Progress"].parse().unwrap_or(0), progress: cwtch_event.data["Progress"].parse().unwrap_or(0),
filesize_in_chunks: cwtch_event.data["FileSizeInChunks"].parse().unwrap_or(0), filesize_in_chunks: cwtch_event.data["FileSizeInChunks"].parse().unwrap_or(0),
name_suggestion: cwtch_event.data["NameSuggestion"].clone(), name_suggestion: cwtch_event.data["NameSuggestion"].clone(),
}, },
"PeerAcknowledgement" => Event::PeerAcknowledgement { "PeerAcknowledgement" => Event::PeerAcknowledgement {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
event_id: cwtch_event.data["EventId"].clone(), event_id: cwtch_event.data["EventID"].clone(),
contact: cwtch_event.data["RemotePeer"].clone().into(), contact_id: cwtch_event.data["RemotePeer"].clone().into(),
conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(0) conversation_id: cwtch_event.data.get("ConversationID").unwrap_or(&"0".to_string()).parse().unwrap_or(0)
}, },
"NewMessageFromGroup" => Event::NewMessageFromGroup { "NewMessageFromGroup" => Event::NewMessageFromGroup {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
timestamp_sent: DateTime::parse_from_rfc3339(cwtch_event.data["TimestampSent"].as_str()).unwrap_or( DateTime::from(Utc::now())), timestamp_sent: DateTime::parse_from_rfc3339(cwtch_event.data["TimestampSent"].as_str()).unwrap_or( DateTime::from(Utc::now())),
index: cwtch_event.data["RemotePeer"].parse().unwrap_or(-1), index: cwtch_event.data["RemotePeer"].parse().unwrap_or(-1),
content_hash: cwtch_event.data["ContentHash"].clone(), content_hash: cwtch_event.data["ContentHash"].clone(),
conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(), conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(),
contact: cwtch_event.data["RemotePeer"].clone().into(), contact_id: cwtch_event.data["RemotePeer"].clone().into(),
nick: cwtch_event.data["Nick"].clone(), nick: cwtch_event.data["Nick"].clone(),
message: serde_json::from_str(&cwtch_event.data["Data"]), message: Message::from_json(&cwtch_event.data["Data"]),
notification: MessageNotification::from(cwtch_event.data["notification"].clone()), notification: MessageNotification::from(cwtch_event.data["notification"].clone()),
picture: cwtch_event.data["Picture"].clone(), picture: cwtch_event.data["picture"].clone(),
}, },
"GroupCreated" => Event::GroupCreated { "GroupCreated" => Event::GroupCreated {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(), conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(),
group_id: cwtch_event.data["GroupID"].clone().into(), group_id: cwtch_event.data["GroupID"].clone().into(),
group_server: cwtch_event.data["GroupServer"].clone(), group_server: cwtch_event.data["GroupServer"].clone(),
@ -762,7 +786,7 @@ impl From<&CwtchEvent> for Event {
access_control_list: serde_json::from_str(cwtch_event.data["accessControlList"].as_str()).unwrap_or(ACL::new()), access_control_list: serde_json::from_str(cwtch_event.data["accessControlList"].as_str()).unwrap_or(ACL::new()),
}, },
"NewGroup" => Event::NewGroup { "NewGroup" => Event::NewGroup {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(), conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2).into(),
group_id: cwtch_event.data["GroupID"].clone().into(), group_id: cwtch_event.data["GroupID"].clone().into(),
group_server: cwtch_event.data["GroupServer"].clone(), group_server: cwtch_event.data["GroupServer"].clone(),
@ -772,13 +796,13 @@ impl From<&CwtchEvent> for Event {
access_control_list: serde_json::from_str(cwtch_event.data["accessControlList"].as_str()).unwrap_or(ACL::new()), access_control_list: serde_json::from_str(cwtch_event.data["accessControlList"].as_str()).unwrap_or(ACL::new()),
}, },
"ServerStateChange" => Event::ServerStateChange { "ServerStateChange" => Event::ServerStateChange {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
group_server: cwtch_event.data["GroupServer"].clone(), group_server: cwtch_event.data["GroupServer"].clone(),
state: ConnectionState::from(cwtch_event.data["ConnectionState"].as_str()), state: ConnectionState::from(cwtch_event.data["ConnectionState"].as_str()),
}, },
"NewRetValMessageFromPeer" => Event::NewRetValMessageFromPeer { "NewRetValMessageFromPeer" => Event::NewRetValMessageFromPeer {
profile: cwtch_event.data["ProfileOnion"].clone().into(), profile_id: cwtch_event.data["ProfileOnion"].clone().into(),
contact: cwtch_event.data["RemotePeer"].clone().into(), contact_id: cwtch_event.data["RemotePeer"].clone().into(),
scope: cwtch_event.data["Scope"].clone(), scope: cwtch_event.data["Scope"].clone(),
path: cwtch_event.data["Path"].clone(), path: cwtch_event.data["Path"].clone(),
exists: cwtch_event.data["Exists"].parse().unwrap_or(false), exists: cwtch_event.data["Exists"].parse().unwrap_or(false),

View File

@ -28,7 +28,7 @@ pub trait CwtchLib {
fn send_app_event(&self, event_json: &str); fn send_app_event(&self, event_json: &str);
/// Send json of a structs::CwtchEvent to a cwtch profile /// Send json of a structs::CwtchEvent to a cwtch profile
fn send_profile_event(&self, profile: ProfileIdentity, event_json: &str); fn send_profile_event(&self, profile: &ProfileIdentity, event_json: &str);
/// Pull json of a structs::CwtchEvent off the appbus for responding to /// Pull json of a structs::CwtchEvent off the appbus for responding to
fn get_appbus_event(&self) -> Event; fn get_appbus_event(&self) -> Event;
@ -40,44 +40,44 @@ pub trait CwtchLib {
fn load_profiles(&self, pass: &str); fn load_profiles(&self, pass: &str);
/// Cause profile to accept conversation /// Cause profile to accept conversation
fn accept_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID); fn accept_conversation(&self, profile: &ProfileIdentity, conversation_id: ConversationID);
/// Cause profile to block conversation /// Cause profile to block conversation
fn block_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID); fn block_contact(&self, profile: &ProfileIdentity, conversation_id: ConversationID);
/// Cause profile to unblock contact /// Cause profile to unblock contact
fn unblock_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID); fn unblock_contact(&self, profile: &ProfileIdentity, conversation_id: ConversationID);
/// Get a specific message for conversation of profile by index /// Get a specific message for conversation of profile by index
fn get_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32) -> String; fn get_message(&self, profile: &ProfileIdentity, conversation_id: ConversationID, message_index: i32) -> String;
/// Get a specific message for a conversation by its id /// Get a specific message for a conversation by its id
fn get_message_by_id(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_id: i32) -> String; fn get_message_by_id(&self, profile: &ProfileIdentity, conversation_id: ConversationID, message_id: i32) -> String;
/// Get a specific message for conversation of profile by hash /// Get a specific message for conversation of profile by hash
fn get_message_by_content_hash( fn get_message_by_content_hash(
&self, &self,
profile: ProfileIdentity, profile: &ProfileIdentity,
conversation_id: ConversationID, conversation_id: ConversationID,
hash: &str, hash: &str,
) -> String; ) -> String;
/// Bulk get messages starting at message index and of count amoung /// Bulk get messages starting at message index and of count amoung
fn get_messages(&self, profile: ProfileIdentity, conversation_id: ConversationID, message_index: i32, count: i32) -> String; fn get_messages(&self, profile: &ProfileIdentity, conversation_id: ConversationID, message_index: i32, count: i32) -> String;
/// Send json of a structs::Message from profile to contact. Returns computed sent message (including index and hash values) /// Send json of a structs::Message from profile to contact. Returns computed sent message (including index and hash values)
fn send_message(&self, profile: ProfileIdentity, conversation_id: ConversationID, msg: &str) -> String; fn send_message(&self, profile: &ProfileIdentity, conversation_id: ConversationID, msg: &str) -> String;
/// Send profile's contact an invite for/to target. Returns computed sent message (including index and hash values) /// Send profile's contact an invite for/to target. Returns computed sent message (including index and hash values)
fn send_invitation(&self, profile: ProfileIdentity, conversation_id: ConversationID, target_id: i32) -> String; fn send_invitation(&self, profile: &ProfileIdentity, conversation_id: ConversationID, target_id: i32) -> String;
/// share a file file_path with a conersation. Returns computed sent message (including index and hash values) /// share a file file_path with a conersation. Returns computed sent message (including index and hash values)
fn share_file(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_path: &str) -> String; fn share_file(&self, profile: &ProfileIdentity, conversation_id: ConversationID, file_path: &str) -> String;
/// download a file from a conversation to the file_path /// download a file from a conversation to the file_path
fn download_file( fn download_file(
&self, &self,
profile: ProfileIdentity, profile: &ProfileIdentity,
conversation_id: ConversationID, conversation_id: ConversationID,
file_path: &str, file_path: &str,
manifest_path: &str, manifest_path: &str,
@ -85,42 +85,42 @@ pub trait CwtchLib {
); );
/// Query the status of a download /// Query the status of a download
fn check_download_status(&self, profile: ProfileIdentity, file_key: FileKey); fn check_download_status(&self, profile: &ProfileIdentity, file_key: FileKey);
/// Verufy a download is done, and if not, resume it /// Verufy a download is done, and if not, resume it
fn verify_or_resume_download(&self, profile: ProfileIdentity, conversation_id: ConversationID, file_key: FileKey); fn verify_or_resume_download(&self, profile: &ProfileIdentity, conversation_id: ConversationID, file_key: FileKey);
/// Ask the ACN inside the Cwtch app to restart the tor connection /// Ask the ACN inside the Cwtch app to restart the tor connection
fn reset_tor(&self); fn reset_tor(&self);
/// Cause profile to create a group on server with name /// Cause profile to create a group on server with name
fn create_group(&self, profile: ProfileIdentity, server: &str, name: &str); fn create_group(&self, profile: &ProfileIdentity, server: &str, name: &str);
/// Delete profile with encryption/password check of pass /// Delete profile with encryption/password check of pass
fn delete_profile(&self, profile: ProfileIdentity, pass: &str); fn delete_profile(&self, profile: &ProfileIdentity, pass: &str);
/// Cause profile to archive conversation with contact /// Cause profile to archive conversation with contact
fn archive_conversation(&self, profile: ProfileIdentity, conversation_id: ConversationID); fn archive_conversation(&self, profile: &ProfileIdentity, conversation_id: ConversationID);
/// Cause profile to delete contact/group identified by handle /// Cause profile to delete contact/group identified by handle
fn delete_contact(&self, profile: ProfileIdentity, conversation_id: ConversationID); fn delete_contact(&self, profile: &ProfileIdentity, conversation_id: ConversationID);
/// Cuase profile to attempt to import a contact/group/keybundle identified by bundle /// Cuase profile to attempt to import a contact/group/keybundle identified by bundle
fn import_bundle(&self, profile: ProfileIdentity, bundle: &str); fn import_bundle(&self, profile: &ProfileIdentity, bundle: &str);
/// Set a profile attribute key to val /// Set a profile attribute key to val
fn set_profile_attribute(&self, profile: ProfileIdentity, key: &str, val: &str); fn set_profile_attribute(&self, profile: &ProfileIdentity, key: &str, val: &str);
/// Get a profile attribute /// Get a profile attribute
fn get_profile_attribute(&self, profile: ProfileIdentity, key: &str) -> Result<Option<String>, CwtchError>; fn get_profile_attribute(&self, profile: &ProfileIdentity, key: &str) -> Result<Option<String>, CwtchError>;
/// Set a profile's contact's attribute of key to val /// Set a profile's contact's attribute of key to val
fn set_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str, val: &str); fn set_conversation_attribute(&self, profile: &ProfileIdentity, conversation_id: ConversationID, key: &str, val: &str);
/// Set an attribute on a message in a conversation /// Set an attribute on a message in a conversation
fn set_message_attribute( fn set_message_attribute(
&self, &self,
profile: ProfileIdentity, profile: &ProfileIdentity,
conversation_id: ConversationID, conversation_id: ConversationID,
channel_id: i32, channel_id: i32,
message_id: i32, message_id: i32,
@ -129,13 +129,13 @@ pub trait CwtchLib {
); );
/// Get an attribute for a conversation /// Get an attribute for a conversation
fn get_conversation_attribute(&self, profile: ProfileIdentity, conversation_id: ConversationID, key: &str) -> Result<Option<String>, CwtchError>; fn get_conversation_attribute(&self, profile: &ProfileIdentity, conversation_id: ConversationID, key: &str) -> Result<Option<String>, CwtchError>;
/// Change a profile's password to new_pass if old_pass is correct /// Change a profile's password to new_pass if old_pass is correct
fn change_password(&self, profile: ProfileIdentity, old_pass: &str, new_pass: &str, new_pass_again: &str); fn change_password(&self, profile: &ProfileIdentity, old_pass: &str, new_pass: &str, new_pass_again: &str);
/// Export a profile to filename /// Export a profile to filename
fn export_profile(&self, profile: ProfileIdentity, filename: &str); fn export_profile(&self, profile: &ProfileIdentity, filename: &str);
/// Import a profile from a file with supplied password. Json of a profile struct returned on success /// Import a profile from a file with supplied password. Json of a profile struct returned on success
fn import_profile(&self, filename: &str, password: &str) -> String; fn import_profile(&self, filename: &str, password: &str) -> String;

View File

@ -1,10 +1,12 @@
use crate::structs::ConnectionState::Disconnected; use crate::structs::ConnectionState::Disconnected;
use crate::CwtchLib; use crate::{ConversationID, CwtchLib, ProfileIdentity};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DefaultOnError}; use serde_with::{serde_as, DefaultOnError};
use serde_repr::*;
use std::collections::HashMap; use std::collections::HashMap;
use crate::event::ContactIdentity;
#[derive(Serialize, Deserialize, Debug)] #[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
/// Defines the states a Cwtch connection can be in /// Defines the states a Cwtch connection can be in
pub enum ConnectionState { pub enum ConnectionState {
/// The Cwtch connection is not conected at all /// The Cwtch connection is not conected at all
@ -81,7 +83,7 @@ pub struct CwtchEvent {
pub data: HashMap<String, String>, pub data: HashMap<String, String>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]
#[allow(non_snake_case)] #[allow(non_snake_case)]
/// AccessControl is a type determining client assigned authorization to a peer /// AccessControl is a type determining client assigned authorization to a peer
@ -98,15 +100,15 @@ pub struct AccessControl {
pub type ACL = HashMap<String, AccessControl>; pub type ACL = HashMap<String, AccessControl>;
#[serde_as] #[serde_as]
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
/// Struct to serialize/deserialize conversations coming from libcwtch-go /// Struct to serialize/deserialize conversations coming from libcwtch-go
pub struct Conversation { pub struct Conversation {
/// onion address / id of the conversation /// onion address / id of the conversation
#[serde(alias = "onion")] #[serde(alias = "onion")]
pub handle: String, pub contact_id: ContactIdentity,
/// unique identifier of the contact/conversation to be used in API access /// unique identifier of the contact/conversation to be used in API access
pub identifier: i32, pub identifier: ConversationID, // TODO TEST does this work here for serde deserializtion
/// display name of the conversation, as determined in libcwtch-go from name specified by contact /// display name of the conversation, as determined in libcwtch-go from name specified by contact
pub name: String, pub name: String,
#[serde_as(deserialize_as = "DefaultOnError")] #[serde_as(deserialize_as = "DefaultOnError")]
@ -125,7 +127,7 @@ pub struct Conversation {
//attr: HashMap<String, String>, //attr: HashMap<String, String>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
/// Struct to serialize/deserialize servers coming from libcwtch-go /// Struct to serialize/deserialize servers coming from libcwtch-go
pub struct Server { pub struct Server {
/// onion address of the server /// onion address of the server
@ -134,13 +136,11 @@ pub struct Server {
pub status: ConnectionState, pub status: ConnectionState,
} }
#[serde_as] #[derive(Debug, Clone)]
#[derive(Debug, Serialize, Deserialize)]
/// Struct to serialize/deserialize profiles coming from libcwtch-go /// Struct to serialize/deserialize profiles coming from libcwtch-go
pub struct Profile { pub struct Profile {
/// onion address / ID of the profile /// onion address / ID of the profile
#[serde(alias = "onion")] pub profile_id: ProfileIdentity,
pub handle: String,
/// nick name of the onion as supplied by libcwtch-go based on "name" attribute /// nick name of the onion as supplied by libcwtch-go based on "name" attribute
pub nick: String, pub nick: String,
/// path to a profile image, controled by "picture" attribute /// path to a profile image, controled by "picture" attribute
@ -148,22 +148,66 @@ pub struct Profile {
/// all profile attributes /// all profile attributes
pub attr: HashMap<String, String>, pub attr: HashMap<String, String>,
/// map of conversation [ onion => conversation ] /// map of conversation [ onion => conversation ]
pub conversations: HashMap<i32, Conversation>, pub conversations: HashMap<ConversationID, Conversation>,
/// map of servers [ onion => server ] /// map of servers [ onion => server ]
pub servers: HashMap<String, Server>, pub servers: HashMap<String, Server>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize_repr, Deserialize_repr, PartialEq, Clone)]
#[repr(i32)]
/// Enum matching message types and their overlays as defined in Cwtch
/// - https://git.openprivacy.ca/cwtch.im/cwtch/src/commit/907a7ca638fbcbaac5d652608d455d4217597fa9/model/overlay.go
/// - https://git.openprivacy.ca/cwtch.im/cwtch-ui/src/commit/3a752b73972cbfc53b6da26116fe018ee2ac2343/lib/models/message.dart
pub enum MessageType {
/// A standard Cwtch message of text
TextMessage = 1,
/// This message is a text message but it also contains a reference to the message it is quoting
QuotedMessage = 10,
/// A shared contact message
SuggestContact = 100,
/// A group invite message
InviteGroup = 101,
/// a share file message
FileShare = 200,
/// This message is of an unsupported type so malformed
MalformedMessage,
}
impl From<i32> for MessageType {
fn from(x: i32) -> Self {
match x {
1 => MessageType::TextMessage,
10 => MessageType::QuotedMessage,
100 => MessageType::SuggestContact,
101 => MessageType::InviteGroup,
200 => MessageType::FileShare,
_ => MessageType::MalformedMessage,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
/// Struct to serialize/deserialize messages sent over Cwtch between profiles / conversation /// Struct to serialize/deserialize messages sent over Cwtch between profiles / conversation
pub struct Message { pub struct Message {
/// overlay id that the message is targeting as defined in cwtch/model/overlay.go /// overlay id that the message is targeting as defined in cwtch/model/overlay.go
/// [ OverlayChat = 1, OverlayInviteContact = 100, OverlayInviteGroup = 101, OverlayFileSharing = 200 ] /// [ OverlayChat = 1, OverlayInviteContact = 100, OverlayInviteGroup = 101, OverlayFileSharing = 200 ]
pub o: i64, pub o: MessageType,
/// data of the message /// data of the message
pub d: String, pub d: String,
} }
#[derive(Debug, Serialize, Deserialize)] impl Message {
/// parse json into a Message
pub fn from_json(json: &str) -> Self {
match serde_json::from_str(json) {
Ok(m) => m,
Err(e) => Message{o: MessageType::MalformedMessage, d: e.to_string()}
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
/// Settings as defined in libcwtch-go/utils/settings.go and should be populated by handeling the UpdateGlobalSettings event emited during cwtch.start() /// Settings as defined in libcwtch-go/utils/settings.go and should be populated by handeling the UpdateGlobalSettings event emited during cwtch.start()
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub struct Settings { pub struct Settings {
@ -265,7 +309,7 @@ impl Profile {
/// contacts_json as supplied by libcwtch-go, a map of conversations /// contacts_json as supplied by libcwtch-go, a map of conversations
/// server_list as supplied by libcwtch-go, a map of servers /// server_list as supplied by libcwtch-go, a map of servers
pub fn new( pub fn new(
identity: &str, identity: ProfileIdentity,
name: &str, name: &str,
picture: &str, picture: &str,
conversations_json: &str, conversations_json: &str,
@ -280,7 +324,7 @@ impl Profile {
Err(e) => return Err(e), Err(e) => return Err(e),
}; };
Ok(Profile { Ok(Profile {
handle: identity.to_string(), profile_id: identity,
nick: name.to_string(), nick: name.to_string(),
image_path: picture.to_string(), image_path: picture.to_string(),
attr: Default::default(), attr: Default::default(),
@ -289,8 +333,8 @@ impl Profile {
}) })
} }
fn process_conversations(conversations_json: &str) -> Result<HashMap<i32, Conversation>, String> { fn process_conversations(conversations_json: &str) -> Result<HashMap<ConversationID, Conversation>, String> {
let mut conversations: HashMap<i32, Conversation> = HashMap::new(); let mut conversations: HashMap<ConversationID, Conversation> = HashMap::new();
if conversations_json == "null" { if conversations_json == "null" {
return Ok(conversations); return Ok(conversations);
} }
@ -320,8 +364,8 @@ impl Profile {
} }
/// Find a conversation_id associated with a remote handle (used for some events with no conversation id like PeerStateChange) /// Find a conversation_id associated with a remote handle (used for some events with no conversation id like PeerStateChange)
pub fn find_conversation_id_by_handle(&self, handle: String) -> Option<i32> { pub fn find_conversation_id_by_handle(&self, contact_id: ContactIdentity) -> Option<ConversationID> {
match self.conversations.values().filter(|c| c.handle == handle).next() { match self.conversations.values().filter(|c| c.contact_id == contact_id).next() {
Some(conversation) => Some(conversation.identifier), Some(conversation) => Some(conversation.identifier),
None => None None => None
} }