First cut of canonical events

This commit is contained in:
Sarah Jamie Lewis 2022-04-26 16:19:28 -07:00
parent d61c4086d4
commit bd03cd9d83
5 changed files with 212 additions and 77 deletions

View File

@ -8,3 +8,4 @@ edition = "2021"
[dependencies] [dependencies]
libcwtch = "0.3.0" libcwtch = "0.3.0"
serde_json = "1.0" serde_json = "1.0"
chrono = "0.4.19"

78
rustfmt.toml Normal file
View File

@ -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

View File

@ -1,27 +1,70 @@
use chrono::{DateTime, FixedOffset};
use libcwtch::structs::CwtchEvent;
use std::collections::HashMap;
#[derive(Debug)] #[derive(Debug)]
pub enum Event { pub enum Event {
CwtchStarted, CwtchStarted {
NewPeer, data: HashMap<String, String>,
NewMessageFromPeer, },
AppError, NewPeer {
ContactCreated, data: HashMap<String, String>,
PeerStateChange, },
UpdateGlobalSettings,
ErrUnhandled(String), NewMessageFromPeer {
handle: String,
timestamp_received: DateTime<FixedOffset>,
message: String,
},
AppError {
data: HashMap<String, String>,
},
ContactCreated {
data: HashMap<String, String>,
},
PeerStateChange {
data: HashMap<String, String>,
},
UpdateGlobalSettings {
data: HashMap<String, String>,
},
ErrUnhandled {
name: String,
data: HashMap<String, String>,
},
} }
impl From<&str> for Event { impl From<&CwtchEvent> for Event {
fn from(name: &str) -> Self { fn from(cwtch_event: &CwtchEvent) -> Self {
match name { match cwtch_event.event_type.as_str() {
"CwtchStarted" => Event::CwtchStarted, "CwtchStarted" => Event::CwtchStarted {
"NewPeer" => Event::NewPeer, data: cwtch_event.data.clone(),
"NewMessageFromPeer" => Event::NewMessageFromPeer, },
"AppError" => Event::AppError, "NewPeer" => Event::NewPeer {
"ContactCreated" => Event::ContactCreated, data: cwtch_event.data.clone(),
"PeerStateChange" => Event::PeerStateChange, },
"UpdateGlobalSettings" => Event::UpdateGlobalSettings, "NewMessageFromPeer" => Event::NewMessageFromPeer {
_ => Event::ErrUnhandled(name.to_string()), 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(),
},
} }
} }
} }

View File

