Skip to main content

slack_rs/commands/
react.rs

1//! Reaction 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/// Add a reaction to a message
9///
10/// # Arguments
11/// * `client` - API client
12/// * `channel` - Channel ID
13/// * `timestamp` - Message timestamp
14/// * `name` - Emoji name (without colons, e.g., "thumbsup")
15///
16/// # Returns
17/// * `Ok(ApiResponse)` with reaction confirmation
18/// * `Err(ApiError)` if the operation fails
19pub async fn react_add(
20    client: &ApiClient,
21    channel: String,
22    timestamp: String,
23    name: String,
24) -> Result<ApiResponse, ApiError> {
25    check_write_allowed()?;
26
27    let mut params = HashMap::new();
28    params.insert("channel".to_string(), json!(channel));
29    params.insert("timestamp".to_string(), json!(timestamp));
30    params.insert("name".to_string(), json!(name));
31
32    client.call_method(ApiMethod::ReactionsAdd, params).await
33}
34
35/// Remove a reaction from a message
36///
37/// # Arguments
38/// * `client` - API client
39/// * `channel` - Channel ID
40/// * `timestamp` - Message timestamp
41/// * `name` - Emoji name (without colons, e.g., "thumbsup")
42/// * `yes` - Skip confirmation prompt
43/// * `non_interactive` - Whether running in non-interactive mode
44///
45/// # Returns
46/// * `Ok(ApiResponse)` with removal confirmation
47/// * `Err(ApiError)` if the operation fails
48pub async fn react_remove(
49    client: &ApiClient,
50    channel: String,
51    timestamp: String,
52    name: String,
53    yes: bool,
54    non_interactive: bool,
55) -> Result<ApiResponse, ApiError> {
56    check_write_allowed()?;
57
58    // Build hint with example command for non-interactive mode
59    let hint = format!(
60        "Example: slack-rs react remove {} {} {} --yes",
61        channel, timestamp, name
62    );
63    confirm_destructive_with_hint(yes, "remove this reaction", non_interactive, Some(&hint))?;
64
65    let mut params = HashMap::new();
66    params.insert("channel".to_string(), json!(channel));
67    params.insert("timestamp".to_string(), json!(timestamp));
68    params.insert("name".to_string(), json!(name));
69
70    client.call_method(ApiMethod::ReactionsRemove, params).await
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use serial_test::serial;
77
78    #[tokio::test]
79    #[serial(write_guard)]
80    async fn test_react_add_with_env_false() {
81        std::env::set_var("SLACKCLI_ALLOW_WRITE", "false");
82        let client = ApiClient::with_token("test_token".to_string());
83        let result = react_add(
84            &client,
85            "C123456".to_string(),
86            "1234567890.123456".to_string(),
87            "thumbsup".to_string(),
88        )
89        .await;
90        assert!(result.is_err());
91        assert!(matches!(result.unwrap_err(), ApiError::WriteNotAllowed));
92        std::env::remove_var("SLACKCLI_ALLOW_WRITE");
93    }
94
95    #[tokio::test]
96    #[serial(write_guard)]
97    async fn test_react_remove_with_env_false() {
98        std::env::set_var("SLACKCLI_ALLOW_WRITE", "false");
99        let client = ApiClient::with_token("test_token".to_string());
100        let result = react_remove(
101            &client,
102            "C123456".to_string(),
103            "1234567890.123456".to_string(),
104            "thumbsup".to_string(),
105            true,
106            false,
107        )
108        .await;
109        assert!(result.is_err());
110        assert!(matches!(result.unwrap_err(), ApiError::WriteNotAllowed));
111        std::env::remove_var("SLACKCLI_ALLOW_WRITE");
112    }
113}