packages feed

slack-web 2.2.0.0 → 2.3.0.0

raw patch · 32 files changed

+854/−159 lines, 32 filesdep +data-defaultdep −data-default-classnew-uploaderPVP ok

version bump matches the API change (PVP)

Dependencies added: data-default

Dependencies removed: data-default-class

API changes (from Hackage documentation)

+ Web.Slack.Conversation: MembersReq :: ConversationId -> Maybe Cursor -> Maybe Int -> MembersReq
+ Web.Slack.Conversation: MembersRsp :: [UserId] -> Maybe ResponseMetadata -> MembersRsp
+ Web.Slack.Conversation: [membersReqChannel] :: MembersReq -> ConversationId
+ Web.Slack.Conversation: [membersReqCursor] :: MembersReq -> Maybe Cursor
+ Web.Slack.Conversation: [membersReqLimit] :: MembersReq -> Maybe Int
+ Web.Slack.Conversation: [membersRspMembers] :: MembersRsp -> [UserId]
+ Web.Slack.Conversation: [membersRspResponseMetadata] :: MembersRsp -> Maybe ResponseMetadata
+ Web.Slack.Conversation: conversationsMembers :: SlackConfig -> MembersReq -> IO (Response MembersRsp)
+ Web.Slack.Conversation: conversationsMembers_ :: AuthenticatedRequest (AuthProtect "token") -> MembersReq -> ClientM (ResponseJSON MembersRsp)
+ Web.Slack.Conversation: data MembersReq
+ Web.Slack.Conversation: data MembersRsp
+ Web.Slack.Conversation: instance Control.DeepSeq.NFData Web.Slack.Conversation.MembersReq
+ Web.Slack.Conversation: instance Control.DeepSeq.NFData Web.Slack.Conversation.MembersRsp
+ Web.Slack.Conversation: instance Data.Aeson.Types.FromJSON.FromJSON Web.Slack.Conversation.MembersReq
+ Web.Slack.Conversation: instance Data.Aeson.Types.FromJSON.FromJSON Web.Slack.Conversation.MembersRsp
+ Web.Slack.Conversation: instance GHC.Classes.Eq Web.Slack.Conversation.MembersReq
+ Web.Slack.Conversation: instance GHC.Classes.Eq Web.Slack.Conversation.MembersRsp
+ Web.Slack.Conversation: instance GHC.Generics.Generic Web.Slack.Conversation.MembersReq
+ Web.Slack.Conversation: instance GHC.Generics.Generic Web.Slack.Conversation.MembersRsp
+ Web.Slack.Conversation: instance GHC.Show.Show Web.Slack.Conversation.MembersReq
+ Web.Slack.Conversation: instance GHC.Show.Show Web.Slack.Conversation.MembersRsp
+ Web.Slack.Conversation: instance Web.Internal.FormUrlEncoded.ToForm Web.Slack.Conversation.MembersReq
+ Web.Slack.Conversation: instance Web.Slack.Pager.Types.PagedRequest Web.Slack.Conversation.MembersReq
+ Web.Slack.Conversation: instance Web.Slack.Pager.Types.PagedResponse Web.Slack.Conversation.MembersRsp
+ Web.Slack.Conversation: mkMembersReq :: ConversationId -> MembersReq
+ Web.Slack.Experimental.Blocks: RichItemMessageMention :: RichMessageMention -> RichItem
+ Web.Slack.Experimental.Blocks: RichMessageMention :: ConversationId -> Text -> Maybe Text -> Maybe Text -> RichMessageMention
+ Web.Slack.Experimental.Blocks: RichTextSection :: [RichItem] -> RichTextSection
+ Web.Slack.Experimental.Blocks: RichTextSectionItemList :: [RichTextSection] -> RichTextSectionItem
+ Web.Slack.Experimental.Blocks: RichTextSectionItemPreformatted :: [RichItem] -> RichTextSectionItem
+ Web.Slack.Experimental.Blocks: RichTextSectionItemQuote :: [RichItem] -> RichTextSectionItem
+ Web.Slack.Experimental.Blocks: [rmmChannelId] :: RichMessageMention -> ConversationId
+ Web.Slack.Experimental.Blocks: [rmmMessageTs] :: RichMessageMention -> Text
+ Web.Slack.Experimental.Blocks: [rmmThreadTs] :: RichMessageMention -> Maybe Text
+ Web.Slack.Experimental.Blocks: [rmmUrl] :: RichMessageMention -> Maybe Text
+ Web.Slack.Experimental.Blocks: data RichMessageMention
+ Web.Slack.Experimental.Blocks: newtype RichTextSection
+ Web.Slack.Experimental.Blocks.Types: RichItemMessageMention :: RichMessageMention -> RichItem
+ Web.Slack.Experimental.Blocks.Types: RichMessageMention :: ConversationId -> Text -> Maybe Text -> Maybe Text -> RichMessageMention
+ Web.Slack.Experimental.Blocks.Types: RichTextSection :: [RichItem] -> RichTextSection
+ Web.Slack.Experimental.Blocks.Types: RichTextSectionItemList :: [RichTextSection] -> RichTextSectionItem
+ Web.Slack.Experimental.Blocks.Types: RichTextSectionItemPreformatted :: [RichItem] -> RichTextSectionItem
+ Web.Slack.Experimental.Blocks.Types: RichTextSectionItemQuote :: [RichItem] -> RichTextSectionItem
+ Web.Slack.Experimental.Blocks.Types: [rmmChannelId] :: RichMessageMention -> ConversationId
+ Web.Slack.Experimental.Blocks.Types: [rmmMessageTs] :: RichMessageMention -> Text
+ Web.Slack.Experimental.Blocks.Types: [rmmThreadTs] :: RichMessageMention -> Maybe Text
+ Web.Slack.Experimental.Blocks.Types: [rmmUrl] :: RichMessageMention -> Maybe Text
+ Web.Slack.Experimental.Blocks.Types: data RichMessageMention
+ Web.Slack.Experimental.Blocks.Types: instance Data.Aeson.Types.FromJSON.FromJSON Web.Slack.Experimental.Blocks.Types.RichTextSection
+ Web.Slack.Experimental.Blocks.Types: instance GHC.Classes.Eq Web.Slack.Experimental.Blocks.Types.RichMessageMention
+ Web.Slack.Experimental.Blocks.Types: instance GHC.Classes.Eq Web.Slack.Experimental.Blocks.Types.RichTextSection
+ Web.Slack.Experimental.Blocks.Types: instance GHC.Show.Show Web.Slack.Experimental.Blocks.Types.RichMessageMention
+ Web.Slack.Experimental.Blocks.Types: instance GHC.Show.Show Web.Slack.Experimental.Blocks.Types.RichTextSection
+ Web.Slack.Experimental.Blocks.Types: newtype RichTextSection
+ Web.Slack.Experimental.Events.Types: ChannelSharedEvent :: TeamId -> ConversationId -> Text -> ChannelSharedEvent
+ Web.Slack.Experimental.Events.Types: ChannelUnsharedEvent :: TeamId -> ConversationId -> Bool -> Text -> ChannelUnsharedEvent
+ Web.Slack.Experimental.Events.Types: EventChannelShared :: ChannelSharedEvent -> Event
+ Web.Slack.Experimental.Events.Types: EventChannelUnshared :: ChannelUnsharedEvent -> Event
+ Web.Slack.Experimental.Events.Types: EventMessageDeleted :: MessageDeletedEvent -> Event
+ Web.Slack.Experimental.Events.Types: MessageDeletedEvent :: ConversationId -> Text -> Text -> MessageDeletedEvent
+ Web.Slack.Experimental.Events.Types: [connectedTeamId] :: ChannelSharedEvent -> TeamId
+ Web.Slack.Experimental.Events.Types: [deletedTs] :: MessageDeletedEvent -> Text
+ Web.Slack.Experimental.Events.Types: [isExtShared] :: ChannelUnsharedEvent -> Bool
+ Web.Slack.Experimental.Events.Types: [previouslyConnectedTeamId] :: ChannelUnsharedEvent -> TeamId
+ Web.Slack.Experimental.Events.Types: data ChannelSharedEvent
+ Web.Slack.Experimental.Events.Types: data ChannelUnsharedEvent
+ Web.Slack.Experimental.Events.Types: data MessageDeletedEvent
+ Web.Slack.Experimental.Events.Types: instance Data.Aeson.Types.FromJSON.FromJSON Web.Slack.Experimental.Events.Types.ChannelSharedEvent
+ Web.Slack.Experimental.Events.Types: instance Data.Aeson.Types.FromJSON.FromJSON Web.Slack.Experimental.Events.Types.ChannelUnsharedEvent
+ Web.Slack.Experimental.Events.Types: instance Data.Aeson.Types.FromJSON.FromJSON Web.Slack.Experimental.Events.Types.MessageDeletedEvent
+ Web.Slack.Experimental.Events.Types: instance GHC.Show.Show Web.Slack.Experimental.Events.Types.ChannelSharedEvent
+ Web.Slack.Experimental.Events.Types: instance GHC.Show.Show Web.Slack.Experimental.Events.Types.ChannelUnsharedEvent
+ Web.Slack.Experimental.Events.Types: instance GHC.Show.Show Web.Slack.Experimental.Events.Types.MessageDeletedEvent
+ Web.Slack.User: [userIsBot] :: User -> Maybe Bool
- Web.Slack.Conversation: type Api = "conversations.info" :> AuthProtect "token" :> ReqBody '[FormUrlEncoded] InfoReq :> Post '[JSON] ResponseJSON InfoRsp :<|> "conversations.join" :> AuthProtect "token" :> ReqBody '[FormUrlEncoded] JoinReq :> Post '[JSON] ResponseJSON JoinRsp
+ Web.Slack.Conversation: type Api = "conversations.info" :> AuthProtect "token" :> ReqBody '[FormUrlEncoded] InfoReq :> Post '[JSON] ResponseJSON InfoRsp :<|> "conversations.join" :> AuthProtect "token" :> ReqBody '[FormUrlEncoded] JoinReq :> Post '[JSON] ResponseJSON JoinRsp :<|> "conversations.members" :> AuthProtect "token" :> ReqBody '[FormUrlEncoded] MembersReq :> Post '[JSON] ResponseJSON MembersRsp
- Web.Slack.Experimental.Blocks: RichTextSectionItemRichText :: [RichItem] -> RichTextSectionItem
+ Web.Slack.Experimental.Blocks: RichTextSectionItemRichText :: RichTextSection -> RichTextSectionItem
- Web.Slack.Experimental.Blocks.Types: RichTextSectionItemRichText :: [RichItem] -> RichTextSectionItem
+ Web.Slack.Experimental.Blocks.Types: RichTextSectionItemRichText :: RichTextSection -> RichTextSectionItem
- Web.Slack.Experimental.Events.Types: [ts] :: BotMessageEvent -> Text
+ Web.Slack.Experimental.Events.Types: [ts] :: MessageDeletedEvent -> Text
- Web.Slack.User: User :: UserId -> Text -> Bool -> Maybe Color -> Maybe Profile -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> POSIXTime -> User
+ Web.Slack.User: User :: UserId -> Text -> Bool -> Maybe Color -> Maybe Profile -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Bool -> POSIXTime -> User

