mirror of
https://github.com/RightNow-AI/openfang.git
synced 2026-07-30 06:32:17 +00:00
feat: add free_response_channels support for Discord
Allow certain Discord channel IDs to respond without requiring @mention, similar to Hermes gateway's free_response_channels. - Add free_response_channels field to DiscordConfig - Add free_response_channels method to ChannelBridgeHandle trait - Implement free_response_channels in KernelBridgeAdapter - Modify dispatch_message to bypass mention_only policy for free channels - Add test for free_response_channels deserialization
This commit is contained in:
@@ -816,6 +816,19 @@ impl ChannelBridgeHandle for KernelBridgeAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
async fn free_response_channels(&self, channel_type: &str) -> Vec<String> {
|
||||
let channels = &self.kernel.config.channels;
|
||||
match channel_type {
|
||||
"discord" => channels
|
||||
.discord
|
||||
.as_ref()
|
||||
.map(|c| c.free_response_channels.clone())
|
||||
.unwrap_or_default(),
|
||||
// Add other channel types here as needed (e.g., "telegram" => ...)
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn authorize_channel_user(
|
||||
&self,
|
||||
channel_type: &str,
|
||||
|
||||
@@ -220,6 +220,13 @@ pub trait ChannelBridgeHandle: Send + Sync {
|
||||
None
|
||||
}
|
||||
|
||||
/// Get channel IDs that respond without requiring @mention (free response mode).
|
||||
///
|
||||
/// Returns an empty vector if the channel type is not configured or has no free response channels.
|
||||
async fn free_response_channels(&self, _channel_type: &str) -> Vec<String> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
/// Record a delivery result for tracking (optional — default no-op).
|
||||
///
|
||||
/// `thread_id` preserves Telegram forum-topic context so cron/workflow
|
||||
@@ -659,16 +666,23 @@ async fn dispatch_message(
|
||||
}
|
||||
}
|
||||
GroupPolicy::MentionOnly => {
|
||||
// Only allow messages where the bot was @mentioned or commands.
|
||||
let was_mentioned = message
|
||||
.metadata
|
||||
.get("was_mentioned")
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false);
|
||||
let is_command = matches!(&message.content, ChannelContent::Command { .. });
|
||||
if !was_mentioned && !is_command {
|
||||
debug!("Ignoring group message on {ct_str} (group_policy=mention_only, not mentioned)");
|
||||
return;
|
||||
// Check if this channel is in the free_response list - if so, allow all messages
|
||||
let free_channels = handle.free_response_channels(ct_str).await;
|
||||
let channel_id = &message.sender.platform_id;
|
||||
let is_free_channel = free_channels.iter().any(|id| id == channel_id);
|
||||
|
||||
if !is_free_channel {
|
||||
// Only allow messages where the bot was @mentioned or commands.
|
||||
let was_mentioned = message
|
||||
.metadata
|
||||
.get("was_mentioned")
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false);
|
||||
let is_command = matches!(&message.content, ChannelContent::Command { .. });
|
||||
if !was_mentioned && !is_command {
|
||||
debug!("Ignoring group message on {ct_str} (group_policy=mention_only, not mentioned)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
GroupPolicy::All => {}
|
||||
|
||||
@@ -1792,6 +1792,10 @@ pub struct DiscordConfig {
|
||||
/// Default channel ID for outgoing messages when no recipient is specified.
|
||||
#[serde(default)]
|
||||
pub default_channel_id: Option<String>,
|
||||
/// Channel IDs that respond without requiring @mention (free response mode).
|
||||
/// In these channels, the bot responds to all group messages without needing to be mentioned.
|
||||
#[serde(default, deserialize_with = "deserialize_string_or_int_vec")]
|
||||
pub free_response_channels: Vec<String>,
|
||||
/// Per-channel behavior overrides.
|
||||
#[serde(default)]
|
||||
pub overrides: ChannelOverrides,
|
||||
@@ -1807,6 +1811,7 @@ impl Default for DiscordConfig {
|
||||
intents: 37376,
|
||||
ignore_bots: true,
|
||||
default_channel_id: None,
|
||||
free_response_channels: vec![],
|
||||
overrides: ChannelOverrides::default(),
|
||||
}
|
||||
}
|
||||
@@ -3706,6 +3711,26 @@ mod tests {
|
||||
assert!(dc2.ignore_bots);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_discord_config_free_response_channels_deserialization() {
|
||||
// Test with free_response_channels as list of strings
|
||||
let toml_str = r#"
|
||||
bot_token_env = "DISCORD_BOT_TOKEN"
|
||||
free_response_channels = ["123456789", "987654321"]
|
||||
"#;
|
||||
let dc: DiscordConfig = toml::from_str(toml_str).unwrap();
|
||||
assert_eq!(dc.free_response_channels.len(), 2);
|
||||
assert_eq!(dc.free_response_channels[0], "123456789");
|
||||
assert_eq!(dc.free_response_channels[1], "987654321");
|
||||
|
||||
// Test default (empty list)
|
||||
let toml_str2 = r#"
|
||||
bot_token_env = "DISCORD_BOT_TOKEN"
|
||||
"#;
|
||||
let dc2: DiscordConfig = toml::from_str(toml_str2).unwrap();
|
||||
assert!(dc2.free_response_channels.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slack_config_defaults() {
|
||||
let sl = SlackConfig::default();
|
||||
|
||||
Reference in New Issue
Block a user