Skip to main content

af_slack/
chat.rs

1// Copyright © 2021 Alexandra Frydl
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7//! Post Slack messages.
8
9mod attachment;
10mod message;
11
12pub use self::attachment::Attachment;
13pub use self::message::{Message, MessageId};
14
15use crate::api;
16use af_core::prelude::*;
17
18/// Gets a permalink to a message.
19pub async fn permalink_to<'a>(client: &api::Client, id: &MessageId) -> Result<String> {
20  #[derive(Serialize)]
21  struct Params<'a> {
22    channel: &'a str,
23    message_ts: &'a str,
24  }
25
26  #[derive(Deserialize)]
27  struct Response {
28    permalink: String,
29  }
30
31  let params = Params { channel: &id.channel, message_ts: &id.ts };
32  let res = client.get::<_, Response>("chat.getPermalink", &params).await?;
33
34  match res {
35    Ok(res) => Ok(res.permalink),
36    Err(err) => match err.error.as_str() {
37      "channel_not_found" => Err(Error::ChannelNotFound),
38      _ => Err(Error::Other(err)),
39    },
40  }
41}
42
43/// Posts a message to a channel.
44pub async fn post<'a>(
45  client: &api::Client,
46  channel: impl AsRef<str>,
47  message: impl Into<Message<'a>>,
48) -> Result<MessageId> {
49  #[derive(Serialize)]
50  struct Request<'a> {
51    channel: &'a str,
52    #[serde(flatten)]
53    message: Message<'a>,
54  }
55
56  let req = Request { channel: channel.as_ref(), message: message.into() };
57  let res = client.post::<_, MessageId>("chat.postMessage", &req).await?;
58
59  match res {
60    Ok(id) => Ok(id),
61    Err(err) => match err.error.as_str() {
62      "channel_not_found" => Err(Error::ChannelNotFound),
63      "not_in_channel" => Err(Error::NotInChannel),
64      _ => Err(Error::Other(err)),
65    },
66  }
67}
68
69/// Replies to an existing message thread in a channel.
70pub async fn reply<'a>(
71  client: &api::Client,
72  thread_id: &MessageId,
73  message: impl Into<Message<'a>>,
74) -> Result<MessageId> {
75  #[derive(Serialize)]
76  struct Request<'a> {
77    channel: &'a str,
78    #[serde(flatten)]
79    message: Message<'a>,
80    thread_ts: &'a str,
81  }
82
83  let req =
84    Request { channel: &thread_id.channel, message: message.into(), thread_ts: &thread_id.ts };
85  let res = client.post::<_, MessageId>("chat.postMessage", &req).await?;
86
87  match res {
88    Ok(id) => Ok(id),
89    Err(err) => match err.error.as_str() {
90      "channel_not_found" => Err(Error::ChannelNotFound),
91      "not_in_channel" => Err(Error::NotInChannel),
92      _ => Err(Error::Other(err)),
93    },
94  }
95}
96
97/// A chat error.
98#[derive(Debug, Error)]
99pub enum Error {
100  /// The given channel was not found.
101  #[error("channel not found")]
102  ChannelNotFound,
103  /// The app is not in the given channel.
104  #[error("not in channel")]
105  NotInChannel,
106  /// A low-level API error occurred.
107  #[error("api error: {0}")]
108  ApiError(#[from] api::Error),
109  /// Some other response error occurred.
110  #[error("{0:#}")]
111  Other(api::ErrorResponse),
112}
113
114pub type Result<T = (), E = Error> = std::result::Result<T, E>;