packages feed

telegram-api 0.6.3.0 → 0.7.2.0

raw patch · 22 files changed

Files

CHANGELOG.md view
@@ -1,3 +1,14 @@+## 0.7.1.0++* Added support up to Bot API v3.5: sticker sets, chats administration, live location, etc+* Added `runTelegramClient` with different than in `runClient` order of arguments. Now possible to write `runTelegramClient token manager $ do`+* Stop adding request specific responses+* Stop adding IO calls, use functions with `M` suffix, for example `runClient getWebhookInfoM`++## 0.7.0.0++* Upgraded to servant-0.11 + ## 0.6.3.0  * New fields *gif_duration* in `InlineQueryResultGif` and *mpeg4_duration* in `InlineQueryResultMpeg4Gif`.
README.md view
@@ -13,7 +13,7 @@ Inline mode is supported. Uploading stickers, documents, video, etc is not supported yet, but you can send items which are already uploaded on the Telegram servers. -**Support of [Bot-3.0 API][bot-3.0]**+**Support of [Bot-3.5 API][bot-api]**  ## Usage @@ -32,16 +32,17 @@ main = do   let token = Token "bot<token>" -- entire Token should be bot123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11   manager <- newManager tlsManagerSettings-  result <- runClient ( do+  result <- runTelegramClient token manager $ do     info <- getWebhookInfoM     let request = setWebhookRequest' "https://example.com/hook"     isSet <- setWebhookM request-    getMeM) token manager+    getMeM   print result   print "done!" ``` -### Runing IO directly+### Running IO directly (planning to depricate this option)+:warning: This method to interact with a Telegram bot is about to be depricated and all new API calls will only have their `M` versions. You can run them using `runTelegramClient` function, for example `runTelegramClient token manager $ sendMessageM message` or in example below replace `getMe token manager` with `runTelegramClient token manager getMeM` to get the same behavior.  `getMe` example @@ -89,7 +90,7 @@       print $ message_id m       print $ text m   where token = Token "bot<token>" -- entire Token should be bot123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11-        chatId = "<chat_id> or <@channelusername>"+        chatId = ChatId <chat_id> -- use ChatId 10231 or ChatChannel "<@channelusername>"         message = "text *bold* _italic_ [github](github.com/klappvisor/haskell-telegram-api)" ``` @@ -100,16 +101,16 @@  With data type constructor: ```haskell-let request = SendMessageRequest "chatId" "text" Nothing (Just True) Nothing Nothing Nothing+let request = SendMessageRequest (ChatId int64_chatId) "text" Nothing (Just True) Nothing Nothing Nothing ``` Using default instance:  ```haskell-let request = sendMessageRequest "chatId" "text" -- only with required fields+let request = sendMessageRequest (ChatId int64_chatId) "text" -- only with required fields ```  ```haskell-let request = (sendMessageRequest "chatId" "text") {+let request = (sendMessageRequest ChatId int64_chatId) "text") {   message_disable_notification = Just True -- with optional fields } ```@@ -164,4 +165,4 @@ [telegram-bot-api]: https://core.telegram.org/bots/api [servant]: https://haskell-servant.github.io/ [hspec-args]: https://hspec.github.io/running-specs.html-[bot-3.0]: https://core.telegram.org/bots/api+[bot-api]: https://core.telegram.org/bots/api
src/Servant/Client/MultipartFormData.hs view
@@ -1,38 +1,46 @@-{-# LANGUAGE DataKinds           #-}-{-# LANGUAGE DeriveDataTypeable  #-}-{-# LANGUAGE FlexibleInstances   #-}-{-# LANGUAGE OverloadedStrings   #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies        #-}-{-# LANGUAGE TypeOperators       #-}-{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE DeriveDataTypeable    #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE TypeOperators         #-}+{-# LANGUAGE UndecidableInstances  #-}  module Servant.Client.MultipartFormData   ( ToMultipartFormData (..)   , MultipartFormDataReqBody   ) where -import           Control.Exception+--import           Control.Exception import           Control.Monad-import           Control.Monad.Reader.Class-import           Control.Monad.IO.Class import           Control.Monad.Error.Class-import           Data.ByteString.Lazy       hiding (pack, filter, map, null, elem)+import           Control.Monad.IO.Class+import           Control.Monad.Reader.Class+import           Data.ByteString.Lazy                  hiding (any, elem,+                                                        filter, map, null, pack) import           Data.Proxy-import           Data.String.Conversions-import           Data.Typeable              (Typeable)-import           Network.HTTP.Client        hiding (Proxy, path)-import qualified Network.HTTP.Client        as Client+--import           Data.Foldable (toList)+import qualified Data.List.NonEmpty                    as NonEmpty+--import           Data.String.Conversions+import qualified Data.Sequence                         as Sequence+import           Data.Text                             (pack)+import           Data.Typeable                         (Typeable)+import           Network.HTTP.Client                   hiding (Proxy, path)+import qualified Network.HTTP.Client                   as Client import           Network.HTTP.Client.MultipartFormData import           Network.HTTP.Media import           Network.HTTP.Types-import qualified Network.HTTP.Types         as H-import qualified Network.HTTP.Types.Header  as HTTP+import qualified Network.HTTP.Types                    as H+import qualified Network.HTTP.Types.Header             as HTTP import           Servant.API-import           Servant.Common.BaseUrl import           Servant.Client-import           Servant.Common.Req         (Req, catchConnectionError,-                                             reqAccept, reqToRequest)+import qualified Servant.Client.Core                   as Core+import           Servant.Client.Internal.HttpClient    (catchConnectionError,+                                                        clientResponseToReponse,+                                                        requestToClientRequest)+ -- | A type that can be converted to a multipart/form-data value. class ToMultipartFormData a where   -- | Convert a Haskell value to a multipart/form-data-friendly intermediate type.@@ -42,60 +50,60 @@ data MultipartFormDataReqBody a     deriving (Typeable) -instance (ToMultipartFormData b, MimeUnrender ct a, cts' ~ (ct ': cts)-  ) => HasClient (MultipartFormDataReqBody b :> Post cts' a) where-  type Client (MultipartFormDataReqBody b :> Post cts' a)-    = b -> ClientM a-  clientWithRoute Proxy req reqData =-    let reqToRequest' req' baseurl' = do-          requestWithoutBody <- reqToRequest req' baseurl'+instance (Core.RunClient m, ToMultipartFormData b, MimeUnrender ct a, cts' ~ (ct ': cts)+  ) => HasClient m (MultipartFormDataReqBody b :> Post cts' a) where+  type Client m (MultipartFormDataReqBody b :> Post cts' a) = b-> ClientM a+  clientWithRoute _pm Proxy req reqData =+    let requestToClientRequest' req' baseurl' = do+          let requestWithoutBody = requestToClientRequest baseurl' req'           formDataBody (toMultipartFormData reqData) requestWithoutBody-    in snd <$> performRequestCT' reqToRequest' (Proxy :: Proxy ct) H.methodPost req+    in snd <$> performRequestCT' requestToClientRequest' (Proxy :: Proxy ct) H.methodPost req --- copied `performRequest` from servant-0.7.1, then modified so it takes a variant of `reqToRequest`+-- copied `performRequest` from servant-0.11, then modified so it takes a variant of `requestToClientRequest` -- as an argument.-performRequest' :: (Req -> BaseUrl -> IO Request)-               -> Method -> Req -> Manager -> BaseUrl+performRequest' :: (Core.Request -> BaseUrl -> IO Request)+               -> Method -> Core.Request                -> ClientM ( Int, ByteString, MediaType-                          , [HTTP.Header], Response ByteString)-performRequest' reqToRequest' reqMethod req manager reqHost = do-  partialRequest <- liftIO $ reqToRequest' req reqHost+                          , [HTTP.Header], Client.Response ByteString)+performRequest' requestToClientRequest' reqMethod req = do+  m <- asks manager+  reqHost <- asks baseUrl+  partialRequest <- liftIO $ requestToClientRequest' req reqHost -  let request = partialRequest { Client.method = reqMethod-                               , checkResponse = \ _request _response -> return ()-                               }+  let request = partialRequest { Client.method = reqMethod } -  eResponse <- liftIO $ catchConnectionError $ Client.httpLbs request manager+  eResponse <- liftIO $ catchConnectionError $ Client.httpLbs request m   case eResponse of     Left err ->-      throwError . ConnectionError $ SomeException err+      throwError . ConnectionError $ pack $ show err      Right response -> do       let status = Client.responseStatus response           body = Client.responseBody response           hdrs = Client.responseHeaders response           status_code = statusCode status+          coreResponse = clientResponseToReponse response       ct <- case lookup "Content-Type" $ Client.responseHeaders response of                  Nothing -> pure $ "application"//"octet-stream"                  Just t -> case parseAccept t of-                   Nothing -> throwError $ InvalidContentTypeHeader (cs t) body+                   Nothing -> throwError $ InvalidContentTypeHeader coreResponse                    Just t' -> pure t'       unless (status_code >= 200 && status_code < 300) $-        throwError $ FailureResponse status ct body+        throwError $ FailureResponse coreResponse       return (status_code, body, ct, hdrs, response) --- copied `performRequestCT` from servant-0.7.1, then modified so it takes a variant of `reqToRequest`+-- copied `performRequestCT` from servant-0.11, then modified so it takes a variant of `requestToClientRequest` -- as an argument. performRequestCT' :: MimeUnrender ct result =>-  (Req -> BaseUrl -> IO Request) ->-  Proxy ct -> Method -> Req+    (Core.Request -> BaseUrl -> IO Request)+    -> Proxy ct -> Method -> Core.Request     -> ClientM ([HTTP.Header], result)-performRequestCT' reqToRequest' ct reqMethod req = do-  let acceptCT = contentType ct-  ClientEnv manager reqHost <- ask+performRequestCT' requestToClientRequest' ct reqMethod req = do+  let acceptCTS = contentTypes ct   (_status, respBody, respCT, hdrs, _response) <--    performRequest' reqToRequest' reqMethod (req { reqAccept = [acceptCT] }) manager reqHost-  unless (matches respCT acceptCT) $ throwError $ UnsupportedContentType respCT respBody+    performRequest' requestToClientRequest' reqMethod (req { Core.requestAccept = Sequence.fromList $ NonEmpty.toList acceptCTS })+  let coreResponse = clientResponseToReponse _response+  unless (any (matches respCT) acceptCTS) $ throwError $ UnsupportedContentType respCT coreResponse   case mimeUnrender ct respBody of-    Left err -> throwError $ DecodeFailure err respCT respBody+    Left err -> throwError $ DecodeFailure (pack err) coreResponse     Right val -> return (hdrs, val)
src/Web/Telegram/API/Bot/API.hs view
@@ -7,6 +7,7 @@     module API   , runClient   , runClient'+  , runTelegramClient     -- * API   , TelegramBotAPI   , api@@ -22,9 +23,10 @@ import           Web.Telegram.API.Bot.API.Edit     as API import           Web.Telegram.API.Bot.API.Get      as API import           Web.Telegram.API.Bot.API.Messages as API+import           Web.Telegram.API.Bot.API.Payments as API import           Web.Telegram.API.Bot.API.Queries  as API+import           Web.Telegram.API.Bot.API.Stickers as API import           Web.Telegram.API.Bot.API.Updates  as API-import           Web.Telegram.API.Bot.API.Payments as API  type TelegramBotAPI =        TelegramBotMessagesAPI@@ -34,6 +36,7 @@   :<|> TelegramBotQueriesAPI   :<|> TelegramBotGetAPI   :<|> TelegramBotPaymentsAPI+  :<|> TelegramBotStickersAPI  -- | Proxy for Thelegram Bot API api :: Proxy TelegramBotAPI
src/Web/Telegram/API/Bot/API/Chats.hs view
@@ -1,15 +1,25 @@-{-# LANGUAGE DataKinds                  #-}-{-# LANGUAGE OverloadedStrings          #-}-{-# LANGUAGE TypeOperators              #-}+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators     #-}  module Web.Telegram.API.Bot.API.Chats   ( -- * Functions     kickChatMember   , kickChatMemberM+  , kickChatMemberUntilM   , leaveChat   , leaveChatM   , unbanChatMember   , unbanChatMemberM+  , restrictChatMemberM+  , promoteChatMemberM+  , exportChatInviteLinkM+  , setChatPhotoM+  , deleteChatPhotoM+  , setChatTitleM+  , setChatDescriptionM+  , pinChatMessageM+  , unpinChatMessageM   , getChat   , getChatM   , getChatAdministrators@@ -18,6 +28,8 @@   , getChatMembersCountM   , getChatMember   , getChatMemberM+  , setChatStickerSetM+  , deleteChatStickerSetM     -- * API   , TelegramBotChatsAPI   , chatsApi@@ -27,8 +39,10 @@ import           Data.Text                        (Text) import           Network.HTTP.Client              (Manager) import           Servant.API-import           Servant.Client+import           Servant.Client            hiding (Response)+import           Servant.Client.MultipartFormData import           Web.Telegram.API.Bot.API.Core+import           Web.Telegram.API.Bot.Requests import           Web.Telegram.API.Bot.Responses  -- | Telegram Bot API@@ -36,6 +50,7 @@          TelegramToken :> "kickChatMember"          :> QueryParam "chat_id" Text          :> QueryParam "user_id" Int+         :> QueryParam "until_date" Int          :> Post '[JSON] KickChatMemberResponse     :<|> TelegramToken :> "leaveChat"          :> QueryParam "chat_id" Text@@ -44,6 +59,37 @@          :> QueryParam "chat_id" Text          :> QueryParam "user_id" Int          :> Post '[JSON] UnbanChatMemberResponse+    :<|> TelegramToken :> "restrictChatMember"+         :> ReqBody '[JSON] RestrictChatMemberRequest+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "promoteChatMember"+         :> ReqBody '[JSON] PromoteChatMemberRequest+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "exportChatInviteLink"+         :> QueryParam "chat_id" Text+         :> Post '[JSON] (Response Text)+    :<|> TelegramToken :> "setChatPhoto"+         :> MultipartFormDataReqBody SetChatPhotoRequest+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "deleteChatPhoto"+         :> QueryParam "chat_id" Text+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "setChatTitle"+         :> QueryParam "chat_id" Text+         :> QueryParam "title" Text+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "setChatDescription"+         :> QueryParam "chat_id" Text+         :> QueryParam "description" Text+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "pinChatMessage"+         :> QueryParam "chat_id" Text+         :> QueryParam "message_id" Int+         :> QueryParam "disable_notification" Bool+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "unpinChatMessage"+         :> QueryParam "chat_id" Text+         :> Post '[JSON] (Response Bool)     :<|> TelegramToken :> "getChat"          :> QueryParam "chat_id" Text          :> Post '[JSON] GetChatResponse@@ -57,34 +103,73 @@          :> QueryParam "chat_id" Text          :> QueryParam "user_id" Int          :> Post '[JSON] GetChatMemberResponse+    :<|> TelegramToken :> "setChatStickerSet"+         :> QueryParam "chat_id" Text+         :> QueryParam "sticker_set_name" Text+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "deleteChatStickerSet"+         :> QueryParam "chat_id" Text+         :> Post '[JSON] (Response Bool)  -- | Proxy for Thelegram Bot API to administrate chats chatsApi :: Proxy TelegramBotChatsAPI chatsApi = Proxy -kickChatMember_            :: Token -> Maybe Text -> Maybe Int -> ClientM KickChatMemberResponse+kickChatMember_            :: Token -> Maybe Text -> Maybe Int -> Maybe Int -> ClientM KickChatMemberResponse leaveChat_                 :: Token -> Maybe Text -> ClientM LeaveChatResponse unbanChatMember_           :: Token -> Maybe Text -> Maybe Int -> ClientM UnbanChatMemberResponse+restrictChatMember_        :: Token -> RestrictChatMemberRequest -> ClientM (Response Bool)+promoteChatMember_         :: Token -> PromoteChatMemberRequest -> ClientM (Response Bool)+exportChatInviteLink_      :: Token -> Maybe Text -> ClientM (Response Text)+setChatPhoto_              :: Token -> SetChatPhotoRequest -> ClientM (Response Bool)+deleteChatPhoto_           :: Token -> Maybe Text -> ClientM (Response Bool)+setChatTitle_              :: Token -> Maybe Text -> Maybe Text -> ClientM (Response Bool)+setChatDescription_        :: Token -> Maybe Text -> Maybe Text -> ClientM (Response Bool)+pinChatMessage_            :: Token -> Maybe Text -> Maybe Int -> Maybe Bool -> ClientM (Response Bool)+unpinChatMessage_          :: Token -> Maybe Text -> ClientM (Response Bool) getChat_                   :: Token -> Maybe Text -> ClientM GetChatResponse getChatAdministrators_     :: Token -> Maybe Text -> ClientM GetChatAdministratorsResponse getChatMembersCount_       :: Token -> Maybe Text -> ClientM GetChatMembersCountResponse getChatMember_             :: Token -> Maybe Text -> Maybe Int -> ClientM GetChatMemberResponse+setChatStickerSet_         :: Token -> Maybe Text -> Maybe Text -> ClientM (Response Bool)+deleteChatStickerSet_      :: Token -> Maybe Text -> ClientM (Response Bool) kickChatMember_   :<|> leaveChat_   :<|> unbanChatMember_+  :<|> restrictChatMember_+  :<|> promoteChatMember_+  :<|> exportChatInviteLink_+  :<|> setChatPhoto_+  :<|> deleteChatPhoto_+  :<|> setChatTitle_+  :<|> setChatDescription_+  :<|> pinChatMessage_+  :<|> unpinChatMessage_   :<|> getChat_   :<|> getChatAdministrators_   :<|> getChatMembersCount_-  :<|> getChatMember_ = client chatsApi+  :<|> getChatMember_+  :<|> setChatStickerSet_+  :<|> deleteChatStickerSet_+    = client chatsApi  -- | Use this method to kick a user from a group or a supergroup. In the case of supergroups, the user will not be able to return to the group on their own using invite links, etc., unless unbanned first. The bot must be an administrator in the group for this to work.-kickChatMember :: Token -> Text -> Int -> Manager -> IO (Either ServantError KickChatMemberResponse)+kickChatMember :: Token+    -> Text -- ^ Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername)+    -> Int -- ^ Unique identifier of the target user+    -> Manager -> IO (Either ServantError KickChatMemberResponse) kickChatMember token chatId userId = runClient (kickChatMemberM chatId userId) token  -- | See 'kickChatMember' kickChatMemberM :: Text -> Int -> TelegramClient KickChatMemberResponse-kickChatMemberM chatId userId = asking $ \t -> kickChatMember_ t (Just chatId) (Just userId)+kickChatMemberM chatId userId = asking $ \t -> kickChatMember_ t (Just chatId) (Just userId) Nothing +kickChatMemberUntilM :: Text -- ^ Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername)+    -> Int -- ^ Unique identifier of the target user+    -> Int -- ^ Date when the user will be unbanned, unix time. If user is banned for more than 366 days or less than 30 seconds from the current time they are considered to be banned forever+    -> TelegramClient KickChatMemberResponse+kickChatMemberUntilM chatId userId untilDate = asking $ \t -> kickChatMember_ t (Just chatId) (Just userId) (Just untilDate)+ -- | Use this method for your bot to leave a group, supergroup or channel. Returns True on success. leaveChat :: Token -> Text -> Manager -> IO (Either ServantError LeaveChatResponse) leaveChat = runM leaveChatM@@ -101,6 +186,42 @@ unbanChatMemberM :: Text -> Int -> TelegramClient UnbanChatMemberResponse unbanChatMemberM chatId userId = asking $ \t -> unbanChatMember_ t (Just chatId) (Just userId) +-- | Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. Pass True for all boolean parameters to lift restrictions from a user. Returns True on success.+restrictChatMemberM :: RestrictChatMemberRequest -> TelegramClient (Response Bool)+restrictChatMemberM = run_ restrictChatMember_++-- | Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Pass False for all boolean parameters to demote a user. Returns True on success.+promoteChatMemberM :: PromoteChatMemberRequest -> TelegramClient (Response Bool)+promoteChatMemberM = run_ promoteChatMember_++-- | Use this method to export an invite link to a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns exported invite link as String on success.+exportChatInviteLinkM :: Text -> TelegramClient (Response Text)+exportChatInviteLinkM chatId = run_ exportChatInviteLink_ (Just chatId)++-- | Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success.+setChatPhotoM :: SetChatPhotoRequest -> TelegramClient (Response Bool)+setChatPhotoM = run_ setChatPhoto_++-- | Use this method to delete a chat photo. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success.+deleteChatPhotoM :: Text -> TelegramClient (Response Bool)+deleteChatPhotoM chatId = run_ deleteChatPhoto_ (Just chatId)++-- | Use this method to change the title of a chat. Titles can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success.+setChatTitleM :: Text -> Maybe Text -> TelegramClient (Response Bool)+setChatTitleM chatId title = asking $ \t -> setChatTitle_ t (Just chatId) title++-- | Use this method to change the description of a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success.+setChatDescriptionM :: Text -> Maybe Text -> TelegramClient (Response Bool)+setChatDescriptionM chatId description = asking $ \t -> setChatDescription_ t (Just chatId) description++-- | Use this method to pin a message in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' admin right in the supergroup or 'can_edit_messages' admin right in the channel. Returns True on success.+pinChatMessageM :: Text -> Int -> Maybe Bool -> TelegramClient (Response Bool)+pinChatMessageM chatId messageId disableNotifications = asking $ \tkn -> pinChatMessage_ tkn (Just chatId) (Just messageId) disableNotifications++-- | Use this method to unpin a message in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' admin right in the supergroup or 'can_edit_messages' admin right in the channel. Returns True on success.+unpinChatMessageM :: Text -> TelegramClient (Response Bool)+unpinChatMessageM chatId = run_ unpinChatMessage_ (Just chatId)+ -- | Use this method to get up to date information about the chat (current name of the user for one-on-one conversations, current username of a user, group or channel, etc.) getChat :: Token -> Text -> Manager -> IO (Either ServantError GetChatResponse) getChat = runM getChatM@@ -132,3 +253,12 @@ -- | See 'getChatMember' getChatMemberM :: Text -> Int -> TelegramClient GetChatMemberResponse getChatMemberM chatId userId = asking $ \t -> getChatMember_ t (Just chatId) (Just userId)++setChatStickerSetM :: Text -- ^ Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)+    -> Text -- ^ Name of the sticker set to be set as the group sticker set+    -> TelegramClient (Response Bool)+setChatStickerSetM chatId stickerSetName = asking $ \t -> setChatStickerSet_ t (Just chatId) (Just stickerSetName)++deleteChatStickerSetM :: Text -- ^ Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)+    -> TelegramClient (Response Bool)+deleteChatStickerSetM chatId = run_ deleteChatStickerSet_ (Just chatId)
src/Web/Telegram/API/Bot/API/Core.hs view
@@ -14,6 +14,7 @@   , asking   , runClient   , runClient'+  , runTelegramClient   , telegramBaseUrl   ) where @@ -44,6 +45,10 @@ -- | Runs 'TelegramClient' runClient :: TelegramClient a -> Token -> Manager -> IO (Either ServantError a) runClient tcm token manager = runClient' tcm token (ClientEnv manager telegramBaseUrl)++-- | Runs 'TelegramClient'+runTelegramClient :: Token -> Manager -> TelegramClient a -> IO (Either ServantError a)+runTelegramClient token manager tcm = runClient tcm token manager  asking :: Monad m => (t -> m b) -> ReaderT t m b asking op = ask >>= \t -> lift $ op t
src/Web/Telegram/API/Bot/API/Edit.hs view
@@ -16,6 +16,8 @@   , editInlineMessageCaptionM   , editInlineMessageReplyMarkup   , editInlineMessageReplyMarkupM+  , editMessageLiveLocationM+  , stopMessageLiveLocationM     -- * API   , TelegramBotEditAPI   , editApi@@ -23,9 +25,9 @@   ) where  import           Data.Proxy-import           Network.HTTP.Client              (Manager)+import           Network.HTTP.Client            (Manager) import           Servant.API-import           Servant.Client+import           Servant.Client          hiding (Response) import           Web.Telegram.API.Bot.API.Core import           Web.Telegram.API.Bot.Requests import           Web.Telegram.API.Bot.Responses@@ -50,6 +52,12 @@     :<|> TelegramToken :> "editMessageReplyMarkup"          :> ReqBody '[JSON] EditMessageReplyMarkupRequest          :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "editMessageLiveLocation"+         :> ReqBody '[JSON] EditMessageLiveLocationRequest+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "stopMessageLiveLocation"+         :> ReqBody '[JSON] StopMessageLiveLocationRequest+         :> Post '[JSON] (Response Bool)   -- | Proxy for Thelegram Bot API@@ -62,12 +70,16 @@ editMessageText__          :: Token -> EditMessageTextRequest -> ClientM (Response Bool) editMessageCaption__       :: Token -> EditMessageCaptionRequest -> ClientM (Response Bool) editMessageReplyMarkup__   :: Token -> EditMessageReplyMarkupRequest -> ClientM (Response Bool)+editMessageLiveLocation_   :: Token -> EditMessageLiveLocationRequest -> ClientM (Response Bool)+stopMessageLiveLocation_   :: Token -> StopMessageLiveLocationRequest -> ClientM (Response Bool) editMessageText_   :<|> editMessageCaption_   :<|> editMessageReplyMarkup_   :<|> editMessageText__   :<|> editMessageCaption__   :<|> editMessageReplyMarkup__+  :<|> editMessageLiveLocation_+  :<|> stopMessageLiveLocation_      = client editApi  -- | Use this method to edit text messages sent by the bot. On success, the edited 'Message' is returned, otherwise True is returned.@@ -116,3 +128,11 @@ -- | See 'editInlineMessageReplyMarkup' editInlineMessageReplyMarkupM :: EditMessageReplyMarkupRequest -> TelegramClient (Response Bool) editInlineMessageReplyMarkupM = run_ editMessageReplyMarkup__++-- | Use this method to edit live location messages sent by the bot or via the bot (for inline bots). A location can be edited until its live_period expires or editing is explicitly disabled by a call to 'stopMessageLiveLocationM'.+editMessageLiveLocationM :: EditMessageLiveLocationRequest -> TelegramClient (Response Bool)+editMessageLiveLocationM = run_ editMessageLiveLocation_++-- | Use this method to stop updating a live location message sent by the bot or via the bot (for inline bots) before live_period expires.+stopMessageLiveLocationM :: StopMessageLiveLocationRequest -> TelegramClient (Response Bool)+stopMessageLiveLocationM = run_ stopMessageLiveLocation_
src/Web/Telegram/API/Bot/API/Messages.hs view
@@ -36,6 +36,7 @@   , uploadVideoNoteM   , sendVideoNote   , sendVideoNoteM+  , sendMediaGroupM   , sendLocation   , sendLocationM   , sendVenue@@ -56,9 +57,10 @@ import           Data.Text                        (Text) import           Network.HTTP.Client              (Manager) import           Servant.API-import           Servant.Client+import           Servant.Client            hiding (Response) import           Servant.Client.MultipartFormData import           Web.Telegram.API.Bot.API.Core+import           Web.Telegram.API.Bot.Data import           Web.Telegram.API.Bot.Requests import           Web.Telegram.API.Bot.Responses @@ -112,6 +114,9 @@     :<|> TelegramToken :> "sendVideoNote"          :> ReqBody '[JSON] (SendVideoNoteRequest Text)          :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendMediaGroup"+         :> ReqBody '[JSON] SendMediaGroupRequest+         :> Post '[JSON] (Response [Message])     :<|> TelegramToken :> "sendLocation"          :> ReqBody '[JSON] SendLocationRequest          :> Post '[JSON] MessageResponse@@ -148,6 +153,7 @@ sendVoice_                 :: Token -> SendVoiceRequest Text -> ClientM MessageResponse uploadVideoNote_           :: Token -> SendVideoNoteRequest FileUpload -> ClientM MessageResponse sendVideoNote_             :: Token -> SendVideoNoteRequest Text -> ClientM MessageResponse+sendMediaGroup_            :: Token -> SendMediaGroupRequest -> ClientM (Response [Message]) sendLocation_              :: Token -> SendLocationRequest -> ClientM MessageResponse sendVenue_                 :: Token -> SendVenueRequest-> ClientM MessageResponse sendContact_               :: Token -> SendContactRequest -> ClientM MessageResponse@@ -169,6 +175,7 @@   :<|> sendVoice_   :<|> uploadVideoNote_   :<|> sendVideoNote_+  :<|> sendMediaGroup_   :<|> sendLocation_   :<|> sendVenue_   :<|> sendContact_@@ -315,6 +322,9 @@ -- | See 'sendLocation' sendLocationM :: SendLocationRequest -> TelegramClient MessageResponse sendLocationM = run_ sendLocation_++sendMediaGroupM :: SendMediaGroupRequest -> TelegramClient (Response [Message])+sendMediaGroupM = run_ sendMediaGroup_  -- | Use this method to send information about a venue. On success, the sent 'Message' is returned. sendVenue :: Token -> SendVenueRequest -> Manager -> IO (Either ServantError MessageResponse)
+ src/Web/Telegram/API/Bot/API/Stickers.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators     #-}++module Web.Telegram.API.Bot.API.Stickers+  ( -- * Functions+    getStickerSetM+  , uploadStickerFileM+  , createNewStickerSetM+  , createNewStickerSetM'+  , addStickerToSetM+  , uploadStickerToSetM+  , setStickerPositionInSetM+  , deleteStickerFromSetM+    -- * API+  , TelegramBotStickersAPI+  , stickerApi+    -- * Types+  ) where++import           Data.Proxy+import           Data.Text                        (Text)+import           Servant.API+import           Servant.Client            hiding (Response)+import           Servant.Client.MultipartFormData+import           Web.Telegram.API.Bot.API.Core+import           Web.Telegram.API.Bot.Data+import           Web.Telegram.API.Bot.Requests++import           Web.Telegram.API.Bot.Responses   (Response)++-- | Telegram Bot API+type TelegramBotStickersAPI =+         TelegramToken :> "getStickerSet"+         :> QueryParam "name" Text+         :> Get '[JSON] (Response StickerSet)+    :<|> TelegramToken :> "uploadStickerFile"+         :> MultipartFormDataReqBody UploadStickerFileRequest+         :> Post '[JSON] (Response File)+    :<|> TelegramToken :> "createNewStickerSet"+         :> ReqBody '[JSON] (CreateNewStickerSetRequest Text)+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "createNewStickerSet"+         :> MultipartFormDataReqBody (CreateNewStickerSetRequest FileUpload)+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "addStickerToSet"+         :> ReqBody '[JSON] (AddStickerToSetRequest Text)+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "addStickerToSet"+         :> MultipartFormDataReqBody (AddStickerToSetRequest FileUpload)+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "setStickerPositionInSet"+         :> QueryParam "sticker" Text+         :> QueryParam "position" Int+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "deleteStickerFromSet"+         :> QueryParam "sticker" Text+         :> Post '[JSON] (Response Bool)++-- | Proxy for Thelegram Bot API+stickerApi :: Proxy TelegramBotStickersAPI+stickerApi = Proxy++getStickerSet_           :: Token -> Maybe Text -> ClientM (Response StickerSet)+uploadStickerFile_       :: Token -> UploadStickerFileRequest -> ClientM (Response File)+createNewStickerSet_     :: Token -> CreateNewStickerSetRequest Text -> ClientM (Response Bool)+createNewStickerSet_'    :: Token -> CreateNewStickerSetRequest FileUpload -> ClientM (Response Bool)+addStickerToSet_         :: Token -> AddStickerToSetRequest Text -> ClientM (Response Bool)+addStickerToSet_'        :: Token -> AddStickerToSetRequest FileUpload -> ClientM (Response Bool)+setStickerPositionInSet_ :: Token -> Maybe Text -> Maybe Int -> ClientM (Response Bool)+deleteStickerFromSet_    :: Token -> Maybe Text -> ClientM (Response Bool)+getStickerSet_+  :<|> uploadStickerFile_+  :<|> createNewStickerSet_+  :<|> createNewStickerSet_'+  :<|> addStickerToSet_+  :<|> addStickerToSet_'+  :<|> setStickerPositionInSet_+  :<|> deleteStickerFromSet_+     = client stickerApi++-- | Use this method to get a sticker set.+getStickerSetM :: Text -- ^ Name of the sticker set+               -> TelegramClient (Response StickerSet)+getStickerSetM name = run_ getStickerSet_ $ Just name++-- | Use this method to upload a .png file with a sticker for later use in 'createNewStickerSet' and 'addStickerToSet' methods (can be used multiple times).+uploadStickerFileM :: UploadStickerFileRequest -> TelegramClient (Response File)+uploadStickerFileM = run_ uploadStickerFile_++-- | Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set.+createNewStickerSetM :: CreateNewStickerSetRequest Text -> TelegramClient (Response Bool)+createNewStickerSetM = run_ createNewStickerSet_++createNewStickerSetM' :: CreateNewStickerSetRequest FileUpload -> TelegramClient (Response Bool)+createNewStickerSetM' = run_ createNewStickerSet_'++-- | Use this method to add a new sticker to a set created by the bot.+addStickerToSetM :: AddStickerToSetRequest Text -> TelegramClient (Response Bool)+addStickerToSetM = run_ addStickerToSet_++uploadStickerToSetM :: AddStickerToSetRequest FileUpload -> TelegramClient (Response Bool)+uploadStickerToSetM = run_ addStickerToSet_'++-- | Use this method to move a sticker in a set created by the bot to a specific position.+setStickerPositionInSetM :: Text -- ^ File identifier of the sticker+                         -> Int -- ^ New sticker position in the set, zero-based+                         -> TelegramClient (Response Bool)+setStickerPositionInSetM fileId position = asking $ \t -> setStickerPositionInSet_ t (Just fileId) (Just position)++-- | Use this method to delete a sticker from a set created by the bot.+deleteStickerFromSetM :: Text -- ^ File identifier of the sticker+                      -> TelegramClient (Response Bool)+deleteStickerFromSetM fileId = run_ deleteStickerFromSet_ (Just fileId)
src/Web/Telegram/API/Bot/API/Updates.hs view
@@ -25,7 +25,7 @@ import           Data.Text                        (Text, empty) import           Network.HTTP.Client              (Manager) import           Servant.API-import           Servant.Client+import           Servant.Client            hiding (Response) import           Servant.Client.MultipartFormData import           Web.Telegram.API.Bot.API.Core import           Web.Telegram.API.Bot.Requests
src/Web/Telegram/API/Bot/Data.hs view
@@ -8,6 +8,7 @@       User                          (..)     , LanguageCode                  (..)     , ChatMember                    (..)+    , ChatPhoto                     (..)     , Chat                          (..)     , Message                       (..)     , MessageEntity                 (..)@@ -47,6 +48,10 @@     , SuccessfulPayment             (..)     , ShippingQuery                 (..)     , PreCheckoutQuery              (..)+    , MaskPositionPoint             (..)+    , MaskPosition                  (..)+    , StickerSet                    (..)+    , InputMedia                    (..)       -- * Functions     , inlineKeyboardButton     , keyboardButton@@ -70,7 +75,8 @@     , inlineQueryResultCachedSticker     , inlineQueryResultCachedVideo     , inlineQueryResultCachedVoice-+    , inputMediaPhoto+    , inputMediaVideo     ) where  import           Prelude                      hiding (id)@@ -89,6 +95,7 @@ data User = User   {     user_id            :: Int        -- ^ Unique identifier for this user or bot+  , user_is_bot        :: Bool -- ^ True, if this user is a bot   , user_first_name    :: Text       -- ^ User‘s or bot’s first name   , user_last_name     :: Maybe Text -- ^ User‘s or bot’s last name   , user_username      :: Maybe Text -- ^ User‘s or bot’s username@@ -156,6 +163,12 @@   , chat_first_name                     :: Maybe Text -- ^ First name of the other party in a private chat   , chat_last_name                      :: Maybe Text -- ^ Last name of the other party in a private chat   , chat_all_members_are_administrators :: Maybe Bool -- ^ True if a group has ‘All Members Are Admins’ enabled.+  , chat_photo                          :: Maybe ChatPhoto -- ^ Chat photo. Returned only in 'getChat'.+  , chat_description                    :: Maybe Text -- ^ Description, for supergroups and channel chats. Returned only in `getChat`.+  , chat_invite_link                    :: Maybe Text -- ^ Chat invite link, for supergroups and channel chats. Returned only in `getChat`.+  , chat_pinned_message                 :: Maybe Message -- ^ Pinned message, for supergroups. Returned only in 'getChat'.+  , chat_sticker_set_name               :: Maybe Text -- ^ For supergroups, name of group sticker set. Returned only in 'getChat'.+  , chat_can_set_sticker_set            :: Maybe Bool -- ^ True, if the bot can change the group sticker set. Returned only in 'getChat'.   } deriving (Show, Generic)  instance ToJSON Chat where@@ -279,12 +292,14 @@ -- | This object represents a sticker. data Sticker = Sticker   {-    sticker_file_id   :: Text             -- ^ Unique identifier for this file-  , sticker_width     :: Int              -- ^ Sticker width-  , sticker_height    :: Int              -- ^ Sticker height-  , sticker_thumb     :: Maybe PhotoSize  -- ^ Sticker thumbnail in .webp or .jpg format-  , sticker_emoji     :: Maybe Text       -- ^ Emoji associated with the sticker-  , sticker_file_size :: Maybe Int        -- ^ File size+    sticker_file_id       :: Text             -- ^ Unique identifier for this file+  , sticker_width         :: Int              -- ^ Sticker width+  , sticker_height        :: Int              -- ^ Sticker height+  , sticker_thumb         :: Maybe PhotoSize  -- ^ Sticker thumbnail in .webp or .jpg format+  , sticker_emoji         :: Maybe Text       -- ^ Emoji associated with the sticker+  , sticker_set_name      :: Maybe Text+  , sticker_mask_position :: Maybe MaskPosition+  , sticker_file_size     :: Maybe Int        -- ^ File size   } deriving (Show, Generic)  instance ToJSON Sticker where@@ -815,8 +830,22 @@  data ChatMember = ChatMember   {-    cm_user   :: User -- ^ Information about the user-  , cm_status :: Text -- ^ The member's status in the chat. Can be “creator”, “administrator”, “member”, “left” or “kicked”+    cm_user                      :: User -- ^ Information about the user+  , cm_status                    :: Text -- ^ The member's status in the chat. Can be “creator”, “administrator”, “member”, “left” or “kicked”+  , cm_until_date                :: Maybe Integer -- ^ Restictred and kicked only. Date when restrictions will be lifted for this user, unix time+  , cm_can_be_edited             :: Maybe Bool -- ^ Administrators only. True, if the bot is allowed to edit administrator privileges of that user+  , cm_can_change_info           :: Maybe Bool -- ^ Administrators only. True, if the administrator can change the chat title, photo and other settings+  , cm_can_post_messages         :: Maybe Bool -- ^ Administrators only. True, if the administrator can post in the channel, channels only+  , cm_can_edit_messages         :: Maybe Bool -- ^ Administrators only. True, if the administrator can edit messages of other users and can pin messages, channels only+  , cm_can_delete_messages       :: Maybe Bool -- ^ Administrators only. True, if the administrator can delete messages of other users+  , cm_can_invite_users          :: Maybe Bool -- ^ Administrators only. True, if the administrator can invite new users to the chat+  , cm_can_restrict_members      :: Maybe Bool -- ^ Administrators only. True, if the administrator can restrict, ban or unban chat members+  , cm_can_pin_messages          :: Maybe Bool -- ^ Administrators only. True, if the administrator can pin messages, supergroups only+  , cm_can_promote_members       :: Maybe Bool -- ^ Administrators only. True, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user)+  , cm_can_send_messages         :: Maybe Bool -- ^ Restricted only. True, if the user can send text messages, contacts, locations and venues+  , cm_can_send_media_messages   :: Maybe Bool -- ^ Restricted only. True, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages+  , cm_can_send_other_messages   :: Maybe Bool -- ^ Restricted only. True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages+  , cm_can_add_web_page_previews :: Maybe Bool -- ^ Restricted only. True, if user may add web page previews to his messages, implies can_send_media_messages   } deriving (Show, Generic)  instance ToJSON ChatMember where@@ -825,6 +854,18 @@ instance FromJSON ChatMember where   parseJSON = parseJsonDrop 3 +data ChatPhoto = ChatPhoto+  {+    chat_photo_small_file_id :: Text -- ^ Unique file identifier of small (160x160) chat photo. This file_id can be used only for photo download.+  , chat_photo_big_file_id   :: Text -- ^ Unique file identifier of big (640x640) chat photo. This file_id can be used only for photo download.+  } deriving (Show, Generic)++instance ToJSON ChatPhoto where+  toJSON = toJsonDrop 11++instance FromJSON ChatPhoto where+  parseJSON = parseJsonDrop 11+ -- | This object represents a message. data Message = Message   {@@ -835,11 +876,15 @@   , forward_from            :: Maybe User -- ^ For forwarded messages, sender of the original message   , forward_from_chat       :: Maybe Chat -- ^ For messages forwarded from a channel, information about the original channel   , forward_from_message_id :: Maybe Int -- ^ For forwarded channel posts, identifier of the original message in the channel+  , forward_signature       :: Maybe Text -- ^ For messages forwarded from channels, signature of the post author if present   , forward_date            :: Maybe Int -- ^ For forwarded messages, date the original message was sent in Unix time   , reply_to_message        :: Maybe Message -- ^ For replies, the original message. Note that the 'Message' object in this field will not contain further 'reply_to_message' fields even if it itself is a reply.   , edit_date               :: Maybe Int -- ^ Date the message was last edited in Unix time+  , media_group_id          :: Maybe Text -- ^ The unique identifier of a media message group this message belongs to+  , author_signature        :: Maybe Text -- ^ Signature of the post author for messages in channels   , text                    :: Maybe Text -- ^ For text messages, the actual UTF-8 text of the message   , entities                :: Maybe [MessageEntity] -- ^ For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text+  , caption_entities        :: Maybe [MessageEntity] -- ^ or messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption   , audio                   :: Maybe Audio -- ^ Message is an audio file, information about the file   , document                :: Maybe Document -- ^ Message is a general file, information about the file   , game                    :: Maybe Game -- ^ Message is a game, information about the game@@ -847,11 +892,13 @@   , sticker                 :: Maybe Sticker -- ^ Message is a sticker, information about the sticker   , video                   :: Maybe Video -- ^ Message is a video, information about the video   , voice                   :: Maybe Voice -- ^ Message is a voice message, information about the file+  , video_note              :: Maybe VideoNote -- ^ Message is a video note, information about the video message   , caption                 :: Maybe Text -- ^ Caption for the photo or video   , contact                 :: Maybe Contact -- ^ Message is a shared contact, information about the contact   , location                :: Maybe Location -- ^ Message is a shared location, information about the location   , venue                   :: Maybe Venue -- ^ Message is a venue, information about the venue   , new_chat_member         :: Maybe User -- ^ A new member was added to the group, information about them (this member may be the bot itself)+  , new_chat_members        :: Maybe [User] -- ^ New members that were added to the group or supergroup and information about them (the bot itself may be one of these members)   , left_chat_member        :: Maybe User -- ^ A member was removed from the group, information about them (this member may be the bot itself)   , new_chat_title          :: Maybe Text -- ^ A chat title was changed to this value   , new_chat_photo          :: Maybe [PhotoSize] -- ^ A chat photo was change to this value@@ -864,8 +911,6 @@   , pinned_message          :: Maybe Message -- ^ Specified message was pinned. Note that the Message object in this field will not contain further reply_to_message fields even if it is itself a reply.   , invoice                 :: Maybe Invoice -- ^  Message is an invoice for a payment, information about the invoice.   , successful_payment      :: Maybe SuccessfulPayment -- ^  Message is a service message about a successful payment, information about the payment.-  , video_note              :: Maybe VideoNote -- ^ Message is a video note, information about the video message-  , new_chat_members        :: Maybe [User] -- ^ New members that were added to the group or supergroup and information about them (the bot itself may be one of these members)   } deriving (FromJSON, ToJSON, Show, Generic)  -- | This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.@@ -1066,3 +1111,90 @@  instance FromJSON PreCheckoutQuery where   parseJSON = parseJsonDrop 8++data StickerSet = StickerSet+  {+    stcr_set_name           :: Text -- ^ Sticker set name+  , stcr_set_title          :: Text -- ^ Sticker set title+  , stcr_set_contains_masks :: Bool -- ^ True, if the sticker set contains masks+  , stcr_set_stickers       :: [Sticker] -- ^ List of all set stickers+  } deriving (Show, Generic)++instance ToJSON StickerSet where+  toJSON = toJsonDrop 9++instance FromJSON StickerSet where+  parseJSON = parseJsonDrop 9++data MaskPositionPoint = Forehead+  | Eyes+  | Mouth+  | Chin deriving (Show, Generic)++instance ToJSON MaskPositionPoint where+  toJSON Forehead = "forehead"+  toJSON Eyes = "eyes"+  toJSON Mouth = "mouth"+  toJSON Chin = "chin"++instance FromJSON MaskPositionPoint where+  parseJSON "forehead" = pure Forehead+  parseJSON "eyes" = pure Eyes+  parseJSON "mouth" = pure Mouth+  parseJSON "chin" = pure Chin+  parseJSON _ = fail $ "Failed to parse MaskPositionPoint"++data MaskPosition = MaskPosition+  {+    mask_pos_point   :: MaskPositionPoint -- ^ The part of the face relative to which the mask should be placed+  , mask_pos_x_shift :: Float -- ^ Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. For example, choosing -1.0 will place mask just to the left of the default mask position.+  , mask_pos_y_shift :: Float -- ^ Shift by Y-axis measured in heights of the mask scaled to the face size, from top to bottom. For example, 1.0 will place the mask just below the default mask position.+  , mask_pos_scale   :: Float -- ^ Mask scaling coefficient. For example, 2.0 means double size.+  } deriving (Show, Generic)++instance ToJSON MaskPosition where+  toJSON = toJsonDrop 9++instance FromJSON MaskPosition where+  parseJSON = parseJsonDrop 9++-- | This object represents the content of a media message to be sent.+data InputMedia =+  InputMediaPhoto+  {+    input_media_media   :: Text -- ^ File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet+  , input_media_caption :: Maybe Text -- ^ Caption of the photo to be sent, 0-200 characters+  }+  | InputMediaVideo+  {+    input_media_media    :: Text -- ^ File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet+  , input_media_caption  :: Maybe Text -- ^ Caption of the video to be sent, 0-200 characters+  , input_media_width    :: Maybe Int -- ^ Video width+  , input_media_height   :: Maybe Int -- ^ Video height+  , input_media_duration :: Maybe Int -- ^ Video duration+  } deriving (Show, Generic)++inputMediaPhoto :: Text -> InputMedia+inputMediaPhoto media = InputMediaPhoto media Nothing++inputMediaVideo :: Text -> InputMedia+inputMediaVideo media = InputMediaVideo media Nothing Nothing Nothing Nothing++inputMediaTagModifier :: String -> String+inputMediaTagModifier "InputMediaPhoto" = "photo"+inputMediaTagModifier "InputMediaVideo" = "video"+inputMediaTagModifier x = x++inputMediaJSONOptions :: Options+inputMediaJSONOptions = defaultOptions {+    fieldLabelModifier     = drop 12+  , omitNothingFields      = True+  , sumEncoding            = TaggedObject { tagFieldName = "type", contentsFieldName = undefined }+  , constructorTagModifier = inputMediaTagModifier+  }++instance ToJSON InputMedia where+  toJSON = genericToJSON inputMediaJSONOptions++instance FromJSON InputMedia where+  parseJSON = genericParseJSON inputMediaJSONOptions
src/Web/Telegram/API/Bot/Requests.hs view
@@ -19,6 +19,7 @@     , SendVideoRequest               (..)     , SendVoiceRequest               (..)     , SendVideoNoteRequest           (..)+    , SendMediaGroupRequest          (..)     , SendLocationRequest            (..)     , SendVenueRequest               (..)     , SendContactRequest             (..)@@ -34,6 +35,14 @@     , SendInvoiceRequest             (..)     , AnswerShippingQueryRequest     (..)     , AnswerPreCheckoutQueryRequest  (..)+    , RestrictChatMemberRequest      (..)+    , PromoteChatMemberRequest       (..)+    , SetChatPhotoRequest            (..)+    , UploadStickerFileRequest       (..)+    , CreateNewStickerSetRequest     (..)+    , AddStickerToSetRequest         (..)+    , EditMessageLiveLocationRequest (..)+    , StopMessageLiveLocationRequest (..)      -- * Functions     , localFileUpload     , setWebhookRequest@@ -55,6 +64,7 @@     , uploadVoiceRequest     , sendVideoNoteRequest     , uploadVideoNoteRequest+    , sendMediaGroupRequest     , sendLocationRequest     , sendVenueRequest     , sendContactRequest@@ -75,6 +85,10 @@     , sendInvoiceRequest     , okShippingQueryRequest     , errorShippingQueryRequest+    , okAnswerPrecheckoutQueryRequest+    , errorAnswerPrecheckoutQueryRequest+    , restrictChatMemberRequest+    , promoteChatMemberRequest     ) where  import           Data.Aeson@@ -94,8 +108,10 @@                                                         InlineKeyboardButton,                                                         InlineKeyboardMarkup,                                                         InlineQueryResult,+                                                        InputMedia,                                                         KeyboardButton,-                                                        LabeledPrice, ParseMode,+                                                        LabeledPrice,+                                                        MaskPosition, ParseMode,                                                         ShippingOption) import           Web.Telegram.API.Bot.JsonExt @@ -453,13 +469,13 @@  data SendVideoNoteRequest payload = SendVideoNoteRequest   {-    _vid_note_chat_id :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @channelusername)-  , _vid_note_video_note :: payload -- ^ Video note to send. Pass a file_id as String to send a video note that exists on the Telegram servers (recommended) or upload a new video using multipart/form-data. More info on Sending Files ». Sending video notes by a URL is currently unsupported-  , _vid_note_duration :: Maybe Int -- ^ Duration of sent video in seconds-  , _vid_note_length :: Maybe Int -- ^ Video width and height+    _vid_note_chat_id              :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @channelusername)+  , _vid_note_video_note           :: payload -- ^ Video note to send. Pass a file_id as String to send a video note that exists on the Telegram servers (recommended) or upload a new video using multipart/form-data. More info on Sending Files ». Sending video notes by a URL is currently unsupported+  , _vid_note_duration             :: Maybe Int -- ^ Duration of sent video in seconds+  , _vid_note_length               :: Maybe Int -- ^ Video width and height   , _vid_note_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.-  , _vid_note_reply_to_message_id :: Maybe Int -- ^ If the message is a reply, ID of the original message-  , _vid_note_reply_markup :: Maybe ReplyKeyboard -- ^ Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user.+  , _vid_note_reply_to_message_id  :: Maybe Int -- ^ If the message is a reply, ID of the original message+  , _vid_note_reply_markup         :: Maybe ReplyKeyboard -- ^ Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user.   } deriving (Show, Generic)  instance ToJSON (SendVideoNoteRequest Text) where@@ -492,6 +508,7 @@     location_chat_id              :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)   , location_latitude             :: Float -- ^ Latitude of location   , location_longitude            :: Float -- ^ Longitude of location+  , location_live_period          :: Maybe Int -- ^ Period in seconds for which the location will be updated, should be between 60 and 86400.   , location_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.   , location_reply_to_message_id  :: Maybe Int -- ^ If the message is a reply, ID of the original message   , location_reply_markup         :: Maybe ReplyKeyboard -- ^ Additional interface options. A JSON-serialized object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user.@@ -504,8 +521,26 @@   parseJSON = parseJsonDrop 9  sendLocationRequest :: ChatId -> Float -> Float -> SendLocationRequest-sendLocationRequest chatId latitude longitude = SendLocationRequest chatId latitude longitude Nothing Nothing Nothing+sendLocationRequest chatId latitude longitude = SendLocationRequest chatId latitude longitude Nothing Nothing Nothing Nothing +-- | This object represents request for 'sendMediaGroup'+data SendMediaGroupRequest = SendMediaGroupRequest+  {+    media_group_chat_id              :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @channelusername)+  , media_group_media                :: [InputMedia] -- ^ Array describing photos and videos to be sent, must include 2–10 items+  , media_group_disable_notification :: Maybe Bool -- ^ Sends the messages silently. Users will receive a notification with no sound.+  , media_group_reply_to_message_id  :: Maybe Int -- ^ If the messages are a reply, ID of the original message+  } deriving(Show, Generic)++instance ToJSON SendMediaGroupRequest where+  toJSON = toJsonDrop 12++instance FromJSON SendMediaGroupRequest where+  parseJSON = parseJsonDrop 12++sendMediaGroupRequest :: ChatId -> [InputMedia] -> SendMediaGroupRequest+sendMediaGroupRequest chatId inputMediaArray = SendMediaGroupRequest chatId inputMediaArray Nothing Nothing+ -- | This object represents request for 'sendVenue' data SendVenueRequest = SendVenueRequest   {@@ -782,26 +817,29 @@  data SendInvoiceRequest = SendInvoiceRequest   {-    snd_inv_chat_id               :: Int64 -- ^ Unique identifier for the target private chat-  , snd_inv_title                 :: Text -- ^ Product name-  , snd_inv_description           :: Text -- ^ Product description-  , snd_inv_payload               :: Text -- ^ Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.-  , snd_inv_provider_token        :: Text -- ^ Payments provider token, obtained via Botfather-  , snd_inv_start_parameter       :: Text -- ^ Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter-  , snd_inv_currency              :: CurrencyCode -- ^ Three-letter ISO 4217 <https://core.telegram.org/bots/payments#supported-currencies currency> code-  , snd_inv_prices                :: [LabeledPrice] -- ^ Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)-  , snd_inv_photo_url             :: Maybe Text -- ^ URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.-  , snd_inv_photo_size            :: Maybe Int -- ^ Photo size-  , snd_inv_photo_width           :: Maybe Int -- ^ Photo width-  , snd_inv_photo_height          :: Maybe Int -- ^ Photo height-  , snd_inv_need_name             :: Maybe Bool -- ^ Pass `True`, if you require the user's full name to complete the order-  , snd_inv_need_phone_number     :: Maybe Bool -- ^ Pass `True`, if you require the user's phone number to complete the order-  , snd_inv_need_email            :: Maybe Bool -- ^ Pass `True`, if you require the user's email to complete the order-  , snd_inv_need_shipping_address :: Maybe Bool -- ^ Pass `True`, if you require the user's shipping address to complete the order-  , snd_inv_is_flexible           :: Maybe Bool -- ^ Pass `True`, if the final price depends on the shipping method-  , snd_inv_disable_notification  :: Maybe Bool -- ^ Sends the message silently. Users will receive a notification with no sound.-  , snd_inv_reply_to_message      :: Maybe Int -- ^ If the message is a reply, ID of the original message-  , snd_inv_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ A JSON-serialized object for an inline keyboard. If empty, one 'Pay total price' button will be shown. If not empty, the first button must be a Pay button.+    snd_inv_chat_id                       :: Int64 -- ^ Unique identifier for the target private chat+  , snd_inv_title                         :: Text -- ^ Product name+  , snd_inv_description                   :: Text -- ^ Product description+  , snd_inv_payload                       :: Text -- ^ Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.+  , snd_inv_provider_token                :: Text -- ^ Payments provider token, obtained via Botfather+  , snd_inv_start_parameter               :: Text -- ^ Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter+  , snd_inv_currency                      :: CurrencyCode -- ^ Three-letter ISO 4217 <https://core.telegram.org/bots/payments#supported-currencies currency> code+  , snd_inv_prices                        :: [LabeledPrice] -- ^ Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)+  , snd_inv_provider_data                 :: Maybe Text -- ^ JSON-encoded data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider.+  , snd_inv_photo_url                     :: Maybe Text -- ^ URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.+  , snd_inv_photo_size                    :: Maybe Int -- ^ Photo size+  , snd_inv_photo_width                   :: Maybe Int -- ^ Photo width+  , snd_inv_photo_height                  :: Maybe Int -- ^ Photo height+  , snd_inv_need_name                     :: Maybe Bool -- ^ Pass `True`, if you require the user's full name to complete the order+  , snd_inv_need_phone_number             :: Maybe Bool -- ^ Pass `True`, if you require the user's phone number to complete the order+  , snd_inv_need_email                    :: Maybe Bool -- ^ Pass `True`, if you require the user's email to complete the order+  , snd_inv_need_shipping_address         :: Maybe Bool -- ^ Pass `True`, if you require the user's shipping address to complete the order+  , snd_inv_send_phone_number_to_provider :: Maybe Bool -- ^ Pass True, if user's phone number should be sent to provider+  , snd_inv_send_email_to_provider        :: Maybe Bool -- ^ Pass True, if user's email address should be sent to provider+  , snd_inv_is_flexible                   :: Maybe Bool -- ^ Pass `True`, if the final price depends on the shipping method+  , snd_inv_disable_notification          :: Maybe Bool -- ^ Sends the message silently. Users will receive a notification with no sound.+  , snd_inv_reply_to_message              :: Maybe Int -- ^ If the message is a reply, ID of the original message+  , snd_inv_reply_markup                  :: Maybe InlineKeyboardMarkup -- ^ A JSON-serialized object for an inline keyboard. If empty, one 'Pay total price' button will be shown. If not empty, the first button must be a Pay button.   } deriving (Show, Generic)  instance ToJSON SendInvoiceRequest where@@ -820,7 +858,7 @@   -> [LabeledPrice] -- ^ Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)   -> SendInvoiceRequest sendInvoiceRequest chatId title description payload providerToken startParameter currency prices-  = SendInvoiceRequest chatId title description payload providerToken startParameter currency prices Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing+  = SendInvoiceRequest chatId title description payload providerToken startParameter currency prices Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing  data AnswerShippingQueryRequest   -- | If you sent an invoice requesting a shipping address and the parameter is_flexible was specified, the Bot API will send an Update with a shipping_query field to the bot. Use this method to reply to shipping queries. On success, True is returned.@@ -858,6 +896,182 @@  instance FromJSON AnswerPreCheckoutQueryRequest where   parseJSON = parseJsonDrop 4++okAnswerPrecheckoutQueryRequest :: Text -> AnswerPreCheckoutQueryRequest+okAnswerPrecheckoutQueryRequest queryId = AnswerPreCheckoutQueryRequest queryId True Nothing++errorAnswerPrecheckoutQueryRequest :: Text -> Text -> AnswerPreCheckoutQueryRequest+errorAnswerPrecheckoutQueryRequest queryId errorMessage = AnswerPreCheckoutQueryRequest queryId False $ Just errorMessage++data RestrictChatMemberRequest = RestrictChatMemberRequest+  {+    rcm_chat_id                   :: ChatId -- ^ Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)+  , rcm_user_id                   :: Int -- ^ Unique identifier of the target user+  , rcm_until_date                :: Maybe Int -- ^ Date when restrictions will be lifted for the user, unix time. If user is restricted for more than 366 days or less than 30 seconds from the current time, they are considered to be restricted forever+  , rcm_can_send_messages         :: Maybe Bool -- ^ Pass True, if the user can send text messages, contacts, locations and venues+  , rcm_can_send_media_messages   :: Maybe Bool -- ^ Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages+  , rcm_can_send_other_messages   :: Maybe Bool -- ^ Pass True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages+  , rcm_can_add_web_page_previews :: Maybe Bool -- ^ Pass True, if the user may add web page previews to their messages, implies can_send_media_messages+  } deriving (Show, Generic)++instance ToJSON RestrictChatMemberRequest where+  toJSON = toJsonDrop 4++instance FromJSON RestrictChatMemberRequest where+  parseJSON = parseJsonDrop 4++restrictChatMemberRequest :: ChatId -> Int -> RestrictChatMemberRequest+restrictChatMemberRequest chatId userId = RestrictChatMemberRequest chatId userId Nothing Nothing Nothing Nothing Nothing++data PromoteChatMemberRequest = PromoteChatMemberRequest+  {+    pcmr_chat_id              :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @channelusername)+  , pcmr_user_id              :: Int -- ^ Unique identifier of the target user+  , pcmr_can_change_info      :: Maybe Bool -- ^ Pass True, if the administrator can change chat title, photo and other settings+  , pcmr_can_post_messages    :: Maybe Bool -- ^ Pass True, if the administrator can create channel posts, channels only+  , pcmr_can_edit_messages    :: Maybe Bool -- ^ Pass True, if the administrator can edit messages of other users and can pin messages, channels only+  , pcmr_can_delete_messages  :: Maybe Bool -- ^ Pass True, if the administrator can delete messages of other users+  , pcmr_can_invite_users     :: Maybe Bool -- ^ Pass True, if the administrator can invite new users to the chat+  , pcmr_can_restrict_members :: Maybe Bool -- ^ Pass True, if the administrator can restrict, ban or unban chat members+  , pcmr_can_pin_messages     :: Maybe Bool -- ^ Pass True, if the administrator can pin messages, supergroups only+  , pcmr_can_promote_members  :: Maybe Bool -- ^ Pass True, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by him)+  } deriving (Show, Generic)++instance ToJSON PromoteChatMemberRequest where+  toJSON = toJsonDrop 5++instance FromJSON PromoteChatMemberRequest where+  parseJSON = parseJsonDrop 5++promoteChatMemberRequest :: ChatId -> Int -> PromoteChatMemberRequest+promoteChatMemberRequest chatId userId = PromoteChatMemberRequest chatId userId Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing++data SetChatPhotoRequest = SetChatPhotoRequest+  {+    scp_chat_id :: ChatId+  , scp_photo   :: FileUpload+  }++instance ToMultipartFormData SetChatPhotoRequest where+  toMultipartFormData req =+    [ utf8Part "chat_id" (chatIdToPart $ scp_chat_id req)+    , fileUploadToPart "photo" (scp_photo req) ]++data UploadStickerFileRequest = UploadStickerFileRequest+  {+    upload_sticker_user_id     :: Int -- ^ User identifier of sticker file owner+  , upload_sticker_png_sticker :: FileUpload -- ^ Png image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px.+  }++instance ToMultipartFormData UploadStickerFileRequest where+  toMultipartFormData req =+    [ utf8Part "user_id" (tshow $ upload_sticker_user_id req)+    , fileUploadToPart "png_sticker" (upload_sticker_png_sticker req) ]++data CreateNewStickerSetRequest payload = CreateNewStickerSetRequest+  {+    new_sticker_set_user_id        :: Int -- ^ User identifier of created sticker set owner+  , new_sticker_set_name           :: Text -- ^ Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., animals). Can contain only english letters, digits and underscores. Must begin with a letter, can't contain consecutive underscores and must end in “_by_<bot username>”. <bot_username> is case insensitive. 1-64 characters.+  , new_sticker_set_title          :: Text -- ^ Sticker set title, 1-64 characters+  , new_sticker_set_png_sticker    :: payload -- ^ Yes 	Png image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. Pass a file_id as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. More info on Sending Files »+  , new_sticker_set_emojis         :: Text -- ^ One or more emoji corresponding to the sticker+  , new_sticker_set_contains_masks :: Maybe Bool -- ^ Pass True, if a set of mask stickers should be created+  , new_sticker_set_mask_position  :: Maybe MaskPosition -- ^ A JSON-serialized object for position where the mask should be placed on faces+  } deriving (Show, Generic)++instance ToJSON (CreateNewStickerSetRequest Text) where+  toJSON = toJsonDrop 16++instance FromJSON (CreateNewStickerSetRequest Text) where+  parseJSON = parseJsonDrop 16++instance ToMultipartFormData (CreateNewStickerSetRequest FileUpload) where+  toMultipartFormData req =+    [ utf8Part "user_id" (tshow $ new_sticker_set_user_id req)+    , utf8Part "name" $ new_sticker_set_name req+    , utf8Part "title" $ new_sticker_set_title req+    , utf8Part "emojis" $ new_sticker_set_emojis req+    , fileUploadToPart "png_sticker" (new_sticker_set_png_sticker req) ] +++    catMaybes+    [ partLBS "contains_masks" . encode <$> new_sticker_set_contains_masks req+    , partLBS "mask_position" . encode <$> new_sticker_set_mask_position req ]++data AddStickerToSetRequest payload = AddStickerToSetRequest+  {+    add_sticker_to_set_user_id       :: Int -- ^ User identifier of created sticker set owner+  , add_sticker_to_set_name          :: Text -- ^ Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., animals). Can contain only english letters, digits and underscores. Must begin with a letter, can't contain consecutive underscores and must end in “_by_<bot username>”. <bot_username> is case insensitive. 1-64 characters.+  , add_sticker_to_set_png_sticker   :: payload -- ^ Yes 	Png image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. Pass a file_id as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. More info on Sending Files »+  , add_sticker_to_set_emojis        :: Text -- ^ One or more emoji corresponding to the sticker+  , add_sticker_to_set_mask_position :: Maybe MaskPosition -- ^ A JSON-serialized object for position where the mask should be placed on faces+  } deriving (Show, Generic)++instance ToJSON (AddStickerToSetRequest Text) where+  toJSON = toJsonDrop 19++instance FromJSON (AddStickerToSetRequest Text) where+  parseJSON = parseJsonDrop 19++instance ToMultipartFormData (AddStickerToSetRequest FileUpload) where+  toMultipartFormData req =+    [ utf8Part "user_id" (tshow $ add_sticker_to_set_user_id req)+    , utf8Part "name" $ add_sticker_to_set_name req+    , utf8Part "emojis" $ add_sticker_to_set_emojis req+    , fileUploadToPart "png_sticker" (add_sticker_to_set_png_sticker req) ] +++    catMaybes+    [ partLBS "mask_position" . encode <$> add_sticker_to_set_mask_position req+    ]++data EditMessageLiveLocationRequest =+  EditMessageLiveLocationRequest+  {+    edit_live_loc_chat_id      :: Text -- ^ Unique identifier for the target chat or username of the target channel (in the format @channelusername)+  , edit_live_loc_latitude     :: Float -- ^ Latitude of new location+  , edit_live_loc_longitude    :: Float -- ^ Longitude of new location+  , edit_live_loc_reply_markup :: Maybe InlineKeyboardMarkup -- ^ An object for a new inline keyboard.+  }+  | EditMessageLiveLocationMessageRequest+  {+    edit_live_loc_message_id   :: Int -- ^ Identifier of the sent message+  , edit_live_loc_latitude     :: Float -- ^ Latitude of new location+  , edit_live_loc_longitude    :: Float -- ^ Longitude of new location+  , edit_live_loc_reply_markup :: Maybe InlineKeyboardMarkup -- ^ An object for a new inline keyboard.+  }+  | EditMessageLiveLocationInlineMessageRequest+  {+    edit_live_loc_inline_message_id :: Text -- ^ Identifier of the inline message+  , edit_live_loc_latitude          :: Float -- ^ Latitude of new location+  , edit_live_loc_longitude         :: Float -- ^ Longitude of new location+  , edit_live_loc_reply_markup      :: Maybe InlineKeyboardMarkup -- ^ An object for a new inline keyboard.+  } deriving (Show, Generic)++instance ToJSON EditMessageLiveLocationRequest where+  toJSON = toJsonDrop 14++instance FromJSON EditMessageLiveLocationRequest where+  parseJSON = parseJsonDrop 14++data StopMessageLiveLocationRequest =+  StopMessageLiveLocationRequest+  {+    stop_live_loc_chat_id      :: Text -- ^ Unique identifier for the target chat or username of the target channel (in the format @channelusername)+  , stop_live_loc_reply_markup :: Maybe InlineKeyboardMarkup -- ^ An object for a new inline keyboard.+  }+  | StopMessageLiveLocationMessageRequest+  {+    stop_live_loc_message_id   :: Int -- ^ Identifier of the sent message+  , stop_live_loc_reply_markup :: Maybe InlineKeyboardMarkup -- ^ An object for a new inline keyboard.+  }+  | StopMessageLiveLocationInlineMessageRequest+  {+    stop_live_loc_inline_message_id :: Text -- ^ Identifier of the inline message+  , stop_live_loc_reply_markup      :: Maybe InlineKeyboardMarkup -- ^ An object for a new inline keyboard.+  } deriving (Show, Generic)++instance ToJSON StopMessageLiveLocationRequest where+  toJSON = toJsonDrop 14++instance FromJSON StopMessageLiveLocationRequest where+  parseJSON = parseJsonDrop 14  tshow :: Show a => a -> Text tshow = T.pack . show
telegram-api.cabal view
@@ -1,5 +1,5 @@ name:                telegram-api-version:             0.6.3.0+version:             0.7.2.0 synopsis:            Telegram Bot API bindings description:         High-level bindings to the Telegram Bot API homepage:            http://github.com/klappvisor/haskell-telegram-api#readme@@ -20,6 +20,8 @@                    , test-data/Possible_PDM_signal_labeled_as_Sputnik_by_NASA.ogg                    , test-data/concerto-for-2-trumpets-in-c-major.mp3                    , test-data/wikipedia-telegram.txt+                   , test-data/sticker_1.png+                   , test-data/sticker_2.png  extra-source-files:   README.md@@ -36,6 +38,7 @@                      , Web.Telegram.API.Bot.API.Updates                      , Web.Telegram.API.Bot.API.Chats                      , Web.Telegram.API.Bot.API.Payments+                     , Web.Telegram.API.Bot.API.Stickers                      , Web.Telegram.API.Bot.Data                      , Web.Telegram.API.Bot.Responses                      , Web.Telegram.API.Bot.Requests@@ -44,10 +47,12 @@                      , Servant.Client.MultipartFormData   build-depends:       base >= 4.7 && < 5                      , aeson >= 1.0 && < 1.3+                     , containers >= 0.5 && < 0.6                      , http-api-data                      , http-client >= 0.5 && < 0.6-                     , servant >= 0.9 && <0.11-                     , servant-client >= 0.9 && <0.11+                     , servant >= 0.12 && < 0.13+                     , servant-client >= 0.12 && < 0.13+                     , servant-client-core >= 0.12 && < 0.13                      , mtl >= 2.2 && < 2.3                      , text                      , transformers@@ -68,6 +73,8 @@                      , PaymentsSpec                      , JsonSpec                      , UpdatesSpec+                     , StickersSpec+                     , TestCore   build-depends:       base                      , aeson >= 1.0 && < 1.3                      , hjpath@@ -77,14 +84,16 @@                      , http-types                      , hspec                      , optparse-applicative-                     , servant >= 0.9 && <0.11-                     , servant-client >= 0.9 && <0.11+                     , servant >= 0.12 && <0.13+                     , servant-client >= 0.12 && <0.13+                     , servant-client-core >= 0.12 && < 0.13                      , telegram-api                      , http-types                      , filepath                      , text                      , transformers                      , utf8-string+                     , random   ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall -fno-warn-name-shadowing   default-language:    Haskell2010 
+ test-data/sticker_1.png view

binary file changed (absent → 410716 bytes)

+ test-data/sticker_2.png view

binary file changed (absent → 31313 bytes)

test/InlineSpec.hs view
@@ -48,6 +48,6 @@  inline_article = InlineQueryResultArticle "2131341" (Just "text article content") (Just message_content) Nothing Nothing Nothing Nothing Nothing Nothing Nothing inline_photo = InlineQueryResultPhoto "1430810" "http://vignette3.wikia.nocookie.net/victorious/images/f/f8/NyanCat.jpg" (Just "http://vignette3.wikia.nocookie.net/victorious/images/f/f8/NyanCat.jpg") Nothing Nothing Nothing Nothing Nothing Nothing Nothing-inline_gif = InlineQueryResultGif "131231234" "https://media.giphy.com/media/zEO5eq3ZsEwbS/giphy.gif" Nothing Nothing (Just "https://media.giphy.com/media/zEO5eq3ZsEwbS/100.gif") Nothing Nothing Nothing Nothing-inline_mpeg = InlineQueryResultMpeg4Gif "131251234" "https://media.giphy.com/media/zEO5eq3ZsEwbS/giphy.gif" Nothing Nothing (Just "https://media.giphy.com/media/zEO5eq3ZsEwbS/100.gif") Nothing Nothing Nothing Nothing+inline_gif = InlineQueryResultGif "131231234" "https://media.giphy.com/media/zEO5eq3ZsEwbS/giphy.gif" Nothing Nothing (Just "https://media.giphy.com/media/zEO5eq3ZsEwbS/100.gif") Nothing Nothing Nothing Nothing Nothing+inline_mpeg = InlineQueryResultMpeg4Gif "131251234" "https://media.giphy.com/media/zEO5eq3ZsEwbS/giphy.gif" Nothing Nothing (Just "https://media.giphy.com/media/zEO5eq3ZsEwbS/100.gif") Nothing Nothing Nothing Nothing Nothing inline_video = InlineQueryResultVideo "123413542" "https://www.youtube.com/embed/TBKN7_vx2xo" "text/html" (Just "https://i.ytimg.com/vi_webp/TBKN7_vx2xo/mqdefault.webp") (Just "Enjoykin — Nyash Myash") Nothing Nothing Nothing Nothing Nothing Nothing Nothing
test/MainSpec.hs view
@@ -7,25 +7,20 @@  import           Control.Concurrent import           Control.Monad-import           Data.Either               (isLeft, isRight) import           Data.Monoid import           Data.Text                 (Text) import qualified Data.Text                 as T import           Network.HTTP.Client       (newManager) import           Network.HTTP.Client.TLS   (tlsManagerSettings) import           Network.HTTP.Types.Status-import           Servant.Client+import           Paths_telegram_api+import           Servant.Client     hiding (Response)+import qualified Servant.Client.Core       as Core import           System.FilePath import           Test.Hspec+import           TestCore import           Web.Telegram.API.Bot -import           Paths_telegram_api---- to print out remote response if response success not match-success, nosuccess :: (Show a, Show b) => Either a b -> Expectation-success   e = e `shouldSatisfy` isRight-nosuccess e = e `shouldSatisfy` isLeft- spec :: Token -> ChatId -> Text -> Spec spec token chatId botName = do   manager <- runIO $ newManager tlsManagerSettings@@ -47,7 +42,7 @@     it "should return error message" $ do       res <- sendMessage token (sendMessageRequest (ChatChannel "") "test message") manager       nosuccess res-      let Left FailureResponse { responseStatus = Status { statusMessage = msg } } = res+      let Left (FailureResponse Core.Response { responseStatusCode = Status { statusMessage = msg } }) = res       msg `shouldBe` "Bad Request"      it "should send message markdown" $ do@@ -105,7 +100,7 @@     it "should forward message" $ do       res <- forwardMessage token (forwardMessageRequest chatId chatId 123000) manager       nosuccess res-      let Left FailureResponse { responseStatus = Status { statusMessage = msg } } = res+      let Left (FailureResponse Core.Response { responseStatusCode = Status { statusMessage = msg } }) = res       msg `shouldBe` "Bad Request"    describe "/sendPhoto" $ do@@ -113,10 +108,10 @@       let photo = (sendPhotoRequest (ChatChannel "") "photo_id") {         photo_caption = Just "photo caption"       }-      Left FailureResponse { responseStatus = Status { statusMessage = msg } } <- sendPhoto token photo manager+      Left (FailureResponse Core.Response { responseStatusCode = Status { statusMessage = msg } }) <- sendPhoto token photo manager       msg `shouldBe` "Bad Request"     it "should upload photo and resend it by id" $ do-      let fileUpload = localFileUpload (testFile "christmas-cat.jpg")+      let fileUpload = localFileUpload $ testFile "christmas-cat.jpg"       let upload = (uploadPhotoRequest chatId fileUpload) {         photo_caption = Just "uploaded photo"       }@@ -138,24 +133,25 @@         _audio_performer = Just "performer"       , _audio_title = Just "title"       }-      Left FailureResponse { responseStatus = Status { statusMessage = msg } } <-+      Left (FailureResponse Core.Response { responseStatusCode = Status { statusMessage = msg } }) <-         sendAudio token audio manager       msg `shouldBe` "Bad Request"     it "should upload audio and resend it by id" $ do-      let fileUpload = localFileUpload (testFile "concerto-for-2-trumpets-in-c-major.mp3")+      let fileUpload = localFileUpload $ testFile "concerto-for-2-trumpets-in-c-major.mp3"           audioTitle = "Concerto for 2 Trumpets in C major, RV. 537 (Rondeau arr.) All."           audioPerformer = "Michel Rondeau"           audio = (uploadAudioRequest chatId fileUpload) {             _audio_performer = Just audioPerformer,             _audio_title = Just audioTitle           }-      Right Response {+      res <- uploadAudio token audio manager+      let Right Response {         result = Message {           audio = Just Audio {             audio_file_id = file_id, audio_title = Just title, audio_performer = Just performer           }         }-      } <- uploadAudio token audio manager+      } = res       title `shouldBe` audioTitle       performer `shouldBe` audioPerformer       let audio = sendAudioRequest chatId file_id@@ -170,19 +166,21 @@         sendSticker token sticker manager       sticker_file_id sticker `shouldBe` "CAADAgADGgADkWgMAAGXlYGBiM_d2wI" --"BQADAgADGgADkWgMAAGXlYGBiM_d2wI"     it "should upload sticker" $ do-      let fileUpload = localFileUpload (testFile "haskell-logo.webp")+      let fileUpload = localFileUpload $ testFile "haskell-logo.webp"           stickerReq = uploadStickerRequest chatId fileUpload-      Right Response { result = Message { sticker = Just sticker } } <--        uploadSticker token stickerReq manager+      res <- uploadSticker token stickerReq manager+      success res+      let Right Response { result = Message { sticker = Just sticker } } = res       sticker_height sticker `shouldBe` 128    describe "/sendVoice" $     it "should upload voice" $ do       -- audio source: https://commons.wikimedia.org/wiki/File:Possible_PDM_signal_labeled_as_Sputnik_by_NASA.ogg-      let fileUpload = localFileUpload (testFile "Possible_PDM_signal_labeled_as_Sputnik_by_NASA.ogg")+      let fileUpload = localFileUpload $ testFile "Possible_PDM_signal_labeled_as_Sputnik_by_NASA.ogg"           voiceReq = (uploadVoiceRequest chatId fileUpload) { _voice_duration = Just 10 }-      Right Response { result = Message { voice = Just voice } } <--        uploadVoice token voiceReq manager+      res <- uploadVoice token voiceReq manager+      success res+      let Right Response { result = Message { voice = Just voice } } = res       voice_duration voice `shouldBe` 10   describe "/sendVideoNote" $     it "should upload video note" $ do@@ -197,15 +195,17 @@   describe "/sendVideo" $     it "should upload video" $ do       -- video source: http://techslides.com/sample-webm-ogg-and-mp4-video-files-for-html5-      let fileUpload = localFileUpload (testFile "lego-video.mp4")+      let fileUpload = localFileUpload $ testFile "lego-video.mp4"           videoReq = uploadVideoRequest chatId fileUpload-      Right Response { result = Message { video = Just video } } <--        uploadVideo token videoReq manager+      res <- uploadVideo token videoReq manager+      success res+      let Right Response { result = Message { video = Just video } } = res+       video_width video `shouldBe` 560    describe "/sendDocument" $     it "should upload document" $ do-      let fileUpload = localFileUpload (testFile "wikipedia-telegram.txt")+      let fileUpload = localFileUpload $ testFile "wikipedia-telegram.txt"           documentReq = uploadDocumentRequest chatId fileUpload       Right Response { result = Message { document = Just document } } <-         uploadDocument token documentReq manager@@ -265,7 +265,7 @@       fmap (T.take 10) (file_path file) `shouldBe` Just "thumbnails"      it "should return error" $ do-      Left FailureResponse { responseStatus = Status { statusMessage = msg } } <-+      Left (FailureResponse Core.Response { responseStatusCode = Status { statusMessage = msg } }) <-         getFile token "AAQEABMXDZEwAARC0Kj3twkzNcMkAmm" manager       msg `shouldBe` "Bad Request" @@ -329,5 +329,19 @@       Right Response { result = Message { caption = Just cpt' } } <-         editMessageCaption token editRequest manager       cpt' `shouldBe` "edited cat picture"++  describe "/sendMediaGroup" $ do+    it "should send all media in group" $ do+      let photo1 = (inputMediaPhoto "http://s2.quickmeme.com/img/c9/c94711e0f933eb488e0cb0baa9d3eff1888a27ead4fd6089fd37d8f7d8f45a97.jpg") {+            input_media_caption = Just "meme"+          }+          photo2 = (inputMediaPhoto "http://adit.io/imgs/lenses/go_deeper.png") {+            input_media_caption = Just "Lenses"+          }+          request = sendMediaGroupRequest chatId [ photo1, photo2 ]+      res <- runTelegramClient token manager $ sendMediaGroupM request+      success res+      let Right Response { result = messages } = res+      length messages `shouldBe` 2      -- it "should edit caption" $ do ... after inline query tests are on place
test/PaymentsSpec.hs view
@@ -5,23 +5,17 @@  module PaymentsSpec (spec) where -import           Data.Either               (isLeft, isRight)-import qualified Data.Text                 as T-import           Data.Text                 (Text)-import           Network.HTTP.Client       (newManager)-import           Network.HTTP.Client.TLS   (tlsManagerSettings)+import           Data.Text               (Text)+import           Network.HTTP.Client     (newManager)+import           Network.HTTP.Client.TLS (tlsManagerSettings) import           Test.Hspec+import           TestCore import           Web.Telegram.API.Bot --- to print out remote response if response success not match-success, nosuccess :: (Show a, Show b) => Either a b -> Expectation-success   e = e `shouldSatisfy` isRight-nosuccess e = e `shouldSatisfy` isLeft- spec :: Token -> ChatId -> Text -> Text -> Spec-spec token (ChatId chatId) _ paymentToken = do+spec token chatId' _ paymentToken = do   manager <- runIO $ newManager tlsManagerSettings-+  let ChatId chatId = chatId'   describe "/sendInvoice" $ do     it "should send invoice" $ do       let description = "The best portal cannon in known universe"
test/Spec.hs view
@@ -12,6 +12,7 @@ import qualified MainSpec import           Options.Applicative import qualified PaymentsSpec+import qualified StickersSpec import           System.Environment           (lookupEnv, withArgs) import           Test.Hspec import qualified Text.PrettyPrint.ANSI.Leijen as PP@@ -75,6 +76,7 @@         describe "Main integration tests" $ MainSpec.spec token chatId botName         describe "Payments integration tests" $ PaymentsSpec.spec token chatId botName paymentToken         describe "Updates API spec" $ UpdatesSpec.spec token botName+        describe "Stickers API spec" $ StickersSpec.spec token chatId botName             --describe "Inline integration tests" $ InlineSpec.spec token chatId botName runIntegrationSpec _ _ _ _ = describe "Integration tests" $         fail "Missing required arguments for integration tests. Run stack test --test-arguments \"--help\" for more info"
+ test/StickersSpec.hs view
@@ -0,0 +1,87 @@+{-# LANGUAGE DataKinds           #-}+{-# LANGUAGE DeriveAnyClass      #-}+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators       #-}++module StickersSpec (spec) where++import           Data.Maybe+import           Data.Monoid+import           Data.Text               (Text)+import qualified Data.Text               as T+import           Network.HTTP.Client     (newManager)+import           Network.HTTP.Client.TLS (tlsManagerSettings)+import           Paths_telegram_api+import           System.FilePath+import           System.Random+import           Test.Hspec+import           TestCore+import           Web.Telegram.API.Bot++spec :: Token -> ChatId -> Text -> Spec+spec token chatId _ = do+  manager <- runIO $ newManager tlsManagerSettings+  dataDir <- runIO getDataDir++  res <- runIO $ runTelegramClient token manager getMeM+  let testFile name = dataDir </> "test-data" </> name+      Right Response { result = meRes } = res+      botUsername = fromMaybe "???" $ user_username meRes+      ChatId chatId' = chatId+      userId :: Int = fromIntegral chatId'+      stickerFile1 = localFileUpload $ testFile "sticker_1.png"+      stickerFile2 = localFileUpload $ testFile "sticker_2.png"++  describe "/getStickerSet" $ do+    it "should get sticker set" $ do+      let stickerName = "non_existing_test_set_by_" <> botUsername+      res <- runTelegramClient token manager $ getStickerSetM stickerName+      nosuccess res++  describe "/uploadStickerFile" $ do+    it "should upload sticker PNG" $ do+      let uploadRequest = UploadStickerFileRequest userId stickerFile1+      Right res <- runTelegramClient token manager $ uploadStickerFileM uploadRequest+      (T.null . file_id . result) res `shouldBe` False++  describe "/createNewStickerSet" $ do+    it "should create sticker set" $ do+      rnd :: Integer <- randomRIO (10000, 99999)+      let stickerSetName = "set_" <> (showText rnd) <> "_by_" <> botUsername+          request = CreateNewStickerSetRequest userId stickerSetName "Haskell Bot API Test Set" stickerFile1 "😃" (Just True) Nothing+      res <- runTelegramClient token manager $ do+        _ <- createNewStickerSetM' request+        getStickerSetM stickerSetName+      success res+      let Right Response { result = set } = res+      stcr_set_name set `shouldBe` stickerSetName++  describe "StickerSet CRUD" $ do+    let setTitle = "Haskell Telegram Bot API Test"+        setName = "sticker_set_by_" <> botUsername+    runIO $ print setName+    _ <- runIO $ runTelegramClient token manager $ do+      uploadResult <- uploadStickerFileM $ UploadStickerFileRequest userId stickerFile2+      let fileId = (file_id . result) uploadResult+          request = CreateNewStickerSetRequest userId setName setTitle fileId "👍" (Just True) Nothing+      createNewStickerSetM request+    it "should add new sticker to the sicker set" $ do+      Right (set, setAfter) <- runTelegramClient token manager $ do+        let maskPosition = MaskPosition Eyes 0.0 0.0 0.0+            uploadRequest = AddStickerToSetRequest userId setName stickerFile1 "😃" (Just maskPosition)+        _ <- uploadStickerToSetM uploadRequest+        Response { result = set } <- getStickerSetM setName+        let firstId = sticker_file_id . head . stcr_set_stickers+        _ <- sendStickerM $ sendStickerRequest chatId $ firstId set+        let lastId = sticker_file_id . last . stcr_set_stickers+        _ <- deleteStickerFromSetM $ lastId set+        Response { result = setAfter } <- getStickerSetM setName+        return (set, setAfter)+      stcr_set_contains_masks set `shouldBe` True+      let stickerCount = length . stcr_set_stickers+      stickerCount set `shouldBe` 2+      stickerCount setAfter `shouldBe` 1+++
+ test/TestCore.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators     #-}++module TestCore (success, nosuccess, showText) where++import           Data.Either (isLeft, isRight)+import           Data.Text   (Text)+import qualified Data.Text   as T+import           Test.Hspec++-- to print out remote response if response success not match+success, nosuccess :: (Show a, Show b) => Either a b -> Expectation+success   e = e `shouldSatisfy` isRight+nosuccess e = e `shouldSatisfy` isLeft++showText :: (Show a) => a -> Text+showText = T.pack . show
test/UpdatesSpec.hs view
@@ -8,28 +8,28 @@ import           Control.Concurrent import           Control.Monad.IO.Class import           Data.Text               (Text)+import qualified Data.Text               as T import           Network.HTTP.Client     (newManager) import           Network.HTTP.Client.TLS (tlsManagerSettings) import           Test.Hspec import           Web.Telegram.API.Bot-import qualified Data.Text as T  spec :: Token -> Text -> Spec spec token botName = do   manager <- runIO $ newManager tlsManagerSettings   describe "/getMe and /getWebhookInfo work together" $     it "responds with correct bot's name and empty webhook" $ do-      res <- runClient ( do+      res <- runTelegramClient token manager $ do           b <- getMeM           info <- getWebhookInfoM-          return (b, info)) token manager+          return (b, info)       let Right (Response {result = me}, Response {result = whi}) = res       user_first_name me `shouldBe` botName       whi_url whi `shouldBe` ""   describe "webhook operations" $ do     let url = "https://example.com/bot"     it "able to get and set webhook" $ do-      res <- runClient ( do+      res <- runTelegramClient token manager $ do           info <- getWebhookInfoM           liftIO $ (whi_url . result) info `shouldBe` ""           liftIO $ threadDelay $ 2 * 1000 * 1000 -- to avoid Too many request error@@ -40,32 +40,31 @@           liftIO $ threadDelay $ 2 * 1000 * 1000 -- to avoid Too many request error           del <- deleteWebhookM           liftIO $ result del `shouldBe` True-          getWebhookInfoM ) token manager+          getWebhookInfoM       let Right Response { result = whi } = res       whi_url whi `shouldBe` ""      it "should set allowed updates" $ do       let allowedUpdates = map T.pack ["message", "callback_query"]-      res <- runClient ( do+      res <- runTelegramClient token manager $ do           let request = (setWebhookRequest' url) {               webhook_allowed_updates = Just allowedUpdates           }           set <- setWebhookM request           liftIO $ result set `shouldBe` True-          getWebhookInfoM ) token manager+          getWebhookInfoM       let Right Response { result = whi } = res       whi_allowed_updates whi `shouldBe` Just allowedUpdates      it "should set max connections" $ do-      res <- runClient ( do-+      res <- runTelegramClient token manager $ do           let request = (setWebhookRequest' url) {               webhook_max_connections = Just 5           }           liftIO $ threadDelay $ 2 * 1000 * 1000 -- to avoid Too many request error           set <- setWebhookM request           liftIO $ result set `shouldBe` True-          getWebhookInfoM ) token manager+          getWebhookInfoM       let Right Response { result = whi } = res       whi_max_connections whi `shouldBe` Just 5