fix: add missing port/host fields to GatewayConfig and apply_env_overrides method
- Add port and host fields to GatewayConfig struct - Add default_gateway_port() and default_gateway_host() functions - Add apply_env_overrides() method to Config for env var support - Fix test to include new GatewayConfig fields All tests pass.
This commit is contained in:
parent
d7769340a3
commit
a310e178db
16 changed files with 372 additions and 83 deletions
|
|
@ -8,8 +8,8 @@
|
|||
#![allow(clippy::too_many_lines)]
|
||||
#![allow(clippy::unnecessary_map_or)]
|
||||
|
||||
use async_trait::async_trait;
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use lettre::transport::smtp::authentication::Credentials;
|
||||
use lettre::{Message, SmtpTransport, Transport};
|
||||
use mail_parser::{MessageParser, MimeHeaders};
|
||||
|
|
@ -59,11 +59,21 @@ pub struct EmailConfig {
|
|||
pub allowed_senders: Vec<String>,
|
||||
}
|
||||
|
||||
fn default_imap_port() -> u16 { 993 }
|
||||
fn default_smtp_port() -> u16 { 587 }
|
||||
fn default_imap_folder() -> String { "INBOX".into() }
|
||||
fn default_poll_interval() -> u64 { 60 }
|
||||
fn default_true() -> bool { true }
|
||||
fn default_imap_port() -> u16 {
|
||||
993
|
||||
}
|
||||
fn default_smtp_port() -> u16 {
|
||||
587
|
||||
}
|
||||
fn default_imap_folder() -> String {
|
||||
"INBOX".into()
|
||||
}
|
||||
fn default_poll_interval() -> u64 {
|
||||
60
|
||||
}
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
impl Default for EmailConfig {
|
||||
fn default() -> Self {
|
||||
|
|
@ -137,7 +147,8 @@ impl EmailChannel {
|
|||
|
||||
/// Extract the sender address from a parsed email
|
||||
fn extract_sender(parsed: &mail_parser::Message) -> String {
|
||||
parsed.from()
|
||||
parsed
|
||||
.from()
|
||||
.and_then(|addr| addr.first())
|
||||
.and_then(|a| a.address())
|
||||
.map(|s| s.to_string())
|
||||
|
|
@ -185,32 +196,31 @@ impl EmailChannel {
|
|||
.with_root_certificates(root_store)
|
||||
.with_no_client_auth(),
|
||||
);
|
||||
let server_name: ServerName<'_> =
|
||||
ServerName::try_from(config.imap_host.clone())?;
|
||||
let conn =
|
||||
rustls::ClientConnection::new(tls_config, server_name)?;
|
||||
let server_name: ServerName<'_> = ServerName::try_from(config.imap_host.clone())?;
|
||||
let conn = rustls::ClientConnection::new(tls_config, server_name)?;
|
||||
let mut tls = rustls::StreamOwned::new(conn, tcp);
|
||||
|
||||
let read_line = |tls: &mut rustls::StreamOwned<rustls::ClientConnection, TcpStream>| -> Result<String> {
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
let mut byte = [0u8; 1];
|
||||
match std::io::Read::read(tls, &mut byte) {
|
||||
Ok(0) => return Err(anyhow!("IMAP connection closed")),
|
||||
Ok(_) => {
|
||||
buf.push(byte[0]);
|
||||
if buf.ends_with(b"\r\n") {
|
||||
return Ok(String::from_utf8_lossy(&buf).to_string());
|
||||
let read_line =
|
||||
|tls: &mut rustls::StreamOwned<rustls::ClientConnection, TcpStream>| -> Result<String> {
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
let mut byte = [0u8; 1];
|
||||
match std::io::Read::read(tls, &mut byte) {
|
||||
Ok(0) => return Err(anyhow!("IMAP connection closed")),
|
||||
Ok(_) => {
|
||||
buf.push(byte[0]);
|
||||
if buf.ends_with(b"\r\n") {
|
||||
return Ok(String::from_utf8_lossy(&buf).to_string());
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
let send_cmd = |tls: &mut rustls::StreamOwned<rustls::ClientConnection, TcpStream>,
|
||||
tag: &str,
|
||||
cmd: &str|
|
||||
tag: &str,
|
||||
cmd: &str|
|
||||
-> Result<Vec<String>> {
|
||||
let full = format!("{} {}\r\n", tag, cmd);
|
||||
IoWrite::write_all(tls, full.as_bytes())?;
|
||||
|
|
@ -241,7 +251,11 @@ impl EmailChannel {
|
|||
}
|
||||
|
||||
// Select folder
|
||||
let _select = send_cmd(&mut tls, "A2", &format!("SELECT \"{}\"", config.imap_folder))?;
|
||||
let _select = send_cmd(
|
||||
&mut tls,
|
||||
"A2",
|
||||
&format!("SELECT \"{}\"", config.imap_folder),
|
||||
)?;
|
||||
|
||||
// Search unseen
|
||||
let search_resp = send_cmd(&mut tls, "A3", "SEARCH UNSEEN")?;
|
||||
|
|
@ -285,8 +299,17 @@ impl EmailChannel {
|
|||
.date()
|
||||
.map(|d| {
|
||||
let naive = chrono::NaiveDate::from_ymd_opt(
|
||||
d.year as i32, u32::from(d.month), u32::from(d.day)
|
||||
).and_then(|date| date.and_hms_opt(u32::from(d.hour), u32::from(d.minute), u32::from(d.second)));
|
||||
d.year as i32,
|
||||
u32::from(d.month),
|
||||
u32::from(d.day),
|
||||
)
|
||||
.and_then(|date| {
|
||||
date.and_hms_opt(
|
||||
u32::from(d.hour),
|
||||
u32::from(d.minute),
|
||||
u32::from(d.second),
|
||||
)
|
||||
});
|
||||
naive.map_or(0, |n| n.and_utc().timestamp() as u64)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
|
|
@ -302,7 +325,11 @@ impl EmailChannel {
|
|||
// Mark as seen with unique tag
|
||||
let store_tag = format!("A{tag_counter}");
|
||||
tag_counter += 1;
|
||||
let _ = send_cmd(&mut tls, &store_tag, &format!("STORE {uid} +FLAGS (\\Seen)"));
|
||||
let _ = send_cmd(
|
||||
&mut tls,
|
||||
&store_tag,
|
||||
&format!("STORE {uid} +FLAGS (\\Seen)"),
|
||||
);
|
||||
}
|
||||
|
||||
// Logout with unique tag
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ pub mod imessage;
|
|||
pub mod matrix;
|
||||
pub mod slack;
|
||||
pub mod telegram;
|
||||
pub mod whatsapp;
|
||||
pub mod traits;
|
||||
pub mod whatsapp;
|
||||
|
||||
pub use cli::CliChannel;
|
||||
pub use discord::DiscordChannel;
|
||||
|
|
@ -14,8 +14,8 @@ pub use imessage::IMessageChannel;
|
|||
pub use matrix::MatrixChannel;
|
||||
pub use slack::SlackChannel;
|
||||
pub use telegram::TelegramChannel;
|
||||
pub use whatsapp::WhatsAppChannel;
|
||||
pub use traits::Channel;
|
||||
pub use whatsapp::WhatsAppChannel;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::memory::{self, Memory};
|
||||
|
|
@ -189,7 +189,7 @@ pub fn build_system_prompt(
|
|||
}
|
||||
}
|
||||
|
||||
/// Inject OpenClaw (markdown) identity files into the prompt
|
||||
/// Inject `OpenClaw` (markdown) identity files into the prompt
|
||||
fn inject_openclaw_identity(prompt: &mut String, workspace_dir: &std::path::Path) {
|
||||
#[allow(unused_imports)]
|
||||
use std::fmt::Write;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use super::traits::{Channel, ChannelMessage};
|
|||
use async_trait::async_trait;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// WhatsApp channel — uses WhatsApp Business Cloud API
|
||||
/// `WhatsApp` channel — uses `WhatsApp` Business Cloud API
|
||||
///
|
||||
/// This channel operates in webhook mode (push-based) rather than polling.
|
||||
/// Messages are received via the gateway's `/whatsapp` webhook endpoint.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue