story-kit: merge 324_story_slack_bot_integration_for_bot_commands

This commit is contained in:
Dave
2026-03-20 01:09:55 +00:00
parent 4fe61c643b
commit 09890b5ea4
7 changed files with 1380 additions and 5 deletions

View File

@@ -87,6 +87,19 @@ pub struct BotConfig {
/// use. Defaults to `"pipeline_notification"`.
#[serde(default)]
pub whatsapp_notification_template: Option<String>,
// ── Slack Bot API fields ─────────────────────────────────────────
// These are only required when `transport = "slack"`.
/// Slack Bot User OAuth Token (starts with `xoxb-`).
#[serde(default)]
pub slack_bot_token: Option<String>,
/// Slack Signing Secret used to verify incoming webhook requests.
#[serde(default)]
pub slack_signing_secret: Option<String>,
/// Slack channel IDs the bot should listen in.
#[serde(default)]
pub slack_channel_ids: Vec<String>,
}
fn default_transport() -> String {
@@ -142,6 +155,29 @@ impl BotConfig {
);
return None;
}
} else if config.transport == "slack" {
// Validate Slack-specific fields.
if config.slack_bot_token.as_ref().is_none_or(|s| s.is_empty()) {
eprintln!(
"[bot] bot.toml: transport=\"slack\" requires \
slack_bot_token"
);
return None;
}
if config.slack_signing_secret.as_ref().is_none_or(|s| s.is_empty()) {
eprintln!(
"[bot] bot.toml: transport=\"slack\" requires \
slack_signing_secret"
);
return None;
}
if config.slack_channel_ids.is_empty() {
eprintln!(
"[bot] bot.toml: transport=\"slack\" requires \
at least one slack_channel_ids entry"
);
return None;
}
} else if config.room_ids.is_empty() {
eprintln!(
"[matrix-bot] bot.toml has no room_ids configured — \
@@ -680,6 +716,97 @@ enabled = true
transport = "whatsapp"
whatsapp_phone_number_id = "123456"
whatsapp_access_token = "EAAtoken"
"#,
)
.unwrap();
assert!(BotConfig::load(tmp.path()).is_none());
}
// ── Slack config tests ─────────────────────────────────────────────
#[test]
fn load_slack_transport_reads_config() {
let tmp = tempfile::tempdir().unwrap();
let sk = tmp.path().join(".story_kit");
fs::create_dir_all(&sk).unwrap();
fs::write(
sk.join("bot.toml"),
r#"
homeserver = "https://matrix.example.com"
username = "@bot:example.com"
password = "secret"
enabled = true
transport = "slack"
slack_bot_token = "xoxb-123"
slack_signing_secret = "secret123"
slack_channel_ids = ["C01ABCDEF"]
"#,
)
.unwrap();
let config = BotConfig::load(tmp.path()).unwrap();
assert_eq!(config.transport, "slack");
assert_eq!(config.slack_bot_token.as_deref(), Some("xoxb-123"));
assert_eq!(config.slack_signing_secret.as_deref(), Some("secret123"));
assert_eq!(config.slack_channel_ids, vec!["C01ABCDEF"]);
}
#[test]
fn load_slack_returns_none_when_missing_bot_token() {
let tmp = tempfile::tempdir().unwrap();
let sk = tmp.path().join(".story_kit");
fs::create_dir_all(&sk).unwrap();
fs::write(
sk.join("bot.toml"),
r#"
homeserver = "https://matrix.example.com"
username = "@bot:example.com"
password = "secret"
enabled = true
transport = "slack"
slack_signing_secret = "secret123"
slack_channel_ids = ["C01ABCDEF"]
"#,
)
.unwrap();
assert!(BotConfig::load(tmp.path()).is_none());
}
#[test]
fn load_slack_returns_none_when_missing_signing_secret() {
let tmp = tempfile::tempdir().unwrap();
let sk = tmp.path().join(".story_kit");
fs::create_dir_all(&sk).unwrap();
fs::write(
sk.join("bot.toml"),
r#"
homeserver = "https://matrix.example.com"
username = "@bot:example.com"
password = "secret"
enabled = true
transport = "slack"
slack_bot_token = "xoxb-123"
slack_channel_ids = ["C01ABCDEF"]
"#,
)
.unwrap();
assert!(BotConfig::load(tmp.path()).is_none());
}
#[test]
fn load_slack_returns_none_when_missing_channel_ids() {
let tmp = tempfile::tempdir().unwrap();
let sk = tmp.path().join(".story_kit");
fs::create_dir_all(&sk).unwrap();
fs::write(
sk.join("bot.toml"),
r#"
homeserver = "https://matrix.example.com"
username = "@bot:example.com"
password = "secret"
enabled = true
transport = "slack"
slack_bot_token = "xoxb-123"
slack_signing_secret = "secret123"
"#,
)
.unwrap();

View File

@@ -62,9 +62,12 @@ pub fn spawn_bot(
}
};
// WhatsApp transport is handled via HTTP webhooks, not the Matrix sync loop.
if config.transport == "whatsapp" {
crate::slog!("[bot] transport=whatsapp — skipping Matrix bot; webhooks handle WhatsApp");
// WhatsApp and Slack transports are handled via HTTP webhooks, not the Matrix sync loop.
if config.transport == "whatsapp" || config.transport == "slack" {
crate::slog!(
"[bot] transport={} — skipping Matrix bot; webhooks handle this transport",
config.transport
);
return;
}