packages feed

slack-api 0.9 → 0.10

raw patch · 17 files changed

+446/−176 lines, 17 filesdep +hashabledep +tlsdep +wussdep −HsOpenSSLdep −openssl-streamsdep ~basedep ~lensdep ~mtlnew-component:exe:example_counterbotnew-component:exe:example_echobotPVP ok

version bump matches the API change (PVP)

Dependencies added: hashable, tls, wuss

Dependencies removed: HsOpenSSL, openssl-streams

Dependency ranges changed: base, lens, mtl, text

API changes (from Hackage documentation)

- Web.Slack.Config: SlackConfig :: String -> SlackConfig
- Web.Slack.Config: [_slackApiToken] :: SlackConfig -> String
- Web.Slack.Config: data SlackConfig
- Web.Slack.Config: instance GHC.Show.Show Web.Slack.Config.SlackConfig
- Web.Slack.Config: slackApiToken :: Iso' SlackConfig String
- Web.Slack.Message: instance Data.Aeson.Types.Class.ToJSON Web.Slack.Message.MessagePayload
- Web.Slack.Message: instance Data.Aeson.Types.Class.ToJSON Web.Slack.Message.PingPayload
- Web.Slack.Message: instance GHC.Show.Show Web.Slack.Message.MessagePayload
- Web.Slack.Message: instance GHC.Show.Show Web.Slack.Message.PingPayload
- Web.Slack.Types.Event: AccountsChanged :: Event
- Web.Slack.Types.Event: BotAdded :: Bot -> Event
- Web.Slack.Types.Event: BotChanged :: Bot -> Event
- Web.Slack.Types.Event: ChannelArchive :: ChannelId -> UserId -> Event
- Web.Slack.Types.Event: ChannelCreated :: Channel -> Event
- Web.Slack.Types.Event: ChannelDeleted :: ChannelId -> Event
- Web.Slack.Types.Event: ChannelHistoryChanged :: SlackTimeStamp -> SlackTimeStamp -> SlackTimeStamp -> Event
- Web.Slack.Types.Event: ChannelJoined :: Channel -> Event
- Web.Slack.Types.Event: ChannelLeft :: ChannelId -> Event
- Web.Slack.Types.Event: ChannelMarked :: ChannelId -> SlackTimeStamp -> Event
- Web.Slack.Types.Event: ChannelRename :: ChannelRenameInfo -> Event
- Web.Slack.Types.Event: ChannelUnarchive :: ChannelId -> UserId -> Event
- Web.Slack.Types.Event: CommandsChanged :: SlackTimeStamp -> Event
- Web.Slack.Types.Event: EmailDomainChange :: Domain -> SlackTimeStamp -> Event
- Web.Slack.Types.Event: EmojiChanged :: SlackTimeStamp -> Event
- Web.Slack.Types.Event: FileChange :: File -> Event
- Web.Slack.Types.Event: FileCommentAdded :: File -> Comment -> Event
- Web.Slack.Types.Event: FileCommentDeleted :: File -> CommentId -> Event
- Web.Slack.Types.Event: FileCommentEdited :: File -> Comment -> Event
- Web.Slack.Types.Event: FileCreated :: File -> Event
- Web.Slack.Types.Event: FileDeleted :: FileId -> SlackTimeStamp -> Event
- Web.Slack.Types.Event: FilePrivate :: FileId -> Event
- Web.Slack.Types.Event: FilePublic :: FileReference -> Event
- Web.Slack.Types.Event: FileShared :: FileReference -> Event
- Web.Slack.Types.Event: FileUnshared :: File -> Event
- Web.Slack.Types.Event: GroupArchive :: ChannelId -> Event
- Web.Slack.Types.Event: GroupClose :: UserId -> ChannelId -> Event
- Web.Slack.Types.Event: GroupHistoryChanged :: SlackTimeStamp -> SlackTimeStamp -> SlackTimeStamp -> Event
- Web.Slack.Types.Event: GroupJoined :: Channel -> Event
- Web.Slack.Types.Event: GroupLeft :: Channel -> Event
- Web.Slack.Types.Event: GroupMarked :: ChannelId -> SlackTimeStamp -> Event
- Web.Slack.Types.Event: GroupOpen :: UserId -> ChannelId -> Event
- Web.Slack.Types.Event: GroupRename :: ChannelRenameInfo -> Event
- Web.Slack.Types.Event: GroupUnarchive :: ChannelId -> Event
- Web.Slack.Types.Event: Hello :: Event
- Web.Slack.Types.Event: HiddenMessage :: ChannelId -> Submitter -> SlackTimeStamp -> Maybe Subtype -> Event
- Web.Slack.Types.Event: ImClose :: UserId -> IMId -> Event
- Web.Slack.Types.Event: ImCreated :: UserId -> IM -> Event
- Web.Slack.Types.Event: ImHistoryChanged :: SlackTimeStamp -> SlackTimeStamp -> SlackTimeStamp -> Event
- Web.Slack.Types.Event: ImMarked :: IMId -> SlackTimeStamp -> Event
- Web.Slack.Types.Event: ImOpen :: UserId -> IMId -> Event
- Web.Slack.Types.Event: ManualPresenceChange :: Presence -> Event
- Web.Slack.Types.Event: Message :: ChannelId -> Submitter -> Text -> SlackTimeStamp -> Maybe Subtype -> Maybe Edited -> Event
- Web.Slack.Types.Event: MessageError :: Int -> SlackError -> Event
- Web.Slack.Types.Event: MessageResponse :: Int -> SlackTimeStamp -> Text -> Event
- Web.Slack.Types.Event: NoEvent :: Event
- Web.Slack.Types.Event: PinAdded :: Event
- Web.Slack.Types.Event: PinRemoved :: Event
- Web.Slack.Types.Event: Pong :: Time -> Event
- Web.Slack.Types.Event: PrefChange :: Pref -> Event
- Web.Slack.Types.Event: PresenceChange :: UserId -> Presence -> Event
- Web.Slack.Types.Event: ReactionAdded :: UserId -> Maybe Text -> Item -> SlackTimeStamp -> Event
- Web.Slack.Types.Event: ReactionRemoved :: UserId -> Maybe Text -> Item -> SlackTimeStamp -> Event
- Web.Slack.Types.Event: ReconnectUrl :: URL -> Event
- Web.Slack.Types.Event: StarAdded :: UserId -> Item -> SlackTimeStamp -> Event
- Web.Slack.Types.Event: StarRemoved :: UserId -> Item -> SlackTimeStamp -> Event
- Web.Slack.Types.Event: StatusChange :: UserId -> Text -> SlackTimeStamp -> Event
- Web.Slack.Types.Event: TeamDomainChange :: URL -> Domain -> Event
- Web.Slack.Types.Event: TeamJoin :: User -> Event
- Web.Slack.Types.Event: TeamMigrationStarted :: Event
- Web.Slack.Types.Event: TeamPrefChange :: Pref -> Event
- Web.Slack.Types.Event: TeamRenameEvent :: Text -> Event
- Web.Slack.Types.Event: UnknownEvent :: Value -> Event
- Web.Slack.Types.Event: UserChange :: User -> Event
- Web.Slack.Types.Event: UserTyping :: ChannelId -> UserId -> Event
- Web.Slack.Types.Event.Subtype: SBotMessage :: BotId -> Maybe Username -> Maybe BotIcons -> Subtype
- Web.Slack.Types.Event.Subtype: SChannelArchive :: [UserId] -> Subtype
- Web.Slack.Types.Event.Subtype: SChannelJoin :: Maybe UserId -> Subtype
- Web.Slack.Types.Event.Subtype: SChannelLeave :: Subtype
- Web.Slack.Types.Event.Subtype: SChannelName :: Text -> Text -> Subtype
- Web.Slack.Types.Event.Subtype: SChannelPurpose :: Text -> Subtype
- Web.Slack.Types.Event.Subtype: SChannelTopic :: Text -> Subtype
- Web.Slack.Types.Event.Subtype: SChannelUnarchive :: Subtype
- Web.Slack.Types.Event.Subtype: SFileComment :: File -> Comment -> Subtype
- Web.Slack.Types.Event.Subtype: SFileMention :: File -> Subtype
- Web.Slack.Types.Event.Subtype: SFileShare :: File -> Bool -> Subtype
- Web.Slack.Types.Event.Subtype: SGroupArchive :: [UserId] -> Subtype
- Web.Slack.Types.Event.Subtype: SGroupJoin :: Maybe UserId -> Subtype
- Web.Slack.Types.Event.Subtype: SGroupLeave :: Subtype
- Web.Slack.Types.Event.Subtype: SGroupName :: Text -> Text -> Subtype
- Web.Slack.Types.Event.Subtype: SGroupPurpose :: Text -> Subtype
- Web.Slack.Types.Event.Subtype: SGroupTopic :: Text -> Subtype
- Web.Slack.Types.Event.Subtype: SGroupUnarchive :: Subtype
- Web.Slack.Types.Event.Subtype: SMeMessage :: Subtype
- Web.Slack.Types.Event.Subtype: SMessageChanged :: MessageUpdate -> Subtype
- Web.Slack.Types.Event.Subtype: SMessageDeleted :: SlackTimeStamp -> Subtype
- Web.Slack.Types.Event.Subtype: SPinnedItem :: Subtype
- Web.Slack.Types.Event.Subtype: SUnpinnedItem :: Subtype
- Web.Slack.Types.Preferences: [_prefShowMemberPresence] :: Preferences -> Bool
- Web.Slack.Types.Preferences: prefShowMemberPresence :: Lens' Preferences Bool
+ Web.Slack: SlackConfig :: String -> SlackConfig
+ Web.Slack: [_slackApiToken] :: SlackConfig -> String
+ Web.Slack: data SlackConfig
+ Web.Slack.Types.Event: [AccountsChanged] :: Event
+ Web.Slack.Types.Event: [BotAdded] :: Bot -> Event
+ Web.Slack.Types.Event: [BotChanged] :: Bot -> Event
+ Web.Slack.Types.Event: [ChannelArchive] :: ChannelId -> UserId -> Event
+ Web.Slack.Types.Event: [ChannelCreated] :: Channel -> Event
+ Web.Slack.Types.Event: [ChannelDeleted] :: ChannelId -> Event
+ Web.Slack.Types.Event: [ChannelHistoryChanged] :: SlackTimeStamp -> SlackTimeStamp -> SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [ChannelJoined] :: Channel -> Event
+ Web.Slack.Types.Event: [ChannelLeft] :: ChannelId -> Event
+ Web.Slack.Types.Event: [ChannelMarked] :: ChannelId -> SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [ChannelRename] :: ChannelRenameInfo -> Event
+ Web.Slack.Types.Event: [ChannelUnarchive] :: ChannelId -> UserId -> Event
+ Web.Slack.Types.Event: [CommandsChanged] :: SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [EmailDomainChange] :: Domain -> SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [EmojiChanged] :: SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [FileChange] :: File -> Event
+ Web.Slack.Types.Event: [FileCommentAdded] :: File -> Comment -> Event
+ Web.Slack.Types.Event: [FileCommentDeleted] :: File -> CommentId -> Event
+ Web.Slack.Types.Event: [FileCommentEdited] :: File -> Comment -> Event
+ Web.Slack.Types.Event: [FileCreated] :: File -> Event
+ Web.Slack.Types.Event: [FileDeleted] :: FileId -> SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [FilePrivate] :: FileId -> Event
+ Web.Slack.Types.Event: [FilePublic] :: FileReference -> Event
+ Web.Slack.Types.Event: [FileShared] :: FileReference -> Event
+ Web.Slack.Types.Event: [FileUnshared] :: File -> Event
+ Web.Slack.Types.Event: [GroupArchive] :: ChannelId -> Event
+ Web.Slack.Types.Event: [GroupClose] :: UserId -> ChannelId -> Event
+ Web.Slack.Types.Event: [GroupHistoryChanged] :: SlackTimeStamp -> SlackTimeStamp -> SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [GroupJoined] :: Channel -> Event
+ Web.Slack.Types.Event: [GroupLeft] :: Channel -> Event
+ Web.Slack.Types.Event: [GroupMarked] :: ChannelId -> SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [GroupOpen] :: UserId -> ChannelId -> Event
+ Web.Slack.Types.Event: [GroupRename] :: ChannelRenameInfo -> Event
+ Web.Slack.Types.Event: [GroupUnarchive] :: ChannelId -> Event
+ Web.Slack.Types.Event: [Hello] :: Event
+ Web.Slack.Types.Event: [HiddenMessage] :: ChannelId -> Submitter -> SlackTimeStamp -> Maybe Subtype -> Event
+ Web.Slack.Types.Event: [ImClose] :: UserId -> IMId -> Event
+ Web.Slack.Types.Event: [ImCreated] :: UserId -> IM -> Event
+ Web.Slack.Types.Event: [ImHistoryChanged] :: SlackTimeStamp -> SlackTimeStamp -> SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [ImMarked] :: IMId -> SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [ImOpen] :: UserId -> IMId -> Event
+ Web.Slack.Types.Event: [ManualPresenceChange] :: Presence -> Event
+ Web.Slack.Types.Event: [MessageError] :: Int -> SlackError -> Event
+ Web.Slack.Types.Event: [MessageResponse] :: Int -> SlackTimeStamp -> Text -> Event
+ Web.Slack.Types.Event: [Message] :: ChannelId -> Submitter -> Text -> SlackTimeStamp -> Maybe Subtype -> Maybe Edited -> Event
+ Web.Slack.Types.Event: [NoEvent] :: Event
+ Web.Slack.Types.Event: [PinAdded] :: Event
+ Web.Slack.Types.Event: [PinRemoved] :: Event
+ Web.Slack.Types.Event: [Pong] :: Time -> Event
+ Web.Slack.Types.Event: [PrefChange] :: Pref -> Event
+ Web.Slack.Types.Event: [PresenceChange] :: UserId -> Presence -> Event
+ Web.Slack.Types.Event: [ReactionAdded] :: UserId -> Text -> UserId -> EmbeddedItem -> SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [ReactionRemoved] :: UserId -> Maybe Text -> EmbeddedItem -> SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [ReconnectUrl] :: URL -> Event
+ Web.Slack.Types.Event: [StarAdded] :: UserId -> Item -> SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [StarRemoved] :: UserId -> Item -> SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [StatusChange] :: UserId -> Text -> SlackTimeStamp -> Event
+ Web.Slack.Types.Event: [TeamDomainChange] :: URL -> Domain -> Event
+ Web.Slack.Types.Event: [TeamJoin] :: User -> Event
+ Web.Slack.Types.Event: [TeamMigrationStarted] :: Event
+ Web.Slack.Types.Event: [TeamPrefChange] :: Pref -> Event
+ Web.Slack.Types.Event: [TeamRenameEvent] :: Text -> Event
+ Web.Slack.Types.Event: [UnknownEvent] :: Value -> Event
+ Web.Slack.Types.Event: [UserChange] :: User -> Event
+ Web.Slack.Types.Event: [UserTyping] :: ChannelId -> UserId -> Event
+ Web.Slack.Types.Event.Subtype: [SBotMessage] :: BotId -> Maybe Username -> Maybe BotIcons -> Subtype
+ Web.Slack.Types.Event.Subtype: [SChannelArchive] :: [UserId] -> Subtype
+ Web.Slack.Types.Event.Subtype: [SChannelJoin] :: Maybe UserId -> Subtype
+ Web.Slack.Types.Event.Subtype: [SChannelLeave] :: Subtype
+ Web.Slack.Types.Event.Subtype: [SChannelName] :: Text -> Text -> Subtype
+ Web.Slack.Types.Event.Subtype: [SChannelPurpose] :: Text -> Subtype
+ Web.Slack.Types.Event.Subtype: [SChannelTopic] :: Text -> Subtype
+ Web.Slack.Types.Event.Subtype: [SChannelUnarchive] :: Subtype
+ Web.Slack.Types.Event.Subtype: [SFileComment] :: File -> Comment -> Subtype
+ Web.Slack.Types.Event.Subtype: [SFileMention] :: File -> Subtype
+ Web.Slack.Types.Event.Subtype: [SFileShare] :: File -> Bool -> Subtype
+ Web.Slack.Types.Event.Subtype: [SGroupArchive] :: [UserId] -> Subtype
+ Web.Slack.Types.Event.Subtype: [SGroupJoin] :: Maybe UserId -> Subtype
+ Web.Slack.Types.Event.Subtype: [SGroupLeave] :: Subtype
+ Web.Slack.Types.Event.Subtype: [SGroupName] :: Text -> Text -> Subtype
+ Web.Slack.Types.Event.Subtype: [SGroupPurpose] :: Text -> Subtype
+ Web.Slack.Types.Event.Subtype: [SGroupTopic] :: Text -> Subtype
+ Web.Slack.Types.Event.Subtype: [SGroupUnarchive] :: Subtype
+ Web.Slack.Types.Event.Subtype: [SMeMessage] :: Subtype
+ Web.Slack.Types.Event.Subtype: [SMessageChanged] :: MessageUpdate -> Subtype
+ Web.Slack.Types.Event.Subtype: [SMessageDeleted] :: SlackTimeStamp -> Subtype
+ Web.Slack.Types.Event.Subtype: [SPinnedItem] :: Subtype
+ Web.Slack.Types.Event.Subtype: [SUnpinnedItem] :: Subtype
+ Web.Slack.Types.IM: channelToIM :: ChannelId -> IMId
+ Web.Slack.Types.IM: imToChannel :: IMId -> ChannelId
+ Web.Slack.Types.Id: instance Data.Hashable.Class.Hashable (Web.Slack.Types.Id.Id a)
+ Web.Slack.Types.Id: instance GHC.Classes.Ord (Web.Slack.Types.Id.Id a)
+ Web.Slack.Types.Id: instance GHC.Generics.Generic (Web.Slack.Types.Id.Id a)
+ Web.Slack.Types.Item: EmbeddedFileCommentItem :: FileId -> CommentId -> EmbeddedItem
+ Web.Slack.Types.Item: EmbeddedFileItem :: FileId -> EmbeddedItem
+ Web.Slack.Types.Item: EmbeddedMessageItem :: ChannelId -> SlackTimeStamp -> EmbeddedItem
+ Web.Slack.Types.Item: data EmbeddedItem
+ Web.Slack.Types.Item: instance Data.Aeson.Types.Class.FromJSON Web.Slack.Types.Item.EmbeddedItem
+ Web.Slack.Types.Item: instance GHC.Show.Show Web.Slack.Types.Item.EmbeddedItem
+ Web.Slack.Types.Message: Attachment :: Text -> AttachmentColor -> Maybe Text -> Maybe Text -> Maybe URL -> Maybe URL -> Maybe Text -> Maybe URL -> Maybe Text -> [Field] -> Maybe URL -> Maybe URL -> Maybe Text -> Maybe URL -> Maybe POSIXTime -> Attachment
+ Web.Slack.Types.Message: CustomColor :: Text -> AttachmentColor
+ Web.Slack.Types.Message: DangerColor :: AttachmentColor
+ Web.Slack.Types.Message: DefaultColor :: AttachmentColor
+ Web.Slack.Types.Message: Field :: Maybe Text -> Text -> Bool -> Field
+ Web.Slack.Types.Message: GoodColor :: AttachmentColor
+ Web.Slack.Types.Message: MessagePayload :: Int -> Text -> ChannelId -> Text -> MessagePayload
+ Web.Slack.Types.Message: PingPayload :: Int -> Text -> Int -> PingPayload
+ Web.Slack.Types.Message: WarningColor :: AttachmentColor
+ Web.Slack.Types.Message: [attachmentAuthorIcon] :: Attachment -> Maybe URL
+ Web.Slack.Types.Message: [attachmentAuthorLink] :: Attachment -> Maybe URL
+ Web.Slack.Types.Message: [attachmentAuthorName] :: Attachment -> Maybe Text
+ Web.Slack.Types.Message: [attachmentColor] :: Attachment -> AttachmentColor
+ Web.Slack.Types.Message: [attachmentFallback] :: Attachment -> Text
+ Web.Slack.Types.Message: [attachmentFields] :: Attachment -> [Field]
+ Web.Slack.Types.Message: [attachmentFooterIcon] :: Attachment -> Maybe URL
+ Web.Slack.Types.Message: [attachmentFooter] :: Attachment -> Maybe Text
+ Web.Slack.Types.Message: [attachmentImageUrl] :: Attachment -> Maybe URL
+ Web.Slack.Types.Message: [attachmentPretext] :: Attachment -> Maybe Text
+ Web.Slack.Types.Message: [attachmentText] :: Attachment -> Maybe Text
+ Web.Slack.Types.Message: [attachmentThumbUrl] :: Attachment -> Maybe URL
+ Web.Slack.Types.Message: [attachmentTitleLink] :: Attachment -> Maybe URL
+ Web.Slack.Types.Message: [attachmentTitle] :: Attachment -> Maybe Text
+ Web.Slack.Types.Message: [attachmentTs] :: Attachment -> Maybe POSIXTime
+ Web.Slack.Types.Message: [fieldShort] :: Field -> Bool
+ Web.Slack.Types.Message: [fieldTitle] :: Field -> Maybe Text
+ Web.Slack.Types.Message: [fieldValue] :: Field -> Text
+ Web.Slack.Types.Message: [messageChannel] :: MessagePayload -> ChannelId
+ Web.Slack.Types.Message: [messageId] :: MessagePayload -> Int
+ Web.Slack.Types.Message: [messageText] :: MessagePayload -> Text
+ Web.Slack.Types.Message: [messageType] :: MessagePayload -> Text
+ Web.Slack.Types.Message: [pingId] :: PingPayload -> Int
+ Web.Slack.Types.Message: [pingTimestamp] :: PingPayload -> Int
+ Web.Slack.Types.Message: [pingType] :: PingPayload -> Text
+ Web.Slack.Types.Message: data Attachment
+ Web.Slack.Types.Message: data AttachmentColor
+ Web.Slack.Types.Message: data Field
+ Web.Slack.Types.Message: data MessagePayload
+ Web.Slack.Types.Message: data PingPayload
+ Web.Slack.Types.Message: defaultAttachment :: Attachment
+ Web.Slack.Types.Message: instance Data.Aeson.Types.Class.ToJSON Web.Slack.Types.Message.Attachment
+ Web.Slack.Types.Message: instance Data.Aeson.Types.Class.ToJSON Web.Slack.Types.Message.AttachmentColor
+ Web.Slack.Types.Message: instance Data.Aeson.Types.Class.ToJSON Web.Slack.Types.Message.Field
+ Web.Slack.Types.Message: instance Data.Aeson.Types.Class.ToJSON Web.Slack.Types.Message.MessagePayload
+ Web.Slack.Types.Message: instance Data.Aeson.Types.Class.ToJSON Web.Slack.Types.Message.PingPayload
+ Web.Slack.Types.Message: instance GHC.Generics.Generic Web.Slack.Types.Message.AttachmentColor
+ Web.Slack.Types.Message: instance GHC.Show.Show Web.Slack.Types.Message.MessagePayload
+ Web.Slack.Types.Message: instance GHC.Show.Show Web.Slack.Types.Message.PingPayload
+ Web.Slack.WebAPI: SlackConfig :: String -> SlackConfig
+ Web.Slack.WebAPI: [_slackApiToken] :: SlackConfig -> String
+ Web.Slack.WebAPI: chat_postMessage :: (MonadError Text m, MonadIO m) => SlackConfig -> ChannelId -> Text -> [Attachment] -> m ()
+ Web.Slack.WebAPI: data SlackConfig
+ Web.Slack.WebAPI: instance GHC.Show.Show Web.Slack.WebAPI.SlackConfig
+ Web.Slack.WebAPI: makeSlackCall :: (MonadError Text m, MonadIO m) => SlackConfig -> String -> (Options -> Options) -> m Value
+ Web.Slack.WebAPI: rtm_start :: (MonadError Text m, MonadIO m) => SlackConfig -> m (Text, SlackSession)
- Web.Slack: runBot :: SlackConfig -> SlackBot s -> s -> IO ()
+ Web.Slack: runBot :: forall s. SlackConfig -> SlackBot s -> s -> IO ()
- Web.Slack: session :: Lens' (SlackState s_a5RO0) SlackSession
+ Web.Slack: session :: forall s_a4p4D. Lens' (SlackState s_a4p4D) SlackSession
- Web.Slack: userState :: Lens (SlackState s_a5RO0) (SlackState s_a5SWs) s_a5RO0 s_a5SWs
+ Web.Slack: userState :: forall s_a4p4D s_a4pRN. Lens (SlackState s_a4p4D) (SlackState s_a4pRN) s_a4p4D s_a4pRN
- Web.Slack.State: config :: Lens' (SlackState s_a5RO0) SlackConfig
+ Web.Slack.State: config :: forall s_a4p4D. Lens' (SlackState s_a4p4D) SlackConfig
- Web.Slack.State: meta :: Lens' (SlackState s_a5RO0) Metainfo
+ Web.Slack.State: meta :: forall s_a4p4D. Lens' (SlackState s_a4p4D) Metainfo
- Web.Slack.State: session :: Lens' (SlackState s_a5RO0) SlackSession
+ Web.Slack.State: session :: forall s_a4p4D. Lens' (SlackState s_a4p4D) SlackSession
- Web.Slack.State: userState :: Lens (SlackState s_a5RO0) (SlackState s_a5SWs) s_a5RO0 s_a5SWs
+ Web.Slack.State: userState :: forall s_a4p4D s_a4pRN. Lens (SlackState s_a4p4D) (SlackState s_a4pRN) s_a4p4D s_a4pRN
- Web.Slack.Types.Event: _ReactionAdded :: Prism' Event (UserId, Maybe Text, Item, SlackTimeStamp)
+ Web.Slack.Types.Event: _ReactionAdded :: Prism' Event (UserId, Text, UserId, EmbeddedItem, SlackTimeStamp)
- Web.Slack.Types.Event: _ReactionRemoved :: Prism' Event (UserId, Maybe Text, Item, SlackTimeStamp)
+ Web.Slack.Types.Event: _ReactionRemoved :: Prism' Event (UserId, Maybe Text, EmbeddedItem, SlackTimeStamp)
- Web.Slack.Types.Id: getId :: Iso (Id a_a1Nmo) (Id a_a1Nq8) Text Text
+ Web.Slack.Types.Id: getId :: forall a_a1jm9 a_a1k0I. Iso (Id a_a1jm9) (Id a_a1k0I) Text Text
- Web.Slack.Types.Preferences: Preferences :: Text -> Text -> Bool -> Bool -> Maybe Text -> Bool -> Bool -> Bool -> Int -> Text -> Text -> Text -> Text -> Int -> Bool -> Bool -> Bool -> Bool -> Text -> Text -> Text -> Bool -> Text -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Text -> Bool -> Bool -> Bool -> Text -> Text -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Text -> Bool -> Bool -> Bool -> Bool -> Text -> Int -> Bool -> Bool -> Bool -> Bool -> Bool -> Text -> Bool -> Bool -> Bool -> Bool -> Text -> Text -> Bool -> Bool -> Bool -> Text -> Int -> Text -> Bool -> Bool -> Bool -> Text -> Bool -> Bool -> Preferences
+ Web.Slack.Types.Preferences: Preferences :: Text -> Text -> Bool -> Bool -> Maybe Text -> Bool -> Bool -> Bool -> Int -> Text -> Text -> Text -> Text -> Int -> Bool -> Bool -> Bool -> Bool -> Text -> Text -> Text -> Text -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Text -> Bool -> Bool -> Bool -> Text -> Text -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Text -> Bool -> Bool -> Bool -> Bool -> Text -> Int -> Bool -> Bool -> Bool -> Bool -> Bool -> Text -> Bool -> Bool -> Bool -> Bool -> Text -> Text -> Bool -> Bool -> Bool -> Text -> Int -> Text -> Bool -> Bool -> Bool -> Text -> Bool -> Bool -> Preferences