@ -1,6 +1,6 @@
use crate::event::Event;
use libcwtch::structs::*; use libcwtch::structs::*;
use libcwtch::CwtchLib; use libcwtch::CwtchLib;
use crate::event::Event;
use serde_json; use serde_json;
@ -31,18 +31,20 @@ pub struct Behaviour {
} }
pub struct BehaviourBuilder { pub struct BehaviourBuilder {
behaviour: Behaviour behaviour: Behaviour,
} }
impl BehaviourBuilder { impl BehaviourBuilder {
pub fn new() -> Self { pub fn new() -> Self {
return BehaviourBuilder{ behaviour: Behaviour { return BehaviourBuilder {
proto_experiments: false, behaviour: Behaviour {
proto_experiment_fileshare: false, proto_experiments: false,
new_contant_policy: NewContactPolicy::Ignore, proto_experiment_fileshare: false,
profile_name: "".to_string(), new_contant_policy: NewContactPolicy::Ignore,
profile_pic_path: None, profile_name: "".to_string(),
}} profile_pic_path: None,
},
};
} }
pub fn build(self) -> Behaviour { 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 /// 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 /// the handle function is called after the default imp automatic event handling has run on each new event
pub trait EventHandler { 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 /// Cwtch bot
@ -98,22 +100,31 @@ impl Imp {
let ret = cwtch.start_cwtch(&home_dir, ""); let ret = cwtch.start_cwtch(&home_dir, "");
println!("start_cwtch returned {}", ret); 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 /// 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<T>(&mut self, handler: &mut T) where T : EventHandler { pub fn event_loop<T>(&mut self, handler: &mut T)
where
T: EventHandler,
{
let mut initialized: bool = false; let mut initialized: bool = false;
loop { loop {
let event_str = self.cwtch.get_appbus_event(); let event_str = self.cwtch.get_appbus_event();
println!("bot: event: {}", event_str); println!("bot: event: {}", event_str);
let event: CwtchEvent = let event: CwtchEvent = serde_json::from_str(&event_str).expect("Error parsing Cwtch event");
serde_json::from_str(&event_str).expect("Error parsing Cwtch event"); let event = Event::from(&event);
let event_type = Event::from(event.event_type.as_str()) ; match &event {
match event_type { Event::CwtchStarted { data } => {
Event::CwtchStarted => {
println!("event CwtchStarted!"); println!("event CwtchStarted!");
initialized = true; initialized = true;
@ -125,9 +136,9 @@ impl Imp {
Some(_) => (), Some(_) => (),
} }
} }
Event::UpdateGlobalSettings => { Event::UpdateGlobalSettings { data } => {
println!("Loading settings froms {}", &event.data["Data"]); println!("Loading settings froms {}", &data["Data"]);
let mut settings: Settings = match serde_json::from_str(&event.data["Data"]) { let mut settings: Settings = match serde_json::from_str(&data["Data"]) {
Ok(s) => s, Ok(s) => s,
Err(e) => panic!("invalid json: {:?}", e), Err(e) => panic!("invalid json: {:?}", e),
}; };
@ -156,19 +167,16 @@ impl Imp {
self.settings = Some(settings); self.settings = Some(settings);
} }
Event::NewPeer => { Event::NewPeer { data } => {
println!( println!("\n***** {} at {} *****\n", data["name"], data["Identity"]);
"\n***** {} at {} *****\n",
event.data["name"], event.data["Identity"]
);
// process json for profile, conversations and servers...else { // process json for profile, conversations and servers...else {
let profile = match Profile::new( let profile = match Profile::new(
&event.data["Identity"], &data["Identity"],
&event.data["name"], &data["name"],
&event.data["picture"], &data["picture"],
&event.data["ContactsJson"], &data["ContactsJson"],
&event.data["ServerList"], &data["ServerList"],
) { ) {
Ok(p) => p, Ok(p) => p,
Err(e) => panic!("error parsing profile: {}", e), Err(e) => panic!("error parsing profile: {}", e),
@ -181,41 +189,47 @@ impl Imp {
None => (), 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 { for (_id, conversation) in &profile.conversations {
match self.behaviour.new_contant_policy { match self.behaviour.new_contant_policy {
NewContactPolicy::Accept => { NewContactPolicy::Accept => {
self.cwtch.accept_conversation(&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::Block => self
.cwtch
.block_contact(&profile.handle.clone(), conversation.identifier),
NewContactPolicy::Ignore => (), NewContactPolicy::Ignore => (),
} }
} }
self.profile = Some(profile); self.profile = Some(profile);
} }
Event::AppError => { Event::AppError { data } => {
if initialized && event.data["Error"] == "Loaded 0 profiles" { if initialized && data["Error"] == "Loaded 0 profiles" {
self.cwtch.create_profile(&self.behaviour.profile_name, &self.password); self.cwtch
.create_profile(&self.behaviour.profile_name, &self.password);
} }
} }
Event::ContactCreated => { Event::ContactCreated { data } => {
println!("Contact Created"); println!("Contact Created");
let convo_handle = event.data["RemotePeer"].to_string(); let convo_handle = data["RemotePeer"].to_string();
let convo_id = event.data["ConversationID"].parse::<i32>().unwrap(); let convo_id = data["ConversationID"].parse::<i32>().unwrap();
let acl: ACL = serde_json::from_str(&event.data["accessControlList"]) let acl: ACL = serde_json::from_str(&data["accessControlList"]).expect("Error parsing conversation");
.expect("Error parsing conversation");
let conversation = Conversation { let conversation = Conversation {
handle: convo_handle.clone(), handle: convo_handle.clone(),
identifier: event.data["ConversationID"].parse::<i32>().unwrap(), identifier: data["ConversationID"].parse::<i32>().unwrap(),
name: event.data["nick"].to_string(), name: data["nick"].to_string(),
status: ConnectionState::new(&event.data["status"]), status: ConnectionState::new(&data["status"]),
blocked: event.data["blocked"] == "true", blocked: data["blocked"] == "true",
accepted: event.data["accepted"] == "true", accepted: data["accepted"] == "true",
access_control_list: acl, access_control_list: acl,
is_group: false, // by definition is_group: false, // by definition
}; };
@ -224,27 +238,26 @@ impl Imp {
Some(profile) => { Some(profile) => {
profile profile
.conversations .conversations
.insert(event.data["RemotePeer"].to_string(), conversation); .insert(data["RemotePeer"].to_string(), conversation);
match self.behaviour.new_contant_policy { match self.behaviour.new_contant_policy {
NewContactPolicy::Accept => { NewContactPolicy::Accept => {
self.cwtch.accept_conversation(&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::Block => self.cwtch.block_contact(&profile.handle.clone(), convo_id),
NewContactPolicy::Ignore => (), NewContactPolicy::Ignore => (),
} }
} }
None => (), None => (),
}; };
} }
Event::PeerStateChange => { Event::PeerStateChange { data } => {}
} Event::ErrUnhandled { name, data } => eprintln!("unhandled event: {}!", name),
Event::ErrUnhandled(err) => eprintln!("unhandled event: {}!", err),
_ => (), _ => (),
}; };
handler.handle(self.cwtch.as_ref(), self.profile.as_ref(), event); handler.handle(self.cwtch.as_ref(), self.profile.as_ref(), event);
} }
} }
} }

View File

@ -1,2 +1,2 @@
pub mod event;
pub mod imp; pub mod imp;
pub mod event;