add deltachat login and command handler stubs
This commit is contained in:
136
src/handler.rs
Normal file
136
src/handler.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
use std::{collections::HashSet, sync::Arc};
|
||||
|
||||
use anyhow::Result as AnyhowResult;
|
||||
use deltachat::{
|
||||
chat::{self, ChatId},
|
||||
contact::ContactId,
|
||||
context::Context,
|
||||
message::Message,
|
||||
};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::config::BotConfig;
|
||||
|
||||
pub struct BotContext {
|
||||
authed_contacts: HashSet<ContactId>,
|
||||
config: BotConfig,
|
||||
}
|
||||
|
||||
impl BotContext {
|
||||
pub fn new(config: BotConfig) -> BotContext {
|
||||
BotContext {
|
||||
authed_contacts: HashSet::new(),
|
||||
config,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn echo(dchat_ctx: Arc<Mutex<Context>>, msg: Message) -> AnyhowResult<()> {
|
||||
let chat_id = msg.get_chat_id();
|
||||
let dchat_ctx_lock = dchat_ctx.lock().await;
|
||||
chat::send_text_msg(&dchat_ctx_lock, chat_id, msg.get_text()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn auth_command(
|
||||
dchat_ctx: Arc<Mutex<Context>>,
|
||||
ctx: Arc<Mutex<BotContext>>,
|
||||
msg: Message,
|
||||
args: &[&str],
|
||||
) -> AnyhowResult<()> {
|
||||
let chat_id = msg.get_chat_id();
|
||||
let contact_id = msg.get_from_id();
|
||||
|
||||
let mut ctx_lock = ctx.lock().await;
|
||||
let dchat_ctx_lock = dchat_ctx.lock().await;
|
||||
|
||||
if ctx_lock.authed_contacts.contains(&contact_id) {
|
||||
chat::send_text_msg(&dchat_ctx_lock, chat_id, "Already authenticated".to_owned()).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Some(password) = args.first() else {
|
||||
chat::send_text_msg(
|
||||
&dchat_ctx_lock,
|
||||
chat_id,
|
||||
"Usage: /auth <password>".to_owned(),
|
||||
)
|
||||
.await?;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if *password == ctx_lock.config.auth.password {
|
||||
ctx_lock.authed_contacts.insert(contact_id);
|
||||
chat::send_text_msg(
|
||||
&dchat_ctx_lock,
|
||||
chat_id,
|
||||
"Authentication successful!".to_owned(),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
chat::send_text_msg(&dchat_ctx_lock, chat_id, "Incorrect password".to_owned()).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn wol_command(
|
||||
dchat_ctx: Arc<Mutex<Context>>,
|
||||
ctx: Arc<Mutex<BotContext>>,
|
||||
msg: Message,
|
||||
args: &[&str],
|
||||
) -> AnyhowResult<()> {
|
||||
let chat_id = msg.get_chat_id();
|
||||
let contact_id = msg.get_from_id();
|
||||
|
||||
let ctx_lock = ctx.lock().await;
|
||||
let dchat_ctx_lock = dchat_ctx.lock().await;
|
||||
|
||||
if !ensure_auth(&dchat_ctx_lock, &ctx_lock, chat_id, contact_id).await? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn ssh_unlock_disk_command(
|
||||
dchat_ctx: Arc<Mutex<Context>>,
|
||||
ctx: Arc<Mutex<BotContext>>,
|
||||
msg: Message,
|
||||
args: &[&str],
|
||||
) -> AnyhowResult<()> {
|
||||
let chat_id = msg.get_chat_id();
|
||||
let contact_id = msg.get_from_id();
|
||||
|
||||
let ctx_lock = ctx.lock().await;
|
||||
let dchat_ctx_lock = dchat_ctx.lock().await;
|
||||
|
||||
if !ensure_auth(&dchat_ctx_lock, &ctx_lock, chat_id, contact_id).await? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn ensure_auth(
|
||||
dchat_ctx: &Context,
|
||||
ctx: &BotContext,
|
||||
chat_id: ChatId,
|
||||
contact_id: ContactId,
|
||||
) -> AnyhowResult<bool> {
|
||||
if !ctx.authed_contacts.contains(&contact_id) {
|
||||
chat::send_text_msg(
|
||||
dchat_ctx,
|
||||
chat_id,
|
||||
"Authenticate yourself first with '/auth <password>'.".to_owned(),
|
||||
)
|
||||
.await?;
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
138
src/main.rs
138
src/main.rs
@@ -1,11 +1,25 @@
|
||||
mod config;
|
||||
mod handler;
|
||||
|
||||
use anyhow::{Context as _, Result as AnyhowResult};
|
||||
use clap::Parser;
|
||||
use std::path::PathBuf;
|
||||
use deltachat::{
|
||||
EventType, Events,
|
||||
config::Config,
|
||||
context::Context,
|
||||
message::{Message, MsgId},
|
||||
securejoin,
|
||||
stock_str::StockStrings,
|
||||
};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::handler::BotContext;
|
||||
|
||||
const CONFIG_FILENAME: &str = "bot.yml";
|
||||
const APP_NAME: &str = "deltachat-remotecontrol-bot";
|
||||
const APP_CONFIG_DIR: &str = APP_NAME;
|
||||
const APP_DATA_DIR: &str = APP_NAME;
|
||||
|
||||
/// Delta Chat bot for remote control of local network machines.
|
||||
#[derive(Parser)]
|
||||
@@ -42,7 +56,124 @@ fn default_config_paths() -> Vec<PathBuf> {
|
||||
paths
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn data_path() -> PathBuf {
|
||||
std::env::var("XDG_DATA_HOME")
|
||||
.map(|dh| PathBuf::from(dh).join(APP_DATA_DIR))
|
||||
.or_else(|_| {
|
||||
std::env::var("HOME").map(|h| {
|
||||
PathBuf::from(h)
|
||||
.join(".local")
|
||||
.join("share")
|
||||
.join(APP_DATA_DIR)
|
||||
})
|
||||
})
|
||||
.unwrap_or(PathBuf::from("."))
|
||||
}
|
||||
|
||||
async fn run_bot(cfg: config::BotConfig) -> AnyhowResult<()> {
|
||||
let dchat_db_dir = data_path();
|
||||
std::fs::create_dir_all(&dchat_db_dir)
|
||||
.with_context(|| format!("Failed to create data directory {}", dchat_db_dir.display()))?;
|
||||
|
||||
let dchat_db_filename = dchat_db_dir.join("deltachat.sqlite");
|
||||
|
||||
let dchat_ctx = Context::new(
|
||||
&dchat_db_filename,
|
||||
0,
|
||||
Events::default(),
|
||||
StockStrings::default(),
|
||||
)
|
||||
.await
|
||||
.context("Failed to open Delta Chat client DB")?;
|
||||
|
||||
if !dchat_ctx.is_configured().await? {
|
||||
dchat_ctx
|
||||
.set_config(Config::Addr, Some(&cfg.delta_chat.email))
|
||||
.await?;
|
||||
dchat_ctx
|
||||
.set_config(Config::MailPw, Some(&cfg.delta_chat.password))
|
||||
.await?;
|
||||
dchat_ctx.set_config(Config::Bot, Some("1")).await?;
|
||||
dchat_ctx
|
||||
.configure()
|
||||
.await
|
||||
.context("Failed to configure Delta Chat client")?;
|
||||
}
|
||||
|
||||
dchat_ctx.start_io().await;
|
||||
|
||||
let invite_link = securejoin::get_securejoin_qr(&dchat_ctx, None)
|
||||
.await
|
||||
.context("Failed to generate invite link")?;
|
||||
|
||||
println!("Add this bot to your Delta Chat contacts:");
|
||||
println!("{invite_link}");
|
||||
|
||||
let dchat_ctx = Arc::new(Mutex::new(dchat_ctx));
|
||||
|
||||
let bot_context = Arc::new(Mutex::new(BotContext::new(cfg)));
|
||||
|
||||
let ev_emitter = dchat_ctx.lock().await.get_event_emitter();
|
||||
|
||||
while let Some(ev) = ev_emitter.recv().await {
|
||||
match ev.typ {
|
||||
EventType::IncomingMsg { chat_id: _, msg_id } => {
|
||||
// let dchat_ctx = dchat_ctx.clone();
|
||||
// let bot_context = Arc::clone(&bot_context);
|
||||
|
||||
if let Err(e) = handle_message(dchat_ctx.clone(), bot_context.clone(), msg_id).await
|
||||
{
|
||||
eprintln!("Error in message handler: {e:#}");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_message(
|
||||
dchat_ctx: Arc<Mutex<Context>>,
|
||||
bot_ctx: Arc<Mutex<BotContext>>,
|
||||
msg_id: MsgId,
|
||||
) -> AnyhowResult<()> {
|
||||
let dchat_ctx_lock = dchat_ctx.lock().await;
|
||||
let msg = Message::load_from_db(&dchat_ctx_lock, msg_id).await?;
|
||||
let text = msg.get_text();
|
||||
drop(dchat_ctx_lock);
|
||||
|
||||
if !text.starts_with('/') {
|
||||
handler::echo(dchat_ctx, msg).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut parts = text.split_whitespace();
|
||||
let Some(cmd) = parts.next() else {
|
||||
return Ok(());
|
||||
};
|
||||
let args: Vec<&str> = parts.collect();
|
||||
|
||||
match cmd {
|
||||
"/auth" => {
|
||||
handler::auth_command(dchat_ctx, bot_ctx, msg, &args).await?;
|
||||
}
|
||||
"/wol" => {
|
||||
handler::wol_command(dchat_ctx, bot_ctx, msg, &args).await?;
|
||||
}
|
||||
"/ssh-unlock-disk" => {
|
||||
handler::ssh_unlock_disk_command(dchat_ctx, bot_ctx, msg, &args).await?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
env_logger::init();
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
let config_path: PathBuf = match args.config {
|
||||
@@ -71,6 +202,5 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: start the bot using `config`.
|
||||
let _ = config;
|
||||
run_bot(config).await.expect("error");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user