Files

+ example/CounterBot.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE TemplateHaskell, CPP #-}+module Main where++import qualified Data.Text as T (pack)++import Web.Slack+import Web.Slack.Message++import System.Environment (lookupEnv)+import Data.Maybe (fromMaybe)+#if !MIN_VERSION_base(4,8,0)+import Control.Applicative+#endif++import Control.Lens++myConfig :: String -> SlackConfig+myConfig apiToken = SlackConfig+         { _slackApiToken = apiToken+         }++data CounterState = CounterState+                  { _messageCount :: Int+                  }++makeLenses ''CounterState++-- Count how many messages the bot recieves+counterBot :: SlackBot CounterState+counterBot (Message cid _ _ _ _ _) = do+  num <- userState . messageCount <%= (+1)+  sendMessage cid (T.pack . show $ num)+counterBot _ = return ()++main :: IO ()+main = do+  apiToken <- fromMaybe (error "SLACK_API_TOKEN not set")+               <$> lookupEnv "SLACK_API_TOKEN"+  runBot (myConfig apiToken) counterBot startState+  where+    startState = CounterState 0
+ example/EchoBot.hs view
@@ -0,0 +1,29 @@+{-# LANGUAGE CPP #-}++module Main where++import Web.Slack+import Web.Slack.Message+import System.Environment (lookupEnv)+import Data.Maybe (fromMaybe)+#if !MIN_VERSION_base(4,8,0)+import Control.Applicative+#endif++myConfig :: String -> SlackConfig+myConfig apiToken = SlackConfig+         { _slackApiToken = apiToken -- Specify your API token here+         }++echoBot :: SlackBot ()+echoBot (Message cid _ msg _ _ _) = sendMessage cid msg+echoBot _ = return ()++main :: IO ()+main = do+  apiToken <- fromMaybe (error "SLACK_API_TOKEN not set")+               <$> lookupEnv "SLACK_API_TOKEN"+  runBot (myConfig apiToken) echoBot ()+++
slack-api.cabal view
@@ -1,5 +1,5 @@ Name:                slack-api-Version:             0.9+Version:             0.10 Synopsis:            Bindings to the Slack RTM API. Description:         This library provides bindings to the <https://api.slack.com/rtm Slack Real Time Messaging API>.                      Users should find it easy to program their own Slack bots using the functionality found in `Web.Slack`.@@ -20,73 +20,88 @@ Stability:           Experimental Category:            Web Build-type:          Simple-Cabal-version:       >=1.8+Cabal-version:       >=1.10 source-repository head     type: git     location: https://github.com/mpickering/slack-api.git --Library+library+  default-language:    Haskell2010   hs-source-dirs: src   ghc-options: -Wall -fno-warn-unused-do-bind-  Exposed-Modules: Web.Slack,-                   Web.Slack.Message,-                   Web.Slack.State,-                   Web.Slack.Types,-                   Web.Slack.Config-                   Web.Slack.Types.Bot,-                   Web.Slack.Types.Comment,-                   Web.Slack.Types.File,-                   Web.Slack.Types.IM,-                   Web.Slack.Types.Topic,-                   Web.Slack.Types.Channel,-                   Web.Slack.Types.Event,-                   Web.Slack.Types.Event.Subtype,-                   Web.Slack.Types.Item,-                   Web.Slack.Types.User,-                   Web.Slack.Types.ChannelOpt,-                   Web.Slack.Types.Id,-                   Web.Slack.Types.Time,-                   Web.Slack.Types.Error,-                   Web.Slack.Types.Preferences,-                   Web.Slack.Types.TeamPreferences,-                   Web.Slack.Types.Team,-                   Web.Slack.Types.Session,-                   Web.Slack.Types.Self,-                   Web.Slack.Types.Presence,-                   Web.Slack.Types.Base-+  exposed-modules:+      Web.Slack+    , Web.Slack.Message+    , Web.Slack.State+    , Web.Slack.Types+    , Web.Slack.Types.Base+    , Web.Slack.Types.Bot+    , Web.Slack.Types.Channel+    , Web.Slack.Types.ChannelOpt+    , Web.Slack.Types.Comment+    , Web.Slack.Types.Error+    , Web.Slack.Types.Event+    , Web.Slack.Types.Event.Subtype+    , Web.Slack.Types.File+    , Web.Slack.Types.IM+    , Web.Slack.Types.Id+    , Web.Slack.Types.Item+    , Web.Slack.Types.Message+    , Web.Slack.Types.Preferences+    , Web.Slack.Types.Presence+    , Web.Slack.Types.Self+    , Web.Slack.Types.Session+    , Web.Slack.Types.Team+    , Web.Slack.Types.TeamPreferences+    , Web.Slack.Types.Time+    , Web.Slack.Types.Topic+    , Web.Slack.Types.User+    , Web.Slack.WebAPI   other-modules: Web.Slack.Utils-  Build-depends:-    -- corePackages (see [cabal2nix/src/Cabal2Nix/CorePackages.hs])-    base                      >= 4.4      && < 5,-    bytestring                >= 0.9.1   && < 0.11,-    containers                >= 0.4,+  build-depends:+      -- corePackages (see [cabal2nix/src/Cabal2Nix/CorePackages.hs])+      base >= 4.4 && < 5+    , bytestring >= 0.9.1 && < 0.11+    , containers >= 0.4+      -- Normal Packages+    , aeson >= 0.8+    , errors >= 1.4 && < 3.0+    , hashable >= 1.2+    , io-streams >= 1.2+    , lens >= 4.8+    , lens-aeson >= 1.0+    , monad-loops >= 0.4+    , mtl >= 2.1+    , network >= 2.6+    , network-uri >= 2.6+    , text >= 1.2+    , time >= 1.4+    , time-locale-compat >= 0.1 && < 0.2+    , tls >= 1.3+    , transformers >= 0.3+    , websockets > 0.9+    , wreq >= 0.2+    , wuss >= 1.0 +test-suite tests+    default-language:    Haskell2010+    type: exitcode-stdio-1.0+    main-is: tests-main.hs+    ghc-options: -threaded  -Wall+    hs-source-dirs: tests .+    build-depends: slack-api, base+    other-modules: Tests.ConnectionTest -    -- Normal Packages-    websockets > 0.9,-    wreq                      >= 0.2,-    text >= 1.2,-    lens >= 4.8,-    lens-aeson >= 1.0 ,-    network >= 2.6,-    network-uri >= 2.6,-    openssl-streams >= 1.2,-    HsOpenSSL >= 0.11 ,-    io-streams >= 1.2,-    mtl >= 2.1,-    aeson >= 0.8 ,-    time-locale-compat >= 0.1 && < 0.2,-    errors >= 1.4 && < 3.0,-    monad-loops >= 0.4,-    transformers >= 0.3,-    time >= 1.4+executable example_echobot+    default-language:    Haskell2010+    ghc-options:         -Wall+    hs-source-dirs:      example+    main-is:             EchoBot.hs+    build-depends:       base, slack-api -Test-Suite tests-    Type: exitcode-stdio-1.0-    Main-Is: tests-main.hs-    ghc-options: -threaded  -Wall-    HS-Source-Dirs: tests .-    Build-Depends: slack-api, base-    Other-Modules: Tests.ConnectionTest+executable example_counterbot+    default-language:    Haskell2010+    ghc-options:         -Wall+    hs-source-dirs:      example+    main-is:             CounterBot.hs+    build-depends:       base, slack-api, text, mtl, lens
src/Web/Slack.hs view
@@ -7,6 +7,7 @@ {-# LANGUAGE ScopedTypeVariables        #-} {-# LANGUAGE TypeFamilies               #-} {-# LANGUAGE TypeSynonymInstances       #-}+{-# LANGUAGE CPP                        #-} ------------------------------------------ -- | -- This module exposes functionality to write bots which responds@@ -38,33 +39,26 @@                  , userState                  , session                  , module Web.Slack.Types-                 , module Web.Slack.Config+                 , SlackConfig(..)                  ) where +#if !MIN_VERSION_base(4,8,0) import           Control.Applicative+#endif import           Control.Lens-import Control.Monad (forever, unless)+import           Control.Monad.Except import qualified Control.Monad.State        as S-import           Control.Monad.Trans-import           Data.Aeson.Lens-import qualified Data.ByteString.Lazy       as B import qualified Data.ByteString.Lazy.Char8 as BC import qualified Data.Text                  as T-import qualified Network.Socket             as S import qualified Network.URI                as URI import qualified Network.WebSockets         as WS-import qualified Network.WebSockets.Stream  as WS-import           Network.Wreq-import qualified OpenSSL                    as SSL-import qualified OpenSSL.Session            as SSL-import qualified System.IO.Streams.Internal as StreamsIO-import qualified System.IO.Streams.SSL      as Streams  import           Data.Aeson -import           Web.Slack.Config import           Web.Slack.State import           Web.Slack.Types+import           Web.Slack.WebAPI+import           Wuss  -- | Run a `SlackBot`. The supplied bot will respond to all events sent by -- the Slack RTM API.@@ -73,45 +67,23 @@ -- to the Slack API fails. runBot :: forall s . SlackConfig -> SlackBot s -> s -> IO () runBot conf bot start = do-  r <- get rtmStartUrl-  let Just (BoolPrim ok) = r ^? responseBody . key "ok"  . _Primitive-  unless ok (do-    putStrLn "Unable to connect"-    ioError . userError . T.unpack $ r ^. responseBody . key "error" . _String)-  let Just url = r ^? responseBody . key "url" . _String-  (sessionInfo :: SlackSession) <--    case eitherDecode (r ^. responseBody) of-      Left e -> print (r ^. responseBody) >> (ioError . userError $ e)-      Right res -> return res+  (url, sessionInfo) <- crashOnError $ rtm_start conf   let partialState :: Metainfo -> SlackState s       partialState metainfo = SlackState metainfo sessionInfo start conf   putStrLn "rtm.start call successful"   case parseWebSocketUrl (T.unpack url) of     Just (host, path) ->-      SSL.withOpenSSL $ do-        ctx <- SSL.context-        is  <- S.getAddrInfo Nothing (Just host) (Just $ show port)-        let a = S.addrAddress $ head is-            f = S.addrFamily $ head is-        s <- S.socket f S.Stream S.defaultProtocol-        S.connect s a-        ssl <- SSL.connection ctx s-        SSL.connect ssl-        (i,o) <- Streams.sslToStreams ssl-        (stream :: WS.Stream) <- WS.makeStream  (StreamsIO.read i) (\b -> StreamsIO.write (B.toStrict <$> b) o )-        WS.runClientWithStream stream host path WS.defaultConnectionOptions []-          (mkBot partialState bot)+      runSecureClient host port path (mkBot partialState bot)     Nothing -> error $ "Couldn't parse WebSockets URL: " ++ T.unpack url   where-    port = 443 :: Int-    rtmStartUrl :: String-    rtmStartUrl = "https://slack.com/api/rtm.start?token="-                    ++ (conf ^. slackApiToken)+    port = 443     parseWebSocketUrl :: String -> Maybe (String, String)     parseWebSocketUrl url = do       uri  <- URI.parseURI url       name <- URI.uriRegName <$> URI.uriAuthority uri       return (name, URI.uriPath uri)+    crashOnError :: ExceptT T.Text IO a -> IO a+    crashOnError = either (ioError . userError . T.unpack) return <=< runExceptT  mkBot :: (Metainfo -> SlackState s) -> SlackBot s -> WS.ClientApp () mkBot partialState bot conn = do
− src/Web/Slack/Config.hs
@@ -1,11 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}-module Web.Slack.Config (SlackConfig(..), slackApiToken) where--import Control.Lens.TH---- | Configuration options needed to connect to the Slack API-data SlackConfig = SlackConfig-                 { _slackApiToken :: String -- ^ API Token for Bot-                 } deriving (Show)--makeLenses ''SlackConfig
src/Web/Slack/Message.hs view
@@ -7,8 +7,6 @@ import           Control.Lens import           Control.Monad.State import           Data.Aeson          (encode)-import           Data.Aeson.TH-import           Data.Char import qualified Data.Text           as T import qualified Network.WebSockets  as WS import           Web.Slack.State@@ -17,14 +15,6 @@  import Prelude -data MessagePayload = MessagePayload-                    { messageId      :: Int-                    , messageType    :: T.Text-                    , messageChannel :: ChannelId-                    , messageText    :: T.Text } deriving Show--$(deriveToJSON defaultOptions {fieldLabelModifier = map toLower . drop 7} ''MessagePayload)- -- | Send a message to the specified channel. -- -- If the message is longer than 4000 bytes then the connection will be@@ -36,14 +26,6 @@   let payload = MessagePayload uid "message" cid message   slackLog payload   liftIO $ WS.sendTextData conn (encode payload)--data PingPayload = PingPayload-                 { pingId :: Int-                 , pingType :: T.Text-                 , pingTimestamp :: Int-                 } deriving Show--$(deriveToJSON defaultOptions {fieldLabelModifier = map toLower . drop 4} ''PingPayload)  makePingPacket :: Slack s (IO ()) makePingPacket = do
src/Web/Slack/State.hs view
@@ -15,7 +15,7 @@ import qualified Control.Monad.State       as S import qualified Network.WebSockets        as WS import           Web.Slack.Types-import           Web.Slack.Config+import           Web.Slack.WebAPI (SlackConfig) import Prelude  newtype Slack s a = Slack {runSlack :: S.StateT (SlackState s) IO a}
src/Web/Slack/Types.hs view
@@ -1,45 +1,47 @@-module Web.Slack.Types (- module Web.Slack.Types.Bot,- module Web.Slack.Types.Comment,- module Web.Slack.Types.File,- module Web.Slack.Types.IM,- module Web.Slack.Types.Topic,- module Web.Slack.Types.Channel,- module Web.Slack.Types.Event.Subtype,- module Web.Slack.Types.Item,- module Web.Slack.Types.User,- module Web.Slack.Types.ChannelOpt,- module Web.Slack.Types.Event,- module Web.Slack.Types.Id,- module Web.Slack.Types.Time,- module Web.Slack.Types.Error,- module Web.Slack.Types.Preferences,- module Web.Slack.Types.TeamPreferences,- module Web.Slack.Types.Team,- module Web.Slack.Types.Self,- module Web.Slack.Types.Session,- module Web.Slack.Types.Presence,- module Web.Slack.Types.Base- ) where+module Web.Slack.Types+    ( module Web.Slack.Types.Base+    , module Web.Slack.Types.Bot+    , module Web.Slack.Types.Channel+    , module Web.Slack.Types.ChannelOpt+    , module Web.Slack.Types.Comment+    , module Web.Slack.Types.Error+    , module Web.Slack.Types.Event+    , module Web.Slack.Types.Event.Subtype+    , module Web.Slack.Types.File+    , module Web.Slack.Types.IM+    , module Web.Slack.Types.Id+    , module Web.Slack.Types.Item+    , module Web.Slack.Types.Message+    , module Web.Slack.Types.Preferences+    , module Web.Slack.Types.Presence+    , module Web.Slack.Types.Self+    , module Web.Slack.Types.Session+    , module Web.Slack.Types.Team+    , module Web.Slack.Types.TeamPreferences+    , module Web.Slack.Types.Time+    , module Web.Slack.Types.Topic+    , module Web.Slack.Types.User+    ) where  import Web.Slack.Types.Base import Web.Slack.Types.Bot-import Web.Slack.Types.Comment-import Web.Slack.Types.File-import Web.Slack.Types.IM-import Web.Slack.Types.Topic import Web.Slack.Types.Channel-import Web.Slack.Types.Event.Subtype-import Web.Slack.Types.Item-import Web.Slack.Types.User import Web.Slack.Types.ChannelOpt+import Web.Slack.Types.Comment+import Web.Slack.Types.Error import Web.Slack.Types.Event+import Web.Slack.Types.Event.Subtype+import Web.Slack.Types.File+import Web.Slack.Types.IM import Web.Slack.Types.Id-import Web.Slack.Types.Time-import Web.Slack.Types.Error+import Web.Slack.Types.Item+import Web.Slack.Types.Message import Web.Slack.Types.Preferences-import Web.Slack.Types.TeamPreferences-import Web.Slack.Types.Team+import Web.Slack.Types.Presence import Web.Slack.Types.Self import Web.Slack.Types.Session-import Web.Slack.Types.Presence+import Web.Slack.Types.Team+import Web.Slack.Types.TeamPreferences+import Web.Slack.Types.Time+import Web.Slack.Types.Topic+import Web.Slack.Types.User
src/Web/Slack/Types/Event.hs view
@@ -21,9 +21,8 @@  import Control.Lens.TH import Control.Applicative+import Control.Monad import Data.Text (Text)-import qualified Data.Text as T-import Data.Monoid import Prelude  type Domain = Text@@ -70,8 +69,8 @@   PrefChange :: Pref -> Event   UserChange :: User -> Event   TeamJoin :: User -> Event-  ReactionAdded :: UserId -> Maybe Text -> Item -> SlackTimeStamp -> Event-  ReactionRemoved :: UserId -> Maybe Text -> Item -> SlackTimeStamp -> Event+  ReactionAdded :: UserId -> Text -> UserId {- item author -} -> EmbeddedItem -> SlackTimeStamp -> Event+  ReactionRemoved :: UserId -> Maybe Text -> EmbeddedItem -> SlackTimeStamp -> Event   StarAdded :: UserId -> Item -> SlackTimeStamp -> Event   StarRemoved :: UserId -> Item -> SlackTimeStamp -> Event   EmojiChanged :: SlackTimeStamp -> Event@@ -125,7 +124,7 @@         submitter <- case subt of                       Just (SBotMessage bid _ _) -> return $ BotComment bid                       _ -> maybe System UserComment <$> v .:? "user"-        (v .: "channel") :: Parser ChannelId+        void $ (v .: "channel" :: Parser ChannelId)         hidden <- (\case {Just True -> True; _ -> False}) <$> v .:? "hidden"         if not hidden           then Message <$>  v .: "channel" <*> pure submitter  <*> v .: "text" <*> v .: "ts" <*> pure subt <*> v .:? "edited"@@ -169,7 +168,7 @@       "pref_change" -> curry PrefChange <$> v .: "name" <*> v .: "value"       "user_change" -> UserChange <$> v .: "user"       "team_join"   -> TeamJoin <$> v .: "user"-      "reaction_added" -> ReactionAdded <$> v .: "user" <*> v .:? "name" <*> v .: "item" <*> v .: "event_ts"+      "reaction_added" -> ReactionAdded <$> v .: "user" <*> v .: "reaction" <*> v .: "item_user" <*> v .: "item" <*> v .: "event_ts"       "reaction_removed" -> ReactionRemoved <$> v .: "user" <*> v .:? "name" <*> v .: "item" <*> v .: "event_ts"       "star_added" ->  StarAdded <$> v .: "user" <*> v .: "item" <*> v .: "event_ts"       "star_removed" -> StarRemoved <$> v .: "user" <*> v .: "item" <*> v .: "event_ts"
src/Web/Slack/Types/Event/Subtype.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE LambdaCase, GADTs, OverloadedStrings, TemplateHaskell #-}+{-# LANGUAGE LambdaCase, GADTs, OverloadedStrings, TemplateHaskell, CPP #-} module Web.Slack.Types.Event.Subtype (Subtype(..), subtype) where  import Data.Aeson@@ -9,7 +9,9 @@ import Web.Slack.Types.Comment import Web.Slack.Types.Bot import Web.Slack.Types.Item+#if !MIN_VERSION_base(4,8,0) import Control.Applicative+#endif import Data.Text (Text) import Control.Lens.TH 
src/Web/Slack/Types/IM.hs view
@@ -3,6 +3,7 @@  import Data.Aeson import Data.Aeson.Types+import Data.Coerce import Control.Applicative import Control.Lens.TH @@ -30,3 +31,9 @@                 <*> o .: "is_open"                 <*> o .: "is_im"                 <*> (pure $ parseMaybe parseJSON (Object o) :: Parser (Maybe ChannelOpt)))++imToChannel :: IMId -> ChannelId+imToChannel = coerce++channelToIM :: ChannelId -> IMId+channelToIM = coerce
src/Web/Slack/Types/Id.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE DataKinds, KindSignatures, TemplateHaskell #-}+{-# LANGUAGE DataKinds, KindSignatures, TemplateHaskell, DeriveGeneric #-} module Web.Slack.Types.Id   ( UserId,     BotId,@@ -14,10 +14,12 @@ import Data.Aeson import Data.Text (Text) import Control.Lens.TH+import Data.Hashable+import GHC.Generics  data FieldType = TUser | TBot | TChannel | TFile | TComment | TIM | TTeam deriving (Eq, Show) -newtype Id (a :: FieldType) = Id { _getId :: Text } deriving (Show, Eq)+newtype Id (a :: FieldType) = Id { _getId :: Text } deriving (Show, Eq, Ord, Generic)   instance ToJSON (Id a) where@@ -25,6 +27,8 @@  instance FromJSON (Id a) where   parseJSON = withText "Id" (return . Id)++instance Hashable (Id a)  type UserId    = Id 'TUser type BotId     = Id 'TBot
src/Web/Slack/Types/Item.hs view
@@ -20,6 +20,12 @@           | IMItem ChannelId           | GroupItem ChannelId deriving Show +data EmbeddedItem+          = EmbeddedMessageItem ChannelId SlackTimeStamp+          | EmbeddedFileItem FileId+          | EmbeddedFileCommentItem FileId CommentId+          deriving Show+ instance  FromJSON Item where   parseJSON = withObject "item" (\o -> do                 (typ :: String) <- o .: "type"@@ -31,6 +37,15 @@                   "im"      -> IMItem <$> o .: "channel"                   "group"   -> GroupItem <$> o .: "group"                   _         -> fail $ "Unrecognised item type: " ++ typ)++instance FromJSON EmbeddedItem where+  parseJSON = withObject "item" $ \o -> do+                (typ :: String) <- o .: "type"+                case typ of+                  "message"      -> EmbeddedMessageItem <$> o .: "channel" <*> o .: "ts"+                  "file"         -> EmbeddedFileItem <$> o .: "file"+                  "file_comment" -> EmbeddedFileCommentItem <$> o .: "file" <*> o .: "file_comment"+                  _              -> fail $ "Unrecognised item type: " ++ typ  data MessageUpdate = MessageUpdate                    { _messageUpdateUser   :: UserId
+ src/Web/Slack/Types/Message.hs view
@@ -0,0 +1,120 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveGeneric #-}++module Web.Slack.Types.Message where++import Data.Aeson+import Data.Aeson.TH+import qualified Data.Text as T+import Data.Time.Clock.POSIX+import GHC.Generics+import Web.Slack.Types.Base+import Web.Slack.Types.Id+import Web.Slack.Utils++data MessagePayload = MessagePayload+    { messageId      :: Int+    , messageType    :: T.Text+    , messageChannel :: ChannelId+    , messageText    :: T.Text+    } deriving (Show)++data PingPayload = PingPayload+    { pingId        :: Int+    , pingType      :: T.Text+    , pingTimestamp :: Int+    } deriving (Show)++data Attachment = Attachment+    { attachmentFallback :: T.Text+        -- ^ A plain-text summary of the attachment.+    , attachmentColor :: AttachmentColor+        -- ^ Used to color the border along the left side of the message+        -- attachment.+    , attachmentPretext :: Maybe T.Text+        -- ^ Optional text that appears above the message attachment block.+    , attachmentAuthorName :: Maybe T.Text+        -- ^ Small text used to display the author's name.+    , attachmentAuthorLink :: Maybe URL+        -- ^ A valid URL that will hyperlink the author_name text mentioned+        -- above.+    , attachmentAuthorIcon :: Maybe URL+        -- ^ A valid URL that displays a small 16x16px image to the left of+        -- the author_name text.+    , attachmentTitle :: Maybe T.Text+        -- ^ The title is displayed as larger, bold text near the top of+        -- a message attachment.+    , attachmentTitleLink :: Maybe URL+        -- ^ By passing a valid URL, the title text will be hyperlinked.+    , attachmentText :: Maybe T.Text+        -- ^ This is the main text in a message attachment, and can contain+        -- standard message markup.+    , attachmentFields :: [Field]+    , attachmentImageUrl :: Maybe URL+        -- ^ An image file that will be displayed inside a message+        -- attachment. GIF, JPEG, PNG, or BMP; scaled down to 400x500px.+    , attachmentThumbUrl :: Maybe URL+        -- ^ Displayed as a thumbnail on the right side of a message+        -- attachment. GIF, JPEG, PNG, or BMP; scaled down to 75x75px.+    , attachmentFooter :: Maybe T.Text+        -- ^ Add some brief text to help contextualize and identify an+        -- attachment.+    , attachmentFooterIcon :: Maybe URL+        -- ^ Render a small icon beside your footer text. Scaled to 16x16px.+    , attachmentTs :: Maybe POSIXTime+        -- ^ Display an additional timestamp value as part of the+        -- attachment's footer.+    }++data Field = Field+    { fieldTitle :: Maybe T.Text+        -- ^ Shown as a bold heading above the value text. It cannot+        -- contain markup and will be escaped for you.+    , fieldValue :: T.Text+        -- ^ The text value of the field. It may contain standard message+        -- markup and must be escaped as normal. May be multi-line.+    , fieldShort :: Bool+        -- ^ Whether the value is short enough to be displayed side-by-side+        -- with other values.+    }++data AttachmentColor+    = DefaultColor       -- grey+    | GoodColor          -- green+    | WarningColor       -- yellow+    | DangerColor        -- red+    | CustomColor T.Text -- hexadecimal RGB colour, eg. CustomColor "#439FE0"+    deriving (Generic)++defaultAttachment :: Attachment+defaultAttachment = Attachment+        { attachmentFallback = ""+        , attachmentColor = DefaultColor+        , attachmentPretext = Nothing+        , attachmentAuthorName = Nothing+        , attachmentAuthorLink = Nothing+        , attachmentAuthorIcon = Nothing+        , attachmentTitle = Nothing+        , attachmentTitleLink = Nothing+        , attachmentText = Nothing+        , attachmentFields = []+        , attachmentImageUrl = Nothing+        , attachmentThumbUrl = Nothing+        , attachmentFooter = Nothing+        , attachmentFooterIcon = Nothing+        , attachmentTs = Nothing+        }++instance ToJSON AttachmentColor where+    toEncoding x = toEncoding $ case x of+        DefaultColor  -> Nothing+        GoodColor     -> Just "good"+        WarningColor  -> Just "warning"+        DangerColor   -> Just "danger"+        CustomColor c -> Just c++$(deriveToJSON defaultOptions {fieldLabelModifier = toSnake . drop 7}  ''MessagePayload)+$(deriveToJSON defaultOptions {fieldLabelModifier = toSnake . drop 4}  ''PingPayload)+$(deriveToJSON defaultOptions {fieldLabelModifier = toSnake . drop 10} ''Attachment)+$(deriveToJSON defaultOptions {fieldLabelModifier = toSnake . drop 5}  ''Field)
src/Web/Slack/Types/Preferences.hs view
@@ -29,7 +29,6 @@                  , _prefLoudChannels                    :: Text                  , _prefNeverChannels                   :: Text                  , _prefLoudChannelsSet                 :: Text-                 , _prefShowMemberPresence              :: Bool                  , _prefSearchSort                      :: Text                  , _prefExpandInlineImgs                :: Bool                  , _prefExpandSnippets                  :: Bool
+ src/Web/Slack/WebAPI.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}++module Web.Slack.WebAPI+    ( SlackConfig(..)+    , makeSlackCall++      -- * Methods+    , rtm_start+    , chat_postMessage+    ) where++import Control.Lens hiding ((??))+import Control.Monad.Except+import Data.Aeson+import Data.Aeson.Lens+import qualified Data.ByteString.Lazy as BL+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Network.Wreq as W+import Web.Slack.Types++-- | Configuration options needed to connect to the Slack API+data SlackConfig = SlackConfig+   { _slackApiToken :: String -- ^ API Token for Bot+   } deriving (Show)++makeLenses ''SlackConfig++makeSlackCall+    :: (MonadError T.Text m, MonadIO m)+    => SlackConfig+    -> String+    -> (W.Options -> W.Options)+    -> m Value+makeSlackCall conf method setArgs = do+    let url = "https://slack.com/api/" ++ method+    let setToken = W.param "token" .~ [T.pack (conf ^. slackApiToken)]+    let opts = W.defaults & setToken & setArgs+    rawResp <- liftIO $ W.getWith opts url+    resp <- rawResp ^? W.responseBody . _Value ?? "Couldn't parse response"+    case resp ^? key "ok"  . _Bool of+        Just True  -> return resp+        Just False -> throwError $ resp ^. key "error" . _String+        Nothing    -> throwError "Couldn't parse key 'ok' from response"++-------------------------------------------------------------------------------+-- Methods++-- See https://api.slack.com/methods for the docs.++rtm_start+    :: (MonadError T.Text m, MonadIO m)+    => SlackConfig+    -> m (T.Text, SlackSession)+rtm_start conf = do+    resp <- makeSlackCall conf "rtm.start" id+    url <- resp ^? key "url" . _String ?? "rtm_start: No url!"+    sessionInfo <- fromJSON' resp+    return (url, sessionInfo)++chat_postMessage+    :: (MonadError T.Text m, MonadIO m)+    => SlackConfig+    -> ChannelId+    -> T.Text+    -> [Attachment]+    -> m ()+chat_postMessage conf (Id cid) msg as =+    void $ makeSlackCall conf "chat.postMessage" $+        (W.param "channel"     .~ [cid]) .+        (W.param "text"        .~ [msg]) .+        (W.param "attachments" .~ [encode' as]) .+        (W.param "as_user"     .~ ["true"])++-------------------------------------------------------------------------------+-- Helpers++encode' :: ToJSON a => a -> T.Text+encode' = T.decodeUtf8 . BL.toStrict . encode++fromJSON' :: (FromJSON a, MonadError T.Text m) => Value -> m a+fromJSON' x = case fromJSON x of+    Error e -> throwError (T.pack e)+    Success r -> return r++-- | Like '(??)' from Control.Error, but a bit more general and with the+-- right precedence.+infixl 7 ??+(??) :: MonadError e m => Maybe a -> e -> m a+x ?? e = maybe (throwError e) return x+
tests/tests-main.hs view
@@ -2,4 +2,5 @@  import qualified Tests.ConnectionTest as C +main :: IO () main = C.main