packages feed

line-bot-sdk 0.5.0.1 → 0.5.0.2

raw patch · 7 files changed

+276/−116 lines, 7 filesdep +exceptionsdep +monad-controldep +mtlPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: exceptions, monad-control, mtl, streaming, transformers-base

API changes (from Hackage documentation)

- Line.Bot.Webhook.Events: SourceGroup :: Id Group -> Maybe (Id User) -> Source
- Line.Bot.Webhook.Events: SourceRoom :: Id Room -> Maybe (Id User) -> Source
- Line.Bot.Webhook.Events: SourceUser :: Id User -> Source
- Line.Bot.Webhook.Events: instance GHC.Classes.Eq Line.Bot.Webhook.Events.Event
- Line.Bot.Webhook.Events: instance GHC.Classes.Eq Line.Bot.Webhook.Events.Events
- Line.Bot.Webhook.Events: instance GHC.Classes.Eq Line.Bot.Webhook.Events.Members
- Line.Bot.Webhook.Events: instance GHC.Classes.Eq Line.Bot.Webhook.Events.Source
+ Line.Bot.Client: broadcastMessage :: [Message] -> Line NoContent
+ Line.Bot.Client: getBroadcastMessageCount :: Day -> Line (Maybe Int)
+ Line.Bot.Client: getGroupMemberUserIds :: Id Group -> Line [Id User]
+ Line.Bot.Client: getGroupMemberUserIdsS :: Id Group -> Stream (Of (Id User)) Line ()
+ Line.Bot.Client: getMessageQuota :: Line Int
+ Line.Bot.Client: getRoomMemberUserIds :: Id Room -> Line [Id User]
+ Line.Bot.Client: getRoomMemberUserIdsS :: Id Room -> Stream (Of (Id User)) Line ()
+ Line.Bot.Client: instance Line.Bot.Client.HasLine (Line.Bot.Client.LineAuth a)
+ Line.Bot.Client: instance Line.Bot.Client.HasLine (a -> Line.Bot.Client.LineAuth b)
+ Line.Bot.Client: instance Line.Bot.Client.HasLine (a -> b -> Line.Bot.Client.LineAuth c)
+ Line.Bot.Internal.Endpoints: type BroadcastMessage = BroadcastMessage' BroadcastMessageBody
+ Line.Bot.Internal.Endpoints: type BroadcastMessage' a = "v2" :> "bot" :> "message" :> "broadcast" :> ReqBody '[JSON] a :> ChannelAuth :> PostNoContent '[JSON] NoContent
+ Line.Bot.Internal.Endpoints: type GetBroadcastMessageCount = GetBroadcastMessageCount' LineDate MessageCount
+ Line.Bot.Internal.Endpoints: type GetBroadcastMessageCount' a b = "v2" :> "bot" :> "message" :> "delivery" :> "broadcast" :> QueryParam' '[Required, Strict] "date" a :> ChannelAuth :> Get '[JSON] b
+ Line.Bot.Internal.Endpoints: type GetGroupMemberUserIds = GetGroupMemberUserIds' MemberIds
+ Line.Bot.Internal.Endpoints: type GetGroupMemberUserIds' a = "v2" :> "bot" :> "group" :> Capture "groupId" (Id Group) :> "members" :> "ids" :> QueryParam "start" String :> ChannelAuth :> Get '[JSON] a
+ Line.Bot.Internal.Endpoints: type GetMessageQuota = GetMessageQuota' MessageQuota
+ Line.Bot.Internal.Endpoints: type GetMessageQuota' a = "v2" :> "bot" :> "message" :> "quota" :> "consumption" :> ChannelAuth :> Get '[JSON] a
+ Line.Bot.Internal.Endpoints: type GetRoomMemberUserIds = GetRoomMemberUserIds' MemberIds
+ Line.Bot.Internal.Endpoints: type GetRoomMemberUserIds' a = "v2" :> "bot" :> "room" :> Capture "roomId" (Id Room) :> "members" :> "ids" :> QueryParam "start" String :> ChannelAuth :> Get '[JSON] a
+ Line.Bot.Types: BroadcastMessageBody :: [Message] -> BroadcastMessageBody
+ Line.Bot.Types: MemberIds :: [Id User] -> Maybe String -> MemberIds
+ Line.Bot.Types: MessageFlex :: Text -> Value -> Maybe QuickReply -> Message
+ Line.Bot.Types: MessageQuota :: Int -> MessageQuota
+ Line.Bot.Types: [$sel:altText:MessageText] :: Message -> Text
+ Line.Bot.Types: [$sel:contents:MessageText] :: Message -> Value
+ Line.Bot.Types: [$sel:memberIds:MemberIds] :: MemberIds -> [Id User]
+ Line.Bot.Types: [$sel:next:MemberIds] :: MemberIds -> Maybe String
+ Line.Bot.Types: [$sel:totalUsage:MessageQuota] :: MessageQuota -> Int
+ Line.Bot.Types: data MemberIds
+ Line.Bot.Types: instance Data.Aeson.Types.FromJSON.FromJSON Line.Bot.Types.MemberIds
+ Line.Bot.Types: instance Data.Aeson.Types.FromJSON.FromJSON Line.Bot.Types.MessageQuota
+ Line.Bot.Types: instance Data.Aeson.Types.ToJSON.ToJSON Line.Bot.Types.BroadcastMessageBody
+ Line.Bot.Types: instance GHC.Classes.Eq Line.Bot.Types.MemberIds
+ Line.Bot.Types: instance GHC.Classes.Eq Line.Bot.Types.MessageQuota
+ Line.Bot.Types: instance GHC.Generics.Generic Line.Bot.Types.BroadcastMessageBody
+ Line.Bot.Types: instance GHC.Generics.Generic Line.Bot.Types.MemberIds
+ Line.Bot.Types: instance GHC.Generics.Generic Line.Bot.Types.MessageQuota
+ Line.Bot.Types: instance GHC.Show.Show Line.Bot.Types.BroadcastMessageBody
+ Line.Bot.Types: instance GHC.Show.Show Line.Bot.Types.MemberIds
+ Line.Bot.Types: instance GHC.Show.Show Line.Bot.Types.MessageQuota
+ Line.Bot.Types: newtype BroadcastMessageBody
+ Line.Bot.Types: newtype MessageQuota
+ Line.Bot.Webhook.Events: Source :: Id a -> Source
+ Line.Bot.Webhook.Events: instance Data.Aeson.Types.ToJSON.ToJSON Line.Bot.Webhook.Events.Source
- Line.Bot.Client: type Line = ReaderT Auth ClientM
+ Line.Bot.Client: type Line = ReaderT ChannelToken ClientM
- Line.Bot.Internal.Endpoints: type GetContent = ChannelAuth :> "v2" :> "bot" :> "message" :> Capture "messageId" MessageId :> "content" :> Get '[OctetStream] ByteString
+ Line.Bot.Internal.Endpoints: type GetContent = "v2" :> "bot" :> "message" :> Capture "messageId" MessageId :> "content" :> ChannelAuth :> Get '[OctetStream] ByteString
- Line.Bot.Internal.Endpoints: type GetContentStream = ChannelAuth :> "v2" :> "bot" :> "message" :> Capture "messageId" MessageId :> "content" :> StreamGet NoFraming OctetStream (SourceIO ByteString)
+ Line.Bot.Internal.Endpoints: type GetContentStream = "v2" :> "bot" :> "message" :> Capture "messageId" MessageId :> "content" :> ChannelAuth :> StreamGet NoFraming OctetStream (SourceIO ByteString)
- Line.Bot.Internal.Endpoints: type GetGroupMemberProfile' a = ChannelAuth :> "v2" :> "bot" :> "group" :> Capture "groupId" (Id Group) :> "member" :> Capture "userId" (Id User) :> Get '[JSON] a
+ Line.Bot.Internal.Endpoints: type GetGroupMemberProfile' a = "v2" :> "bot" :> "group" :> Capture "groupId" (Id Group) :> "member" :> Capture "userId" (Id User) :> ChannelAuth :> Get '[JSON] a
- Line.Bot.Internal.Endpoints: type GetMulticastMessageCount' a b = ChannelAuth :> "v2" :> "bot" :> "message" :> "delivery" :> "multicast" :> QueryParam' '[Required, Strict] "date" a :> Get '[JSON] b
+ Line.Bot.Internal.Endpoints: type GetMulticastMessageCount' a b = "v2" :> "bot" :> "message" :> "delivery" :> "multicast" :> QueryParam' '[Required, Strict] "date" a :> ChannelAuth :> Get '[JSON] b
- Line.Bot.Internal.Endpoints: type GetProfile' a = ChannelAuth :> "v2" :> "bot" :> "profile" :> Capture "userId" (Id User) :> Get '[JSON] a
+ Line.Bot.Internal.Endpoints: type GetProfile' a = "v2" :> "bot" :> "profile" :> Capture "userId" (Id User) :> ChannelAuth :> Get '[JSON] a
- Line.Bot.Internal.Endpoints: type GetPushMessageCount' a b = ChannelAuth :> "v2" :> "bot" :> "message" :> "delivery" :> "push" :> QueryParam' '[Required, Strict] "date" a :> Get '[JSON] b
+ Line.Bot.Internal.Endpoints: type GetPushMessageCount' a b = "v2" :> "bot" :> "message" :> "delivery" :> "push" :> QueryParam' '[Required, Strict] "date" a :> ChannelAuth :> Get '[JSON] b
- Line.Bot.Internal.Endpoints: type GetReplyMessageCount' a b = ChannelAuth :> "v2" :> "bot" :> "message" :> "delivery" :> "reply" :> QueryParam' '[Required, Strict] "date" a :> Get '[JSON] b
+ Line.Bot.Internal.Endpoints: type GetReplyMessageCount' a b = "v2" :> "bot" :> "message" :> "delivery" :> "reply" :> QueryParam' '[Required, Strict] "date" a :> ChannelAuth :> Get '[JSON] b
- Line.Bot.Internal.Endpoints: type GetRoomMemberProfile' a = ChannelAuth :> "v2" :> "bot" :> "room" :> Capture "roomId" (Id Room) :> "member" :> Capture "userId" (Id User) :> Get '[JSON] a
+ Line.Bot.Internal.Endpoints: type GetRoomMemberProfile' a = "v2" :> "bot" :> "room" :> Capture "roomId" (Id Room) :> "member" :> Capture "userId" (Id User) :> ChannelAuth :> Get '[JSON] a
- Line.Bot.Internal.Endpoints: type IssueChannelToken' a b = ReqBody '[FormUrlEncoded] a :> "v2" :> "oauth" :> "accessToken" :> Post '[JSON] b
+ Line.Bot.Internal.Endpoints: type IssueChannelToken' a b = "v2" :> "oauth" :> "accessToken" :> ReqBody '[FormUrlEncoded] a :> Post '[JSON] b
- Line.Bot.Internal.Endpoints: type IssueLinkToken' a = ChannelAuth :> "v2" :> "bot" :> "user" :> Capture "userId" (Id User) :> "linkToken" :> Get '[JSON] a
+ Line.Bot.Internal.Endpoints: type IssueLinkToken' a = "v2" :> "bot" :> "user" :> Capture "userId" (Id User) :> "linkToken" :> ChannelAuth :> Get '[JSON] a
- Line.Bot.Internal.Endpoints: type LeaveGroup = ChannelAuth :> "v2" :> "bot" :> "group" :> Capture "groupId" (Id Group) :> "leave" :> PostNoContent '[JSON] NoContent
+ Line.Bot.Internal.Endpoints: type LeaveGroup = "v2" :> "bot" :> "group" :> Capture "groupId" (Id Group) :> "leave" :> ChannelAuth :> PostNoContent '[JSON] NoContent
- Line.Bot.Internal.Endpoints: type LeaveRoom = ChannelAuth :> "v2" :> "bot" :> "room" :> Capture "roomId" (Id Room) :> "leave" :> PostNoContent '[JSON] NoContent
+ Line.Bot.Internal.Endpoints: type LeaveRoom = "v2" :> "bot" :> "room" :> Capture "roomId" (Id Room) :> "leave" :> ChannelAuth :> PostNoContent '[JSON] NoContent
- Line.Bot.Internal.Endpoints: type MulticastMessage' a = ChannelAuth :> "v2" :> "bot" :> "message" :> "multicast" :> ReqBody '[JSON] a :> PostNoContent '[JSON] NoContent
+ Line.Bot.Internal.Endpoints: type MulticastMessage' a = "v2" :> "bot" :> "message" :> "multicast" :> ReqBody '[JSON] a :> ChannelAuth :> PostNoContent '[JSON] NoContent
- Line.Bot.Internal.Endpoints: type PushMessage' a = ChannelAuth :> "v2" :> "bot" :> "message" :> "push" :> ReqBody '[JSON] a :> PostNoContent '[JSON] NoContent
+ Line.Bot.Internal.Endpoints: type PushMessage' a = "v2" :> "bot" :> "message" :> "push" :> ReqBody '[JSON] a :> ChannelAuth :> PostNoContent '[JSON] NoContent
- Line.Bot.Internal.Endpoints: type ReplyMessage' a = ChannelAuth :> "v2" :> "bot" :> "message" :> "reply" :> ReqBody '[JSON] a :> PostNoContent '[JSON] NoContent
+ Line.Bot.Internal.Endpoints: type ReplyMessage' a = "v2" :> "bot" :> "message" :> "reply" :> ReqBody '[JSON] a :> ChannelAuth :> PostNoContent '[JSON] NoContent
- Line.Bot.Internal.Endpoints: type RevokeChannelToken' a = ReqBody '[FormUrlEncoded] a :> "v2" :> "oauth" :> "revoke" :> PostNoContent '[JSON] NoContent
+ Line.Bot.Internal.Endpoints: type RevokeChannelToken' a = "v2" :> "oauth" :> "revoke" :> ReqBody '[FormUrlEncoded] a :> PostNoContent '[JSON] NoContent
- Line.Bot.Webhook.Events: MessageLocation :: MessageId -> Text -> Text -> Double -> Double -> Message
+ Line.Bot.Webhook.Events: MessageLocation :: MessageId -> Maybe Text -> Text -> Double -> Double -> Message
- Line.Bot.Webhook.Events: [$sel:title:MessageText] :: Message -> Text
+ Line.Bot.Webhook.Events: [$sel:title:MessageText] :: Message -> Maybe Text

