Merge pull request 'add a few common used on_event handlers' (#6) from on_handlers into main
Reviewed-on: #6 Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
This commit is contained in:
commit
2a9a4781c1
|
@ -1,11 +1,10 @@
|
||||||
[package]
|
[package]
|
||||||
name = "imp"
|
name = "cwtch-imp"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libcwtch = "0.3.2"
|
libcwtch = "0.4.0"
|
||||||
serde_json = "1.0"
|
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
|
@ -12,9 +12,12 @@ It is in the very early prototype stage with one prototype use in the Cwtch [upd
|
||||||
|
|
||||||
Start with creating a `Behaviour` struct and populating it with your desired set of bot behaviours, then `imp::spawn` your bot with the behaviour.
|
Start with creating a `Behaviour` struct and populating it with your desired set of bot behaviours, then `imp::spawn` your bot with the behaviour.
|
||||||
|
|
||||||
Define a struct fulfilling the `imp::EventHandler` trait with all your custom event handling code.
|
To handle Cwtch events you can either
|
||||||
|
- Define a struct fulfilling the `imp::EventHandler::event_loop` function which has the capacity to support all the events libCwtch can emit
|
||||||
|
- Override specific `on_x_event` functions in `imp::EventHandler` such as `on_new_message_from_contact`
|
||||||
|
- This is newer and more will be defined in later versions
|
||||||
|
|
||||||
Finally, run the imp `my_imp.event_loop(Box::new(custom_event_handler));`
|
Finally, run the imp `my_imp.event_loop::<MyEventHandlerType>(update_bot.borrow_mut());`
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
|
use libcwtch::event::{ContactIdentity, GroupID};
|
||||||
|
|
||||||
/// defines a locked list of allowed peers and groups the bot may communicate with
|
/// defines a locked list of allowed peers and groups the bot may communicate with
|
||||||
/// others will be blocked, and peers listed here will be peered with actively
|
/// others will be blocked, and peers listed here will be peered with actively
|
||||||
pub struct AllowListMembers {
|
pub struct AllowListMembers {
|
||||||
/// list of peers to allow by handle
|
/// list of peers to allow by handle
|
||||||
pub peers: Vec<String>,
|
pub peers: Vec<ContactIdentity>,
|
||||||
/// list of groups to join and listen for peers in peer list from
|
/// list of groups to join and listen for peers in peer list from
|
||||||
pub groups: Vec<String>,
|
pub groups: Vec<GroupID>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AllowListMembers {
|
impl AllowListMembers {
|
||||||
/// constructs a new AllowListMembers struct
|
/// constructs a new AllowListMembers struct
|
||||||
pub fn new(peers: Vec<String>, groups: Vec<String>) -> Self {
|
pub fn new(peers: Vec<ContactIdentity>, groups: Vec<GroupID>) -> Self {
|
||||||
AllowListMembers {peers: peers, groups: groups}
|
AllowListMembers {peers: peers, groups: groups}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
72
src/event.rs
72
src/event.rs
|
@ -1,72 +0,0 @@
|
||||||
use chrono::{DateTime, FixedOffset};
|
|
||||||
use libcwtch::structs::CwtchEvent;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Event {
|
|
||||||
CwtchStarted {
|
|
||||||
data: HashMap<String, String>,
|
|
||||||
},
|
|
||||||
NewPeer {
|
|
||||||
data: HashMap<String, String>,
|
|
||||||
},
|
|
||||||
|
|
||||||
NewMessageFromPeer {
|
|
||||||
conversation_id: i32,
|
|
||||||
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<&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 {
|
|
||||||
conversation_id: cwtch_event.data["ConversationID"].parse().unwrap_or(-2),
|
|
||||||
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(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
145
src/imp.rs
145
src/imp.rs
|
@ -1,15 +1,23 @@
|
||||||
use crate::event::Event;
|
use chrono::{DateTime, FixedOffset};
|
||||||
use libcwtch::structs::*;
|
use libcwtch::structs::*;
|
||||||
use libcwtch::CwtchLib;
|
use libcwtch::CwtchLib;
|
||||||
|
use libcwtch::event::{ConversationID, Event};
|
||||||
|
|
||||||
use serde_json;
|
|
||||||
use crate::behaviour::{Behaviour, NewContactPolicy};
|
use crate::behaviour::{Behaviour, NewContactPolicy};
|
||||||
use crate::behaviour::NewContactPolicy::AllowList;
|
use crate::behaviour::NewContactPolicy::AllowList;
|
||||||
|
|
||||||
/// 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 libcwtch::CwtchLib, profile: Option<&Profile>, event: Event);
|
#[allow(unused_variables)]
|
||||||
|
fn handle(&mut self, cwtch: &dyn libcwtch::CwtchLib, profile: Option<&Profile>, event: &Event) {}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn on_contact_online(&self, cwtch: &dyn libcwtch::CwtchLib, profile: &Profile, convo_id: ConversationID) {}
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn on_new_contact(&self, cwtch: &dyn libcwtch::CwtchLib, profile: &Profile, convo_id: ConversationID) {}
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn on_new_message_from_contact(&self, cwtch: &dyn libcwtch::CwtchLib, profile: &Profile, conversation_id: ConversationID, handle: String, timestamp_received: DateTime<FixedOffset>, message: Message) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cwtch bot
|
/// Cwtch bot
|
||||||
|
@ -43,6 +51,7 @@ impl Imp {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
|
#[allow(unused_variables, unused_mut)]
|
||||||
pub fn event_loop<T>(&mut self, handler: &mut T)
|
pub fn event_loop<T>(&mut self, handler: &mut T)
|
||||||
where
|
where
|
||||||
T: EventHandler,
|
T: EventHandler,
|
||||||
|
@ -50,13 +59,10 @@ impl Imp {
|
||||||
let mut initialized: bool = false;
|
let mut initialized: bool = false;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let event_str = self.cwtch.get_appbus_event();
|
let event = 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 = Event::from(&event);
|
|
||||||
match &event {
|
match &event {
|
||||||
Event::CwtchStarted { data } => {
|
Event::CwtchStarted => {
|
||||||
println!("event CwtchStarted!");
|
println!("event CwtchStarted!");
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
|
||||||
|
@ -68,27 +74,24 @@ impl Imp {
|
||||||
Some(_) => (),
|
Some(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::UpdateGlobalSettings { data } => {
|
Event::UpdateGlobalSettings { settings } => {
|
||||||
println!("Loading settings froms {}", &data["Data"]);
|
let mut local_settings = settings.clone();
|
||||||
let mut settings: Settings = match serde_json::from_str(&data["Data"]) {
|
println!("Loading settings froms {:?}", local_settings);
|
||||||
Ok(s) => s,
|
|
||||||
Err(e) => panic!("invalid json: {:?}", e),
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.behaviour.proto_experiments {
|
if self.behaviour.proto_experiments {
|
||||||
settings.ExperimentsEnabled = true;
|
local_settings.ExperimentsEnabled = true;
|
||||||
}
|
}
|
||||||
if self.behaviour.proto_experiment_fileshare {
|
if self.behaviour.proto_experiment_fileshare {
|
||||||
settings
|
local_settings
|
||||||
.Experiments
|
.Experiments
|
||||||
.insert(Experiments::FileSharingExperiment.to_key_string(), true);
|
.insert(Experiments::FileSharingExperiment.to_key_string(), true);
|
||||||
}
|
}
|
||||||
if self.behaviour.proto_experiment_groups {
|
if self.behaviour.proto_experiment_groups {
|
||||||
settings
|
local_settings
|
||||||
.Experiments
|
.Experiments
|
||||||
.insert(Experiments::GroupExperiment.to_key_string(), true);
|
.insert(Experiments::GroupExperiment.to_key_string(), true);
|
||||||
}
|
}
|
||||||
match settings.save(self.cwtch.as_ref()) {
|
match local_settings.save(self.cwtch.as_ref()) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => println!("ERROR: could not save settings: {}", e),
|
Err(e) => println!("ERROR: could not save settings: {}", e),
|
||||||
};
|
};
|
||||||
|
@ -96,77 +99,67 @@ impl Imp {
|
||||||
match self.profile.as_ref() {
|
match self.profile.as_ref() {
|
||||||
Some(profile) => {
|
Some(profile) => {
|
||||||
if let Some(profile_pic_path) = &self.behaviour.profile_pic_path {
|
if let Some(profile_pic_path) = &self.behaviour.profile_pic_path {
|
||||||
self.cwtch.share_file(&profile.handle, -1, profile_pic_path);
|
self.cwtch.share_file(&profile.profile_id, ConversationID(-1), profile_pic_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => (),
|
None => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.settings = Some(settings);
|
self.settings = Some(local_settings);
|
||||||
}
|
}
|
||||||
Event::NewPeer { data } => {
|
Event::NewPeer { profile_id, tag, created, name, default_picture, picture, online, profile_data} => {
|
||||||
println!("\n***** {} at {} *****\n", data["name"], data["Identity"]);
|
|
||||||
|
if let Err(e) = profile_data {
|
||||||
|
panic!("error parsing profile: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
// process json for profile, conversations and servers...else {
|
|
||||||
let profile = match Profile::new(
|
|
||||||
&data["Identity"],
|
|
||||||
&data["name"],
|
|
||||||
&data["picture"],
|
|
||||||
&data["ContactsJson"],
|
|
||||||
&data["ServerList"],
|
|
||||||
) {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => panic!("error parsing profile: {}", e),
|
|
||||||
};
|
|
||||||
// Share profile image
|
// Share profile image
|
||||||
match self.settings.as_ref() {
|
match self.settings.as_ref() {
|
||||||
Some(_settings) => {
|
Some(_settings) => {
|
||||||
self.cwtch.share_file(&profile.handle, -1, "build_bot.png");
|
self.cwtch.share_file(&profile_id, ConversationID(-1), "build_bot.png");
|
||||||
}
|
}
|
||||||
None => (),
|
None => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.cwtch.set_profile_attribute(
|
self.cwtch.set_profile_attribute(
|
||||||
&profile.handle,
|
&profile_id,
|
||||||
"profile.name",
|
"profile.name",
|
||||||
&self.behaviour.profile_name,
|
&self.behaviour.profile_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (_id, conversation) in &profile.conversations {
|
if let Ok(ok_profile) = profile_data {
|
||||||
self.process_contact(conversation.identifier);
|
for (_id, conversation) in &ok_profile.conversations {
|
||||||
}
|
self.process_contact(conversation.identifier);
|
||||||
|
}
|
||||||
|
|
||||||
// Allow list should add all people in the list
|
// Allow list should add all people in the list
|
||||||
if let AllowList(allow_list) = &self.behaviour.new_contant_policy {
|
if let AllowList(allow_list) = &self.behaviour.new_contant_policy {
|
||||||
for handle in &allow_list.peers {
|
for contact_id in &allow_list.peers {
|
||||||
if let None = profile.find_conversation_id_by_handle(handle.clone()) {
|
if let None = ok_profile.find_conversation_id_by_handle(contact_id.clone()) {
|
||||||
self.cwtch.import_bundle(&profile.handle.clone(), &handle.clone());
|
self.cwtch.import_bundle(&profile_id, contact_id.clone().as_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
self.profile = Some(profile);
|
|
||||||
|
self.profile = Some(ok_profile.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Event::AppError { data } => {
|
Event::AppError { error } => {
|
||||||
if initialized && data.contains_key("Error") && data["Error"] == "Loaded 0 profiles" {
|
if initialized && error == "Loaded 0 profiles" {
|
||||||
self.cwtch
|
self.cwtch
|
||||||
.create_profile(&self.behaviour.profile_name, &self.password);
|
.create_profile(&self.behaviour.profile_name, &self.password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::ContactCreated { data } => {
|
Event::ContactCreated {profile_id, conversation_id, contact_id, nick, status, unread, picture, default_picture, num_messages, accepted, access_control_list, blocked, loading, last_msg_time, .. } => {
|
||||||
println!("Contact Created");
|
|
||||||
let convo_handle = data["RemotePeer"].to_string();
|
|
||||||
|
|
||||||
let acl: ACL = serde_json::from_str(&data["accessControlList"]).expect("Error parsing conversation");
|
|
||||||
|
|
||||||
let conversation = Conversation {
|
let conversation = Conversation {
|
||||||
handle: convo_handle.clone(),
|
contact_id: contact_id.clone(),
|
||||||
identifier: data["ConversationID"].parse::<i32>().unwrap(),
|
identifier: conversation_id.clone(),
|
||||||
name: data["nick"].to_string(),
|
name: nick.clone(),
|
||||||
status: ConnectionState::new(&data["status"]),
|
status: status.clone(),
|
||||||
blocked: data["blocked"] == "true",
|
blocked: blocked.clone(),
|
||||||
accepted: data["accepted"] == "true",
|
accepted: accepted.clone(),
|
||||||
access_control_list: acl,
|
access_control_list: access_control_list.clone(),
|
||||||
is_group: false, // by definition
|
is_group: false, // by definition
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -177,35 +170,55 @@ impl Imp {
|
||||||
profile
|
profile
|
||||||
.conversations
|
.conversations
|
||||||
.insert(conversation.identifier, conversation);
|
.insert(conversation.identifier, conversation);
|
||||||
|
handler.on_new_contact(self.cwtch.as_ref(), profile, conversation_id.clone());
|
||||||
|
handler.on_contact_online(self.cwtch.as_ref(), profile, conversation_id.clone());
|
||||||
}
|
}
|
||||||
None => (),
|
None => (),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Event::PeerStateChange { data } => {}
|
Event::PeerStateChange { profile_id, contact_id, connection_state } => {
|
||||||
|
if *connection_state == ConnectionState::Authenticated {
|
||||||
|
match self.profile.as_ref() {
|
||||||
|
Some(profile) => {
|
||||||
|
match profile.find_conversation_id_by_handle(contact_id.clone()) {
|
||||||
|
Some(conversation_id) => handler.on_contact_online(self.cwtch.as_ref(), profile,conversation_id),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::NewMessageFromPeer {profile_id, conversation_id,contact_id, nick, timestamp_received, message, notification, picture } => {
|
||||||
|
match self.profile.as_ref() {
|
||||||
|
Some(profile) => handler.on_new_message_from_contact(self.cwtch.as_ref(), profile, conversation_id.clone(), nick.clone(), timestamp_received.clone(), message.clone()),
|
||||||
|
None => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
Event::ErrUnhandled { name, data } => eprintln!("unhandled event: {}!", name),
|
Event::ErrUnhandled { name, data } => eprintln!("unhandled event: {}!", name),
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
handler.handle(self.cwtch.as_ref(), self.profile.as_ref(), event);
|
handler.handle(self.cwtch.as_ref(), self.profile.as_ref(), &event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_contact(&self, conversation_id: i32) {
|
fn process_contact(&self, conversation_id: ConversationID) {
|
||||||
match &self.profile {
|
match &self.profile {
|
||||||
Some(profile) => {
|
Some(profile) => {
|
||||||
let profile_handle = profile.handle.clone();
|
let profile_handle = profile.profile_id.clone();
|
||||||
|
|
||||||
|
|
||||||
match &self.behaviour.new_contant_policy {
|
match &self.behaviour.new_contant_policy {
|
||||||
NewContactPolicy::Accept => {
|
NewContactPolicy::Accept => {
|
||||||
self.cwtch
|
self.cwtch
|
||||||
.accept_conversation(&profile_handle.clone(), conversation_id);
|
.accept_conversation(&profile.profile_id, conversation_id);
|
||||||
}
|
}
|
||||||
NewContactPolicy::Block => self.cwtch.block_contact(&profile_handle.clone(), conversation_id),
|
NewContactPolicy::Block => self.cwtch.block_contact(&profile_handle.clone(), conversation_id),
|
||||||
NewContactPolicy::AllowList(allow_list) => {
|
NewContactPolicy::AllowList(allow_list) => {
|
||||||
match profile.conversations.get(&conversation_id) {
|
match profile.conversations.get(&conversation_id) {
|
||||||
Some(conversation) => {
|
Some(conversation) => {
|
||||||
if allow_list.peers.contains(&conversation.handle) {
|
if allow_list.peers.contains(&conversation.contact_id) {
|
||||||
self.cwtch
|
self.cwtch
|
||||||
.accept_conversation(&profile_handle.clone(), conversation_id);
|
.accept_conversation(&profile_handle.clone(), conversation_id);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
pub mod event;
|
|
||||||
pub mod imp;
|
pub mod imp;
|
||||||
pub mod behaviour;
|
pub mod behaviour;
|
||||||
|
|
Loading…
Reference in New Issue