281 lines
10 KiB
Rust
281 lines
10 KiB
Rust
use crate::event::Event;
|
|
use libcwtch::structs::*;
|
|
use libcwtch::CwtchLib;
|
|
|
|
use serde_json;
|
|
|
|
/// How new contacts should be treated
|
|
pub enum NewContactPolicy {
|
|
/// Do not react, leave it for the custom event handler
|
|
Ignore,
|
|
/// Block all new contacts
|
|
Block,
|
|
/// Accept all new contacts
|
|
Accept,
|
|
}
|
|
|
|
/// Settings for the bot on how it should automatically behave
|
|
pub struct Behaviour {
|
|
/// The bot will enable experimental feautres (required for any experiments to be used)
|
|
pub proto_experiments: bool,
|
|
|
|
/// The bot will enable the file sharing experiment
|
|
pub proto_experiment_fileshare: bool,
|
|
|
|
/// The bot will enable the groups experiment
|
|
pub proto_experiment_groups: bool,
|
|
|
|
/// The profile name the bot will share with accepted conversations
|
|
pub profile_name: String,
|
|
/// The profile pic the bot with share with accepted conversations IF the file share exoeriment is enabled
|
|
pub profile_pic_path: Option<String>,
|
|
|
|
/// Policy dictacting how the bot should automatically handle ContactCreated events
|
|
pub new_contant_policy: NewContactPolicy,
|
|
}
|
|
|
|
pub struct BehaviourBuilder {
|
|
behaviour: Behaviour,
|
|
}
|
|
|
|
impl BehaviourBuilder {
|
|
pub fn new() -> Self {
|
|
return BehaviourBuilder {
|
|
behaviour: Behaviour {
|
|
proto_experiments: false,
|
|
proto_experiment_fileshare: false,
|
|
proto_experiment_groups: false,
|
|
new_contant_policy: NewContactPolicy::Ignore,
|
|
profile_name: "".to_string(),
|
|
profile_pic_path: None,
|
|
},
|
|
};
|
|
}
|
|
|
|
pub fn build(self) -> Behaviour {
|
|
self.behaviour
|
|
}
|
|
|
|
pub fn groups(mut self, val: bool) -> Self {
|
|
self.behaviour.proto_experiment_groups = val;
|
|
self.behaviour.proto_experiments = true;
|
|
self
|
|
}
|
|
|
|
pub fn fileshare(mut self, val: bool) -> Self {
|
|
self.behaviour.proto_experiment_fileshare = val;
|
|
self.behaviour.proto_experiments = true;
|
|
self
|
|
}
|
|
|
|
pub fn profile_pic_path(mut self, val: String) -> Self {
|
|
self.behaviour.profile_pic_path = Some(val);
|
|
self.behaviour.proto_experiment_fileshare = true;
|
|
self.behaviour.proto_experiments = true;
|
|
self
|
|
}
|
|
|
|
pub fn name(mut self, val: String) -> Self {
|
|
self.behaviour.profile_name = val;
|
|
self
|
|
}
|
|
|
|
pub fn new_contact_policy(mut self, val: NewContactPolicy) -> Self {
|
|
self.behaviour.new_contant_policy = val;
|
|
self
|
|
}
|
|
}
|
|
|
|
/// 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: Event);
|
|
}
|
|
|
|
/// Cwtch bot
|
|
pub struct Imp {
|
|
cwtch: Box<dyn CwtchLib>,
|
|
behaviour: Behaviour,
|
|
password: String,
|
|
home_dir: String,
|
|
|
|
settings: Option<Settings>,
|
|
profile: Option<Profile>,
|
|
}
|
|
|
|
impl Imp {
|
|
/// Create a new imp bot with the specified behaviour
|
|
/// start_cwtch is called on it
|
|
pub fn spawn(behaviour: Behaviour, password: String, home_dir: String) -> Self {
|
|
let cwtch = libcwtch::new_cwtchlib_go();
|
|
println!("start_cwtch");
|
|
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,
|
|
};
|
|
}
|
|
|
|
/// 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,
|
|
{
|
|
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 = Event::from(&event);
|
|
match &event {
|
|
Event::CwtchStarted { data } => {
|
|
println!("event CwtchStarted!");
|
|
initialized = true;
|
|
|
|
match self.profile {
|
|
None => {
|
|
println!("Creating bot");
|
|
self.cwtch.load_profiles(&self.password);
|
|
}
|
|
Some(_) => (),
|
|
}
|
|
}
|
|
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),
|
|
};
|
|
|
|
if self.behaviour.proto_experiments {
|
|
settings.ExperimentsEnabled = true;
|
|
}
|
|
if self.behaviour.proto_experiment_fileshare {
|
|
settings
|
|
.Experiments
|
|
.insert(Experiments::FileSharingExperiment.to_key_string(), true);
|
|
}
|
|
if self.behaviour.proto_experiment_groups {
|
|
settings
|
|
.Experiments
|
|
.insert(Experiments::GroupExperiment.to_key_string(), true);
|
|
}
|
|
match settings.save(self.cwtch.as_ref()) {
|
|
Ok(_) => (),
|
|
Err(e) => println!("ERROR: could not save settings: {}", e),
|
|
};
|
|
|
|
match self.profile.as_ref() {
|
|
Some(profile) => {
|
|
if let Some(profile_pic_path) = &self.behaviour.profile_pic_path {
|
|
self.cwtch.share_file(&profile.handle, -1, profile_pic_path);
|
|
}
|
|
}
|
|
None => (),
|
|
};
|
|
|
|
self.settings = Some(settings);
|
|
}
|
|
Event::NewPeer { data } => {
|
|
println!("\n***** {} at {} *****\n", data["name"], data["Identity"]);
|
|
|
|
// 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
|
|
match self.settings.as_ref() {
|
|
Some(_settings) => {
|
|
self.cwtch.share_file(&profile.handle, -1, "build_bot.png");
|
|
}
|
|
None => (),
|
|
};
|
|
|
|
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),
|
|
NewContactPolicy::Ignore => (),
|
|
}
|
|
}
|
|
|
|
self.profile = Some(profile);
|
|
}
|
|
Event::AppError { data } => {
|
|
if initialized && data["Error"] == "Loaded 0 profiles" {
|
|
self.cwtch
|
|
.create_profile(&self.behaviour.profile_name, &self.password);
|
|
}
|
|
}
|
|
Event::ContactCreated { data } => {
|
|
println!("Contact Created");
|
|
let convo_handle = data["RemotePeer"].to_string();
|
|
let convo_id = data["ConversationID"].parse::<i32>().unwrap();
|
|
|
|
let acl: ACL = serde_json::from_str(&data["accessControlList"]).expect("Error parsing conversation");
|
|
|
|
let conversation = Conversation {
|
|
handle: convo_handle.clone(),
|
|
identifier: data["ConversationID"].parse::<i32>().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
|
|
};
|
|
|
|
match self.profile.as_mut() {
|
|
Some(profile) => {
|
|
profile
|
|
.conversations
|
|
.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),
|
|
NewContactPolicy::Ignore => (),
|
|
}
|
|
}
|
|
None => (),
|
|
};
|
|
}
|
|
Event::PeerStateChange { data } => {}
|
|
Event::ErrUnhandled { name, data } => eprintln!("unhandled event: {}!", name),
|
|
_ => (),
|
|
};
|
|
|
|
handler.handle(self.cwtch.as_ref(), self.profile.as_ref(), event);
|
|
}
|
|
}
|
|
}
|