Files

README.md view
@@ -41,21 +41,24 @@ ## Usage  ```haskell+{-# LANGUAGE DataKinds         #-} {-# LANGUAGE OverloadedStrings #-} -import Data.String (fromString)-import Line.Bot.Client (Line, getProfile, runLine)-import Line.Bot.Types (Profile)-import System.Environment (getEnv)+import           Data.String        (fromString)+import           Line.Bot.Client+import           Line.Bot.Types+import           System.Environment (getEnv) -profile :: Line Profile-profile = getProfile "U4af4980629..."+getProfiles :: Id Room -> Line [Profile]+getProfiles roomId = do+  userIds <- getRoomMemberUserIds roomId+  sequence $ getRoomMemberProfile roomId <$> userIds  main = do-  token <- fromString <$> getEnv "CHANNEL_TOKEN"-  result <- runLine profile token+  token  <- fromString <$> getEnv "CHANNEL_TOKEN"+  result <- runLine (getProfiles "U4af4980629...") token   case result of-    Left err -> print err+    Left err      -> print err     Right profile -> print profile ``` @@ -66,3 +69,8 @@  Please report bugs via the [github issue tracker](https://github.com/moleike/line-bot-sdk/issues).++## Acknowledgments++Thanks to the authors of [servant-github](https://hackage.haskell.org/package/servant-github), for inspiration.+
line-bot-sdk.cabal view
@@ -1,5 +1,5 @@ name:                line-bot-sdk-version:             0.5.0.1+version:             0.5.0.2 synopsis:            Haskell SDK for LINE Messaging API homepage:            https://github.com/moleike/line-bot-sdk#readme bug-reports:         https://github.com/moleike/line-bot-sdk/issues@@ -55,6 +55,11 @@                      , servant-server       >= 0.15 && < 0.16                      , wai                  >= 3.2.2 && < 3.3                      , wai-extra            >= 3.0.25 && < 3.1+                     , exceptions           >= 0.10 && < 0.11+                     , transformers-base    >= 0.4 && < 0.5+                     , mtl                  >= 2.2.2 && < 2.3+                     , monad-control        >= 1.0 && < 1.1+                     , streaming            >= 0.2 && < 0.3    default-language:    Haskell2010 
src/Line/Bot/Client.hs view
@@ -3,8 +3,11 @@ {-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE RecordWildCards       #-} {-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeFamilies          #-} {-# LANGUAGE TypeOperators         #-}+ -- | -- Module      : Line.Bot.Client -- Copyright   : (c) Alexandre Moreno, 2019@@ -22,17 +25,24 @@   -- ** Group   , getGroupMemberProfile   , leaveGroup+  , getGroupMemberUserIds+  , getGroupMemberUserIdsS   -- ** Room   , getRoomMemberProfile   , leaveRoom+  , getRoomMemberUserIds+  , getRoomMemberUserIdsS   -- ** Message   , replyMessage   , pushMessage   , multicastMessage+  , broadcastMessage   , getContent   , getPushMessageCount   , getReplyMessageCount   , getMulticastMessageCount+  , getBroadcastMessageCount+  , getMessageQuota   -- ** Account Link   , issueLinkToken   -- ** OAuth@@ -41,25 +51,28 @@   ) where +import           Control.Monad.IO.Class+import           Control.Monad.Reader import           Control.Monad.Trans.Class   (lift)-import           Control.Monad.Trans.Reader  (ReaderT, ask, runReaderT)-import           Data.ByteString.Lazy        (ByteString)+import qualified Data.ByteString.Lazy        as LB import           Data.Proxy import           Data.Time.Calendar          (Day)-import           Line.Bot.Internal.Auth+import           Line.Bot.Internal.Auth      (Auth, mkAuth) import           Line.Bot.Internal.Endpoints import           Line.Bot.Types import           Network.HTTP.Client         (newManager) import           Network.HTTP.Client.TLS     (tlsManagerSettings)-import           Servant.API+import           Servant.API                 hiding (Stream) import           Servant.Client+import           Streaming+import qualified Streaming.Prelude           as S  host :: BaseUrl host = BaseUrl Https "api.line.me" 443 ""  -- | @Line@ is the monad in which bot requests run. Contains the -- OAuth access token for a channel-type Line = ReaderT Auth ClientM+type Line = ReaderT ChannelToken ClientM  withLineEnv :: (ClientEnv -> IO a) -> IO a withLineEnv app = do@@ -72,78 +85,136 @@  -- | Runs a @Line@ computation with the given channel access token runLine :: Line a -> ChannelToken -> IO (Either ServantError a)-runLine comp = runLine' . runReaderT comp . mkAuth+runLine comp = runLine' . runReaderT comp +type LineAuth a = Auth -> ClientM a++type family AddLineAuth a :: * where+  AddLineAuth (LineAuth a) = Line a+  AddLineAuth (a -> b) = a -> AddLineAuth b++class HasLine a where+  addLineAuth :: a -> AddLineAuth a++instance HasLine (LineAuth a) where+  addLineAuth comp = ask >>= \token -> lift $ comp (mkAuth token)++instance HasLine (a -> LineAuth b) where+  addLineAuth comp = addLineAuth . comp++instance HasLine (a -> b -> LineAuth c) where+  addLineAuth comp = addLineAuth . comp++line :: (HasLine (Client ClientM api), HasClient ClientM api)+     => Proxy api -> AddLineAuth (Client ClientM api)+line api = addLineAuth (client api)+ getProfile :: Id User -> Line Profile-getProfile a = ask >>= \auth ->-  lift $ client (Proxy :: Proxy GetProfile) auth a+getProfile = line (Proxy :: Proxy GetProfile)  getGroupMemberProfile :: Id Group -> Id User -> Line Profile-getGroupMemberProfile a b = ask >>= \auth ->-  lift $ client (Proxy :: Proxy GetGroupMemberProfile) auth a b+getGroupMemberProfile = line (Proxy :: Proxy GetGroupMemberProfile)  leaveGroup :: Id Group -> Line NoContent-leaveGroup a = ask >>= \auth ->-  lift $ client (Proxy :: Proxy LeaveGroup) auth a+leaveGroup = line (Proxy :: Proxy LeaveGroup) +getGroupMemberUserIds' :: Id Group -> Maybe String -> Line MemberIds+getGroupMemberUserIds' = line (Proxy :: Proxy GetGroupMemberUserIds)++getGroupMemberUserIdsS :: Id Group -> Stream (Of (Id User)) Line ()+getGroupMemberUserIdsS gid = go gid Nothing+  where+    go gid token = do+      MemberIds{..} <- lift $ getGroupMemberUserIds' gid token+      S.each memberIds+      case next of+        Nothing -> return ()+        token'  -> go gid token'++getGroupMemberUserIds :: Id Group -> Line [Id User]+getGroupMemberUserIds = S.toList_ . getGroupMemberUserIdsS+ getRoomMemberProfile :: Id Room -> Id User -> Line Profile-getRoomMemberProfile a b = ask >>= \auth ->-  lift $ client (Proxy :: Proxy GetRoomMemberProfile) auth a b+getRoomMemberProfile = line (Proxy :: Proxy GetRoomMemberProfile)  leaveRoom :: Id Room -> Line NoContent-leaveRoom a = ask >>= \auth ->-  lift $ client (Proxy :: Proxy LeaveRoom) auth a+leaveRoom = line (Proxy :: Proxy LeaveRoom) +getRoomMemberUserIds' :: Id Room -> Maybe String -> Line MemberIds+getRoomMemberUserIds' = line (Proxy :: Proxy GetRoomMemberUserIds)++getRoomMemberUserIdsS :: Id Room -> Stream (Of (Id User)) Line ()+getRoomMemberUserIdsS gid = go gid Nothing+  where+    go gid token = do+      MemberIds{..} <- lift $ getRoomMemberUserIds' gid token+      S.each memberIds+      case next of+        Nothing -> return ()+        token'  -> go gid token'++getRoomMemberUserIds :: Id Room -> Line [Id User]+getRoomMemberUserIds = S.toList_ . getRoomMemberUserIdsS+ replyMessage' :: ReplyMessageBody -> Line NoContent-replyMessage' a = ask >>= \auth ->-  lift $ client (Proxy :: Proxy ReplyMessage) auth a+replyMessage' = line (Proxy :: Proxy ReplyMessage)  replyMessage :: ReplyToken -> [Message] -> Line NoContent replyMessage a ms = replyMessage' (ReplyMessageBody a ms)  pushMessage' :: PushMessageBody -> Line NoContent-pushMessage' a = ask >>= \auth ->-  lift $ client (Proxy :: Proxy PushMessage) auth a+pushMessage' = line (Proxy :: Proxy PushMessage)  pushMessage :: Id a -> [Message] -> Line NoContent pushMessage a ms = pushMessage' (PushMessageBody a ms)  multicastMessage' :: MulticastMessageBody -> Line NoContent-multicastMessage' a = ask >>= \auth ->-  lift $ client (Proxy :: Proxy MulticastMessage) auth a+multicastMessage' = line (Proxy :: Proxy MulticastMessage)  multicastMessage :: [Id User] -> [Message] -> Line NoContent multicastMessage a ms = multicastMessage' (MulticastMessageBody a ms) --- | TODO: this should use a streaming library for constant memory usage over large data-getContent :: MessageId -> Line ByteString-getContent a = ask >>= \auth ->-  lift $ client (Proxy :: Proxy GetContent) auth a+broadcastMessage' :: BroadcastMessageBody -> Line NoContent+broadcastMessage' = line (Proxy :: Proxy BroadcastMessage) +broadcastMessage :: [Message] -> Line NoContent+broadcastMessage = broadcastMessage' . BroadcastMessageBody++getContent :: MessageId -> Line LB.ByteString+getContent = line (Proxy :: Proxy GetContent)+ getPushMessageCount' :: LineDate -> Line MessageCount-getPushMessageCount' a = ask >>= \auth ->-  lift $ client (Proxy :: Proxy GetPushMessageCount) auth a+getPushMessageCount' = line (Proxy :: Proxy GetPushMessageCount)  getPushMessageCount :: Day -> Line (Maybe Int) getPushMessageCount = fmap count . getPushMessageCount' . LineDate  getReplyMessageCount' :: LineDate -> Line MessageCount-getReplyMessageCount' a = ask >>= \auth ->-  lift $ client (Proxy :: Proxy GetReplyMessageCount) auth a+getReplyMessageCount' = line (Proxy :: Proxy GetReplyMessageCount)  getReplyMessageCount :: Day -> Line (Maybe Int) getReplyMessageCount = fmap count . getReplyMessageCount' . LineDate  getMulticastMessageCount' :: LineDate -> Line MessageCount-getMulticastMessageCount' a = ask >>= \auth ->-  lift $ client (Proxy :: Proxy GetMulticastMessageCount) auth a+getMulticastMessageCount' = line (Proxy :: Proxy GetMulticastMessageCount)  getMulticastMessageCount :: Day -> Line (Maybe Int) getMulticastMessageCount = fmap count . getMulticastMessageCount' . LineDate +getBroadcastMessageCount' :: LineDate -> Line MessageCount+getBroadcastMessageCount' = line (Proxy :: Proxy GetBroadcastMessageCount)++getBroadcastMessageCount :: Day -> Line (Maybe Int)+getBroadcastMessageCount = fmap count . getBroadcastMessageCount' . LineDate++getMessageQuota' :: Line MessageQuota+getMessageQuota' = line (Proxy :: Proxy GetMessageQuota)++getMessageQuota :: Line Int+getMessageQuota = fmap totalUsage getMessageQuota'+ issueLinkToken :: Id User -> Line LinkToken-issueLinkToken a = ask >>= \auth ->-  lift $ client (Proxy :: Proxy IssueLinkToken) auth a+issueLinkToken = line (Proxy :: Proxy IssueLinkToken)  issueChannelToken' :: ClientCredentials -> ClientM ShortLivedChannelToken issueChannelToken' = client (Proxy :: Proxy IssueChannelToken)
src/Line/Bot/Internal/Endpoints.hs view
@@ -23,142 +23,184 @@ -- | Combinator for authenticating with the channel access token type ChannelAuth = AuthProtect "channel-access-token" -type GetProfile' a = ChannelAuth-  :> "v2" :> "bot"-  :> "profile"+type GetProfile' a =+     "v2":> "bot" :> "profile"   :> Capture "userId" (Id User)+  :> ChannelAuth   :> Get '[JSON] a  type GetProfile = GetProfile' Profile -type GetGroupMemberProfile' a = ChannelAuth-  :> "v2" :> "bot"-  :> "group"+type GetGroupMemberProfile' a =+     "v2":> "bot" :> "group"   :> Capture "groupId" (Id Group)   :> "member"   :> Capture "userId" (Id User)+  :> ChannelAuth   :> Get '[JSON] a  type GetGroupMemberProfile = GetGroupMemberProfile' Profile -type LeaveGroup = ChannelAuth-  :> "v2" :> "bot"-  :> "group"+type LeaveGroup =+     "v2":> "bot" :> "group"   :> Capture "groupId" (Id Group)   :> "leave"+  :> ChannelAuth   :> PostNoContent '[JSON] NoContent -type GetRoomMemberProfile' a = ChannelAuth-  :> "v2" :> "bot"-  :> "room"+type GetGroupMemberUserIds' a =+     "v2":> "bot" :> "group"+  :> Capture "groupId" (Id Group)+  :> "members"+  :> "ids"+  :> QueryParam "start" String+  :> ChannelAuth+  :> Get '[JSON] a++type GetGroupMemberUserIds = GetGroupMemberUserIds' MemberIds++type GetRoomMemberProfile' a =+     "v2":> "bot" :> "room"   :> Capture "roomId" (Id Room)   :> "member"   :> Capture "userId" (Id User)+  :> ChannelAuth   :> Get '[JSON] a  type GetRoomMemberProfile = GetRoomMemberProfile' Profile -type LeaveRoom = ChannelAuth-  :> "v2" :> "bot"-  :> "room"+type LeaveRoom =+     "v2":> "bot" :> "room"   :> Capture "roomId" (Id Room)   :> "leave"+  :> ChannelAuth   :> PostNoContent '[JSON] NoContent -type ReplyMessage' a = ChannelAuth-  :> "v2" :> "bot"-  :> "message"-  :> "reply"+type GetRoomMemberUserIds' a =+     "v2":> "bot" :> "room"+  :> Capture "roomId" (Id Room)+  :> "members"+  :> "ids"+  :> QueryParam "start" String+  :> ChannelAuth+  :> Get '[JSON] a++type GetRoomMemberUserIds = GetRoomMemberUserIds' MemberIds++type ReplyMessage' a =+     "v2":> "bot" :> "message" :> "reply"   :> ReqBody '[JSON] a+  :> ChannelAuth   :> PostNoContent '[JSON] NoContent  type ReplyMessage = ReplyMessage' ReplyMessageBody -type PushMessage' a = ChannelAuth-  :> "v2" :> "bot"-  :> "message"+type PushMessage' a =+     "v2":> "bot" :> "message"   :> "push"   :> ReqBody '[JSON] a+  :> ChannelAuth   :> PostNoContent '[JSON] NoContent  type PushMessage = PushMessage' PushMessageBody -type MulticastMessage' a = ChannelAuth-  :> "v2" :> "bot"-  :> "message"+type MulticastMessage' a =+     "v2":> "bot" :> "message"   :> "multicast"   :> ReqBody '[JSON] a+  :> ChannelAuth   :> PostNoContent '[JSON] NoContent  type MulticastMessage = MulticastMessage' MulticastMessageBody -type GetContent = ChannelAuth-  :> "v2" :> "bot"-  :> "message"+type BroadcastMessage' a =+     "v2":> "bot" :> "message"+  :> "broadcast"+  :> ReqBody '[JSON] a+  :> ChannelAuth+  :> PostNoContent '[JSON] NoContent++type BroadcastMessage = BroadcastMessage' BroadcastMessageBody++type GetContent =+     "v2":> "bot" :> "message"   :> Capture "messageId" MessageId   :> "content"+  :> ChannelAuth   :> Get '[OctetStream] ByteString -type GetContentStream = ChannelAuth-  :> "v2" :> "bot"-  :> "message"+type GetContentStream =+     "v2":> "bot" :> "message"   :> Capture "messageId" MessageId   :> "content"+  :> ChannelAuth   :> StreamGet NoFraming OctetStream (SourceIO ByteString) -type GetReplyMessageCount' a b = ChannelAuth-  :> "v2" :> "bot"-  :> "message"-  :> "delivery"+type GetReplyMessageCount' a b =+     "v2":> "bot" :> "message" :> "delivery"   :> "reply"   :> QueryParam' '[Required, Strict] "date" a+  :> ChannelAuth   :> Get '[JSON] b  type GetReplyMessageCount = GetReplyMessageCount' LineDate MessageCount -type GetPushMessageCount' a b = ChannelAuth-  :> "v2" :> "bot"-  :> "message"-  :> "delivery"+type GetPushMessageCount' a b =+     "v2":> "bot" :> "message" :> "delivery"   :> "push"   :> QueryParam' '[Required, Strict] "date" a+  :> ChannelAuth   :> Get '[JSON] b  type GetPushMessageCount = GetPushMessageCount' LineDate MessageCount -type GetMulticastMessageCount' a b = ChannelAuth-  :> "v2" :> "bot"-  :> "message"-  :> "delivery"+type GetMulticastMessageCount' a b =+     "v2" :> "bot" :> "message" :> "delivery"   :> "multicast"   :> QueryParam' '[Required, Strict] "date" a+  :> ChannelAuth   :> Get '[JSON] b  type GetMulticastMessageCount = GetMulticastMessageCount' LineDate MessageCount -type IssueLinkToken' a = ChannelAuth-  :> "v2" :> "bot"-  :> "user"+type GetBroadcastMessageCount' a b =+     "v2" :> "bot" :> "message" :> "delivery"+  :> "broadcast"+  :> QueryParam' '[Required, Strict] "date" a+  :> ChannelAuth+  :> Get '[JSON] b++type GetBroadcastMessageCount = GetBroadcastMessageCount' LineDate MessageCount++type GetMessageQuota' a =+     "v2":> "bot" :> "message" :> "quota"+  :> "consumption"+  :> ChannelAuth+  :> Get '[JSON] a++type GetMessageQuota = GetMessageQuota' MessageQuota++type IssueLinkToken' a =+     "v2":> "bot" :> "user"   :> Capture "userId" (Id User)   :> "linkToken"+  :> ChannelAuth   :> Get '[JSON] a  type IssueLinkToken = IssueLinkToken' LinkToken  type IssueChannelToken' a b =-  ReqBody '[FormUrlEncoded] a-  :> "v2"-  :> "oauth"+     "v2" :> "oauth"   :> "accessToken"+  :> ReqBody '[FormUrlEncoded] a   :> Post '[JSON] b  type IssueChannelToken = IssueChannelToken' ClientCredentials ShortLivedChannelToken  type RevokeChannelToken' a =-  ReqBody '[FormUrlEncoded] a-  :> "v2"-  :> "oauth"+     "v2" :> "oauth"   :> "revoke"+  :> ReqBody '[FormUrlEncoded] a   :> PostNoContent '[JSON] NoContent  type RevokeChannelToken = RevokeChannelToken' ChannelToken
src/Line/Bot/Types.hs view
@@ -33,6 +33,7 @@   , ReplyMessageBody(ReplyMessageBody)   , PushMessageBody(PushMessageBody)   , MulticastMessageBody(MulticastMessageBody)+  , BroadcastMessageBody(BroadcastMessageBody)   , Profile(..)   , QuickReply(..)   , QuickReplyButton(..)@@ -41,6 +42,8 @@   , ShortLivedChannelToken(..)   , LineDate(..)   , MessageCount(..)+  , MessageQuota(..)+  , MemberIds(..)   ) where @@ -177,6 +180,10 @@                     , longitude  :: Double                     , quickReply :: Maybe QuickReply                     }+  | MessageFlex     { altText    :: Text+                    , contents   :: Value+                    , quickReply :: Maybe QuickReply+                    }   deriving (Eq, Show, Generic)  instance ToJSON Message where@@ -242,6 +249,12 @@  instance ToJSON MulticastMessageBody +newtype BroadcastMessageBody = BroadcastMessageBody+  { messages :: [Message] }+  deriving (Show, Generic)++instance ToJSON BroadcastMessageBody+ newtype QuickReply = QuickReply   { items :: [QuickReplyButton] }   deriving (Eq, Show, Generic)@@ -333,3 +346,15 @@     count  <- o .:? "success"     status <- o .:  "status"     return MessageCount{..}++newtype MessageQuota = MessageQuota { totalUsage :: Int }+  deriving (Eq, Show, Generic)++instance FromJSON MessageQuota++data MemberIds = MemberIds+  { memberIds :: [Id User]+  , next :: Maybe String+  } deriving (Eq, Show, Generic)++instance FromJSON MemberIds
src/Line/Bot/Webhook/Events.hs view
@@ -4,9 +4,13 @@ {-# LANGUAGE ExistentialQuantification  #-} {-# LANGUAGE ExtendedDefaultRules       #-} {-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures             #-} {-# LANGUAGE OverloadedStrings          #-} {-# LANGUAGE RecordWildCards            #-}+{-# LANGUAGE StandaloneDeriving         #-}+ -- | -- Module      : Line.Bot.Webhook.Events -- Copyright   : (c) Alexandre Moreno, 2019@@ -56,7 +60,7 @@   { destination :: Id User -- ^ User ID of a bot that should receive webhook events   , events      :: [Event] -- ^ List of webhook event objects   }-  deriving (Eq, Show, Generic)+  deriving (Show, Generic)  instance FromJSON Events @@ -110,7 +114,7 @@                       , timestamp  :: EpochMilli                       , things     :: Things                       }-  deriving (Eq, Show, Generic)+  deriving (Show, Generic)  instance FromJSON Event where   parseJSON = genericParseJSON defaultOptions@@ -141,7 +145,7 @@                     , fileName  :: Text                     }   | MessageLocation { messageId :: MessageId-                    , title     :: Text+                    , title     :: Maybe Text                     , address   :: Text                     , latitude  :: Double                     , longitude :: Double@@ -189,24 +193,28 @@              . toRational              . (/ 1000) -data Source =-    SourceUser (Id User)-  | SourceGroup (Id Group) (Maybe (Id User))-  | SourceRoom (Id Room) (Maybe (Id User))-  deriving (Eq, Show)+data Source = forall a. Source (Id a) +deriving instance Show Source+ instance FromJSON Source where   parseJSON = withObject "Source" $ \o -> do     messageType <- o .: "type"     case messageType of-      "user"  -> SourceUser  <$> o .: "userId"-      "group" -> SourceGroup <$> o .: "groupId" <*> o .:? "userId"-      "room"  -> SourceRoom  <$> o .: "roomId"  <*> o .:? "userId"+      "user"  -> (Source . UserId)  <$> o .: "userId"+      "group" -> (Source . GroupId) <$> o .: "groupId"+      "room"  -> (Source . RoomId)  <$> o .: "roomId"       _       -> fail ("unknown source: " ++ messageType)  +instance ToJSON Source where+  toJSON (Source (UserId a))  = object ["type" .= "user", "userId" .= a]+  toJSON (Source (GroupId a)) = object ["type" .= "group", "groupId" .= a]+  toJSON (Source (RoomId a))  = object ["type" .= "room", "roomId" .= a]++ newtype Members = Members { members :: [Source] }-  deriving (Eq, Show, Generic)+  deriving (Show, Generic)  instance FromJSON Members 
test/Line/Bot/ClientSpec.hs view
@@ -56,13 +56,13 @@       :<|> GetGroupMemberProfile' Value       :<|> GetRoomMemberProfile' Value -getReplyMessageCountF :: Auth -> LineDate -> Free ClientF MessageCount+getReplyMessageCountF :: LineDate -> Auth -> Free ClientF MessageCount getReplyMessageCountF = F.client (Proxy :: Proxy GetReplyMessageCount) -getPushMessageCountF :: Auth -> LineDate -> Free ClientF MessageCount+getPushMessageCountF :: LineDate -> Auth -> Free ClientF MessageCount getPushMessageCountF = F.client (Proxy :: Proxy GetPushMessageCount) -getMulticastMessageCountF :: Auth -> LineDate -> Free ClientF MessageCount+getMulticastMessageCountF :: LineDate -> Auth -> Free ClientF MessageCount getMulticastMessageCountF = F.client (Proxy :: Proxy GetMulticastMessageCount)  testProfile :: Value@@ -80,10 +80,11 @@   manager <- newManager defaultManagerSettings   app $ mkClientEnv manager $ BaseUrl Http "localhost" port "" -auth = mkAuth "fake"+token :: ChannelToken+token = "fake"  runLine :: Line a -> Port -> IO (Either ServantError a)-runLine comp port = withPort port $ runClientM $ runReaderT comp auth+runLine comp port = withPort port $ runClientM $ runReaderT comp token  app :: Application app = serveWithContext (Proxy :: Proxy API) serverContext $@@ -106,15 +107,15 @@       runLine (getRoomMemberProfile "1" "1") >=> (`shouldSatisfy` isRight)    it "should send `date` query param for push message count" $ do-    let Free (RunRequest Request{..} _) = getPushMessageCountF auth date+    let Free (RunRequest Request{..} _) = getPushMessageCountF date (mkAuth token)     toList requestQueryString `shouldBe` [("date", Just "20190407")]    it "should send `date` query param for reply message count" $ do-    let Free (RunRequest Request{..} _) = getReplyMessageCountF auth date+    let Free (RunRequest Request{..} _) = getReplyMessageCountF date (mkAuth token)     toList requestQueryString `shouldBe` [("date", Just "20190407")]    it "should send `date` query param for multicast message count" $ do-    let Free (RunRequest Request{..} _) = getMulticastMessageCountF auth date+    let Free (RunRequest Request{..} _) = getMulticastMessageCountF date (mkAuth token)     toList requestQueryString `shouldBe` [("date", Just "20190407")]   where     date = LineDate $ fromGregorian 2019 4 7