diff --git a/Cargo.toml b/Cargo.toml index d8f827f..93b9aff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,4 @@ edition = "2021" [dependencies] libcwtch = "0.3.0" serde_json = "1.0" +chrono = "0.4.19" \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..4b3b067 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,78 @@ +max_width = 200 +hard_tabs = false +tab_spaces = 4 +newline_style = "Auto" +indent_style = "Block" +use_small_heuristics = "Default" +fn_call_width = 60 +attr_fn_like_width = 70 +struct_lit_width = 18 +struct_variant_width = 35 +array_width = 60 +chain_width = 60 +single_line_if_else_max_width = 50 +wrap_comments = false +format_code_in_doc_comments = false +comment_width = 80 +normalize_comments = false +normalize_doc_attributes = false +license_template_path = "" +format_strings = false +format_macro_matchers = false +format_macro_bodies = true +hex_literal_case = "Preserve" +empty_item_single_line = true +struct_lit_single_line = true +fn_single_line = false +where_single_line = false +imports_indent = "Block" +imports_layout = "Mixed" +imports_granularity = "Preserve" +group_imports = "Preserve" +reorder_imports = true +reorder_modules = true +reorder_impl_items = false +type_punctuation_density = "Wide" +space_before_colon = false +space_after_colon = true +spaces_around_ranges = false +binop_separator = "Front" +remove_nested_parens = true +combine_control_expr = true +short_array_element_width_threshold = 10 +overflow_delimited_expr = false +struct_field_align_threshold = 0 +enum_discrim_align_threshold = 0 +match_arm_blocks = true +match_arm_leading_pipes = "Never" +force_multiline_blocks = false +fn_args_layout = "Tall" +brace_style = "SameLineWhere" +control_brace_style = "AlwaysSameLine" +trailing_semicolon = true +trailing_comma = "Vertical" +match_block_trailing_comma = false +blank_lines_upper_bound = 1 +blank_lines_lower_bound = 0 +edition = "2015" +version = "One" +inline_attribute_width = 0 +format_generated_files = true +merge_derives = true +use_try_shorthand = false +use_field_init_shorthand = false +force_explicit_abi = true +condense_wildcard_suffixes = false +color = "Auto" +required_version = "1.4.38" +unstable_features = false +disable_all_formatting = false +skip_children = false +hide_parse_errors = false +error_on_line_overflow = false +error_on_unformatted = false +report_todo = "Never" +report_fixme = "Never" +ignore = [] +emit_mode = "Files" +make_backup = false diff --git a/src/event.rs b/src/event.rs index 96477ed..5de6ebc 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,27 +1,70 @@ +use chrono::{DateTime, FixedOffset}; +use libcwtch::structs::CwtchEvent; +use std::collections::HashMap; + #[derive(Debug)] pub enum Event { - CwtchStarted, - NewPeer, - NewMessageFromPeer, - AppError, - ContactCreated, - PeerStateChange, - UpdateGlobalSettings, + CwtchStarted { + data: HashMap, + }, + NewPeer { + data: HashMap, + }, - ErrUnhandled(String), + NewMessageFromPeer { + handle: String, + timestamp_received: DateTime, + message: String, + }, + AppError { + data: HashMap, + }, + ContactCreated { + data: HashMap, + }, + PeerStateChange { + data: HashMap, + }, + UpdateGlobalSettings { + data: HashMap, + }, + + ErrUnhandled { + name: String, + data: HashMap, + }, } -impl From<&str> for Event { - fn from(name: &str) -> Self { - match name { - "CwtchStarted" => Event::CwtchStarted, - "NewPeer" => Event::NewPeer, - "NewMessageFromPeer" => Event::NewMessageFromPeer, - "AppError" => Event::AppError, - "ContactCreated" => Event::ContactCreated, - "PeerStateChange" => Event::PeerStateChange, - "UpdateGlobalSettings" => Event::UpdateGlobalSettings, - _ => Event::ErrUnhandled(name.to_string()), +impl From<&CwtchEvent> for Event { + fn from(cwtch_event: &CwtchEvent) -> Self { + match cwtch_event.event_type.as_str() { + "CwtchStarted" => Event::CwtchStarted { + data: cwtch_event.data.clone(), + }, + "NewPeer" => Event::NewPeer { + data: cwtch_event.data.clone(), + }, + "NewMessageFromPeer" => Event::NewMessageFromPeer { + handle: cwtch_event.data["RemotePeer"].clone(), + timestamp_received: DateTime::parse_from_rfc3339(cwtch_event.data["TimestampReceived"].as_str()).unwrap(), + message: cwtch_event.data["Data"].clone(), + }, + "AppError" => Event::AppError { + data: cwtch_event.data.clone(), + }, + "ContactCreated" => Event::ContactCreated { + data: cwtch_event.data.clone(), + }, + "PeerStateChange" => Event::PeerStateChange { + data: cwtch_event.data.clone(), + }, + "UpdateGlobalSettings" => Event::UpdateGlobalSettings { + data: cwtch_event.data.clone(), + }, + x => Event::ErrUnhandled { + name: x.to_string(), + data: cwtch_event.data.clone(), + }, } } } diff --git a/src/imp.rs b/src/imp.rs index a6fcfa2..8e5dd11 100644 --- a/src/imp.rs +++ b/src/imp.rs @@ -1,6 +1,6 @@ +use crate::event::Event; use libcwtch::structs::*; use libcwtch::CwtchLib; -use crate::event::Event; use serde_json; @@ -31,18 +31,20 @@ pub struct Behaviour { } pub struct BehaviourBuilder { - behaviour: Behaviour + behaviour: Behaviour, } impl BehaviourBuilder { pub fn new() -> Self { - return BehaviourBuilder{ behaviour: Behaviour { - proto_experiments: false, - proto_experiment_fileshare: false, - new_contant_policy: NewContactPolicy::Ignore, - profile_name: "".to_string(), - profile_pic_path: None, - }} + return BehaviourBuilder { + behaviour: Behaviour { + proto_experiments: false, + proto_experiment_fileshare: false, + new_contant_policy: NewContactPolicy::Ignore, + profile_name: "".to_string(), + profile_pic_path: None, + }, + }; } pub fn build(self) -> Behaviour { @@ -75,7 +77,7 @@ impl BehaviourBuilder { /// Trait to be used by implementors of imp bots to supply their custom event handling /// the handle function is called after the default imp automatic event handling has run on each new event pub trait EventHandler { - fn handle(&mut self, cwtch: &dyn CwtchLib, profile: Option<&Profile>, event: CwtchEvent); + fn handle(&mut self, cwtch: &dyn CwtchLib, profile: Option<&Profile>, event: Event); } /// Cwtch bot @@ -98,22 +100,31 @@ impl Imp { let ret = cwtch.start_cwtch(&home_dir, ""); println!("start_cwtch returned {}", ret); - return Imp {behaviour, cwtch: Box::new(cwtch), password, home_dir, profile: None, settings: None} + return Imp { + behaviour, + cwtch: Box::new(cwtch), + password, + home_dir, + profile: None, + settings: None, + }; } /// The main event loop handler for the bot, supply your own customer handler to handle events after the imp's automatic handling has processed the event - pub fn event_loop(&mut self, handler: &mut T) where T : EventHandler { + pub fn event_loop(&mut self, handler: &mut T) + where + T: EventHandler, + { let mut initialized: bool = false; loop { let event_str = self.cwtch.get_appbus_event(); println!("bot: event: {}", event_str); - let event: CwtchEvent = - serde_json::from_str(&event_str).expect("Error parsing Cwtch event"); - let event_type = Event::from(event.event_type.as_str()) ; - match event_type { - Event::CwtchStarted => { + let event: CwtchEvent = serde_json::from_str(&event_str).expect("Error parsing Cwtch event"); + let event = Event::from(&event); + match &event { + Event::CwtchStarted { data } => { println!("event CwtchStarted!"); initialized = true; @@ -125,9 +136,9 @@ impl Imp { Some(_) => (), } } - Event::UpdateGlobalSettings => { - println!("Loading settings froms {}", &event.data["Data"]); - let mut settings: Settings = match serde_json::from_str(&event.data["Data"]) { + Event::UpdateGlobalSettings { data } => { + println!("Loading settings froms {}", &data["Data"]); + let mut settings: Settings = match serde_json::from_str(&data["Data"]) { Ok(s) => s, Err(e) => panic!("invalid json: {:?}", e), }; @@ -156,19 +167,16 @@ impl Imp { self.settings = Some(settings); } - Event::NewPeer => { - println!( - "\n***** {} at {} *****\n", - event.data["name"], event.data["Identity"] - ); + Event::NewPeer { data } => { + println!("\n***** {} at {} *****\n", data["name"], data["Identity"]); // process json for profile, conversations and servers...else { let profile = match Profile::new( - &event.data["Identity"], - &event.data["name"], - &event.data["picture"], - &event.data["ContactsJson"], - &event.data["ServerList"], + &data["Identity"], + &data["name"], + &data["picture"], + &data["ContactsJson"], + &data["ServerList"], ) { Ok(p) => p, Err(e) => panic!("error parsing profile: {}", e), @@ -181,41 +189,47 @@ impl Imp { None => (), }; - self.cwtch.set_profile_attribute(&profile.handle, "profile.name", &self.behaviour.profile_name); + self.cwtch.set_profile_attribute( + &profile.handle, + "profile.name", + &self.behaviour.profile_name, + ); for (_id, conversation) in &profile.conversations { match self.behaviour.new_contant_policy { NewContactPolicy::Accept => { - self.cwtch.accept_conversation(&profile.handle.clone(), conversation.identifier); - }, - NewContactPolicy::Block => - self.cwtch.block_contact(&profile.handle.clone(), conversation.identifier), + self.cwtch + .accept_conversation(&profile.handle.clone(), conversation.identifier); + } + NewContactPolicy::Block => self + .cwtch + .block_contact(&profile.handle.clone(), conversation.identifier), NewContactPolicy::Ignore => (), } } self.profile = Some(profile); } - Event::AppError => { - if initialized && event.data["Error"] == "Loaded 0 profiles" { - self.cwtch.create_profile(&self.behaviour.profile_name, &self.password); + Event::AppError { data } => { + if initialized && data["Error"] == "Loaded 0 profiles" { + self.cwtch + .create_profile(&self.behaviour.profile_name, &self.password); } } - Event::ContactCreated => { + Event::ContactCreated { data } => { println!("Contact Created"); - let convo_handle = event.data["RemotePeer"].to_string(); - let convo_id = event.data["ConversationID"].parse::().unwrap(); + let convo_handle = data["RemotePeer"].to_string(); + let convo_id = data["ConversationID"].parse::().unwrap(); - let acl: ACL = serde_json::from_str(&event.data["accessControlList"]) - .expect("Error parsing conversation"); + let acl: ACL = serde_json::from_str(&data["accessControlList"]).expect("Error parsing conversation"); let conversation = Conversation { handle: convo_handle.clone(), - identifier: event.data["ConversationID"].parse::().unwrap(), - name: event.data["nick"].to_string(), - status: ConnectionState::new(&event.data["status"]), - blocked: event.data["blocked"] == "true", - accepted: event.data["accepted"] == "true", + identifier: data["ConversationID"].parse::().unwrap(), + name: data["nick"].to_string(), + status: ConnectionState::new(&data["status"]), + blocked: data["blocked"] == "true", + accepted: data["accepted"] == "true", access_control_list: acl, is_group: false, // by definition }; @@ -224,27 +238,26 @@ impl Imp { Some(profile) => { profile .conversations - .insert(event.data["RemotePeer"].to_string(), conversation); + .insert(data["RemotePeer"].to_string(), conversation); match self.behaviour.new_contant_policy { NewContactPolicy::Accept => { - self.cwtch.accept_conversation(&profile.handle.clone(), convo_id); - }, - NewContactPolicy::Block => - self.cwtch.block_contact(&profile.handle.clone(), convo_id), + self.cwtch + .accept_conversation(&profile.handle.clone(), convo_id); + } + NewContactPolicy::Block => self.cwtch.block_contact(&profile.handle.clone(), convo_id), NewContactPolicy::Ignore => (), } } None => (), }; } - Event::PeerStateChange => { - } - Event::ErrUnhandled(err) => eprintln!("unhandled event: {}!", err), + Event::PeerStateChange { data } => {} + Event::ErrUnhandled { name, data } => eprintln!("unhandled event: {}!", name), _ => (), }; handler.handle(self.cwtch.as_ref(), self.profile.as_ref(), event); } } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 0dc8d7d..171f4a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,2 @@ +pub mod event; pub mod imp; -pub mod event; \ No newline at end of file