Skip to main content

mur_common/bridge/
slack_config.rs

1//! Slack bridge configuration (stored in agent profile.yaml `bridge:` block).
2
3use serde::{Deserialize, Serialize};
4
5/// Config block for a Slack bridge agent.
6/// Tokens are stored in the system keychain; the account names below
7/// are pointers, not the secrets themselves.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct SlackConfig {
10    /// Human-readable workspace URL shown in error messages and logs.
11    pub workspace_url: String,
12    /// Keychain account name for the xoxb-… Bot Token.
13    pub bot_token_keychain_account: String,
14    /// Keychain account name for the xapp-… App Token (Socket Mode).
15    pub app_token_keychain_account: String,
16    /// Privacy gate: which Slack event types reach the user agent.
17    #[serde(default)]
18    pub privacy_mode: SlackPrivacyMode,
19    /// Allowed Slack channel IDs (C…). Empty = all channels allowed.
20    #[serde(default)]
21    pub allowed_channels: Vec<String>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
25#[serde(rename_all = "snake_case")]
26pub enum SlackPrivacyMode {
27    /// Only DMs to the bot reach the agent. Channel mentions are dropped.
28    DmOnly,
29    /// DMs and @mentions in channels both reach the agent (default).
30    #[default]
31    DmAndMentions,
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[test]
39    fn round_trips_yaml() {
40        let yaml = r#"
41workspace_url: "https://myteam.slack.com"
42bot_token_keychain_account: "mur_slack_bot_myagent"
43app_token_keychain_account: "mur_slack_app_myagent"
44"#;
45        let cfg: SlackConfig = serde_yaml::from_str(yaml).unwrap();
46        assert_eq!(cfg.privacy_mode, SlackPrivacyMode::DmAndMentions);
47        assert!(cfg.allowed_channels.is_empty());
48    }
49
50    #[test]
51    fn dm_only_mode_round_trips() {
52        let yaml = r#"
53workspace_url: "https://myteam.slack.com"
54bot_token_keychain_account: "mur_slack_bot_x"
55app_token_keychain_account: "mur_slack_app_x"
56privacy_mode: dm_only
57"#;
58        let cfg: SlackConfig = serde_yaml::from_str(yaml).unwrap();
59        assert_eq!(cfg.privacy_mode, SlackPrivacyMode::DmOnly);
60    }
61
62    #[test]
63    fn allowed_channels_round_trips() {
64        let yaml = r#"
65workspace_url: "https://myteam.slack.com"
66bot_token_keychain_account: "mur_slack_bot_x"
67app_token_keychain_account: "mur_slack_app_x"
68allowed_channels: ["C111", "C222"]
69"#;
70        let cfg: SlackConfig = serde_yaml::from_str(yaml).unwrap();
71        assert_eq!(cfg.allowed_channels, vec!["C111", "C222"]);
72    }
73}