initial import of imp bot framework from update_bot
This commit is contained in:
commit
ff8f8985d8
|
@ -0,0 +1,3 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
.idea
|
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "imp"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libcwtch = {path = "../libcwtch-rs" } #"0.2.0"
|
||||||
|
serde_json = "1.0"
|
|
@ -0,0 +1,17 @@
|
||||||
|
# imp
|
||||||
|
|
||||||
|
*small demon, a familiar of a witch*
|
||||||
|
|
||||||
|
![](imp_color.jpg)
|
||||||
|
|
||||||
|
imp is a set of bot creating utilities built on top of [libcwtch-rs](https://git.openprivacy.ca/cwtch.im/libcwtch-rs)
|
||||||
|
|
||||||
|
It is in the very early prototype stage with one prototype use in the Cwtch [update bot](https://git.openprivacy.ca/dan/update_bot/)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Finally, run the imp `my_imp.event_loop(Box::new(custom_event_handler));`
|
Binary file not shown.
After Width: | Height: | Size: 293 KiB |
|
@ -0,0 +1,12 @@
|
||||||
|
https://en.m.wikipedia.org/wiki/File:Imp_with_cards_-_illustration_from_Le_grand_Etteilla.jpg
|
||||||
|
|
||||||
|
Description
|
||||||
|
English: Illustration of an imp looking at a hand of playing cards. Published on page 193 of Le grand Etteilla, ou, l'art de tirer les cartes.
|
||||||
|
Date circa 1838
|
||||||
|
Source Le grand Etteilla, ou, l'art de tirer les cartes, p. 193
|
||||||
|
Author Simon Blocquel (1780–1863, under the pseudonym Julia Orsini)
|
||||||
|
|
||||||
|
Licensing
|
||||||
|
Public domain
|
||||||
|
This work is in the public domain in its country of origin and other countries and areas where the copyright term is the author's life plus 70 years or fewer.
|
||||||
|
This work is in the public domain in the United States because it was published (or registered with the U.S. Copyright Office) before January 1, 1927.
|
|
@ -0,0 +1,27 @@
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Event {
|
||||||
|
CwtchStarted,
|
||||||
|
NewPeer,
|
||||||
|
NewMessageFromPeer,
|
||||||
|
AppError,
|
||||||
|
ContactCreated,
|
||||||
|
PeerStateChange,
|
||||||
|
UpdateGlobalSettings,
|
||||||
|
|
||||||
|
ErrUnhandled(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
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()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,210 @@
|
||||||
|
use libcwtch::structs::*;
|
||||||
|
use libcwtch::CwtchLib;
|
||||||
|
use crate::event::Event;
|
||||||
|
|
||||||
|
use serde_json;
|
||||||
|
use crate::imp::DefaultContactPolicy::Accept;
|
||||||
|
|
||||||
|
/// How new contacts should be treated
|
||||||
|
pub enum DefaultContactPolicy {
|
||||||
|
/// 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 use the file sharing experiment
|
||||||
|
pub proto_experiment_fileshare: 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 default_contant_policy: DefaultContactPolicy,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Behaviour {
|
||||||
|
/// Returns a named, profile pic using, always accepting conversations bot
|
||||||
|
pub fn new_default_acceptor(name: String, profile_pic_path: String) -> Self {
|
||||||
|
return Behaviour{
|
||||||
|
proto_experiments: true,
|
||||||
|
proto_experiment_fileshare: true,
|
||||||
|
default_contant_policy: Accept,
|
||||||
|
profile_name: name,
|
||||||
|
profile_pic_path: Some(profile_pic_path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(&self, cwtch: &dyn CwtchLib, profile: Option<&Profile>, event: CwtchEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(&mut self, handler: Box<dyn 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 => {
|
||||||
|
println!("event CwtchStarted!");
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
match self.profile {
|
||||||
|
None => {
|
||||||
|
println!("Creating bot");
|
||||||
|
self.cwtch.load_profiles(&self.password);
|
||||||
|
}
|
||||||
|
Some(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::UpdateGlobalSettings => {
|
||||||
|
println!("Loading settings froms {}", &event.data["Data"]);
|
||||||
|
let mut settings: Settings = match serde_json::from_str(&event.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);
|
||||||
|
}
|
||||||
|
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 => {
|
||||||
|
println!(
|
||||||
|
"\n***** {} at {} *****\n",
|
||||||
|
event.data["name"], event.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"],
|
||||||
|
) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => panic!("error parsing profile: {}", e),
|
||||||
|
};
|
||||||
|
print!("profile: {:?}", profile);
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
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::ContactCreated => {
|
||||||
|
if event.data["ConnectionState"] == "Authenticated" {
|
||||||
|
let profile_onion = event.data["RemotePeer"].to_string();
|
||||||
|
let convo_id = event.data["ConversationID"].parse::<i32>().unwrap();
|
||||||
|
|
||||||
|
let acl: ACL = serde_json::from_str(&event.data["accessControlList"])
|
||||||
|
.expect("Error parsing conversation");
|
||||||
|
|
||||||
|
let conversation = Conversation {
|
||||||
|
handle: profile_onion.clone(),
|
||||||
|
identifier: event.data["ConversationID"].parse::<i32>().unwrap(),
|
||||||
|
name: event.data["nick"].to_string(),
|
||||||
|
status: ConnectionState::new(&event.data["status"]),
|
||||||
|
blocked: event.data["blocked"] == "true",
|
||||||
|
accepted: event.data["accepted"] == "true",
|
||||||
|
access_control_list: acl,
|
||||||
|
is_group: false, // by definition
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.behaviour.default_contant_policy {
|
||||||
|
DefaultContactPolicy::Accept =>
|
||||||
|
self.cwtch.accept_conversation(&conversation.handle.clone(), convo_id),
|
||||||
|
DefaultContactPolicy::Block =>
|
||||||
|
self.cwtch.block_contact(&conversation.handle.clone(), convo_id),
|
||||||
|
DefaultContactPolicy::Ignore => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.profile.as_mut() {
|
||||||
|
Some(profile) => {
|
||||||
|
profile
|
||||||
|
.conversations
|
||||||
|
.insert(event.data["RemotePeer"].to_string(), conversation);
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::ErrUnhandled(err) => eprintln!("unhandled event: {}!", err),
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
handler.handle(self.cwtch.as_ref(), self.profile.as_ref(), event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod imp;
|
||||||
|
pub mod event;
|
Loading…
Reference in New Issue