packages feed

slack-web 0.2.1.0 → 0.3.0.0

raw patch · 8 files changed

+40/−764 lines, 8 filesdep ~aesondep ~megaparsecdep ~servantnew-uploader

Dependency ranges changed: aeson, megaparsec, servant, servant-client, servant-client-core

Files

slack-web.cabal view
@@ -1,5 +1,5 @@ name: slack-web-version: 0.2.1.0+version: 0.3.0.0  build-type: Simple cabal-version: 1.20@@ -33,12 +33,9 @@       Web.Slack       Web.Slack.Api       Web.Slack.Auth-      Web.Slack.Channel       Web.Slack.Chat       Web.Slack.Common       Web.Slack.Conversation-      Web.Slack.Group-      Web.Slack.Im       Web.Slack.MessageParser       Web.Slack.Types       Web.Slack.User@@ -46,7 +43,7 @@   other-modules:       Web.Slack.Util   build-depends:-      aeson >= 1.0 && < 1.5+      aeson >= 1.0 && < 1.6     , base >= 4.11 && < 4.15     , scientific     , containers@@ -56,15 +53,15 @@     , http-api-data >= 0.3 && < 0.5     , http-client >= 0.5 && < 0.7     , http-client-tls >= 0.3 && < 0.4-    , servant >= 0.12 && < 0.18-    , servant-client >= 0.12 && < 0.18-    , servant-client-core >= 0.12 && < 0.18+    , servant >= 0.12 && < 0.19+    , servant-client >= 0.12 && < 0.19+    , servant-client-core >= 0.12 && < 0.19     , text >= 1.2 && < 1.3     , transformers     , mtl     , time     , errors-    , megaparsec >= 5.0 && < 9+    , megaparsec >= 5.0 && < 10   default-language:       Haskell2010   ghc-options:
src/Web/Slack.hs view
@@ -19,21 +19,11 @@   , apiTest   , authTest   , chatPostMessage-  , channelsCreate   , conversationsList   , conversationsHistory   , conversationsHistoryAll   , conversationsReplies-  , channelsList-  , channelsHistory-  , groupsHistory-  , groupsList-  , historyFetchAll   , repliesFetchAll-  , imHistory-  , imList-  , mpimList-  , mpimHistory   , getUserDesc   , usersList   , userLookupByEmail@@ -56,9 +46,6 @@ -- containers import qualified Data.Map as Map --- error-import Control.Error (lastZ)- -- http-client import Network.HTTP.Client (Manager, newManager) @@ -79,11 +66,8 @@ import qualified Web.Slack.Api as Api import qualified Web.Slack.Auth as Auth import qualified Web.Slack.Conversation as Conversation-import qualified Web.Slack.Channel as Channel import qualified Web.Slack.Chat as Chat import qualified Web.Slack.Common as Common-import qualified Web.Slack.Im as Im-import qualified Web.Slack.Group as Group import qualified Web.Slack.User as User import           Web.Slack.Pager @@ -169,53 +153,11 @@       :> ReqBody '[FormUrlEncoded] Conversation.RepliesReq       :> Post '[JSON] (ResponseJSON Conversation.HistoryRsp)   :<|>-    "channels.create"-      :> AuthProtect "token"-      :> ReqBody '[FormUrlEncoded] Channel.CreateReq-      :> Post '[JSON] (ResponseJSON Channel.CreateRsp)-  :<|>-    "channels.history"-      :> AuthProtect "token"-      :> ReqBody '[FormUrlEncoded] Common.HistoryReq-      :> Post '[JSON] (ResponseJSON Common.HistoryRsp)-  :<|>-    "channels.list"-      :> AuthProtect "token"-      :> ReqBody '[FormUrlEncoded] Channel.ListReq-      :> Post '[JSON] (ResponseJSON Channel.ListRsp)-  :<|>     "chat.postMessage"       :> AuthProtect "token"       :> ReqBody '[FormUrlEncoded] Chat.PostMsgReq       :> Post '[JSON] (ResponseJSON Chat.PostMsgRsp)   :<|>-    "groups.history"-      :> AuthProtect "token"-      :> ReqBody '[FormUrlEncoded] Common.HistoryReq-      :> Post '[JSON] (ResponseJSON Common.HistoryRsp)-  :<|>-     "groups.list"-      :> AuthProtect "token"-      :> Post '[JSON] (ResponseJSON Group.ListRsp)-  :<|>-    "im.history"-      :> AuthProtect "token"-      :> ReqBody '[FormUrlEncoded] Common.HistoryReq-      :> Post '[JSON] (ResponseJSON Common.HistoryRsp)-  :<|>-    "im.list"-      :> AuthProtect "token"-      :> Post '[JSON] (ResponseJSON Im.ListRsp)-  :<|>-    "mpim.list"-      :> AuthProtect "token"-      :> Post '[JSON] (ResponseJSON Group.ListRsp)-  :<|>-    "mpim.history"-      :> AuthProtect "token"-      :> ReqBody '[FormUrlEncoded] Common.HistoryReq-      :> Post '[JSON] (ResponseJSON Common.HistoryRsp)-  :<|>     "users.list"       :> AuthProtect "token"       :> Post '[JSON] (ResponseJSON User.ListRsp)@@ -324,64 +266,6 @@  -- | ----- Create a channel.------ <https://api.slack.com/methods/channels.create>--channelsCreate-  :: (MonadReader env m, HasManager env, HasToken env, MonadIO m)-  => Channel.CreateReq-  -> m (Response Channel.CreateRsp)-channelsCreate createReq = do-  authR <- mkSlackAuthenticateReq-  run (channelsCreate_ authR createReq)--channelsCreate_-  :: AuthenticatedRequest (AuthProtect "token")-  -> Channel.CreateReq-  -> ClientM (ResponseJSON Channel.CreateRsp)---- |------ Retrieve channel list.------ <https://api.slack.com/methods/channels.list>--channelsList-  :: (MonadReader env m, HasManager env, HasToken env, MonadIO m)-  => Channel.ListReq-  -> m (Response Channel.ListRsp)-channelsList listReq = do-  authR <- mkSlackAuthenticateReq-  run (channelsList_ authR listReq)--channelsList_-  :: AuthenticatedRequest (AuthProtect "token")-  -> Channel.ListReq-  -> ClientM (ResponseJSON Channel.ListRsp)---- |------ Retrieve channel history.--- Consider using 'historyFetchAll' in combination with this function------ <https://api.slack.com/methods/channels.history>--channelsHistory-  :: (MonadReader env m, HasManager env, HasToken env, MonadIO m)-  => Common.HistoryReq-  -> m (Response Common.HistoryRsp)-channelsHistory histReq = do-  authR <- mkSlackAuthenticateReq-  run (channelsHistory_ authR histReq)--channelsHistory_-  :: AuthenticatedRequest (AuthProtect "token")-  -> Common.HistoryReq-  -> ClientM (ResponseJSON Common.HistoryRsp)---- |--- -- Send a message to a channel. -- -- <https://api.slack.com/methods/chat.postMessage>@@ -399,123 +283,9 @@   -> Chat.PostMsgReq   -> ClientM (ResponseJSON Chat.PostMsgRsp) --- |------ This method returns a list of private channels in the team that the caller--- is in and archived groups that the caller was in. The list of--- (non-deactivated) members in each private channel is also returned.------ <https://api.slack.com/methods/groups.list> -groupsList-  :: (MonadReader env m, HasManager env, HasToken env, MonadIO m)-  => m (Response Group.ListRsp)-groupsList = do-  authR <- mkSlackAuthenticateReq-  run (groupsList_ authR)--groupsList_-  :: AuthenticatedRequest (AuthProtect "token")-  -> ClientM (ResponseJSON Group.ListRsp)- -- | ----- This method returns a portion of messages/events from the specified--- private channel. To read the entire history for a private channel,--- call the method with no latest or oldest arguments, and then continue paging.--- Consider using 'historyFetchAll' in combination with this function------ <https://api.slack.com/methods/groups.history>--groupsHistory-  :: (MonadReader env m, HasManager env, HasToken env, MonadIO m)-  => Common.HistoryReq-  -> m (Response Common.HistoryRsp)-groupsHistory hisReq = do-  authR <- mkSlackAuthenticateReq-  run (groupsHistory_ authR hisReq)--groupsHistory_-  :: AuthenticatedRequest (AuthProtect "token")-  -> Common.HistoryReq-  -> ClientM (ResponseJSON Common.HistoryRsp)---- |------ Returns a list of all direct message channels that the user has------ <https://api.slack.com/methods/im.list>--imList-  :: (MonadReader env m, HasManager env, HasToken env, MonadIO m)-  => m (Response Im.ListRsp)-imList = do-  authR <- mkSlackAuthenticateReq-  run (imList_ authR)--imList_-  :: AuthenticatedRequest (AuthProtect "token")-  -> ClientM (ResponseJSON Im.ListRsp)---- |------ Retrieve direct message channel history.--- Consider using 'historyFetchAll' in combination with this function------ <https://api.slack.com/methods/im.history>--imHistory-  :: (MonadReader env m, HasManager env, HasToken env, MonadIO m)-  => Common.HistoryReq-  -> m (Response Common.HistoryRsp)-imHistory histReq = do-  authR <- mkSlackAuthenticateReq-  run (imHistory_ authR histReq)--imHistory_-  :: AuthenticatedRequest (AuthProtect "token")-  -> Common.HistoryReq-  -> ClientM (ResponseJSON Common.HistoryRsp)---- |------ Returns a list of all multiparty direct message channels that the user has------ <https://api.slack.com/methods/mpim.list>--mpimList-  :: (MonadReader env m, HasManager env, HasToken env, MonadIO m)-  => m (Response Group.ListRsp)-mpimList = do-  authR <- mkSlackAuthenticateReq-  run (mpimList_ authR)--mpimList_-  :: AuthenticatedRequest (AuthProtect "token")-  -> ClientM (ResponseJSON Group.ListRsp)---- |------ Retrieve multiparty direct message channel history.--- Consider using 'historyFetchAll' in combination with this function------ <https://api.slack.com/methods/mpim.history>--mpimHistory-  :: (MonadReader env m, HasManager env, HasToken env, MonadIO m)-  => Common.HistoryReq-  -> m (Response Common.HistoryRsp)-mpimHistory histReq = do-  authR <- mkSlackAuthenticateReq-  run (mpimHistory_ authR histReq)--mpimHistory_-  :: AuthenticatedRequest (AuthProtect "token")-  -> Common.HistoryReq-  -> ClientM (ResponseJSON Common.HistoryRsp)---- |--- -- This method returns a list of all users in the team. -- This includes deleted/deactivated users. --@@ -567,36 +337,7 @@   in     \userId -> fromMaybe (unknownUserFn userId) $ Map.lookup userId userMap --- |--- Fetch all history items between two dates. The basic calls--- 'channelsHistory', 'groupsHistory', 'imHistory' and so on--- may not return exhaustive results if there were too many--- records. You need to use 'Web.Slack.Common.historyRspHasMore' to find out--- whether you got all the data.------ This function will repeatedly call the underlying history--- function until all the data is fetched or until a call--- fails, merging the messages obtained from each call.-historyFetchAll-  :: (MonadReader env m, HasManager env, HasToken env, MonadIO m)-  => (Common.HistoryReq -> m (Response Common.HistoryRsp))-  -- ^ The request to make. Can be for instance 'conversationsHistory'...-  -> Text-  -- ^ The channel name to query-  -> Int-  -- ^ The number of entries to fetch at once.-  -> Common.SlackTimestamp-  -- ^ The oldest timestamp to fetch records from-  -> Common.SlackTimestamp-  -- ^ The newest timestamp to fetch records to-  -> m (Response Common.HistoryRsp)-  -- ^ A list merging all the history records that were fetched-  -- through the individual queries.-historyFetchAll makeReq channel count =-  commonHistoryFetchAll $ \latest oldest ->-    makeReq . Common.HistoryReq channel count latest oldest - -- | Returns an action to send a request to get the history of a conversation. -- --   To fetch all messages in the conversation, run the returned 'LoadPage' action@@ -605,7 +346,7 @@   :: (MonadReader env m, HasManager env, HasToken env, MonadIO m)   =>  Conversation.HistoryReq   -- ^ The first request to send. _NOTE_: 'Conversation.historyReqCursor' is silently ignored.-  -> m (LoadPage m [Common.Message])+  -> m (LoadPage m Common.Message)   -- ^ An action which returns a new page of messages every time called.   --   If there are no pages anymore, it returns an empty list. conversationsHistoryAll = conversationsHistoryAllBy conversationsHistory@@ -624,60 +365,17 @@   :: (MonadReader env m, HasManager env, HasToken env, MonadIO m)   =>  Conversation.RepliesReq   -- ^ The first request to send. _NOTE_: 'Conversation.repliesReqCursor' is silently ignored.-  -> m (LoadPage m [Common.Message])+  -> m (LoadPage m Common.Message)   -- ^ An action which returns a new page of messages every time called.   --   If there are no pages anymore, it returns an empty list. repliesFetchAll = repliesFetchAllBy conversationsReplies --commonHistoryFetchAll-  :: (MonadReader env m, HasManager env, HasToken env, MonadIO m)-  => (Maybe Common.SlackTimestamp -> Maybe Common.SlackTimestamp -> Bool -> m (Response Common.HistoryRsp))-  -> Common.SlackTimestamp-  -> Common.SlackTimestamp-  -> m (Response Common.HistoryRsp)-commonHistoryFetchAll makeReq oldest latest = do-  -- From slack apidoc: If there are more than 100 messages between-  -- the two timestamps then the messages returned are the ones closest to latest.-  -- In most cases an application will want the most recent messages-  -- and will page backward from there.-  ---  -- for reference (does not apply here) => If oldest is provided but not-  -- latest then the messages returned are those closest to oldest,-  -- allowing you to page forward through history if desired.-  rsp <- makeReq (Just latest) (Just oldest) False-  case rsp of-    Left _ -> return rsp-    Right (Common.HistoryRsp msgs hasMore) -> do-        let oldestReceived = Common.messageTs <$> lastZ msgs-        if not hasMore || isNothing oldestReceived-            then return rsp-            else mergeResponses msgs <$>-                 commonHistoryFetchAll makeReq oldest (fromJust oldestReceived)--mergeResponses-  :: [Common.Message]-  -> Response Common.HistoryRsp-  -> Response Common.HistoryRsp-mergeResponses _ err@(Left _) = err-mergeResponses msgs (Right rsp) =-    Right (rsp { Common.historyRspMessages = msgs ++ Common.historyRspMessages rsp })- apiTest_   :<|> authTest_   :<|> conversationsList_   :<|> conversationsHistory_   :<|> conversationsReplies_-  :<|> channelsCreate_-  :<|> channelsHistory_-  :<|> channelsList_   :<|> chatPostMessage_-  :<|> groupsHistory_-  :<|> groupsList_-  :<|> imHistory_-  :<|> imList_-  :<|> mpimList_-  :<|> mpimHistory_   :<|> usersList_   :<|> userLookupByEmail_   =
− src/Web/Slack/Channel.hs
@@ -1,237 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}--------------------------------------------------------------------------- |--- Module: Web.Slack.Channel--- Description:----------------------------------------------------------------------------------module Web.Slack.Channel-  ( Channel(..)-  , Purpose(..)-  , Topic(..)-  , CreateReq(..)-  , mkCreateReq-  , CreateRsp(..)-  , ListReq(..)-  , mkListReq-  , ListRsp(..)-  )-  where---- aeson-import Data.Aeson.TH---- base-import GHC.Generics (Generic)---- deepseq-import Control.DeepSeq (NFData)---- http-api-data-import Web.FormUrlEncoded---- slack-web-import Web.Slack.Common-import Web.Slack.Util---- text-import Data.Text (Text)----- |--------data Channel =-  Channel-    { channelId :: Text-    , channelName :: Text-    , channelCreated :: Integer-    , channelCreator :: UserId-    , channelIsArchived :: Bool-    , channelIsMember :: Bool-    , channelIsGeneral :: Bool-    , channelLastRead :: Maybe Text-    , channelLatest :: Maybe Text-    , channelUnreadCount :: Maybe Integer-    , channelUnreadCountDisplay :: Maybe Integer-    , channelMembers :: [UserId]-    , channelTopic :: Topic-    , channelPurpose :: Purpose-    }-  deriving (Eq, Generic, Show)--instance NFData Channel----- |--------data Purpose =-  Purpose-    { purposeValue :: Text-    , purposeCreator :: Text-    , purposeLastSet :: Integer-    }-  deriving (Eq, Generic, Show)--instance NFData Purpose----- |--------data Topic =-  Topic-    { topicValue :: Text-    , topicCreator :: Text-    , topicLastSet :: Integer-    }-  deriving (Eq, Generic, Show)--instance NFData Topic----- |--------$(deriveFromJSON (jsonOpts "channel") ''Channel)----- |--------$(deriveJSON (jsonOpts "purpose") ''Purpose)----- |--------$(deriveJSON (jsonOpts "topic") ''Topic)----- |--------data CreateReq =-  CreateReq-    { createReqName :: Text-    , createReqValidate :: Maybe Bool-    }-  deriving (Eq, Generic, Show)--instance NFData CreateReq----- |--------$(deriveJSON (jsonOpts "createReq") ''CreateReq)----- |--------instance ToForm CreateReq where-  toForm =-    genericToForm (formOpts "createReq")----- |--------mkCreateReq-  :: Text-  -> CreateReq-mkCreateReq name =-  CreateReq-    { createReqName = name-    , createReqValidate = Nothing-    }----- |--------data CreateRsp =-  CreateRsp-    { createRspChannel :: Channel-    }-  deriving (Eq, Generic, Show)--instance NFData CreateRsp----- |-------$(deriveFromJSON (jsonOpts "createRsp") ''CreateRsp)--data ListReq =-  ListReq-    { listReqExcludeArchived :: Maybe Bool-    , listReqExcludeMembers :: Maybe Bool-    }-  deriving (Eq, Generic, Show)--instance NFData ListReq----- |--------$(deriveJSON (jsonOpts "listReq") ''ListReq)----- |--------instance ToForm ListReq where-  toForm =-    genericToForm (formOpts "listReq")----- |--------mkListReq-  :: ListReq-mkListReq =-  ListReq-    { listReqExcludeArchived = Nothing-    , listReqExcludeMembers = Nothing-    }----- |--------data ListRsp =-  ListRsp-    { listRspChannels :: [Channel]-    }-  deriving (Eq, Generic, Show)--instance NFData ListRsp--$(deriveFromJSON (jsonOpts "listRsp") ''ListRsp)
src/Web/Slack/Common.hs view
@@ -18,17 +18,14 @@ ----------------------------------------------------------------------  module Web.Slack.Common-  ( Color(unColor)-  , UserId(unUserId)+  ( Color(..)+  , UserId(..)   , ConversationId (..)   , TeamId (..)   , Cursor (..)   , SlackTimestamp(..)   , mkSlackTimestamp   , timestampFromText-  , HistoryReq(..)-  , mkHistoryReq-  , HistoryRsp(..)   , Message(..)   , MessageType(..)   , SlackClientError(..)@@ -47,9 +44,6 @@ -- deepseq import Control.DeepSeq (NFData) --- http-api-data-import Web.HttpApiData-import Web.FormUrlEncoded  -- servant-client import Servant.Client@@ -65,61 +59,6 @@ type ClientError = ServantError #endif --- |--------data HistoryReq =-  HistoryReq-    { historyReqChannel :: Text-    , historyReqCount :: Int-    , historyReqLatest :: Maybe SlackTimestamp-    , historyReqOldest :: Maybe SlackTimestamp-    , historyReqInclusive :: Bool-    }-  deriving (Eq, Show, Generic)--instance NFData HistoryReq----- |--------$(deriveJSON (jsonOpts "historyReq") ''HistoryReq)----- |--------instance ToForm HistoryReq where-  -- can't use genericToForm because slack expects booleans as 0/1-  toForm HistoryReq{..} =-    [ ("channel", toQueryParam historyReqChannel)-    , ("count", toQueryParam historyReqCount)-    ]-      <> toQueryParamIfJust "latest" historyReqLatest-      <> toQueryParamIfJust "oldest" historyReqOldest-      <> [("inclusive", toQueryParam (if historyReqInclusive then 1 :: Int else 0))]----- |--------mkHistoryReq-  :: Text-  -> HistoryReq-mkHistoryReq channel =-  HistoryReq-    { historyReqChannel = channel-    , historyReqCount = 100-    , historyReqLatest = Nothing-    , historyReqOldest = Nothing-    , historyReqInclusive = True-    }- data MessageType = MessageTypeMessage   deriving (Eq, Show, Generic) @@ -146,17 +85,6 @@ instance NFData Message  $(deriveJSON (jsonOpts "message") ''Message)--data HistoryRsp =-  HistoryRsp-    { historyRspMessages :: [Message]-    , historyRspHasMore :: Bool-    }-  deriving (Eq, Generic, Show)--instance NFData HistoryRsp--$(deriveJSON (jsonOpts "historyRsp") ''HistoryRsp)  -- | -- Errors that can be triggered by a slack request.
src/Web/Slack/Conversation.hs view
@@ -57,7 +57,7 @@ import Web.HttpApiData  -- slack-web-import Web.Slack.Common hiding (HistoryReq, HistoryRsp, mkHistoryReq)+import Web.Slack.Common import Web.Slack.Util  -- scientific
− src/Web/Slack/Group.hs
@@ -1,63 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}--------------------------------------------------------------------------- |--- Module: Web.Slack.Group--- Description:----------------------------------------------------------------------------------module Web.Slack.Group-  ( Group(..)-  , ListRsp(..)-  )-  where---- aeson-import Data.Aeson.TH---- base-import GHC.Generics (Generic)---- deepseq-import Control.DeepSeq (NFData)---- slack-web-import Web.Slack.Common-import Web.Slack.Util---- text-import Data.Text (Text)---- time-import Data.Time.Clock.POSIX--data Group =-  Group-    { groupId :: Text-    , groupName :: Text-    , groupIsMpim :: Bool-    , groupCreated :: POSIXTime-    , groupCreator :: UserId-    , groupIsArchived :: Bool-    , groupMembers :: [UserId]-    }-  deriving (Eq, Generic, Show)--instance NFData Group--$(deriveFromJSON (jsonOpts "group") ''Group)--data ListRsp =-  ListRsp-    { listRspGroups :: [Group]-    }-  deriving (Eq, Generic, Show)--instance NFData ListRsp--$(deriveFromJSON (jsonOpts "listRsp") ''ListRsp)
− src/Web/Slack/Im.hs
@@ -1,62 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TypeApplications #-}--------------------------------------------------------------------------- |--- Module: Web.Slack.Im--- Description:----------------------------------------------------------------------------------module Web.Slack.Im-  ( Im(..)-  , ListRsp(..)-  )-  where---- aeson-import Data.Aeson.TH---- base-import GHC.Generics (Generic)---- deepseq-import Control.DeepSeq (NFData)---- slack-web-import Web.Slack.Util-import Web.Slack.Common---- text-import Data.Text (Text)---- time-import Data.Time.Clock.POSIX--data Im =-  Im-    { imId :: Text-    , imIsIm :: Bool-    , imUser :: UserId-    , imCreated :: POSIXTime-    , imIsUserDeleted :: Bool-    }-  deriving (Eq, Show, Generic)--instance NFData Im--$(deriveFromJSON (jsonOpts "im") ''Im)--data ListRsp =-  ListRsp-    { listRspIms :: [Im]-    }-  deriving (Eq, Show, Generic)--instance NFData ListRsp--$(deriveFromJSON (jsonOpts "listRsp") ''ListRsp)
src/Web/Slack/Pager.hs view
@@ -1,7 +1,7 @@-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveFoldable    #-}+{-# LANGUAGE DeriveFunctor     #-} {-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE NamedFieldPuns    #-} {-# LANGUAGE OverloadedStrings #-}  module Web.Slack.Pager@@ -9,18 +9,18 @@   , conversationsHistoryAllBy   , repliesFetchAllBy   , LoadPage+  , loadingPage   ) where  -- base-import Control.Monad (join)-import Control.Monad.IO.Class (MonadIO, liftIO)-import Data.IORef (newIORef, readIORef, writeIORef)-import Data.Maybe (isNothing)+import           Control.Monad.IO.Class (MonadIO, liftIO)+import           Data.IORef             (newIORef, readIORef, writeIORef)+import           Data.Maybe             (isNothing)  -- slack-web-import           Web.Slack.Types (Cursor)+import qualified Web.Slack.Common       as Common import qualified Web.Slack.Conversation as Conversation-import qualified Web.Slack.Common as Common+import           Web.Slack.Types        (Cursor)   -- | Public only for testing.@@ -30,8 +30,8 @@   -- ^ Response generator   -> Conversation.HistoryReq   -- ^ The first request to send. _NOTE_: 'Conversation.historyReqCursor' is silently ignored.-  -> m (LoadPage m [Common.Message])-  -- ^ An action which returns a new page of messages every time called. +  -> m (LoadPage m Common.Message)+  -- ^ An action which returns a new page of messages every time called.   --   If there are no pages anymore, it returns an empty list. conversationsHistoryAllBy sendRequest initialRequest =   genericFetchAllBy@@ -46,7 +46,7 @@   -- ^ Response generator   -> Conversation.RepliesReq   -- ^ The first request to send. _NOTE_: 'Conversation.historyReqCursor' is silently ignored.-  -> m (LoadPage m [Common.Message])+  -> m (LoadPage m Common.Message)   -- ^ An action which returns a new page of messages every time called.   --   If there are no pages anymore, it returns an empty list. repliesFetchAllBy sendRequest initialRequest =@@ -62,14 +62,29 @@ --   Every time calling the action, it performs a request with a new cursor --   to get the next page. --   If there is no more response, the action returns an empty list.-type LoadPage m a = m (Response a)+type LoadPage m a = m (Response [a])  +-- | Utility function for 'LoadPage'. Perform the 'LoadPage' action to call+--   the function with the loaded page, until an empty page is loaded.+loadingPage :: (Monad m, Monoid n) => LoadPage m a -> (Response [a] -> m n) -> m n+loadingPage loadPage usePage = go mempty+ where+  go result = do+    epage <- loadPage+    case epage of+        Right page ->+          if null page+            then return result+            else (go $!) . (result <>) =<< usePage epage+        Left e -> (result <>) <$> usePage (Left e)++ genericFetchAllBy   :: MonadIO m   => (a -> m (Response Conversation.HistoryRsp))   -> (Maybe Cursor -> a)-  -> m (LoadPage m [Common.Message])+  -> m (LoadPage m Common.Message) genericFetchAllBy sendRequest requestFromCursor = do   cursorRef <- liftIO $ newIORef Nothing