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 +6/−9
- src/Web/Slack.hs +2/−304
- src/Web/Slack/Channel.hs +0/−237
- src/Web/Slack/Common.hs +2/−74
- src/Web/Slack/Conversation.hs +1/−1
- src/Web/Slack/Group.hs +0/−63
- src/Web/Slack/Im.hs +0/−62
- src/Web/Slack/Pager.hs +29/−14
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