1use anyhow::{Context, Result};
2use reqwest::Client;
3use serde::Serialize;
4
5const SLACK_API_URL: &str = "https://slack.com/api/chat.postMessage";
6
7#[derive(Debug, Clone)]
8pub struct SlackClient {
9 token: String,
10 channel: String,
11 client: Client,
12}
13
14#[derive(Serialize)]
15struct SlackMessage<'a> {
16 channel: &'a str,
17 text: String,
18}
19
20pub trait IntoFormattedMessage {
21 fn into_formatted_message(self) -> String;
22}
23
24impl IntoFormattedMessage for String {
25 fn into_formatted_message(self) -> String {
26 self
27 }
28}
29
30impl IntoFormattedMessage for Vec<String> {
31 fn into_formatted_message(self) -> String {
32 self.join("\n")
33 }
34}
35
36impl SlackClient {
37 pub fn new(token: String, channel: String) -> Self {
39 SlackClient {
40 token,
41 channel,
42 client: Client::new(),
43 }
44 }
45
46 pub async fn send_message<T: IntoFormattedMessage>(&self, message: T) -> Result<()> {
48 let formatted_message = message.into_formatted_message();
49
50 let request = SlackMessage {
51 channel: &self.channel,
52 text: formatted_message,
53 };
54
55 let response = self
56 .client
57 .post(SLACK_API_URL)
58 .header("Content-type", "application/json;charset=utf-8")
59 .header("Authorization", format!("Bearer {}", self.token))
60 .json(&request)
61 .send()
62 .await
63 .context("Failed to send Slack message.")?
64 .json::<serde_json::Value>()
65 .await
66 .context("Failed to parse Slack response.")?;
67
68 if response["ok"] != true {
69 return Err(anyhow::anyhow!("Slack API error: {}", response["error"]));
70 }
71
72 Ok(())
73 }
74
75 pub fn send_message_sync<T: IntoFormattedMessage>(&self, message: T) -> Result<()> {
78 let formatted_message = message.into_formatted_message();
79
80 let request = SlackMessage {
81 channel: &self.channel,
82 text: formatted_message,
83 };
84
85 self
86 .client
87 .post(SLACK_API_URL)
88 .header("Content-type", "application/json;charset=utf-8")
89 .header("Authorization", format!("Bearer {}", self.token))
90 .json(&request)
91 .send();
92
93 Ok(())
94 }
95}