Skip to main content

slack_rs/commands/
msg.rs

1//! Message command implementations
2
3use crate::api::{ApiClient, ApiError, ApiMethod, ApiResponse};
4use crate::commands::guards::{check_write_allowed, confirm_destructive_with_hint};
5use serde_json::json;
6use std::collections::HashMap;
7
8/// Post a message to a channel
9///
10/// # Arguments
11/// * `client` - API client
12/// * `channel` - Channel ID
13/// * `text` - Message text
14/// * `thread_ts` - Optional thread timestamp to reply to
15/// * `reply_broadcast` - Whether to broadcast thread reply to channel
16/// * `yes` - Skip confirmation prompt
17/// * `non_interactive` - Whether running in non-interactive mode
18///
19/// # Returns
20/// * `Ok(ApiResponse)` with posted message information
21/// * `Err(ApiError)` if the operation fails
22pub async fn msg_post(
23    client: &ApiClient,
24    channel: String,
25    text: String,
26    thread_ts: Option<String>,
27    reply_broadcast: bool,
28    yes: bool,
29    non_interactive: bool,
30) -> Result<ApiResponse, ApiError> {
31    check_write_allowed()?;
32
33    // Build hint with example command for non-interactive mode
34    let hint = format!("Example: slack-rs msg post {} \"{}\" --yes", channel, text);
35    confirm_destructive_with_hint(yes, "post this message", non_interactive, Some(&hint))?;
36
37    let mut params = HashMap::new();
38    params.insert("channel".to_string(), json!(channel));
39    params.insert("text".to_string(), json!(text));
40
41    if let Some(ts) = thread_ts {
42        params.insert("thread_ts".to_string(), json!(ts));
43        if reply_broadcast {
44            params.insert("reply_broadcast".to_string(), json!(true));
45        }
46    }
47
48    client.call_method(ApiMethod::ChatPostMessage, params).await
49}
50
51/// Update a message
52///
53/// # Arguments
54/// * `client` - API client
55/// * `channel` - Channel ID
56/// * `ts` - Message timestamp
57/// * `text` - New message text
58/// * `yes` - Skip confirmation prompt
59/// * `non_interactive` - Whether running in non-interactive mode
60///
61/// # Returns
62/// * `Ok(ApiResponse)` with updated message information
63/// * `Err(ApiError)` if the operation fails
64pub async fn msg_update(
65    client: &ApiClient,
66    channel: String,
67    ts: String,
68    text: String,
69    yes: bool,
70    non_interactive: bool,
71) -> Result<ApiResponse, ApiError> {
72    check_write_allowed()?;
73
74    // Build hint with example command for non-interactive mode
75    let hint = format!(
76        "Example: slack-rs msg update {} {} \"new text\" --yes",
77        channel, ts
78    );
79    confirm_destructive_with_hint(yes, "update this message", non_interactive, Some(&hint))?;
80
81    let mut params = HashMap::new();
82    params.insert("channel".to_string(), json!(channel));
83    params.insert("ts".to_string(), json!(ts));
84    params.insert("text".to_string(), json!(text));
85
86    client.call_method(ApiMethod::ChatUpdate, params).await
87}
88
89/// Delete a message
90///
91/// # Arguments
92/// * `client` - API client
93/// * `channel` - Channel ID
94/// * `ts` - Message timestamp
95/// * `yes` - Skip confirmation prompt
96/// * `non_interactive` - Whether running in non-interactive mode
97///
98/// # Returns
99/// * `Ok(ApiResponse)` with deletion confirmation
100/// * `Err(ApiError)` if the operation fails
101pub async fn msg_delete(
102    client: &ApiClient,
103    channel: String,
104    ts: String,
105    yes: bool,
106    non_interactive: bool,
107) -> Result<ApiResponse, ApiError> {
108    check_write_allowed()?;
109
110    // Build hint with example command for non-interactive mode
111    let hint = format!("Example: slack-rs msg delete {} {} --yes", channel, ts);
112    confirm_destructive_with_hint(yes, "delete this message", non_interactive, Some(&hint))?;
113
114    let mut params = HashMap::new();
115    params.insert("channel".to_string(), json!(channel));
116    params.insert("ts".to_string(), json!(ts));
117
118    client.call_method(ApiMethod::ChatDelete, params).await
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124    use serial_test::serial;
125
126    #[tokio::test]
127    #[serial(write_guard)]
128    async fn test_msg_post_with_env_false() {
129        std::env::set_var("SLACKCLI_ALLOW_WRITE", "false");
130        let client = ApiClient::with_token("test_token".to_string());
131        let result = msg_post(
132            &client,
133            "C123456".to_string(),
134            "test message".to_string(),
135            None,
136            false,
137            true,
138            false,
139        )
140        .await;
141        assert!(result.is_err());
142        assert!(matches!(result.unwrap_err(), ApiError::WriteNotAllowed));
143        std::env::remove_var("SLACKCLI_ALLOW_WRITE");
144    }
145
146    #[tokio::test]
147    #[serial(write_guard)]
148    async fn test_msg_update_with_env_false() {
149        std::env::set_var("SLACKCLI_ALLOW_WRITE", "false");
150        let client = ApiClient::with_token("test_token".to_string());
151        let result = msg_update(
152            &client,
153            "C123456".to_string(),
154            "1234567890.123456".to_string(),
155            "updated text".to_string(),
156            true,
157            false,
158        )
159        .await;
160        assert!(result.is_err());
161        assert!(matches!(result.unwrap_err(), ApiError::WriteNotAllowed));
162        std::env::remove_var("SLACKCLI_ALLOW_WRITE");
163    }
164
165    #[tokio::test]
166    #[serial(write_guard)]
167    async fn test_msg_delete_with_env_false() {
168        std::env::set_var("SLACKCLI_ALLOW_WRITE", "false");
169        let client = ApiClient::with_token("test_token".to_string());
170        let result = msg_delete(
171            &client,
172            "C123456".to_string(),
173            "1234567890.123456".to_string(),
174            true,
175            false,
176        )
177        .await;
178        assert!(result.is_err());
179        assert!(matches!(result.unwrap_err(), ApiError::WriteNotAllowed));
180        std::env::remove_var("SLACKCLI_ALLOW_WRITE");
181    }
182}