Files

CHANGELOG.md view
@@ -1,4 +1,56 @@-# 2.1.1.0+# 2.3.0.0 (2026-10-09)+* [#158](https://github.com/MercuryTechnologies/slack-web/pull/158),+  [#159](https://github.com/MercuryTechnologies/slack-web/pull/159)+  Decode the optional `is_bot` field on user objects.++  Breaking change: `User` has a new `userIsBot :: Maybe Bool` field.+* [#160](https://github.com/MercuryTechnologies/slack-web/pull/160)+  Add cursor-based pagination and page-size control to `conversationsMembers`.++  Breaking change: `MembersReq` has new `membersReqCursor` and `membersReqLimit`+  fields; `MembersRsp` has a new `membersRspResponseMetadata` field.+* [#155](https://github.com/MercuryTechnologies/slack-web/pull/155)+  Replace the `data-default-class` dependency with `data-default`.+* [#164](https://github.com/MercuryTechnologies/slack-web/pull/164),+  [#165](https://github.com/MercuryTechnologies/slack-web/pull/165)+  Include more information in Block Kit parse errors. If an unexpected value is+  found, it will now include the invalid value as part of the error.++  Before:++      Unknown SlackActionComponent type, must be one of ['button']++  After:++      Unknown SlackActionComponent type 'weird_type', must be one of ['button']++* [#168](https://github.com/MercuryTechnologies/slack-web/pull/168)+  Parse rich-text `message_mention` elements as `RichItemMessageMention`.+  These elements were previously returned as `RichItemOther`.++  Breaking change: `RichItem` has a new `RichItemMessageMention` constructor.+* [#169](https://github.com/MercuryTechnologies/slack-web/pull/169)+  Introduce `RichTextSection` for `rich_text_section` objects.++  Breaking change: `RichTextSectionItemRichText` now wraps `RichTextSection`+  instead of `[RichItem]`.+* [#170](https://github.com/MercuryTechnologies/slack-web/pull/170)+  Breaking change: `RichTextSectionItem` has new `RichTextSectionItemList`,+  `RichTextSectionItemQuote`, and `RichTextSectionItemPreformatted` constructors.++# 2.2.3.0 (2025-09-15)+* [#156](https://github.com/MercuryTechnologies/slack-web/pull/156)+  Implement `message_deleted` event.++# 2.2.2.0 (2025-09-15)+* [#153](https://github.com/MercuryTechnologies/slack-web/pull/153)+  Implement `channel_shared` and `channel_unshared` events.++# 2.2.1.0 (2025-09-15)+* [#151](https://github.com/MercuryTechnologies/slack-web/pull/151)+  Implement `conversations.member` API method.++# 2.2.0.0 (2025-03-21) * [#145](https://github.com/MercuryTechnologies/slack-web/pull/145)   Implement `conversations.info` API method. 
slack-web.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: slack-web-version: 2.2.0.0+version: 2.3.0.0  build-type: Simple @@ -153,12 +153,12 @@     , classy-prelude     , containers     , crypton-    , data-default-class+    , data-default     , deepseq     , either     , errors     , hashable-    , http-api-data >= 0.3 && < 0.7+    , http-api-data >= 0.3 && < 0.8     , http-client >= 0.5 && < 0.8     , http-client-tls >= 0.3 && < 0.4     , megaparsec >= 5.0 && < 10@@ -197,6 +197,7 @@       Web.Slack.ChatSpec       Web.Slack.Files.TypesSpec       Web.Slack.InternalSpec+      Web.Slack.UserSpec       Web.Slack.UsersConversationsSpec       Web.Slack.Experimental.RequestVerificationSpec       Web.Slack.Experimental.Events.TypesSpec
src/Web/Slack/Conversation.hs view
@@ -44,6 +44,11 @@   JoinRsp (..),   conversationsJoin,   conversationsJoin_,+  MembersReq (..),+  mkMembersReq,+  MembersRsp (..),+  conversationsMembers,+  conversationsMembers_, ) where  import Data.Aeson@@ -475,6 +480,62 @@  $(deriveJSON (jsonOpts "infoRsp") ''InfoRsp) +-- | @conversations.members@ request: retrieve a conversation's members.+--+-- <https://api.slack.com/methods/conversations.members>+--+-- @since 2.2.1.0+data MembersReq = MembersReq+  { membersReqChannel :: ConversationId+  , membersReqCursor :: Maybe Cursor+  , membersReqLimit :: Maybe Int+  }+  deriving stock (Eq, Show, Generic)++instance NFData MembersReq++$(deriveFromJSON (jsonOpts "membersReq") ''MembersReq)++instance ToForm MembersReq where+  toForm MembersReq {..} =+    [("channel", toQueryParam membersReqChannel)]+      <> toQueryParamIfJust "cursor" membersReqCursor+      <> toQueryParamIfJust "limit" membersReqLimit++-- | Smart constructor for 'MembersReq' with defaults.+--+-- @since 2.2.1.0+mkMembersReq :: ConversationId -> MembersReq+mkMembersReq channel =+  MembersReq+    { membersReqChannel = channel+    , membersReqCursor = Nothing+    , membersReqLimit = Nothing+    }++instance PagedRequest MembersReq where+  setCursor c r = r {membersReqCursor = c}++-- | @conversation.members@ response+--+-- <https://api.slack.com/methods/conversations.members>+--+-- @since 2.2.1.0+data MembersRsp = MembersRsp+  { membersRspMembers :: [UserId]+  , membersRspResponseMetadata :: Maybe ResponseMetadata+  }+  deriving stock (Eq, Show, Generic)++instance NFData MembersRsp++$(deriveFromJSON (jsonOpts "membersRsp") ''MembersRsp)++instance PagedResponse MembersRsp where+  type ResponseObject MembersRsp = UserId+  getResponseData MembersRsp {membersRspMembers} = membersRspMembers+  getResponseMetadata MembersRsp {membersRspResponseMetadata} = membersRspResponseMetadata+ -- | FIXME(jadel): move the rest of the Conversations API into here since the old "shoving all the API in one spot" is soft deprecated. -- @since 2.2.0.0 type Api =@@ -486,6 +547,10 @@       :> AuthProtect "token"       :> ReqBody '[FormUrlEncoded] JoinReq       :> Post '[JSON] (ResponseJSON JoinRsp)+    :<|> "conversations.members"+      :> AuthProtect "token"+      :> ReqBody '[FormUrlEncoded] MembersReq+      :> Post '[JSON] (ResponseJSON MembersRsp)  -- | Joins a conversation. --@@ -552,12 +617,15 @@ -- | @since 2.2.0.0 conversationsJoin_ :: AuthenticatedRequest (AuthProtect "token") -> JoinReq -> ClientM (ResponseJSON JoinRsp) +-- | @since 2.2.1.0+conversationsMembers_ :: AuthenticatedRequest (AuthProtect "token") -> MembersReq -> ClientM (ResponseJSON MembersRsp)+ -- | @since 2.2.0.0 conversationsInfo_ ::   AuthenticatedRequest (AuthProtect "token") ->   InfoReq ->   ClientM (ResponseJSON InfoRsp)-conversationsInfo_ :<|> conversationsJoin_ = client (Proxy @Api)+conversationsInfo_ :<|> conversationsJoin_ :<|> conversationsMembers_ = client (Proxy @Api)  -- | Retrieve a conversation's metadata. --@@ -584,3 +652,12 @@ conversationsJoin slackConfig req = do   let authR = mkSlackAuthenticateReq slackConfig   run (conversationsJoin_ authR req) . slackConfigManager $ slackConfig++-- | @since 2.2.1.0+conversationsMembers ::+  SlackConfig ->+  MembersReq ->+  IO (Response MembersRsp)+conversationsMembers slackConfig req = do+  let authR = mkSlackAuthenticateReq slackConfig+  run (conversationsMembers_ authR req) . slackConfigManager $ slackConfig
src/Web/Slack/Experimental/Blocks.hs view
@@ -31,6 +31,8 @@   RichItem (..),   RichStyle (..),   RichLinkAttrs (..),+  RichMessageMention (..),+  RichTextSection (..),   RichTextSectionItem (..),   RichText (..), 
src/Web/Slack/Experimental/Blocks/Types.hs view
@@ -88,7 +88,7 @@       "mrkdwn" -> do         text <- obj .: "text"         pure . SlackMarkdownText . SlackText $ lines text-      _ -> fail "Unknown SlackTextObject type, must be one of ['plain_text', 'mrkdwn']"+      _ -> fail $ "Unknown SlackTextObject type " <> show slackTextType <> ", must be one of ['plain_text', 'mrkdwn']"  instance Show SlackText where   show (SlackText arr) = show $ concat arr@@ -216,7 +216,7 @@           Just innerObj -> innerObj .: "text"           Nothing -> pure Nothing         pure $ SlackContentImage $ SlackImage slackImageTitleText slackImageAltText slackImageUrl-      _ -> fail "Unknown SlackContent type, must be one of ['mrkdwn', 'image']"+      _ -> fail $ "Unknown SlackContent type " <> show slackContentType <> ", must be one of ['mrkdwn', 'image']"  newtype SlackContext = SlackContext [SlackContent]   deriving newtype (Semigroup, Monoid, Eq)@@ -289,15 +289,48 @@   }   deriving stock (Eq, Show) --- | Seemingly only documented at---  <https://api.slack.com/changelog/2019-09-what-they-see-is-what-you-get-and-more-and-less>+-- | An inline reference to a message. The URL is optional; consumers can build+-- a permalink from the channel and message timestamp in the current workspace. -----  They warn of undocumented element types. Joy.+-- Parsed as part of 'RichItemMessageMention' in incoming rich text.+--+-- <https://docs.slack.dev/reference/block-kit/block-elements/message-mention-element/>+--+-- @since 2.3.0.0+data RichMessageMention = RichMessageMention+  { rmmChannelId :: ConversationId+  -- ^ Channel containing the referenced message.+  --+  -- @since 2.3.0.0+  , rmmMessageTs :: Text+  -- ^ Timestamp identifying the referenced message, preserved as Slack's text.+  --+  -- @since 2.3.0.0+  , rmmThreadTs :: Maybe Text+  -- ^ Optional @thread_ts@ timestamp supplied by Slack.+  --+  -- @since 2.3.0.0+  , rmmUrl :: Maybe Text+  -- ^ URL of the referenced message, when supplied by Slack.+  --+  -- @since 2.3.0.0+  }+  deriving stock (Eq, Show)++-- | Inline content inside rich-text sections, quotes, and preformatted blocks.+--+-- <https://docs.slack.dev/reference/block-kit/block-elements/rich-text-section-element/>+--+-- Unrecognized element types are preserved as 'RichItemOther'. data RichItem   = RichItemText Text RichStyle   | RichItemChannel ConversationId   | RichItemUser UserId RichStyle   | RichItemLink RichLinkAttrs+  | -- | An inline @message_mention@ reference to another message.+    --+    -- @since 2.3.0.0+    RichItemMessageMention RichMessageMention   | RichItemEmoji Emoji   | RichItemOther Text Value   -- FIXME(jadel): date, usergroup, team, broadcast@@ -322,14 +355,73 @@         text <- obj .:? "text"         style <- obj .:? "style" .!= mempty         pure $ RichItemLink RichLinkAttrs {..}+      "message_mention" -> do+        rmmChannelId <- obj .: "channel_id"+        rmmMessageTs <- obj .: "message_ts"+        rmmThreadTs <- obj .:? "thread_ts"+        rmmUrl <- obj .:? "url"+        pure $ RichItemMessageMention RichMessageMention {..}       "user" -> do         userId <- obj .: "user_id"         style <- obj .:? "style" .!= mempty         pure $ RichItemUser userId style       _ -> pure $ RichItemOther kind (Object obj) +-- | A @rich_text_section@ object containing inline 'RichItem' values.+-- A section can appear directly in a @rich_text@ block or as an entry in a+-- @rich_text_list@. Its JSON decoder requires @type = "rich_text_section"@.+--+-- t'RichTextSectionItem' represents the broader set of structural children of+-- a rich-text block; this type represents only a section. Lists contain+-- sections, so their entries use this narrower type.+--+-- <https://docs.slack.dev/reference/block-kit/block-elements/rich-text-section-element/>+--+-- @since 2.3.0.0+newtype RichTextSection = RichTextSection [RichItem]+  deriving stock (Eq, Show)++instance FromJSON RichTextSection where+  parseJSON = withObject "RichTextSection" \obj -> do+    kind :: Text <- obj .: "type"+    case kind of+      "rich_text_section" -> RichTextSection <$> obj .: "elements"+      _ -> fail $ "Unexpected RichTextSection type " <> show kind <> ", must be 'rich_text_section'"++-- | Structural children of a @rich_text@ block: sections, lists, quotes, and+-- preformatted blocks. The name refers to this broader set of containers;+-- t'RichTextSection' represents a single @rich_text_section@, and 'RichItem'+-- represents inline content such as text, links, and message mentions.+--+-- <https://docs.slack.dev/reference/block-kit/blocks/rich-text-block/> data RichTextSectionItem-  = RichTextSectionItemRichText [RichItem]+  = -- | A @rich_text_section@ containing inline items.+    --+    -- <https://docs.slack.dev/reference/block-kit/block-elements/rich-text-section-element/>+    RichTextSectionItemRichText RichTextSection+  | -- | A @rich_text_list@ whose entries are sections. The t'RichTextSection'+    -- decoder rejects entries with any other container type.+    -- The decoder ignores Slack's @style@, @indent@, @offset@, and @border@ fields.+    --+    -- <https://docs.slack.dev/reference/block-kit/block-elements/rich-text-list-element/>+    --+    -- @since 2.3.0.0+    RichTextSectionItemList [RichTextSection]+  | -- | A @rich_text_quote@ containing inline items.+    -- The decoder ignores Slack's optional @border@ field.+    --+    -- <https://docs.slack.dev/reference/block-kit/block-elements/rich-text-quote-element/>+    --+    -- @since 2.3.0.0+    RichTextSectionItemQuote [RichItem]+  | -- | A @rich_text_preformatted@ code block. Slack documents text and link+    -- elements as its contents; 'RichItem' also permits other constructors.+    -- Border and syntax-highlighting language are not retained.+    --+    -- <https://docs.slack.dev/reference/block-kit/block-elements/rich-text-preformatted-element/>+    --+    -- @since 2.3.0.0+    RichTextSectionItemPreformatted [RichItem]   | RichTextSectionItemUnknown Text Value   deriving stock (Eq, Show) @@ -337,9 +429,10 @@   parseJSON = withObject "RichTextSectionItem" \obj -> do     kind <- obj .: "type"     case kind of-      "rich_text_section" -> do-        elts <- obj .: "elements"-        pure $ RichTextSectionItemRichText elts+      "rich_text_section" -> RichTextSectionItemRichText <$> parseJSON (Object obj)+      "rich_text_list" -> RichTextSectionItemList <$> obj .: "elements"+      "rich_text_quote" -> RichTextSectionItemQuote <$> obj .: "elements"+      "rich_text_preformatted" -> RichTextSectionItemPreformatted <$> obj .: "elements"       _ -> pure $ RichTextSectionItemUnknown kind (Object obj)  data RichText = RichText@@ -364,7 +457,11 @@   toJSON (SlackButtonAccessory btn) = toJSON btn  instance FromJSON SlackAccessory where-  parseJSON v = SlackButtonAccessory <$> parseJSON v+  parseJSON = withObject "SlackAccessory" \obj -> do+    kind :: Text <- obj .: "type"+    case kind of+      "button" -> SlackButtonAccessory <$> parseJSON (Object obj)+      _ -> fail $ "Unknown SlackAccessory type " <> show kind <> ", must be one of ['button']"  instance Show SlackAccessory where   show (SlackButtonAccessory btn) = show btn@@ -794,7 +891,7 @@   deriving stock (Eq)  instance FromJSON SlackActionComponent where-  parseJSON = withObject "SlactActionComponent" $ \obj -> do+  parseJSON = withObject "SlackActionComponent" $ \obj -> do     (slackActionType :: Text) <- obj .: "type"     case slackActionType of       "button" -> do@@ -805,7 +902,7 @@         slackButtonStyle <- obj .:? "style"         slackButtonConfirm <- obj .:? "confirm"         pure $ SlackButton {..}-      _ -> fail "Unknown SlackActionComponent type, must be one of ['button']"+      _ -> fail $ "Unknown SlackActionComponent type " <> show slackActionType <> ", must be one of ['button']"  instance Show SlackActionComponent where   show SlackButton {..} = "[button " <> show slackButtonText <> "]"
src/Web/Slack/Experimental/Events/Types.hs view
@@ -244,6 +244,20 @@  $(deriveFromJSON snakeCaseOptions ''MessageUpdateEvent) +-- | A message was deleted from a channel or DM.+--+-- <https://docs.slack.dev/reference/events/message/message_deleted/>+--+-- @since 2.2.3.0+data MessageDeletedEvent = MessageDeletedEvent+  { channel :: ConversationId+  , ts :: Text+  , deletedTs :: Text+  }+  deriving stock (Show)++$(deriveFromJSON snakeCaseOptions ''MessageDeletedEvent)+ -- | FIXME: this might be a Channel, but may also be missing some fields/have bonus -- because Slack is cursed. data CreatedChannel = CreatedChannel@@ -283,6 +297,35 @@  $(deriveFromJSON snakeCaseOptions ''ChannelLeftEvent) +-- | A channel has been shared with an external workspace.+--+-- <https://docs.slack.dev/reference/events/channel_shared/>+--+-- @since 2.2.2.0+data ChannelSharedEvent = ChannelSharedEvent+  { connectedTeamId :: TeamId+  , channel :: ConversationId+  , eventTs :: Text+  }+  deriving stock (Show)++$(deriveFromJSON snakeCaseOptions ''ChannelSharedEvent)++-- | A channel has been unshared with an external workspace.+--+-- <https://docs.slack.dev/reference/events/channel_unshared/>+--+-- @since 2.2.2.0+data ChannelUnsharedEvent = ChannelUnsharedEvent+  { previouslyConnectedTeamId :: TeamId+  , channel :: ConversationId+  , isExtShared :: Bool+  , eventTs :: Text+  }+  deriving stock (Show)++$(deriveFromJSON snakeCaseOptions ''ChannelUnsharedEvent)+ -- | <https://api.slack.com/events/url_verification> data UrlVerificationPayload = UrlVerificationPayload   { challenge :: Text@@ -339,6 +382,8 @@   = EventMessage MessageEvent   | EventBotMessage BotMessageEvent   | EventMessageChanged+  | -- | @since 2.2.3.0+    EventMessageDeleted MessageDeletedEvent   | -- | Weird message event of subtype channel_join. Sent "sometimes", according     -- to a random Slack blog post from 2017:     -- <https://api.slack.com/changelog/2017-05-rethinking-channel-entrance-and-exit-events-and-messages>@@ -347,6 +392,10 @@     EventChannelJoinMessage   | EventChannelCreated ChannelCreatedEvent   | EventChannelLeft ChannelLeftEvent+  | -- | @since 2.2.2.0+    EventChannelShared ChannelSharedEvent+  | -- | @since 2.2.2.0+    EventChannelUnshared ChannelUnsharedEvent   | -- | @since 2.1.0.0     EventAppHomeOpened AppHomeOpenedEvent   | EventUnknown Value@@ -360,12 +409,15 @@       ("message", Nothing) -> EventMessage <$> parseJSON @MessageEvent (Object obj)       ("message", Just "bot_message") -> EventBotMessage <$> parseJSON @BotMessageEvent (Object obj)       ("message", Just "message_changed") -> pure EventMessageChanged+      ("message", Just "message_deleted") -> EventMessageDeleted <$> parseJSON @MessageDeletedEvent (Object obj)       ("message", Just "channel_join") -> pure EventChannelJoinMessage       -- n.b. these are unified since it is *identical* to a Message event with       -- a bonus files field       ("message", Just "file_share") -> EventMessage <$> parseJSON @MessageEvent (Object obj)       ("channel_created", Nothing) -> EventChannelCreated <$> parseJSON (Object obj)       ("channel_left", Nothing) -> EventChannelLeft <$> parseJSON (Object obj)+      ("channel_shared", Nothing) -> EventChannelShared <$> parseJSON (Object obj)+      ("channel_unshared", Nothing) -> EventChannelUnshared <$> parseJSON (Object obj)       ("app_home_opened", Nothing) -> EventAppHomeOpened <$> parseJSON (Object obj)       _ -> pure $ EventUnknown (Object obj) 
src/Web/Slack/Internal.hs view
@@ -1,3 +1,5 @@+{-# OPTIONS_GHC -Wno-orphans #-}+ -- | Internal things in slack-web. May be changed arbitrarily! module Web.Slack.Internal where 
src/Web/Slack/Prelude.hs view
@@ -12,7 +12,7 @@ import ClassyPrelude hiding (link) import Data.Aeson (FromJSON (..), ToJSON (..), object, withObject, (.:), (.:?), (.=)) import Data.Aeson.TH (deriveFromJSON, deriveJSON, deriveToJSON)-import Data.Default.Class (Default (..))+import Data.Default (Default (..)) import Data.List.NonEmpty (NonEmpty (..)) import Data.Proxy (Proxy (..)) import Data.String.Conversions (cs)
src/Web/Slack/User.hs view
@@ -55,6 +55,7 @@   , userIsPrimaryOwner :: Maybe Bool   , userIsRestricted :: Maybe Bool   , userIsUltraRestricted :: Maybe Bool+  , userIsBot :: Maybe Bool   , userUpdated :: POSIXTime   }   deriving stock (Eq, Generic, Show)
tests/Web/Slack/ConversationSpec.hs view
@@ -116,6 +116,8 @@     mapM_ (oneGoldenTestDecode @Conversation) ["shared_channel"]     mapM_ (oneGoldenTestDecode @InfoRsp) ["general"]     mapM_ (oneGoldenTestDecode @JoinRsp) ["test"]+    mapM_ (oneGoldenTestDecode @MembersReq) ["with_limit"]+    mapM_ (oneGoldenTestDecode @MembersRsp) ["test"]    it "errors accurately if no variant matches" $ do     let badData =
tests/Web/Slack/Experimental/Blocks/TypesSpec.hs view
@@ -1,9 +1,12 @@ module Web.Slack.Experimental.Blocks.TypesSpec where  import Data.Aeson qualified as Aeson+import Data.Aeson.Types (parseEither)+import Data.Either (isLeft) import Data.StringVariants.NonEmptyText.Internal (pattern NonEmptyText) import Refined.Unsafe (reallyUnsafeRefine) import TestImport+import Web.Slack.Common (ConversationId (..)) import Web.Slack.Experimental.Blocks.Types  jsonRoundtrips :: (Show a, Eq a, Aeson.ToJSON a, Aeson.FromJSON a) => a -> Spec@@ -13,6 +16,58 @@  spec :: Spec spec = do+  describe "unknown component diagnostics" do+    it "reports the rejected text object type" do+      Aeson.eitherDecode @SlackTextObject "{\"type\":\"unexpected_text\"}"+        `shouldBe` Left "Error in $: Unknown SlackTextObject type \"unexpected_text\", must be one of ['plain_text', 'mrkdwn']"++    it "reports the rejected content type at its nested JSON path" do+      Aeson.eitherDecode @SlackBlock "{\"type\":\"context\",\"elements\":[{\"type\":\"unexpected_content\"}]}"+        `shouldBe` Left "Error in $.elements: Unknown SlackContent type \"unexpected_content\", must be one of ['mrkdwn', 'image']"++    it "reports the rejected action component type at its nested JSON path" do+      Aeson.eitherDecode @SlackBlock "{\"type\":\"actions\",\"elements\":[{\"type\":\"static_select\",\"action_id\":\"select\"}]}"+        `shouldBe` Left "Error in $.elements[0]: Unknown SlackActionComponent type \"static_select\", must be one of ['button']"++    it "reports the rejected accessory type before requiring action fields" do+      Aeson.eitherDecode @SlackAccessory "{\"type\":\"image\",\"image_url\":\"https://example.com/image.png\",\"alt_text\":\"example\"}"+        `shouldBe` Left "Error in $: Unknown SlackAccessory type \"image\", must be one of ['button']"++    it "reports the rejected accessory type even when action fields are present" do+      Aeson.eitherDecode @SlackAccessory "{\"type\":\"static_select\",\"action_id\":\"select\"}"+        `shouldBe` Left "Error in $: Unknown SlackAccessory type \"static_select\", must be one of ['button']"++  describe "incoming Block Kit components" do+    it "parses a message mention without a URL" do+      Aeson.eitherDecode @RichItem "{\"type\":\"message_mention\",\"channel_id\":\"C123ABC456\",\"message_ts\":\"1720710212.123456\"}"+        `shouldBe` Right (RichItemMessageMention (RichMessageMention (ConversationId "C123ABC456") "1720710212.123456" Nothing Nothing))++    describe "rich-text list entries" do+      let listWith entry =+            object+              [ "type" .= ("rich_text_list" :: Text)+              , "style" .= ("bullet" :: Text)+              , "elements" .= [entry]+              ]+          decodeList = parseEither (parseJSON @RichTextSectionItem) . listWith++      for_ ["rich_text_list", "rich_text_quote", "rich_text_preformatted", "text", "future_container"] \(kind :: Text) ->+        it ("rejects a " <> unpack kind <> " as a list entry") do+          decodeList+            (object ["type" .= kind, "elements" .= ([] :: [Aeson.Value]), "style" .= ("bullet" :: Text), "text" .= ("inline" :: Text)])+            `shouldSatisfy` isLeft++      it "requires the section type tag" do+        decodeList (object ["elements" .= ([] :: [Aeson.Value])]) `shouldSatisfy` isLeft++      it "requires the section contents" do+        decodeList (object ["type" .= ("rich_text_section" :: Text)]) `shouldSatisfy` isLeft++      it "preserves unknown inline items inside a section" do+        let inlineItem = object ["type" .= ("future_inline" :: Text), "text" .= ("preserved" :: Text)]+        decodeList (object ["type" .= ("rich_text_section" :: Text), "elements" .= [inlineItem]])+          `shouldBe` Right (RichTextSectionItemList [RichTextSection [RichItemOther "future_inline" inlineItem]])+   let     aSlackAccessory = SlackButtonAccessory aSlackAction     aSlackAction = SlackAction
tests/Web/Slack/Experimental/Events/TypesSpec.hs view
@@ -12,7 +12,9 @@         (oneGoldenTestDecode @SlackWebhookEvent)         [ "messageExample"         , "messageChange"+        , "message_deleted"         , "message_rich_text"+        , "message_rich_text_containers"         , "message_file_share"         , "message_file_share_slack_connect"         , "link"@@ -22,6 +24,8 @@         , "messageIm"         , "slackbotIm"         , "channel_left"+        , "channel_shared"+        , "channel_unshared"         , "share_without_message"         , "share_with_message"         , "circleci"
+ tests/Web/Slack/UserSpec.hs view
@@ -0,0 +1,13 @@+module Web.Slack.UserSpec (spec) where++import JSONGolden+import TestImport+import Web.Slack.User (ListRsp)++-- Fixture modeled on the example response at+-- https://api.slack.com/methods/users.list#examples+spec :: Spec+spec = describe "User methods" do+  describe "users.list" do+    describe "Response FromJSON" do+      mapM_ (oneGoldenTestDecode @ListRsp) ["with_bots"]
tests/golden/SlackWebhookEvent/botMessage.golden view
@@ -16,13 +16,15 @@                             ( NonEmptyText "FVI" )                         , elements =                             [ RichTextSectionItemRichText-                                [ RichItemText "TEST"-                                    ( RichStyle-                                        { rsBold = False-                                        , rsItalic = False-                                        }-                                    )-                                ]+                                ( RichTextSection+                                    [ RichItemText "TEST"+                                        ( RichStyle+                                            { rsBold = False+                                            , rsItalic = False+                                            }+                                        )+                                    ]+                                )                             ]                         }                     ]
+ tests/golden/SlackWebhookEvent/channel_shared.golden view
@@ -0,0 +1,21 @@+EventEventCallback+    ( EventCallback+        { eventId = EventId+            { unEventId = "Ev044RKJHB8W" }+        , teamId = TeamId+            { unTeamId = "T043DB835ML" }+        , eventTime = MkSystemTime+            { systemSeconds = 1664827321+            , systemNanoseconds = 0+            }+        , event = EventChannelShared+            ( ChannelSharedEvent+                { connectedTeamId = TeamId+                    { unTeamId = "E163Q94DX" }+                , channel = ConversationId+                    { unConversationId = "C123ABC456" }+                , eventTs = "1561064063.001100"+                }+            )+        }+    )
+ tests/golden/SlackWebhookEvent/channel_shared.json view
@@ -0,0 +1,24 @@+{+  "token": "aaaa",+  "team_id": "T043DB835ML",+  "api_app_id": "A0442TUPHGR",+  "event": {+    "type": "channel_shared",+    "connected_team_id": "E163Q94DX",+    "channel": "C123ABC456",+    "event_ts": "1561064063.001100"+  },+  "type": "event_callback",+  "event_id": "Ev044RKJHB8W",+  "event_time": 1664827321,+  "authorizations": [+    {+      "enterprise_id": null,+      "team_id": "T043DB835ML",+      "user_id": "U0442US8QGH",+      "is_bot": false,+      "is_enterprise_install": false+    }+  ],+  "is_ext_shared_channel": true+}
+ tests/golden/SlackWebhookEvent/channel_unshared.golden view
@@ -0,0 +1,22 @@+EventEventCallback+    ( EventCallback+        { eventId = EventId+            { unEventId = "Ev044RKJHB8W" }+        , teamId = TeamId+            { unTeamId = "T043DB835ML" }+        , eventTime = MkSystemTime+            { systemSeconds = 1664827321+            , systemNanoseconds = 0+            }+        , event = EventChannelUnshared+            ( ChannelUnsharedEvent+                { previouslyConnectedTeamId = TeamId+                    { unTeamId = "E163Q94DX" }+                , channel = ConversationId+                    { unConversationId = "C123ABC456" }+                , isExtShared = False+                , eventTs = "1561064063.001100"+                }+            )+        }+    )
+ tests/golden/SlackWebhookEvent/channel_unshared.json view
@@ -0,0 +1,25 @@+{+  "token": "aaaa",+  "team_id": "T043DB835ML",+  "api_app_id": "A0442TUPHGR",+  "event": {+    "type": "channel_unshared",+    "previously_connected_team_id": "E163Q94DX",+    "channel": "C123ABC456",+    "is_ext_shared": false,+    "event_ts": "1561064063.001100"+  },+  "type": "event_callback",+  "event_id": "Ev044RKJHB8W",+  "event_time": 1664827321,+  "authorizations": [+    {+      "enterprise_id": null,+      "team_id": "T043DB835ML",+      "user_id": "U0442US8QGH",+      "is_bot": false,+      "is_enterprise_install": false+    }+  ],+  "is_ext_shared_channel": false+}
tests/golden/SlackWebhookEvent/forwarded_message.golden view
@@ -57,13 +57,15 @@                                                         ( NonEmptyText "nXUog" )                                                     , elements =                                                         [ RichTextSectionItemRichText-                                                            [ RichItemText "123"-                                                                ( RichStyle-                                                                    { rsBold = False-                                                                    , rsItalic = False-                                                                    }-                                                                )-                                                            ]+                                                            ( RichTextSection+                                                                [ RichItemText "123"+                                                                    ( RichStyle+                                                                        { rsBold = False+                                                                        , rsItalic = False+                                                                        }+                                                                    )+                                                                ]+                                                            )                                                         ]                                                     }                                                 ]
tests/golden/SlackWebhookEvent/link.golden view
@@ -16,17 +16,19 @@                             ( NonEmptyText "MEdM" )                         , elements =                             [ RichTextSectionItemRichText-                                [ RichItemLink-                                    ( RichLinkAttrs-                                        { style = RichStyle-                                            { rsBold = False-                                            , rsItalic = False+                                ( RichTextSection+                                    [ RichItemLink+                                        ( RichLinkAttrs+                                            { style = RichStyle+                                                { rsBold = False+                                                , rsItalic = False+                                                }+                                            , url = "https://jadeapptesting.slack.com/archives/C043YJGBY49/p1663961604007869"+                                            , text = Nothing                                             }-                                        , url = "https://jadeapptesting.slack.com/archives/C043YJGBY49/p1663961604007869"-                                        , text = Nothing-                                        }-                                    )-                                ]+                                        )+                                    ]+                                )                             ]                         }                     ]
tests/golden/SlackWebhookEvent/messageExample.golden view
@@ -16,13 +16,15 @@                             ( NonEmptyText "u6n" )                         , elements =                             [ RichTextSectionItemRichText-                                [ RichItemText "dgsfklsdgf"-                                    ( RichStyle-                                        { rsBold = False-                                        , rsItalic = False-                                        }-                                    )-                                ]+                                ( RichTextSection+                                    [ RichItemText "dgsfklsdgf"+                                        ( RichStyle+                                            { rsBold = False+                                            , rsItalic = False+                                            }+                                        )+                                    ]+                                )                             ]                         }                     ]
tests/golden/SlackWebhookEvent/messageIm.golden view
@@ -16,13 +16,15 @@                             ( NonEmptyText "LSo" )                         , elements =                             [ RichTextSectionItemRichText-                                [ RichItemText "test"-                                    ( RichStyle-                                        { rsBold = False-                                        , rsItalic = False-                                        }-                                    )-                                ]+                                ( RichTextSection+                                    [ RichItemText "test"+                                        ( RichStyle+                                            { rsBold = False+                                            , rsItalic = False+                                            }+                                        )+                                    ]+                                )                             ]                         }                     ]
+ tests/golden/SlackWebhookEvent/message_deleted.golden view
@@ -0,0 +1,20 @@+EventEventCallback+    ( EventCallback+        { eventId = EventId+            { unEventId = "Ev043QRVT9FE" }+        , teamId = TeamId+            { unTeamId = "T043DB835ML" }+        , eventTime = MkSystemTime+            { systemSeconds = 1663969383+            , systemNanoseconds = 0+            }+        , event = EventMessageDeleted+            ( MessageDeletedEvent+                { channel = ConversationId+                    { unConversationId = "C043YJGBY49" }+                , ts = "1663969383.001500"+                , deletedTs = "1663966382.046509"+                }+            )+        }+    )
+ tests/golden/SlackWebhookEvent/message_deleted.json view
@@ -0,0 +1,37 @@+{+  "token": "aaaa",+  "team_id": "T043DB835ML",+  "api_app_id": "A0442TUPHGR",+  "event": {+    "type": "message",+    "subtype": "message_deleted",+    "hidden": true,+    "channel": "C043YJGBY49",+    "previous_message": {+      "client_msg_id": "de349a9f-62a4-4293-b64d-701a1f566e8a",+      "type": "message",+      "text": "This message will be deleted",+      "user": "U043H11ES4V",+      "ts": "1663966382.046509",+      "team": "T043DB835ML"+    },+    "deleted_ts": "1663966382.046509",+    "ts": "1663969383.001500",+    "event_ts": "1663969383.001500",+    "channel_type": "channel"+  },+  "type": "event_callback",+  "event_id": "Ev043QRVT9FE",+  "event_time": 1663969383,+  "authorizations": [+    {+      "enterprise_id": null,+      "team_id": "T043DB835ML",+      "user_id": "U0442US8QGH",+      "is_bot": true,+      "is_enterprise_install": false+    }+  ],+  "is_ext_shared_channel": false,+  "event_context": "aaaa"+}
tests/golden/SlackWebhookEvent/message_file_share.golden view
@@ -16,13 +16,15 @@                             ( NonEmptyText "MeAN" )                         , elements =                             [ RichTextSectionItemRichText-                                [ RichItemText "message message message"-                                    ( RichStyle-                                        { rsBold = False-                                        , rsItalic = False-                                        }-                                    )-                                ]+                                ( RichTextSection+                                    [ RichItemText "message message message"+                                        ( RichStyle+                                            { rsBold = False+                                            , rsItalic = False+                                            }+                                        )+                                    ]+                                )                             ]                         }                     ]
tests/golden/SlackWebhookEvent/message_rich_text.golden view
@@ -16,58 +16,60 @@                             ( NonEmptyText "6r5Zo" )                         , elements =                             [ RichTextSectionItemRichText-                                [ RichItemChannel-                                    ( ConversationId-                                        { unConversationId = "C043YJGBY49" }-                                    )-                                , RichItemText " "-                                    ( RichStyle-                                        { rsBold = False-                                        , rsItalic = False-                                        }-                                    )-                                , RichItemEmoji-                                    ( Emoji-                                        { unEmoji = "cat" }-                                    )-                                , RichItemText " "-                                    ( RichStyle-                                        { rsBold = False-                                        , rsItalic = False-                                        }-                                    )-                                , RichItemUser-                                    ( UserId-                                        { unUserId = "U043H11ES4V" }-                                    )-                                    ( RichStyle-                                        { rsBold = False-                                        , rsItalic = False-                                        }-                                    )-                                , RichItemText " "-                                    ( RichStyle-                                        { rsBold = False-                                        , rsItalic = False-                                        }-                                    )-                                , RichItemLink-                                    ( RichLinkAttrs-                                        { style = RichStyle+                                ( RichTextSection+                                    [ RichItemChannel+                                        ( ConversationId+                                            { unConversationId = "C043YJGBY49" }+                                        )+                                    , RichItemText " "+                                        ( RichStyle                                             { rsBold = False                                             , rsItalic = False                                             }-                                        , url = "https://google.com"-                                        , text = Nothing-                                        }-                                    )-                                , RichItemText " text text text"-                                    ( RichStyle-                                        { rsBold = False-                                        , rsItalic = False-                                        }-                                    )-                                ]+                                        )+                                    , RichItemEmoji+                                        ( Emoji+                                            { unEmoji = "cat" }+                                        )+                                    , RichItemText " "+                                        ( RichStyle+                                            { rsBold = False+                                            , rsItalic = False+                                            }+                                        )+                                    , RichItemUser+                                        ( UserId+                                            { unUserId = "U043H11ES4V" }+                                        )+                                        ( RichStyle+                                            { rsBold = False+                                            , rsItalic = False+                                            }+                                        )+                                    , RichItemText " "+                                        ( RichStyle+                                            { rsBold = False+                                            , rsItalic = False+                                            }+                                        )+                                    , RichItemLink+                                        ( RichLinkAttrs+                                            { style = RichStyle+                                                { rsBold = False+                                                , rsItalic = False+                                                }+                                            , url = "https://google.com"+                                            , text = Nothing+                                            }+                                        )+                                    , RichItemText " text text text"+                                        ( RichStyle+                                            { rsBold = False+                                            , rsItalic = False+                                            }+                                        )+                                    ]+                                )                             ]                         }                     ]
+ tests/golden/SlackWebhookEvent/message_rich_text_containers.golden view
@@ -0,0 +1,91 @@+EventEventCallback+    ( EventCallback+        { eventId = EventId+            { unEventId = "EvRichTextContainers" }+        , teamId = TeamId+            { unTeamId = "T043DB835ML" }+        , eventTime = MkSystemTime+            { systemSeconds = 1720710300+            , systemNanoseconds = 0+            }+        , event = EventMessage+            ( MessageEvent+                { blocks = Just+                    [ RichText+                        { blockId = Nothing+                        , elements =+                            [ RichTextSectionItemList+                                [ RichTextSection+                                    [ RichItemMessageMention+                                        ( RichMessageMention+                                            { rmmChannelId = ConversationId+                                                { unConversationId = "C123ABC456" }+                                            , rmmMessageTs = "1720710212.123456"+                                            , rmmThreadTs = Nothing+                                            , rmmUrl = Nothing+                                            }+                                        )+                                    ]+                                , RichTextSection+                                    [ RichItemLink+                                        ( RichLinkAttrs+                                            { style = RichStyle+                                                { rsBold = False+                                                , rsItalic = False+                                                }+                                            , url = "https://example.com/list-item"+                                            , text = Nothing+                                            }+                                        )+                                    ]+                                ]+                            , RichTextSectionItemQuote+                                [ RichItemMessageMention+                                    ( RichMessageMention+                                        { rmmChannelId = ConversationId+                                            { unConversationId = "C123ABC456" }+                                        , rmmMessageTs = "1720710250.012345"+                                        , rmmThreadTs = Nothing+                                        , rmmUrl = Nothing+                                        }+                                    )+                                ]+                            , RichTextSectionItemPreformatted+                                [ RichItemText "See the documentation:+                                  "+                                    ( RichStyle+                                        { rsBold = False+                                        , rsItalic = False+                                        }+                                    )+                                , RichItemLink+                                    ( RichLinkAttrs+                                        { style = RichStyle+                                            { rsBold = False+                                            , rsItalic = False+                                            }+                                        , url = "https://example.com/code-reference"+                                        , text = Nothing+                                        }+                                    )+                                ]+                            ]+                        }+                    ]+                , channel = ConversationId+                    { unConversationId = "C045V0VJT16" }+                , text = "References in a list, a quote, and a code block"+                , channelType = Channel+                , files = Nothing+                , user = UserId+                    { unUserId = "U043H11ES4V" }+                , ts = "1720710300.654321"+                , threadTs = Nothing+                , appId = Nothing+                , botId = Nothing+                , botProfile = Nothing+                , attachments = Nothing+                }+            )+        }+    )
+ tests/golden/SlackWebhookEvent/message_rich_text_containers.json view
@@ -0,0 +1,73 @@+{+  "type": "event_callback",+  "team_id": "T043DB835ML",+  "event_id": "EvRichTextContainers",+  "event_time": 1720710300,+  "event": {+    "type": "message",+    "channel": "C045V0VJT16",+    "channel_type": "channel",+    "user": "U043H11ES4V",+    "ts": "1720710300.654321",+    "text": "References in a list, a quote, and a code block",+    "blocks": [+      {+        "type": "rich_text",+        "elements": [+          {+            "type": "rich_text_list",+            "style": "bullet",+            "indent": 0,+            "elements": [+              {+                "type": "rich_text_section",+                "elements": [+                  {+                    "type": "message_mention",+                    "channel_id": "C123ABC456",+                    "message_ts": "1720710212.123456"+                  }+                ]+              },+              {+                "type": "rich_text_section",+                "elements": [+                  {+                    "type": "link",+                    "url": "https://example.com/list-item"+                  }+                ]+              }+            ]+          },+          {+            "type": "rich_text_quote",+            "border": 1,+            "elements": [+              {+                "type": "message_mention",+                "channel_id": "C123ABC456",+                "message_ts": "1720710250.012345"+              }+            ]+          },+          {+            "type": "rich_text_preformatted",+            "language": "text",+            "border": 0,+            "elements": [+              {+                "type": "text",+                "text": "See the documentation:\n"+              },+              {+                "type": "link",+                "url": "https://example.com/code-reference"+              }+            ]+          }+        ]+      }+    ]+  }+}
tests/golden/SlackWebhookEvent/message_subtype_bot_message.golden view
@@ -16,17 +16,19 @@                             ( NonEmptyText "nmh" )                         , elements =                             [ RichTextSectionItemRichText-                                [ RichItemLink-                                    ( RichLinkAttrs-                                        { style = RichStyle-                                            { rsBold = False-                                            , rsItalic = False+                                ( RichTextSection+                                    [ RichItemLink+                                        ( RichLinkAttrs+                                            { style = RichStyle+                                                { rsBold = False+                                                , rsItalic = False+                                                }+                                            , url = "https://myworkspace.slack.com/archives/C07KTH1T4CQ/p1724954997876929"+                                            , text = Nothing                                             }-                                        , url = "https://myworkspace.slack.com/archives/C07KTH1T4CQ/p1724954997876929"-                                        , text = Nothing-                                        }-                                    )-                                ]+                                        )+                                    ]+                                )                             ]                         }                     ]
tests/golden/SlackWebhookEvent/share_with_message.golden view
@@ -16,13 +16,15 @@                             ( NonEmptyText "Yyg6K" )                         , elements =                             [ RichTextSectionItemRichText-                                [ RichItemText "hello check that out!"-                                    ( RichStyle-                                        { rsBold = False-                                        , rsItalic = False-                                        }-                                    )-                                ]+                                ( RichTextSection+                                    [ RichItemText "hello check that out!"+                                        ( RichStyle+                                            { rsBold = False+                                            , rsItalic = False+                                            }+                                        )+                                    ]+                                )                             ]                         }                     ]@@ -72,13 +74,15 @@                                                         ( NonEmptyText "BXn1o" )                                                     , elements =                                                         [ RichTextSectionItemRichText-                                                            [ RichItemText "this will be shared with a message"-                                                                ( RichStyle-                                                                    { rsBold = False-                                                                    , rsItalic = False-                                                                    }-                                                                )-                                                            ]+                                                            ( RichTextSection+                                                                [ RichItemText "this will be shared with a message"+                                                                    ( RichStyle+                                                                        { rsBold = False+                                                                        , rsItalic = False+                                                                        }+                                                                    )+                                                                ]+                                                            )                                                         ]                                                     }                                                 ]
tests/golden/SlackWebhookEvent/share_without_message.golden view
@@ -57,13 +57,15 @@                                                         ( NonEmptyText "TNHa4" )                                                     , elements =                                                         [ RichTextSectionItemRichText-                                                            [ RichItemText "blahblahblahblahblah"-                                                                ( RichStyle-                                                                    { rsBold = False-                                                                    , rsItalic = False-                                                                    }-                                                                )-                                                            ]+                                                            ( RichTextSection+                                                                [ RichItemText "blahblahblahblahblah"+                                                                    ( RichStyle+                                                                        { rsBold = False+                                                                        , rsItalic = False+                                                                        }+                                                                    )+                                                                ]+                                                            )                                                         ]                                                     }                                                 ]
tests/golden/SlackWebhookEvent/slackbotIm.golden view
@@ -16,22 +16,24 @@                             ( NonEmptyText "ZRe" )                         , elements =                             [ RichTextSectionItemRichText-                                [ RichItemText "You have been removed from #testing-slack-app by "-                                    ( RichStyle-                                        { rsBold = False-                                        , rsItalic = False-                                        }-                                    )-                                , RichItemUser-                                    ( UserId-                                        { unUserId = "U043H11ES4V" }-                                    )-                                    ( RichStyle-                                        { rsBold = False-                                        , rsItalic = False-                                        }-                                    )-                                ]+                                ( RichTextSection+                                    [ RichItemText "You have been removed from #testing-slack-app by "+                                        ( RichStyle+                                            { rsBold = False+                                            , rsItalic = False+                                            }+                                        )+                                    , RichItemUser+                                        ( UserId+                                            { unUserId = "U043H11ES4V" }+                                        )+                                        ( RichStyle+                                            { rsBold = False+                                            , rsItalic = False+                                            }+                                        )+                                    ]+                                )                             ]                         }                     ]