Compare commits
2 Commits
master
...
work-issue
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
303c182a4f | ||
|
|
6cdea12508 |
@ -18,6 +18,8 @@ pub struct DingTalkChannel {
|
||||
/// Per-chat session webhooks for sending replies (chatID -> webhook URL).
|
||||
/// DingTalk provides a unique webhook URL with each incoming message.
|
||||
session_webhooks: Arc<RwLock<HashMap<String, String>>>,
|
||||
/// Per-channel proxy URL override.
|
||||
proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
/// Response from DingTalk gateway connection registration.
|
||||
@ -34,11 +36,18 @@ impl DingTalkChannel {
|
||||
client_secret,
|
||||
allowed_users,
|
||||
session_webhooks: Arc::new(RwLock::new(HashMap::new())),
|
||||
proxy_url: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a per-channel proxy URL that overrides the global proxy config.
|
||||
pub fn with_proxy_url(mut self, proxy_url: Option<String>) -> Self {
|
||||
self.proxy_url = proxy_url;
|
||||
self
|
||||
}
|
||||
|
||||
fn http_client(&self) -> reqwest::Client {
|
||||
crate::config::build_runtime_proxy_client("channel.dingtalk")
|
||||
crate::config::build_channel_proxy_client("channel.dingtalk", self.proxy_url.as_deref())
|
||||
}
|
||||
|
||||
fn is_user_allowed(&self, user_id: &str) -> bool {
|
||||
|
||||
@ -18,6 +18,8 @@ pub struct DiscordChannel {
|
||||
listen_to_bots: bool,
|
||||
mention_only: bool,
|
||||
typing_handles: Mutex<HashMap<String, tokio::task::JoinHandle<()>>>,
|
||||
/// Per-channel proxy URL override.
|
||||
proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl DiscordChannel {
|
||||
@ -35,11 +37,18 @@ impl DiscordChannel {
|
||||
listen_to_bots,
|
||||
mention_only,
|
||||
typing_handles: Mutex::new(HashMap::new()),
|
||||
proxy_url: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a per-channel proxy URL that overrides the global proxy config.
|
||||
pub fn with_proxy_url(mut self, proxy_url: Option<String>) -> Self {
|
||||
self.proxy_url = proxy_url;
|
||||
self
|
||||
}
|
||||
|
||||
fn http_client(&self) -> reqwest::Client {
|
||||
crate::config::build_runtime_proxy_client("channel.discord")
|
||||
crate::config::build_channel_proxy_client("channel.discord", self.proxy_url.as_deref())
|
||||
}
|
||||
|
||||
/// Check if a Discord user ID is in the allowlist.
|
||||
|
||||
@ -380,6 +380,8 @@ pub struct LarkChannel {
|
||||
tenant_token: Arc<RwLock<Option<CachedTenantToken>>>,
|
||||
/// Dedup set: WS message_ids seen in last ~30 min to prevent double-dispatch
|
||||
ws_seen_ids: Arc<RwLock<HashMap<String, Instant>>>,
|
||||
/// Per-channel proxy URL override.
|
||||
proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl LarkChannel {
|
||||
@ -423,6 +425,7 @@ impl LarkChannel {
|
||||
receive_mode: crate::config::schema::LarkReceiveMode::default(),
|
||||
tenant_token: Arc::new(RwLock::new(None)),
|
||||
ws_seen_ids: Arc::new(RwLock::new(HashMap::new())),
|
||||
proxy_url: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,6 +447,7 @@ impl LarkChannel {
|
||||
platform,
|
||||
);
|
||||
ch.receive_mode = config.receive_mode.clone();
|
||||
ch.proxy_url = config.proxy_url.clone();
|
||||
ch
|
||||
}
|
||||
|
||||
@ -461,6 +465,7 @@ impl LarkChannel {
|
||||
LarkPlatform::Lark,
|
||||
);
|
||||
ch.receive_mode = config.receive_mode.clone();
|
||||
ch.proxy_url = config.proxy_url.clone();
|
||||
ch
|
||||
}
|
||||
|
||||
@ -476,11 +481,15 @@ impl LarkChannel {
|
||||
LarkPlatform::Feishu,
|
||||
);
|
||||
ch.receive_mode = config.receive_mode.clone();
|
||||
ch.proxy_url = config.proxy_url.clone();
|
||||
ch
|
||||
}
|
||||
|
||||
fn http_client(&self) -> reqwest::Client {
|
||||
crate::config::build_runtime_proxy_client(self.platform.proxy_service_key())
|
||||
crate::config::build_channel_proxy_client(
|
||||
self.platform.proxy_service_key(),
|
||||
self.proxy_url.as_deref(),
|
||||
)
|
||||
}
|
||||
|
||||
fn channel_name(&self) -> &'static str {
|
||||
@ -2113,6 +2122,7 @@ mod tests {
|
||||
use_feishu: false,
|
||||
receive_mode: LarkReceiveMode::default(),
|
||||
port: None,
|
||||
proxy_url: None,
|
||||
};
|
||||
let json = serde_json::to_string(&lc).unwrap();
|
||||
let parsed: LarkConfig = serde_json::from_str(&json).unwrap();
|
||||
@ -2135,6 +2145,7 @@ mod tests {
|
||||
use_feishu: false,
|
||||
receive_mode: LarkReceiveMode::Webhook,
|
||||
port: Some(9898),
|
||||
proxy_url: None,
|
||||
};
|
||||
let toml_str = toml::to_string(&lc).unwrap();
|
||||
let parsed: LarkConfig = toml::from_str(&toml_str).unwrap();
|
||||
@ -2169,6 +2180,7 @@ mod tests {
|
||||
use_feishu: false,
|
||||
receive_mode: LarkReceiveMode::Webhook,
|
||||
port: Some(9898),
|
||||
proxy_url: None,
|
||||
};
|
||||
|
||||
let ch = LarkChannel::from_config(&cfg);
|
||||
@ -2193,6 +2205,7 @@ mod tests {
|
||||
use_feishu: true,
|
||||
receive_mode: LarkReceiveMode::Webhook,
|
||||
port: Some(9898),
|
||||
proxy_url: None,
|
||||
};
|
||||
|
||||
let ch = LarkChannel::from_lark_config(&cfg);
|
||||
@ -2214,6 +2227,7 @@ mod tests {
|
||||
allowed_users: vec!["*".into()],
|
||||
receive_mode: LarkReceiveMode::Webhook,
|
||||
port: Some(9898),
|
||||
proxy_url: None,
|
||||
};
|
||||
|
||||
let ch = LarkChannel::from_feishu_config(&cfg);
|
||||
@ -2386,6 +2400,7 @@ mod tests {
|
||||
allowed_users: vec!["*".into()],
|
||||
receive_mode: crate::config::schema::LarkReceiveMode::Webhook,
|
||||
port: Some(9898),
|
||||
proxy_url: None,
|
||||
};
|
||||
let ch_feishu = LarkChannel::from_feishu_config(&feishu_cfg);
|
||||
assert_eq!(
|
||||
|
||||
@ -17,6 +17,8 @@ pub struct MattermostChannel {
|
||||
mention_only: bool,
|
||||
/// Handle for the background typing-indicator loop (aborted on stop_typing).
|
||||
typing_handle: Mutex<Option<tokio::task::JoinHandle<()>>>,
|
||||
/// Per-channel proxy URL override.
|
||||
proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl MattermostChannel {
|
||||
@ -38,11 +40,18 @@ impl MattermostChannel {
|
||||
thread_replies,
|
||||
mention_only,
|
||||
typing_handle: Mutex::new(None),
|
||||
proxy_url: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a per-channel proxy URL that overrides the global proxy config.
|
||||
pub fn with_proxy_url(mut self, proxy_url: Option<String>) -> Self {
|
||||
self.proxy_url = proxy_url;
|
||||
self
|
||||
}
|
||||
|
||||
fn http_client(&self) -> reqwest::Client {
|
||||
crate::config::build_runtime_proxy_client("channel.mattermost")
|
||||
crate::config::build_channel_proxy_client("channel.mattermost", self.proxy_url.as_deref())
|
||||
}
|
||||
|
||||
/// Check if a user ID is in the allowlist.
|
||||
|
||||
@ -3691,7 +3691,8 @@ fn collect_configured_channels(
|
||||
.with_streaming(tg.stream_mode, tg.draft_update_interval_ms)
|
||||
.with_transcription(config.transcription.clone())
|
||||
.with_tts(config.tts.clone())
|
||||
.with_workspace_dir(config.workspace_dir.clone()),
|
||||
.with_workspace_dir(config.workspace_dir.clone())
|
||||
.with_proxy_url(tg.proxy_url.clone()),
|
||||
),
|
||||
});
|
||||
}
|
||||
@ -3699,13 +3700,16 @@ fn collect_configured_channels(
|
||||
if let Some(ref dc) = config.channels_config.discord {
|
||||
channels.push(ConfiguredChannel {
|
||||
display_name: "Discord",
|
||||
channel: Arc::new(DiscordChannel::new(
|
||||
dc.bot_token.clone(),
|
||||
dc.guild_id.clone(),
|
||||
dc.allowed_users.clone(),
|
||||
dc.listen_to_bots,
|
||||
dc.mention_only,
|
||||
)),
|
||||
channel: Arc::new(
|
||||
DiscordChannel::new(
|
||||
dc.bot_token.clone(),
|
||||
dc.guild_id.clone(),
|
||||
dc.allowed_users.clone(),
|
||||
dc.listen_to_bots,
|
||||
dc.mention_only,
|
||||
)
|
||||
.with_proxy_url(dc.proxy_url.clone()),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@ -3722,7 +3726,8 @@ fn collect_configured_channels(
|
||||
)
|
||||
.with_thread_replies(sl.thread_replies.unwrap_or(true))
|
||||
.with_group_reply_policy(sl.mention_only, Vec::new())
|
||||
.with_workspace_dir(config.workspace_dir.clone()),
|
||||
.with_workspace_dir(config.workspace_dir.clone())
|
||||
.with_proxy_url(sl.proxy_url.clone()),
|
||||
),
|
||||
});
|
||||
}
|
||||
@ -3730,14 +3735,17 @@ fn collect_configured_channels(
|
||||
if let Some(ref mm) = config.channels_config.mattermost {
|
||||
channels.push(ConfiguredChannel {
|
||||
display_name: "Mattermost",
|
||||
channel: Arc::new(MattermostChannel::new(
|
||||
mm.url.clone(),
|
||||
mm.bot_token.clone(),
|
||||
mm.channel_id.clone(),
|
||||
mm.allowed_users.clone(),
|
||||
mm.thread_replies.unwrap_or(true),
|
||||
mm.mention_only.unwrap_or(false),
|
||||
)),
|
||||
channel: Arc::new(
|
||||
MattermostChannel::new(
|
||||
mm.url.clone(),
|
||||
mm.bot_token.clone(),
|
||||
mm.channel_id.clone(),
|
||||
mm.allowed_users.clone(),
|
||||
mm.thread_replies.unwrap_or(true),
|
||||
mm.mention_only.unwrap_or(false),
|
||||
)
|
||||
.with_proxy_url(mm.proxy_url.clone()),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@ -3775,14 +3783,17 @@ fn collect_configured_channels(
|
||||
if let Some(ref sig) = config.channels_config.signal {
|
||||
channels.push(ConfiguredChannel {
|
||||
display_name: "Signal",
|
||||
channel: Arc::new(SignalChannel::new(
|
||||
sig.http_url.clone(),
|
||||
sig.account.clone(),
|
||||
sig.group_id.clone(),
|
||||
sig.allowed_from.clone(),
|
||||
sig.ignore_attachments,
|
||||
sig.ignore_stories,
|
||||
)),
|
||||
channel: Arc::new(
|
||||
SignalChannel::new(
|
||||
sig.http_url.clone(),
|
||||
sig.account.clone(),
|
||||
sig.group_id.clone(),
|
||||
sig.allowed_from.clone(),
|
||||
sig.ignore_attachments,
|
||||
sig.ignore_stories,
|
||||
)
|
||||
.with_proxy_url(sig.proxy_url.clone()),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@ -3799,12 +3810,15 @@ fn collect_configured_channels(
|
||||
if wa.is_cloud_config() {
|
||||
channels.push(ConfiguredChannel {
|
||||
display_name: "WhatsApp",
|
||||
channel: Arc::new(WhatsAppChannel::new(
|
||||
wa.access_token.clone().unwrap_or_default(),
|
||||
wa.phone_number_id.clone().unwrap_or_default(),
|
||||
wa.verify_token.clone().unwrap_or_default(),
|
||||
wa.allowed_numbers.clone(),
|
||||
)),
|
||||
channel: Arc::new(
|
||||
WhatsAppChannel::new(
|
||||
wa.access_token.clone().unwrap_or_default(),
|
||||
wa.phone_number_id.clone().unwrap_or_default(),
|
||||
wa.verify_token.clone().unwrap_or_default(),
|
||||
wa.allowed_numbers.clone(),
|
||||
)
|
||||
.with_proxy_url(wa.proxy_url.clone()),
|
||||
),
|
||||
});
|
||||
} else {
|
||||
tracing::warn!("WhatsApp Cloud API configured but missing required fields (phone_number_id, access_token, verify_token)");
|
||||
@ -3861,11 +3875,12 @@ fn collect_configured_channels(
|
||||
if let Some(ref wati_cfg) = config.channels_config.wati {
|
||||
channels.push(ConfiguredChannel {
|
||||
display_name: "WATI",
|
||||
channel: Arc::new(WatiChannel::new(
|
||||
channel: Arc::new(WatiChannel::new_with_proxy(
|
||||
wati_cfg.api_token.clone(),
|
||||
wati_cfg.api_url.clone(),
|
||||
wati_cfg.tenant_id.clone(),
|
||||
wati_cfg.allowed_numbers.clone(),
|
||||
wati_cfg.proxy_url.clone(),
|
||||
)),
|
||||
});
|
||||
}
|
||||
@ -3873,10 +3888,11 @@ fn collect_configured_channels(
|
||||
if let Some(ref nc) = config.channels_config.nextcloud_talk {
|
||||
channels.push(ConfiguredChannel {
|
||||
display_name: "Nextcloud Talk",
|
||||
channel: Arc::new(NextcloudTalkChannel::new(
|
||||
channel: Arc::new(NextcloudTalkChannel::new_with_proxy(
|
||||
nc.base_url.clone(),
|
||||
nc.app_token.clone(),
|
||||
nc.allowed_users.clone(),
|
||||
nc.proxy_url.clone(),
|
||||
)),
|
||||
});
|
||||
}
|
||||
@ -3948,11 +3964,14 @@ fn collect_configured_channels(
|
||||
if let Some(ref dt) = config.channels_config.dingtalk {
|
||||
channels.push(ConfiguredChannel {
|
||||
display_name: "DingTalk",
|
||||
channel: Arc::new(DingTalkChannel::new(
|
||||
dt.client_id.clone(),
|
||||
dt.client_secret.clone(),
|
||||
dt.allowed_users.clone(),
|
||||
)),
|
||||
channel: Arc::new(
|
||||
DingTalkChannel::new(
|
||||
dt.client_id.clone(),
|
||||
dt.client_secret.clone(),
|
||||
dt.allowed_users.clone(),
|
||||
)
|
||||
.with_proxy_url(dt.proxy_url.clone()),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@ -3965,7 +3984,8 @@ fn collect_configured_channels(
|
||||
qq.app_secret.clone(),
|
||||
qq.allowed_users.clone(),
|
||||
)
|
||||
.with_workspace_dir(config.workspace_dir.clone()),
|
||||
.with_workspace_dir(config.workspace_dir.clone())
|
||||
.with_proxy_url(qq.proxy_url.clone()),
|
||||
),
|
||||
});
|
||||
}
|
||||
@ -8721,6 +8741,7 @@ This is an example JSON object for profile settings."#;
|
||||
thread_replies: Some(true),
|
||||
mention_only: Some(false),
|
||||
interrupt_on_new_message: false,
|
||||
proxy_url: None,
|
||||
});
|
||||
|
||||
let channels = collect_configured_channels(&config, "test");
|
||||
@ -9613,6 +9634,7 @@ This is an example JSON object for profile settings."#;
|
||||
interrupt_on_new_message: false,
|
||||
mention_only: false,
|
||||
ack_reactions: None,
|
||||
proxy_url: None,
|
||||
});
|
||||
match build_channel_by_id(&config, "telegram") {
|
||||
Ok(channel) => assert_eq!(channel.name(), "telegram"),
|
||||
|
||||
@ -17,11 +17,23 @@ pub struct NextcloudTalkChannel {
|
||||
|
||||
impl NextcloudTalkChannel {
|
||||
pub fn new(base_url: String, app_token: String, allowed_users: Vec<String>) -> Self {
|
||||
Self::new_with_proxy(base_url, app_token, allowed_users, None)
|
||||
}
|
||||
|
||||
pub fn new_with_proxy(
|
||||
base_url: String,
|
||||
app_token: String,
|
||||
allowed_users: Vec<String>,
|
||||
proxy_url: Option<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
base_url: base_url.trim_end_matches('/').to_string(),
|
||||
app_token,
|
||||
allowed_users,
|
||||
client: reqwest::Client::new(),
|
||||
client: crate::config::build_channel_proxy_client(
|
||||
"channel.nextcloud_talk",
|
||||
proxy_url.as_deref(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -285,6 +285,8 @@ pub struct QQChannel {
|
||||
upload_cache: Arc<RwLock<HashMap<String, UploadCacheEntry>>>,
|
||||
/// Passive reply tracker for QQ API rate limiting.
|
||||
reply_tracker: Arc<RwLock<HashMap<String, ReplyRecord>>>,
|
||||
/// Per-channel proxy URL override.
|
||||
proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl QQChannel {
|
||||
@ -298,6 +300,7 @@ impl QQChannel {
|
||||
workspace_dir: None,
|
||||
upload_cache: Arc::new(RwLock::new(HashMap::new())),
|
||||
reply_tracker: Arc::new(RwLock::new(HashMap::new())),
|
||||
proxy_url: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,8 +310,14 @@ impl QQChannel {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set a per-channel proxy URL that overrides the global proxy config.
|
||||
pub fn with_proxy_url(mut self, proxy_url: Option<String>) -> Self {
|
||||
self.proxy_url = proxy_url;
|
||||
self
|
||||
}
|
||||
|
||||
fn http_client(&self) -> reqwest::Client {
|
||||
crate::config::build_runtime_proxy_client("channel.qq")
|
||||
crate::config::build_channel_proxy_client("channel.qq", self.proxy_url.as_deref())
|
||||
}
|
||||
|
||||
fn is_user_allowed(&self, user_id: &str) -> bool {
|
||||
|
||||
@ -28,6 +28,8 @@ pub struct SignalChannel {
|
||||
allowed_from: Vec<String>,
|
||||
ignore_attachments: bool,
|
||||
ignore_stories: bool,
|
||||
/// Per-channel proxy URL override.
|
||||
proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
// ── signal-cli SSE event JSON shapes ────────────────────────────
|
||||
@ -87,12 +89,23 @@ impl SignalChannel {
|
||||
allowed_from,
|
||||
ignore_attachments,
|
||||
ignore_stories,
|
||||
proxy_url: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a per-channel proxy URL that overrides the global proxy config.
|
||||
pub fn with_proxy_url(mut self, proxy_url: Option<String>) -> Self {
|
||||
self.proxy_url = proxy_url;
|
||||
self
|
||||
}
|
||||
|
||||
fn http_client(&self) -> Client {
|
||||
let builder = Client::builder().connect_timeout(Duration::from_secs(10));
|
||||
let builder = crate::config::apply_runtime_proxy_to_builder(builder, "channel.signal");
|
||||
let builder = crate::config::apply_channel_proxy_to_builder(
|
||||
builder,
|
||||
"channel.signal",
|
||||
self.proxy_url.as_deref(),
|
||||
);
|
||||
builder.build().expect("Signal HTTP client should build")
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,8 @@ pub struct SlackChannel {
|
||||
workspace_dir: Option<PathBuf>,
|
||||
/// Maps channel_id -> thread_ts for active assistant threads (used for status indicators).
|
||||
active_assistant_thread: Mutex<HashMap<String, String>>,
|
||||
/// Per-channel proxy URL override.
|
||||
proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
const SLACK_HISTORY_MAX_RETRIES: u32 = 3;
|
||||
@ -121,6 +123,7 @@ impl SlackChannel {
|
||||
user_display_name_cache: Mutex::new(HashMap::new()),
|
||||
workspace_dir: None,
|
||||
active_assistant_thread: Mutex::new(HashMap::new()),
|
||||
proxy_url: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,8 +151,19 @@ impl SlackChannel {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set a per-channel proxy URL that overrides the global proxy config.
|
||||
pub fn with_proxy_url(mut self, proxy_url: Option<String>) -> Self {
|
||||
self.proxy_url = proxy_url;
|
||||
self
|
||||
}
|
||||
|
||||
fn http_client(&self) -> reqwest::Client {
|
||||
crate::config::build_runtime_proxy_client_with_timeouts("channel.slack", 30, 10)
|
||||
crate::config::build_channel_proxy_client_with_timeouts(
|
||||
"channel.slack",
|
||||
self.proxy_url.as_deref(),
|
||||
30,
|
||||
10,
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if a Slack user ID is in the allowlist.
|
||||
@ -804,12 +818,13 @@ impl SlackChannel {
|
||||
}
|
||||
|
||||
fn slack_media_http_client_no_redirect(&self) -> anyhow::Result<reqwest::Client> {
|
||||
let builder = crate::config::apply_runtime_proxy_to_builder(
|
||||
let builder = crate::config::apply_channel_proxy_to_builder(
|
||||
reqwest::Client::builder()
|
||||
.redirect(reqwest::redirect::Policy::none())
|
||||
.timeout(Duration::from_secs(30))
|
||||
.connect_timeout(Duration::from_secs(10)),
|
||||
"channel.slack",
|
||||
self.proxy_url.as_deref(),
|
||||
);
|
||||
builder
|
||||
.build()
|
||||
|
||||
@ -337,6 +337,8 @@ pub struct TelegramChannel {
|
||||
voice_chats: Arc<std::sync::Mutex<std::collections::HashSet<String>>>,
|
||||
pending_voice:
|
||||
Arc<std::sync::Mutex<std::collections::HashMap<String, (String, std::time::Instant)>>>,
|
||||
/// Per-channel proxy URL override.
|
||||
proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@ -379,6 +381,7 @@ impl TelegramChannel {
|
||||
tts_config: None,
|
||||
voice_chats: Arc::new(std::sync::Mutex::new(std::collections::HashSet::new())),
|
||||
pending_voice: Arc::new(std::sync::Mutex::new(std::collections::HashMap::new())),
|
||||
proxy_url: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -388,6 +391,12 @@ impl TelegramChannel {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set a per-channel proxy URL that overrides the global proxy config.
|
||||
pub fn with_proxy_url(mut self, proxy_url: Option<String>) -> Self {
|
||||
self.proxy_url = proxy_url;
|
||||
self
|
||||
}
|
||||
|
||||
/// Configure workspace directory for saving downloaded attachments.
|
||||
pub fn with_workspace_dir(mut self, dir: std::path::PathBuf) -> Self {
|
||||
self.workspace_dir = Some(dir);
|
||||
@ -478,7 +487,7 @@ impl TelegramChannel {
|
||||
}
|
||||
|
||||
fn http_client(&self) -> reqwest::Client {
|
||||
crate::config::build_runtime_proxy_client("channel.telegram")
|
||||
crate::config::build_channel_proxy_client("channel.telegram", self.proxy_url.as_deref())
|
||||
}
|
||||
|
||||
fn normalize_identity(value: &str) -> String {
|
||||
|
||||
@ -22,13 +22,23 @@ impl WatiChannel {
|
||||
api_url: String,
|
||||
tenant_id: Option<String>,
|
||||
allowed_numbers: Vec<String>,
|
||||
) -> Self {
|
||||
Self::new_with_proxy(api_token, api_url, tenant_id, allowed_numbers, None)
|
||||
}
|
||||
|
||||
pub fn new_with_proxy(
|
||||
api_token: String,
|
||||
api_url: String,
|
||||
tenant_id: Option<String>,
|
||||
allowed_numbers: Vec<String>,
|
||||
proxy_url: Option<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
api_token,
|
||||
api_url,
|
||||
tenant_id,
|
||||
allowed_numbers,
|
||||
client: crate::config::build_runtime_proxy_client("channel.wati"),
|
||||
client: crate::config::build_channel_proxy_client("channel.wati", proxy_url.as_deref()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,6 +27,8 @@ pub struct WhatsAppChannel {
|
||||
endpoint_id: String,
|
||||
verify_token: String,
|
||||
allowed_numbers: Vec<String>,
|
||||
/// Per-channel proxy URL override.
|
||||
proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl WhatsAppChannel {
|
||||
@ -41,11 +43,18 @@ impl WhatsAppChannel {
|
||||
endpoint_id,
|
||||
verify_token,
|
||||
allowed_numbers,
|
||||
proxy_url: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a per-channel proxy URL that overrides the global proxy config.
|
||||
pub fn with_proxy_url(mut self, proxy_url: Option<String>) -> Self {
|
||||
self.proxy_url = proxy_url;
|
||||
self
|
||||
}
|
||||
|
||||
fn http_client(&self) -> reqwest::Client {
|
||||
crate::config::build_runtime_proxy_client("channel.whatsapp")
|
||||
crate::config::build_channel_proxy_client("channel.whatsapp", self.proxy_url.as_deref())
|
||||
}
|
||||
|
||||
/// Check if a phone number is allowed (E.164 format: +1234567890)
|
||||
|
||||
@ -4,7 +4,8 @@ pub mod workspace;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub use schema::{
|
||||
apply_runtime_proxy_to_builder, build_runtime_proxy_client,
|
||||
apply_channel_proxy_to_builder, apply_runtime_proxy_to_builder, build_channel_proxy_client,
|
||||
build_channel_proxy_client_with_timeouts, build_runtime_proxy_client,
|
||||
build_runtime_proxy_client_with_timeouts, runtime_proxy_config, set_runtime_proxy_config,
|
||||
AgentConfig, AssemblyAiSttConfig, AuditConfig, AutonomyConfig, BackupConfig,
|
||||
BrowserComputerUseConfig, BrowserConfig, BuiltinHooksConfig, ChannelsConfig,
|
||||
@ -58,6 +59,7 @@ mod tests {
|
||||
interrupt_on_new_message: false,
|
||||
mention_only: false,
|
||||
ack_reactions: None,
|
||||
proxy_url: None,
|
||||
};
|
||||
|
||||
let discord = DiscordConfig {
|
||||
@ -67,6 +69,7 @@ mod tests {
|
||||
listen_to_bots: false,
|
||||
interrupt_on_new_message: false,
|
||||
mention_only: false,
|
||||
proxy_url: None,
|
||||
};
|
||||
|
||||
let lark = LarkConfig {
|
||||
@ -79,6 +82,7 @@ mod tests {
|
||||
use_feishu: false,
|
||||
receive_mode: crate::config::schema::LarkReceiveMode::Websocket,
|
||||
port: None,
|
||||
proxy_url: None,
|
||||
};
|
||||
let feishu = FeishuConfig {
|
||||
app_id: "app-id".into(),
|
||||
@ -88,6 +92,7 @@ mod tests {
|
||||
allowed_users: vec![],
|
||||
receive_mode: crate::config::schema::LarkReceiveMode::Websocket,
|
||||
port: None,
|
||||
proxy_url: None,
|
||||
};
|
||||
|
||||
let nextcloud_talk = NextcloudTalkConfig {
|
||||
@ -95,6 +100,7 @@ mod tests {
|
||||
app_token: "app-token".into(),
|
||||
webhook_secret: None,
|
||||
allowed_users: vec!["*".into()],
|
||||
proxy_url: None,
|
||||
};
|
||||
|
||||
assert_eq!(telegram.allowed_users.len(), 1);
|
||||
|
||||
@ -3381,6 +3381,116 @@ pub fn build_runtime_proxy_client_with_timeouts(
|
||||
client
|
||||
}
|
||||
|
||||
/// Build an HTTP client for a channel, using an explicit per-channel proxy URL
|
||||
/// when configured. Falls back to the global runtime proxy when `proxy_url` is
|
||||
/// `None` or empty.
|
||||
pub fn build_channel_proxy_client(service_key: &str, proxy_url: Option<&str>) -> reqwest::Client {
|
||||
match normalize_proxy_url_option(proxy_url) {
|
||||
Some(url) => build_explicit_proxy_client(service_key, &url, None, None),
|
||||
None => build_runtime_proxy_client(service_key),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build an HTTP client for a channel with custom timeouts, using an explicit
|
||||
/// per-channel proxy URL when configured. Falls back to the global runtime
|
||||
/// proxy when `proxy_url` is `None` or empty.
|
||||
pub fn build_channel_proxy_client_with_timeouts(
|
||||
service_key: &str,
|
||||
proxy_url: Option<&str>,
|
||||
timeout_secs: u64,
|
||||
connect_timeout_secs: u64,
|
||||
) -> reqwest::Client {
|
||||
match normalize_proxy_url_option(proxy_url) {
|
||||
Some(url) => build_explicit_proxy_client(
|
||||
service_key,
|
||||
&url,
|
||||
Some(timeout_secs),
|
||||
Some(connect_timeout_secs),
|
||||
),
|
||||
None => build_runtime_proxy_client_with_timeouts(
|
||||
service_key,
|
||||
timeout_secs,
|
||||
connect_timeout_secs,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply an explicit proxy URL to a `reqwest::ClientBuilder`, returning the
|
||||
/// modified builder. Used by channels that specify a per-channel `proxy_url`.
|
||||
pub fn apply_channel_proxy_to_builder(
|
||||
builder: reqwest::ClientBuilder,
|
||||
service_key: &str,
|
||||
proxy_url: Option<&str>,
|
||||
) -> reqwest::ClientBuilder {
|
||||
match normalize_proxy_url_option(proxy_url) {
|
||||
Some(url) => apply_explicit_proxy_to_builder(builder, service_key, &url),
|
||||
None => apply_runtime_proxy_to_builder(builder, service_key),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a client with a single explicit proxy URL (http+https via `Proxy::all`).
|
||||
fn build_explicit_proxy_client(
|
||||
service_key: &str,
|
||||
proxy_url: &str,
|
||||
timeout_secs: Option<u64>,
|
||||
connect_timeout_secs: Option<u64>,
|
||||
) -> reqwest::Client {
|
||||
let cache_key = format!(
|
||||
"explicit|{}|{}|timeout={}|connect_timeout={}",
|
||||
service_key.trim().to_ascii_lowercase(),
|
||||
proxy_url,
|
||||
timeout_secs
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
connect_timeout_secs
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
);
|
||||
if let Some(client) = runtime_proxy_cached_client(&cache_key) {
|
||||
return client;
|
||||
}
|
||||
|
||||
let mut builder = reqwest::Client::builder();
|
||||
if let Some(t) = timeout_secs {
|
||||
builder = builder.timeout(std::time::Duration::from_secs(t));
|
||||
}
|
||||
if let Some(ct) = connect_timeout_secs {
|
||||
builder = builder.connect_timeout(std::time::Duration::from_secs(ct));
|
||||
}
|
||||
builder = apply_explicit_proxy_to_builder(builder, service_key, proxy_url);
|
||||
let client = builder.build().unwrap_or_else(|error| {
|
||||
tracing::warn!(
|
||||
service_key,
|
||||
proxy_url,
|
||||
"Failed to build channel proxy client: {error}"
|
||||
);
|
||||
reqwest::Client::new()
|
||||
});
|
||||
set_runtime_proxy_cached_client(cache_key, client.clone());
|
||||
client
|
||||
}
|
||||
|
||||
/// Apply a single explicit proxy URL to a builder via `Proxy::all`.
|
||||
fn apply_explicit_proxy_to_builder(
|
||||
mut builder: reqwest::ClientBuilder,
|
||||
service_key: &str,
|
||||
proxy_url: &str,
|
||||
) -> reqwest::ClientBuilder {
|
||||
match reqwest::Proxy::all(proxy_url) {
|
||||
Ok(proxy) => {
|
||||
builder = builder.proxy(proxy);
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!(
|
||||
proxy_url,
|
||||
service_key,
|
||||
"Ignoring invalid channel proxy_url: {error}"
|
||||
);
|
||||
}
|
||||
}
|
||||
builder
|
||||
}
|
||||
|
||||
fn parse_proxy_scope(raw: &str) -> Option<ProxyScope> {
|
||||
match raw.trim().to_ascii_lowercase().as_str() {
|
||||
"environment" | "env" => Some(ProxyScope::Environment),
|
||||
@ -4885,6 +4995,10 @@ pub struct TelegramConfig {
|
||||
/// explicitly, it takes precedence.
|
||||
#[serde(default)]
|
||||
pub ack_reactions: Option<bool>,
|
||||
/// Per-channel proxy URL (http, https, socks5, socks5h).
|
||||
/// Overrides the global `[proxy]` setting for this channel only.
|
||||
#[serde(default)]
|
||||
pub proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl ChannelConfig for TelegramConfig {
|
||||
@ -4918,6 +5032,10 @@ pub struct DiscordConfig {
|
||||
/// Other messages in the guild are silently ignored.
|
||||
#[serde(default)]
|
||||
pub mention_only: bool,
|
||||
/// Per-channel proxy URL (http, https, socks5, socks5h).
|
||||
/// Overrides the global `[proxy]` setting for this channel only.
|
||||
#[serde(default)]
|
||||
pub proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl ChannelConfig for DiscordConfig {
|
||||
@ -4954,6 +5072,10 @@ pub struct SlackConfig {
|
||||
/// Direct messages remain allowed.
|
||||
#[serde(default)]
|
||||
pub mention_only: bool,
|
||||
/// Per-channel proxy URL (http, https, socks5, socks5h).
|
||||
/// Overrides the global `[proxy]` setting for this channel only.
|
||||
#[serde(default)]
|
||||
pub proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl ChannelConfig for SlackConfig {
|
||||
@ -4989,6 +5111,10 @@ pub struct MattermostConfig {
|
||||
/// cancels the in-flight request and starts a fresh response with preserved history.
|
||||
#[serde(default)]
|
||||
pub interrupt_on_new_message: bool,
|
||||
/// Per-channel proxy URL (http, https, socks5, socks5h).
|
||||
/// Overrides the global `[proxy]` setting for this channel only.
|
||||
#[serde(default)]
|
||||
pub proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl ChannelConfig for MattermostConfig {
|
||||
@ -5101,6 +5227,10 @@ pub struct SignalConfig {
|
||||
/// Skip incoming story messages.
|
||||
#[serde(default)]
|
||||
pub ignore_stories: bool,
|
||||
/// Per-channel proxy URL (http, https, socks5, socks5h).
|
||||
/// Overrides the global `[proxy]` setting for this channel only.
|
||||
#[serde(default)]
|
||||
pub proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl ChannelConfig for SignalConfig {
|
||||
@ -5195,6 +5325,10 @@ pub struct WhatsAppConfig {
|
||||
/// user's own self-chat (Notes to Self). Defaults to false.
|
||||
#[serde(default)]
|
||||
pub self_chat_mode: bool,
|
||||
/// Per-channel proxy URL (http, https, socks5, socks5h).
|
||||
/// Overrides the global `[proxy]` setting for this channel only.
|
||||
#[serde(default)]
|
||||
pub proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl ChannelConfig for WhatsAppConfig {
|
||||
@ -5243,6 +5377,10 @@ pub struct WatiConfig {
|
||||
/// Allowed phone numbers (E.164 format) or "*" for all.
|
||||
#[serde(default)]
|
||||
pub allowed_numbers: Vec<String>,
|
||||
/// Per-channel proxy URL (http, https, socks5, socks5h).
|
||||
/// Overrides the global `[proxy]` setting for this channel only.
|
||||
#[serde(default)]
|
||||
pub proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
fn default_wati_api_url() -> String {
|
||||
@ -5273,6 +5411,10 @@ pub struct NextcloudTalkConfig {
|
||||
/// Allowed Nextcloud actor IDs (`[]` = deny all, `"*"` = allow all).
|
||||
#[serde(default)]
|
||||
pub allowed_users: Vec<String>,
|
||||
/// Per-channel proxy URL (http, https, socks5, socks5h).
|
||||
/// Overrides the global `[proxy]` setting for this channel only.
|
||||
#[serde(default)]
|
||||
pub proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl ChannelConfig for NextcloudTalkConfig {
|
||||
@ -5400,6 +5542,10 @@ pub struct LarkConfig {
|
||||
/// Not required (and ignored) for websocket mode.
|
||||
#[serde(default)]
|
||||
pub port: Option<u16>,
|
||||
/// Per-channel proxy URL (http, https, socks5, socks5h).
|
||||
/// Overrides the global `[proxy]` setting for this channel only.
|
||||
#[serde(default)]
|
||||
pub proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl ChannelConfig for LarkConfig {
|
||||
@ -5434,6 +5580,10 @@ pub struct FeishuConfig {
|
||||
/// Not required (and ignored) for websocket mode.
|
||||
#[serde(default)]
|
||||
pub port: Option<u16>,
|
||||
/// Per-channel proxy URL (http, https, socks5, socks5h).
|
||||
/// Overrides the global `[proxy]` setting for this channel only.
|
||||
#[serde(default)]
|
||||
pub proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl ChannelConfig for FeishuConfig {
|
||||
@ -5895,6 +6045,10 @@ pub struct DingTalkConfig {
|
||||
/// Allowed user IDs (staff IDs). Empty = deny all, "*" = allow all
|
||||
#[serde(default)]
|
||||
pub allowed_users: Vec<String>,
|
||||
/// Per-channel proxy URL (http, https, socks5, socks5h).
|
||||
/// Overrides the global `[proxy]` setting for this channel only.
|
||||
#[serde(default)]
|
||||
pub proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl ChannelConfig for DingTalkConfig {
|
||||
@ -5935,6 +6089,10 @@ pub struct QQConfig {
|
||||
/// Allowed user IDs. Empty = deny all, "*" = allow all
|
||||
#[serde(default)]
|
||||
pub allowed_users: Vec<String>,
|
||||
/// Per-channel proxy URL (http, https, socks5, socks5h).
|
||||
/// Overrides the global `[proxy]` setting for this channel only.
|
||||
#[serde(default)]
|
||||
pub proxy_url: Option<String>,
|
||||
}
|
||||
|
||||
impl ChannelConfig for QQConfig {
|
||||
@ -9335,6 +9493,7 @@ default_temperature = 0.7
|
||||
interrupt_on_new_message: false,
|
||||
mention_only: false,
|
||||
ack_reactions: None,
|
||||
proxy_url: None,
|
||||
}),
|
||||
discord: None,
|
||||
slack: None,
|
||||
@ -9789,6 +9948,7 @@ tool_dispatcher = "xml"
|
||||
allowed_users: vec!["*".into()],
|
||||
receive_mode: LarkReceiveMode::Websocket,
|
||||
port: None,
|
||||
proxy_url: None,
|
||||
});
|
||||
|
||||
config.agents.insert(
|
||||
@ -9930,6 +10090,7 @@ tool_dispatcher = "xml"
|
||||
interrupt_on_new_message: true,
|
||||
mention_only: false,
|
||||
ack_reactions: None,
|
||||
proxy_url: None,
|
||||
};
|
||||
let json = serde_json::to_string(&tc).unwrap();
|
||||
let parsed: TelegramConfig = serde_json::from_str(&json).unwrap();
|
||||
@ -9958,6 +10119,7 @@ tool_dispatcher = "xml"
|
||||
listen_to_bots: false,
|
||||
interrupt_on_new_message: false,
|
||||
mention_only: false,
|
||||
proxy_url: None,
|
||||
};
|
||||
let json = serde_json::to_string(&dc).unwrap();
|
||||
let parsed: DiscordConfig = serde_json::from_str(&json).unwrap();
|
||||
@ -9974,6 +10136,7 @@ tool_dispatcher = "xml"
|
||||
listen_to_bots: false,
|
||||
interrupt_on_new_message: false,
|
||||
mention_only: false,
|
||||
proxy_url: None,
|
||||
};
|
||||
let json = serde_json::to_string(&dc).unwrap();
|
||||
let parsed: DiscordConfig = serde_json::from_str(&json).unwrap();
|
||||
@ -10075,6 +10238,7 @@ allowed_users = ["@ops:matrix.org"]
|
||||
allowed_from: vec!["+1111111111".into()],
|
||||
ignore_attachments: true,
|
||||
ignore_stories: false,
|
||||
proxy_url: None,
|
||||
};
|
||||
let json = serde_json::to_string(&sc).unwrap();
|
||||
let parsed: SignalConfig = serde_json::from_str(&json).unwrap();
|
||||
@ -10095,6 +10259,7 @@ allowed_users = ["@ops:matrix.org"]
|
||||
allowed_from: vec!["*".into()],
|
||||
ignore_attachments: false,
|
||||
ignore_stories: true,
|
||||
proxy_url: None,
|
||||
};
|
||||
let toml_str = toml::to_string(&sc).unwrap();
|
||||
let parsed: SignalConfig = toml::from_str(&toml_str).unwrap();
|
||||
@ -10325,6 +10490,7 @@ channel_id = "C123"
|
||||
dm_policy: WhatsAppChatPolicy::default(),
|
||||
group_policy: WhatsAppChatPolicy::default(),
|
||||
self_chat_mode: false,
|
||||
proxy_url: None,
|
||||
};
|
||||
let json = serde_json::to_string(&wc).unwrap();
|
||||
let parsed: WhatsAppConfig = serde_json::from_str(&json).unwrap();
|
||||
@ -10349,6 +10515,7 @@ channel_id = "C123"
|
||||
dm_policy: WhatsAppChatPolicy::default(),
|
||||
group_policy: WhatsAppChatPolicy::default(),
|
||||
self_chat_mode: false,
|
||||
proxy_url: None,
|
||||
};
|
||||
let toml_str = toml::to_string(&wc).unwrap();
|
||||
let parsed: WhatsAppConfig = toml::from_str(&toml_str).unwrap();
|
||||
@ -10378,6 +10545,7 @@ channel_id = "C123"
|
||||
dm_policy: WhatsAppChatPolicy::default(),
|
||||
group_policy: WhatsAppChatPolicy::default(),
|
||||
self_chat_mode: false,
|
||||
proxy_url: None,
|
||||
};
|
||||
let toml_str = toml::to_string(&wc).unwrap();
|
||||
let parsed: WhatsAppConfig = toml::from_str(&toml_str).unwrap();
|
||||
@ -10399,6 +10567,7 @@ channel_id = "C123"
|
||||
dm_policy: WhatsAppChatPolicy::default(),
|
||||
group_policy: WhatsAppChatPolicy::default(),
|
||||
self_chat_mode: false,
|
||||
proxy_url: None,
|
||||
};
|
||||
assert!(wc.is_ambiguous_config());
|
||||
assert_eq!(wc.backend_type(), "cloud");
|
||||
@ -10419,6 +10588,7 @@ channel_id = "C123"
|
||||
dm_policy: WhatsAppChatPolicy::default(),
|
||||
group_policy: WhatsAppChatPolicy::default(),
|
||||
self_chat_mode: false,
|
||||
proxy_url: None,
|
||||
};
|
||||
assert!(!wc.is_ambiguous_config());
|
||||
assert_eq!(wc.backend_type(), "web");
|
||||
@ -10449,6 +10619,7 @@ channel_id = "C123"
|
||||
dm_policy: WhatsAppChatPolicy::default(),
|
||||
group_policy: WhatsAppChatPolicy::default(),
|
||||
self_chat_mode: false,
|
||||
proxy_url: None,
|
||||
}),
|
||||
linq: None,
|
||||
wati: None,
|
||||
@ -11453,6 +11624,7 @@ default_model = "legacy-model"
|
||||
allowed_users: vec!["*".into()],
|
||||
receive_mode: LarkReceiveMode::Websocket,
|
||||
port: None,
|
||||
proxy_url: None,
|
||||
});
|
||||
config.save().await.unwrap();
|
||||
|
||||
@ -12164,6 +12336,7 @@ default_model = "persisted-profile"
|
||||
use_feishu: true,
|
||||
receive_mode: LarkReceiveMode::Websocket,
|
||||
port: None,
|
||||
proxy_url: None,
|
||||
};
|
||||
let json = serde_json::to_string(&lc).unwrap();
|
||||
let parsed: LarkConfig = serde_json::from_str(&json).unwrap();
|
||||
@ -12187,6 +12360,7 @@ default_model = "persisted-profile"
|
||||
use_feishu: false,
|
||||
receive_mode: LarkReceiveMode::Webhook,
|
||||
port: Some(9898),
|
||||
proxy_url: None,
|
||||
};
|
||||
let toml_str = toml::to_string(&lc).unwrap();
|
||||
let parsed: LarkConfig = toml::from_str(&toml_str).unwrap();
|
||||
@ -12233,6 +12407,7 @@ default_model = "persisted-profile"
|
||||
allowed_users: vec!["user_123".into(), "user_456".into()],
|
||||
receive_mode: LarkReceiveMode::Websocket,
|
||||
port: None,
|
||||
proxy_url: None,
|
||||
};
|
||||
let json = serde_json::to_string(&fc).unwrap();
|
||||
let parsed: FeishuConfig = serde_json::from_str(&json).unwrap();
|
||||
@ -12253,6 +12428,7 @@ default_model = "persisted-profile"
|
||||
allowed_users: vec!["*".into()],
|
||||
receive_mode: LarkReceiveMode::Webhook,
|
||||
port: Some(9898),
|
||||
proxy_url: None,
|
||||
};
|
||||
let toml_str = toml::to_string(&fc).unwrap();
|
||||
let parsed: FeishuConfig = toml::from_str(&toml_str).unwrap();
|
||||
@ -12280,6 +12456,7 @@ default_model = "persisted-profile"
|
||||
app_token: "app-token".into(),
|
||||
webhook_secret: Some("webhook-secret".into()),
|
||||
allowed_users: vec!["user_a".into(), "*".into()],
|
||||
proxy_url: None,
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&nc).unwrap();
|
||||
@ -12488,6 +12665,7 @@ require_otp_to_resume = true
|
||||
interrupt_on_new_message: false,
|
||||
mention_only: false,
|
||||
ack_reactions: None,
|
||||
proxy_url: None,
|
||||
});
|
||||
|
||||
// Save (triggers encryption)
|
||||
|
||||
@ -646,6 +646,7 @@ mod tests {
|
||||
interrupt_on_new_message: false,
|
||||
mention_only: false,
|
||||
ack_reactions: None,
|
||||
proxy_url: None,
|
||||
});
|
||||
assert!(has_supervised_channels(&config));
|
||||
}
|
||||
@ -657,6 +658,7 @@ mod tests {
|
||||
client_id: "client_id".into(),
|
||||
client_secret: "client_secret".into(),
|
||||
allowed_users: vec!["*".into()],
|
||||
proxy_url: None,
|
||||
});
|
||||
assert!(has_supervised_channels(&config));
|
||||
}
|
||||
@ -672,6 +674,7 @@ mod tests {
|
||||
thread_replies: Some(true),
|
||||
mention_only: Some(false),
|
||||
interrupt_on_new_message: false,
|
||||
proxy_url: None,
|
||||
});
|
||||
assert!(has_supervised_channels(&config));
|
||||
}
|
||||
@ -683,6 +686,7 @@ mod tests {
|
||||
app_id: "app-id".into(),
|
||||
app_secret: "app-secret".into(),
|
||||
allowed_users: vec!["*".into()],
|
||||
proxy_url: None,
|
||||
});
|
||||
assert!(has_supervised_channels(&config));
|
||||
}
|
||||
@ -695,6 +699,7 @@ mod tests {
|
||||
app_token: "app-token".into(),
|
||||
webhook_secret: None,
|
||||
allowed_users: vec!["*".into()],
|
||||
proxy_url: None,
|
||||
});
|
||||
assert!(has_supervised_channels(&config));
|
||||
}
|
||||
@ -761,6 +766,7 @@ mod tests {
|
||||
interrupt_on_new_message: false,
|
||||
mention_only: false,
|
||||
ack_reactions: None,
|
||||
proxy_url: None,
|
||||
});
|
||||
|
||||
let target = resolve_heartbeat_delivery(&config).unwrap();
|
||||
@ -778,6 +784,7 @@ mod tests {
|
||||
interrupt_on_new_message: false,
|
||||
mention_only: false,
|
||||
ack_reactions: None,
|
||||
proxy_url: None,
|
||||
});
|
||||
|
||||
let target = resolve_heartbeat_delivery(&config).unwrap();
|
||||
|
||||
@ -1457,6 +1457,7 @@ mod tests {
|
||||
api_url: "https://live-mt-server.wati.io".to_string(),
|
||||
tenant_id: None,
|
||||
allowed_numbers: vec![],
|
||||
proxy_url: None,
|
||||
});
|
||||
cfg.channels_config.feishu = Some(crate::config::schema::FeishuConfig {
|
||||
app_id: "cli_aabbcc".to_string(),
|
||||
@ -1466,6 +1467,7 @@ mod tests {
|
||||
allowed_users: vec!["*".to_string()],
|
||||
receive_mode: crate::config::schema::LarkReceiveMode::Websocket,
|
||||
port: None,
|
||||
proxy_url: None,
|
||||
});
|
||||
cfg.channels_config.email = Some(crate::channels::email_channel::EmailConfig {
|
||||
imap_host: "imap.example.com".to_string(),
|
||||
@ -1591,6 +1593,7 @@ mod tests {
|
||||
api_url: "https://live-mt-server.wati.io".to_string(),
|
||||
tenant_id: None,
|
||||
allowed_numbers: vec![],
|
||||
proxy_url: None,
|
||||
});
|
||||
current.channels_config.feishu = Some(crate::config::schema::FeishuConfig {
|
||||
app_id: "cli_current".to_string(),
|
||||
@ -1600,6 +1603,7 @@ mod tests {
|
||||
allowed_users: vec!["*".to_string()],
|
||||
receive_mode: crate::config::schema::LarkReceiveMode::Websocket,
|
||||
port: None,
|
||||
proxy_url: None,
|
||||
});
|
||||
current.channels_config.email = Some(crate::channels::email_channel::EmailConfig {
|
||||
imap_host: "imap.example.com".to_string(),
|
||||
|
||||
@ -841,6 +841,7 @@ mod tests {
|
||||
interrupt_on_new_message: false,
|
||||
mention_only: false,
|
||||
ack_reactions: None,
|
||||
proxy_url: None,
|
||||
});
|
||||
let entries = all_integrations();
|
||||
let tg = entries.iter().find(|e| e.name == "Telegram").unwrap();
|
||||
|
||||
@ -3790,6 +3790,7 @@ fn setup_channels() -> Result<ChannelsConfig> {
|
||||
interrupt_on_new_message: false,
|
||||
mention_only: false,
|
||||
ack_reactions: None,
|
||||
proxy_url: None,
|
||||
});
|
||||
}
|
||||
ChannelMenuChoice::Discord => {
|
||||
@ -3890,6 +3891,7 @@ fn setup_channels() -> Result<ChannelsConfig> {
|
||||
listen_to_bots: false,
|
||||
interrupt_on_new_message: false,
|
||||
mention_only: false,
|
||||
proxy_url: None,
|
||||
});
|
||||
}
|
||||
ChannelMenuChoice::Slack => {
|
||||
@ -4020,6 +4022,7 @@ fn setup_channels() -> Result<ChannelsConfig> {
|
||||
interrupt_on_new_message: false,
|
||||
thread_replies: None,
|
||||
mention_only: false,
|
||||
proxy_url: None,
|
||||
});
|
||||
}
|
||||
ChannelMenuChoice::IMessage => {
|
||||
@ -4271,6 +4274,7 @@ fn setup_channels() -> Result<ChannelsConfig> {
|
||||
allowed_from,
|
||||
ignore_attachments,
|
||||
ignore_stories,
|
||||
proxy_url: None,
|
||||
});
|
||||
|
||||
println!(" {} Signal configured", style("✅").green().bold());
|
||||
@ -4372,6 +4376,7 @@ fn setup_channels() -> Result<ChannelsConfig> {
|
||||
dm_policy: WhatsAppChatPolicy::default(),
|
||||
group_policy: WhatsAppChatPolicy::default(),
|
||||
self_chat_mode: false,
|
||||
proxy_url: None,
|
||||
});
|
||||
|
||||
println!(
|
||||
@ -4477,6 +4482,7 @@ fn setup_channels() -> Result<ChannelsConfig> {
|
||||
dm_policy: WhatsAppChatPolicy::default(),
|
||||
group_policy: WhatsAppChatPolicy::default(),
|
||||
self_chat_mode: false,
|
||||
proxy_url: None,
|
||||
});
|
||||
}
|
||||
ChannelMenuChoice::Linq => {
|
||||
@ -4810,6 +4816,7 @@ fn setup_channels() -> Result<ChannelsConfig> {
|
||||
Some(webhook_secret.trim().to_string())
|
||||
},
|
||||
allowed_users,
|
||||
proxy_url: None,
|
||||
});
|
||||
|
||||
println!(" {} Nextcloud Talk configured", style("✅").green().bold());
|
||||
@ -4882,6 +4889,7 @@ fn setup_channels() -> Result<ChannelsConfig> {
|
||||
client_id,
|
||||
client_secret,
|
||||
allowed_users,
|
||||
proxy_url: None,
|
||||
});
|
||||
}
|
||||
ChannelMenuChoice::QqOfficial => {
|
||||
@ -4958,6 +4966,7 @@ fn setup_channels() -> Result<ChannelsConfig> {
|
||||
app_id,
|
||||
app_secret,
|
||||
allowed_users,
|
||||
proxy_url: None,
|
||||
});
|
||||
}
|
||||
ChannelMenuChoice::Lark | ChannelMenuChoice::Feishu => {
|
||||
@ -5147,6 +5156,7 @@ fn setup_channels() -> Result<ChannelsConfig> {
|
||||
use_feishu: is_feishu,
|
||||
receive_mode,
|
||||
port,
|
||||
proxy_url: None,
|
||||
});
|
||||
}
|
||||
#[cfg(feature = "channel-nostr")]
|
||||
@ -7511,6 +7521,7 @@ mod tests {
|
||||
allowed_from: vec!["*".into()],
|
||||
ignore_attachments: false,
|
||||
ignore_stories: true,
|
||||
proxy_url: None,
|
||||
});
|
||||
assert!(has_launchable_channels(&channels));
|
||||
|
||||
@ -7523,6 +7534,7 @@ mod tests {
|
||||
thread_replies: Some(true),
|
||||
mention_only: Some(false),
|
||||
interrupt_on_new_message: false,
|
||||
proxy_url: None,
|
||||
});
|
||||
assert!(has_launchable_channels(&channels));
|
||||
|
||||
@ -7531,6 +7543,7 @@ mod tests {
|
||||
app_id: "app-id".into(),
|
||||
app_secret: "app-secret".into(),
|
||||
allowed_users: vec!["*".into()],
|
||||
proxy_url: None,
|
||||
});
|
||||
assert!(has_launchable_channels(&channels));
|
||||
|
||||
@ -7540,6 +7553,7 @@ mod tests {
|
||||
app_token: "token".into(),
|
||||
webhook_secret: Some("secret".into()),
|
||||
allowed_users: vec!["*".into()],
|
||||
proxy_url: None,
|
||||
});
|
||||
assert!(has_launchable_channels(&channels));
|
||||
|
||||
@ -7552,6 +7566,7 @@ mod tests {
|
||||
allowed_users: vec!["*".into()],
|
||||
receive_mode: crate::config::schema::LarkReceiveMode::Websocket,
|
||||
port: None,
|
||||
proxy_url: None,
|
||||
});
|
||||
assert!(has_launchable_channels(&channels));
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user