packages feed

telegram-api 0.5.2.0 → 0.7.2.0

raw patch · 34 files changed

Files

CHANGELOG.md view
@@ -1,103 +1,149 @@-## 0.5.2.0
-
-Features:
-
-* Added webhook methods such as getWebhookInfo, deleteWebhook, etc.
-* Changes to update api
-* Added sendGame  
-
-## 0.5.1.1
-
-Updated to use servant-0.9 and aeson-1.0
-
-## 0.5.0.0 [Breaking]
-
-Features:
-
-* **[Breaking]** Changed `Response` data record to be generic
-* Added certificate upload to set web-hook method for self-signed certificates
-
-Bugfixes:
-
-* Removed `O2` tag from cabal file
-
-## 0.4.3.1
-
-Bugfixes:
-
-* Exposed `MessageEntity`
-
-## 0.4.3.0
-
-Features:
-
-* Added Inline Keyboard to messages
-
-## 0.4.2.0
-
-Features:
-
-* Bot-2.1 support
-  * Added new methods: `getChat`, `leaveChat`, `getChatAdministrators`, `getChatMember`, `getChatMembersCount`.
-  * Added support for edited messages and new mentions from Telegram v.3.9. New fields: `edited_message` in `Update`, `edit_date` in `Message, user in `MessageEntity`. New value `text_mention` for the type field in `MessageEntity`.
-
-## 0.4.1.0
-
-Features:
-
-* Implemented file uploading for audio, voice, sticker, video and documents
-
-Bugfixes:
-
-* Exposed constrictors for inline edit requests
-
-## 0.4.0.1
-
-Bugfixes:
-
-* Parsing issue with answer callback query response
-
-## 0.4.0.0 [Breaking]
-
-Features:
-
-* Bot-2.0 API support is almost complete. Everything from [announce](https://core.telegram.org/bots/2-0-intro) is on place except file upload (works for photo BTW).
-  * Inline keyboards
-  * Updating messages
-  * Send Location and Phone number
-  * Inline Bots 2.0
-  * Group Admins
-  * and many others
-* Added new and more convenient way to create request data records avoiding many optional parameters.
-* migrated to servant 0.7.*
-
-## 0.3.1.0
-
-Features:
-
-* Added possibility to upload and send photo
-
-## 0.3.0.0
-
-Bugfixes:
-
-* *[Breaking]* Changed User to be optional in `from` field of the `Message` data record since sender can be empty for messages sent to channels
-
-## 0.2.1.1
-
-Bugfixes:
-
-* Minor fix of chat action deserialization code
-
-## 0.2.1.0
-
-Features:
-
-* Added reply keyboard
-
-## 0.2.0.0
-
-Features:
-
-* Main functionality is on place except content upload.
-* Inline mode added
+## 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`.+* Replaced the field *new_chat_member* in `Message` with *new_chat_members* (the old field will still be available for a while for compatibility purposes).+* The `User` object now may have a *language_code* field that contains the IETF language tag of the user's language.+* Added the `sendVideoNote` method, the new field *video_note* to `Message`, the fields `RecordVideoNote* or *UploadVideoNote* to `sendChatAction`.+* Added a new type of button, the pay button to `InlineKeyboardButton`.+* Updated dependencies++## 0.6.2.0++* Added new kinds of updates, *shipping_query* and *pre_checkout_query*, and new types of message content, *invoice* and *successful_payment*.+* Added new methods for payments: `sendInvoiceM`, `answerShippingQueryM`, and `answerPreCheckoutQueryM`.++## 0.6.1.1++Bugfixes:++* Migration to `Int64` to represent chat id and fix integer overflow issue++## 0.6.1.0++* Added `ChatId` data type since it can be integer or string starting from `@`, f.e. `@channelusername`+* Changes in `getUpdates` and `getUpdateM` function++## 0.6.0.1++* Bump aeson upper bound to include 1.1.*++## 0.6.0.0++* Added `TelegramClient`, see example of usage in README.md+* Changes from December update of Telegram Bot API+* `TelegramBotAPI` splitted in separate sub-types  ++## 0.5.2.0++Features:++* Added webhook methods such as getWebhookInfo, deleteWebhook, etc.+* Changes to update api+* Added sendGame  ++## 0.5.1.1++Updated to use servant-0.9 and aeson-1.0++## 0.5.0.0 [Breaking]++Features:++* **[Breaking]** Changed `Response` data record to be generic+* Added certificate upload to set web-hook method for self-signed certificates++Bugfixes:++* Removed `O2` tag from cabal file++## 0.4.3.1++Bugfixes:++* Exposed `MessageEntity`++## 0.4.3.0++Features:++* Added Inline Keyboard to messages++## 0.4.2.0++Features:++* Bot-2.1 support+  * Added new methods: `getChat`, `leaveChat`, `getChatAdministrators`, `getChatMember`, `getChatMembersCount`.+  * Added support for edited messages and new mentions from Telegram v.3.9. New fields: `edited_message` in `Update`, `edit_date` in `Message, user in `MessageEntity`. New value `text_mention` for the type field in `MessageEntity`.++## 0.4.1.0++Features:++* Implemented file uploading for audio, voice, sticker, video and documents++Bugfixes:++* Exposed constrictors for inline edit requests++## 0.4.0.1++Bugfixes:++* Parsing issue with answer callback query response++## 0.4.0.0 [Breaking]++Features:++* Bot-2.0 API support is almost complete. Everything from [announce](https://core.telegram.org/bots/2-0-intro) is on place except file upload (works for photo BTW).+  * Inline keyboards+  * Updating messages+  * Send Location and Phone number+  * Inline Bots 2.0+  * Group Admins+  * and many others+* Added new and more convenient way to create request data records avoiding many optional parameters.+* migrated to servant 0.7.*++## 0.3.1.0++Features:++* Added possibility to upload and send photo++## 0.3.0.0++Bugfixes:++* *[Breaking]* Changed User to be optional in `from` field of the `Message` data record since sender can be empty for messages sent to channels++## 0.2.1.1++Bugfixes:++* Minor fix of chat action deserialization code++## 0.2.1.0++Features:++* Added reply keyboard++## 0.2.0.0++Features:++* Main functionality is on place except content upload.+* Inline mode added
LICENSE view
@@ -1,30 +1,30 @@-Copyright Alexey Rodiontsev (c) 2015
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-
-    * Redistributions in binary form must reproduce the above
-      copyright notice, this list of conditions and the following
-      disclaimer in the documentation and/or other materials provided
-      with the distribution.
-
-    * Neither the name of Alexey Rodiontsev nor the names of other
-      contributors may be used to endorse or promote products derived
-      from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+Copyright Alexey Rodiontsev (c) 2015++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Alexey Rodiontsev nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
README.md view
@@ -1,140 +1,168 @@-# telegram-api
-
-[![Join the chat at https://gitter.im/klappvisor/haskell-telegram-api](https://badges.gitter.im/klappvisor/haskell-telegram-api.svg)](https://gitter.im/klappvisor/haskell-telegram-api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-
-![Build Status](https://img.shields.io/circleci/project/klappvisor/haskell-telegram-api.svg)
-![Hackage](https://img.shields.io/hackage/v/telegram-api.svg)
-![Hackage Dependencies](https://img.shields.io/hackage-deps/v/telegram-api.svg)
-![Haskell Programming Language](https://img.shields.io/badge/language-Haskell-blue.svg)
-![BSD3 License](http://img.shields.io/badge/license-BSD3-brightgreen.svg)
-
-High-level bindings to the [Telegram Bot API][telegram-bot-api] based on [servant][servant] library.
-Both `getUpdates` request or webhook can be used to receive updates for your bot. 
-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-2.0 API][bot-2.0]**
-
-## Usage
-
-`getMe` example
-
-```haskell
-{-# LANGUAGE OverloadedStrings #-}
-
-import           Network.HTTP.Client      (newManager)
-import           Network.HTTP.Client.TLS  (tlsManagerSettings)
-import           Web.Telegram.API.Bot
-
-main :: IO ()
-main = do
-  manager <- newManager tlsManagerSettings
-  res <- getMe token manager
-  case res of
-    Left e -> do
-      putStrLn "Request failed"
-      print e
-    Right GetMeResponse { user_result = u } -> do
-      putStrLn "Request succeded"
-      print $ user_first_name u
-  where token = Token "bot<token>" -- entire Token should be bot123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
-```
-
-`sendMessage` example
-
-```haskell
-{-# LANGUAGE OverloadedStrings #-}
-
-import           Network.HTTP.Client      (newManager)
-import           Network.HTTP.Client.TLS  (tlsManagerSettings)
-import           Web.Telegram.API.Bot
-
-main :: IO ()
-main = do
-  manager <- newManager tlsManagerSettings
-  let request = sendMessageRequest chatId message
-  res <- sendMessage token request manager
-  case res of
-    Left e -> do
-      putStrLn "Request failed"
-      print e
-    Right MessageResponse { message_result = m } -> do
-      putStrLn "Request succeded"
-      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>" 
-        message = "text *bold* _italic_ [github](github.com/klappvisor/haskell-telegram-api)"
-```
-
-#### Note on requests:
-
-Many request data records have a lot of optional parameters which are usually redundant.
-There are two ways to create requests:
-
-With data type constructor:
-```haskell
-let request = SendMessageRequest "chatId" "text" Nothing (Just True) Nothing Nothing Nothing
-```
-Using default instance:
-
-```haskell
-let request = sendMessageRequest "chatId" "text" -- only with required fields
-```
-
-```haskell
-let request = (sendMessageRequest "chatId" "text") {
-  message_disable_notification = Just True -- with optional fields
-}
-```
-
-## Contribution
-
-Contributions are welcome!
-
-1. Fork repository
-2. Do some changes
-3. Create pull request
-4. Wait for CI build and review
-5. ??????
-6. PROFIT
-
-Bear in mind that the CI build won't run integration test suite against your pull request since the necessary environment
-variables (`$BOT_TOKEN`, `$CHAT_ID` and `$BOT_NAME`) aren't exported when a fork
-starts the build (for security reasons). If you do want to run them before creating RP, you can setup integration of your fork
-with CircleCI.
-
-You can use `stack` to build project
-
-```
-stack build
-```
-
-To run test you have to create your own bot. Go to [BotFather](https://telegram.me/botfather) and create the bot. As the result you will have private bot's access token. Keep it safe!
-
-```
-stack test --test-arguments "--integration -t BOT_TOKEN -c CHAT_ID -b BOT_NAME -- HSPEC_ARGS"
-```
-
-where
-
-* `BOT_TOKEN` is the token obtained from BotFather
-* `CHAT_ID` can be id of your chat with your bot. Send some messages to this chat in Telegram and do `curl "https://api.telegram.org/bot<replace_with_token>/getUpdates"`, you'll have to parse some JSON with your brain ;-) or any other suitable tool and you will find chat id there.
-* `BOT_NAME` is the name of your bot
-* `HSPEC_ARGS` are the normal `hspec` arguments you can find [here][hspec-args]
-
-The help option is available for the tests and for hspec:
-
-```
-stack test --test-arguments "-h"
-stack test --test-arguments "--integration -t BOT_TOKEN -c CHAT_ID -b BOT_NAME -- -h"
-```
-
-Note: Inline Spec is disabled for now...
-
-If everything is fine after running the tests you will receive a few new messages from your bot.
-
-[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-2.0]: https://core.telegram.org/bots/2-0-intro
+# telegram-api++[![Join the chat at https://gitter.im/klappvisor/haskell-telegram-api](https://badges.gitter.im/klappvisor/haskell-telegram-api.svg)](https://gitter.im/klappvisor/haskell-telegram-api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)++![Build Status](https://img.shields.io/circleci/project/klappvisor/haskell-telegram-api.svg)+![Hackage](https://img.shields.io/hackage/v/telegram-api.svg)+![Hackage Dependencies](https://img.shields.io/hackage-deps/v/telegram-api.svg)+![Haskell Programming Language](https://img.shields.io/badge/language-Haskell-blue.svg)+![BSD3 License](http://img.shields.io/badge/license-BSD3-brightgreen.svg)++High-level bindings to the [Telegram Bot API][telegram-bot-api] based on [servant][servant] library.+Both `getUpdates` request or webhook can be used to receive updates for your bot.+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.5 API][bot-api]**++## Usage++There are two ways of using Telegram Bot API. First and original way is to run IO directly for every Telegram servers request, another one is based on `TelegramClient` which is just `ReaderT`.++### Use `TelegramClient`++```haskell+{-# LANGUAGE OverloadedStrings #-}++import           Network.HTTP.Client      (newManager)+import           Network.HTTP.Client.TLS  (tlsManagerSettings)+import           Web.Telegram.API.Bot++main :: IO ()+main = do+  let token = Token "bot<token>" -- entire Token should be bot123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11+  manager <- newManager tlsManagerSettings+  result <- runTelegramClient token manager $ do+    info <- getWebhookInfoM+    let request = setWebhookRequest' "https://example.com/hook"+    isSet <- setWebhookM request+    getMeM+  print result+  print "done!"+```++### 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++```haskell+{-# LANGUAGE OverloadedStrings #-}++import           Network.HTTP.Client      (newManager)+import           Network.HTTP.Client.TLS  (tlsManagerSettings)+import           Web.Telegram.API.Bot++main :: IO ()+main = do+  manager <- newManager tlsManagerSettings+  res <- getMe token manager+  case res of+    Left e -> do+      putStrLn "Request failed"+      print e+    Right Response { result = u } -> do+      putStrLn "Request succeded"+      print $ user_first_name u+  where token = Token "bot<token>" -- entire Token should be bot123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11+```++`sendMessage` example++```haskell+{-# LANGUAGE OverloadedStrings #-}++import           Network.HTTP.Client      (newManager)+import           Network.HTTP.Client.TLS  (tlsManagerSettings)+import           Web.Telegram.API.Bot++main :: IO ()+main = do+  manager <- newManager tlsManagerSettings+  let request = sendMessageRequest chatId message+  res <- sendMessage token request manager+  case res of+    Left e -> do+      putStrLn "Request failed"+      print e+    Right Response { result = m } -> do+      putStrLn "Request succeded"+      print $ message_id m+      print $ text m+  where token = Token "bot<token>" -- entire Token should be bot123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11+        chatId = ChatId <chat_id> -- use ChatId 10231 or ChatChannel "<@channelusername>"+        message = "text *bold* _italic_ [github](github.com/klappvisor/haskell-telegram-api)"+```++#### Note on requests:++Many request data records have a lot of optional parameters which are usually redundant.+There are two ways to create requests:++With data type constructor:+```haskell+let request = SendMessageRequest (ChatId int64_chatId) "text" Nothing (Just True) Nothing Nothing Nothing+```+Using default instance:++```haskell+let request = sendMessageRequest (ChatId int64_chatId) "text" -- only with required fields+```++```haskell+let request = (sendMessageRequest ChatId int64_chatId) "text") {+  message_disable_notification = Just True -- with optional fields+}+```++## Contribution++Contributions are welcome!++1. Fork repository+2. Do some changes+3. Create pull request+4. Wait for CI build and review+5. ??????+6. PROFIT++Bear in mind that the CI build won't run integration test suite against your pull request since the necessary environment+variables (`$BOT_TOKEN`, `$STRIPE_TOKEN`, `$CHAT_ID` and `$BOT_NAME`) aren't exported when a fork+starts the build (for security reasons). If you do want to run them before creating RP, you can setup integration of your fork+with CircleCI.++You can use `stack` to build project++```+stack build+```++To run test you have to create your own bot. Go to [BotFather](https://telegram.me/botfather) and create the bot. As the result you will have private bot's access token. Keep it safe!++```+stack test --test-arguments "--integration -c CHAT_ID -b BOT_NAME -- HSPEC_ARGS"+```++where++* `BOT_TOKEN` is the token obtained from BotFather and must be defined as environment variable+* `PAYMENT_TOKEN` is the token obtained from BotFather and must be defined as environment variable+* `CHAT_ID` can be id of your chat with your bot. Send some messages to this chat in Telegram and do `curl "https://api.telegram.org/bot<replace_with_token>/getUpdates"`, you'll have to parse some JSON with your brain ;-) or any other suitable tool and you will find chat id there.+* `BOT_NAME` is the name of your bot+* `HSPEC_ARGS` are the normal `hspec` arguments you can find [here][hspec-args]++The help option is available for the tests and for hspec:++```+stack test --test-arguments "-h"+stack test --test-arguments "--integration -c CHAT_ID -b BOT_NAME -- -h"+```++Note: Inline Spec is disabled for now...++If everything is fine after running the tests you will receive a few new messages from your bot.++[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-api]: https://core.telegram.org/bots/api
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple
-main = defaultMain
+import Distribution.Simple+main = defaultMain
src/Servant/Client/MultipartFormData.hs view
@@ -1,101 +1,109 @@-{-# LANGUAGE DataKinds           #-}
-{-# LANGUAGE DeriveDataTypeable  #-}
-{-# LANGUAGE FlexibleInstances   #-}
-{-# LANGUAGE OverloadedStrings   #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TypeFamilies        #-}
-{-# LANGUAGE TypeOperators       #-}
-{-# LANGUAGE UndecidableInstances #-}
-
-module Servant.Client.MultipartFormData
-  ( ToMultipartFormData (..)
-  , MultipartFormDataReqBody
-  ) where
-
-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           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           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           Servant.API
-import           Servant.Common.BaseUrl
-import           Servant.Client
-import           Servant.Common.Req         (Req, catchConnectionError,
-                                             reqAccept, reqToRequest)
--- | 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.
-  toMultipartFormData :: a -> [Part]
-
--- | Extract the request body as a value of type @a@.
-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'
-          formDataBody (toMultipartFormData reqData) requestWithoutBody
-    in snd <$> performRequestCT' reqToRequest' (Proxy :: Proxy ct) H.methodPost req
-
--- copied `performRequest` from servant-0.7.1, then modified so it takes a variant of `reqToRequest`
--- as an argument.
-performRequest' :: (Req -> BaseUrl -> IO Request)
-               -> Method -> Req -> Manager -> BaseUrl
-               -> ClientM ( Int, ByteString, MediaType
-                          , [HTTP.Header], Response ByteString)
-performRequest' reqToRequest' reqMethod req manager reqHost = do
-  partialRequest <- liftIO $ reqToRequest' req reqHost
-
-  let request = partialRequest { Client.method = reqMethod
-                               , checkResponse = \ _request _response -> return ()
-                               }
-
-  eResponse <- liftIO $ catchConnectionError $ Client.httpLbs request manager
-  case eResponse of
-    Left err ->
-      throwError . ConnectionError $ SomeException err
-
-    Right response -> do
-      let status = Client.responseStatus response
-          body = Client.responseBody response
-          hdrs = Client.responseHeaders response
-          status_code = statusCode status
-      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
-                   Just t' -> pure t'
-      unless (status_code >= 200 && status_code < 300) $
-        throwError $ FailureResponse status ct body
-      return (status_code, body, ct, hdrs, response)
-
--- copied `performRequestCT` from servant-0.7.1, then modified so it takes a variant of `reqToRequest`
--- as an argument.
-performRequestCT' :: MimeUnrender ct result =>
-  (Req -> BaseUrl -> IO Request) ->
-  Proxy ct -> Method -> Req
-    -> ClientM ([HTTP.Header], result)
-performRequestCT' reqToRequest' ct reqMethod req = do
-  let acceptCT = contentType ct
-  ClientEnv manager reqHost <- ask
-  (_status, respBody, respCT, hdrs, _response) <-
-    performRequest' reqToRequest' reqMethod (req { reqAccept = [acceptCT] }) manager reqHost
-  unless (matches respCT acceptCT) $ throwError $ UnsupportedContentType respCT respBody
-  case mimeUnrender ct respBody of
-    Left err -> throwError $ DecodeFailure err respCT respBody
-    Right val -> return (hdrs, val)
+{-# 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.Monad+import           Control.Monad.Error.Class+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.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           Servant.API+import           Servant.Client+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.+  toMultipartFormData :: a -> [Part]++-- | Extract the request body as a value of type @a@.+data MultipartFormDataReqBody a+    deriving (Typeable)++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' requestToClientRequest' (Proxy :: Proxy ct) H.methodPost req++-- copied `performRequest` from servant-0.11, then modified so it takes a variant of `requestToClientRequest`+-- as an argument.+performRequest' :: (Core.Request -> BaseUrl -> IO Request)+               -> Method -> Core.Request+               -> ClientM ( Int, ByteString, MediaType+                          , [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 }++  eResponse <- liftIO $ catchConnectionError $ Client.httpLbs request m+  case eResponse of+    Left 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 coreResponse+                   Just t' -> pure t'+      unless (status_code >= 200 && status_code < 300) $+        throwError $ FailureResponse coreResponse+      return (status_code, body, ct, hdrs, response)++-- copied `performRequestCT` from servant-0.11, then modified so it takes a variant of `requestToClientRequest`+-- as an argument.+performRequestCT' :: MimeUnrender ct result =>+    (Core.Request -> BaseUrl -> IO Request)+    -> Proxy ct -> Method -> Core.Request+    -> ClientM ([HTTP.Header], result)+performRequestCT' requestToClientRequest' ct reqMethod req = do+  let acceptCTS = contentTypes ct+  (_status, respBody, respCT, hdrs, _response) <-+    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 (pack err) coreResponse+    Right val -> return (hdrs, val)
src/Web/Telegram/API/Bot.hs view
@@ -1,10 +1,10 @@--- | This module provides Telegram Bot API
-module Web.Telegram.API.Bot
-  (
-    module TAPI
-  ) where
-
-import           Web.Telegram.API.Bot.API as TAPI
-import           Web.Telegram.API.Bot.Data as TAPI
-import           Web.Telegram.API.Bot.Responses as TAPI
-import           Web.Telegram.API.Bot.Requests as TAPI
+-- | This module provides Telegram Bot API+module Web.Telegram.API.Bot+  (+    module TAPI+  ) where++import           Web.Telegram.API.Bot.API as TAPI+import           Web.Telegram.API.Bot.Data as TAPI+import           Web.Telegram.API.Bot.Responses as TAPI+import           Web.Telegram.API.Bot.Requests as TAPI
src/Web/Telegram/API/Bot/API.hs view
@@ -1,487 +1,43 @@-{-# LANGUAGE DataKinds                  #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE OverloadedStrings          #-}
-{-# LANGUAGE TypeOperators              #-}
-
-module Web.Telegram.API.Bot.API
-  ( -- * Functions
-    getMe
-  , sendMessage
-  , forwardMessage
-  , uploadPhoto
-  , sendPhoto
-  , uploadAudio
-  , sendAudio
-  , uploadDocument
-  , sendDocument
-  , uploadSticker
-  , sendSticker
-  , uploadVideo
-  , sendVideo
-  , uploadVoice
-  , sendVoice
-  , sendLocation
-  , sendVenue
-  , sendContact
-  , sendChatAction
-  , sendGame
-  , getUpdates
-  , getFile
-  , getUserProfilePhotos
-  , setWebhook
-  , setWebhookWithCertificate
-  , deleteWebhook
-  , getWebhookInfo
-  , answerInlineQuery
-  , answerCallbackQuery
-  , kickChatMember
-  , leaveChat
-  , unbanChatMember
-  , getChat
-  , getChatAdministrators
-  , getChatMembersCount
-  , getChatMember
-  , editMessageText
-  , editMessageCaption
-  , editMessageReplyMarkup
-  , editInlineMessageText
-  , editInlineMessageCaption
-  , editInlineMessageReplyMarkup
-    -- * API
-  , TelegramBotAPI
-  , api
-    -- * Types
-  , Token             (..)
-  ) where
-
-import           Data.Proxy
-import           Data.Text (Text)
-import           Network.HTTP.Client (Manager)
-import           Servant.API
-import           Servant.Client
-import           Servant.Client.MultipartFormData
-import           Web.Telegram.API.Bot.Responses
-import           Web.Telegram.API.Bot.Requests
-
--- | Telegram Bot's Token
-newtype Token = Token Text
-  deriving (Show, Eq, Ord, ToHttpApiData, FromHttpApiData)
-
-telegramBaseUrl :: BaseUrl
-telegramBaseUrl = BaseUrl Https "api.telegram.org" 443 ""
-
--- | Type for token
-type TelegramToken = Capture ":token" Token
-
--- | Telegram Bot API
-type TelegramBotAPI =
-         TelegramToken :> "getMe"
-         :> Get '[JSON] GetMeResponse
-    :<|> TelegramToken :> "sendMessage"
-         :> ReqBody '[JSON] SendMessageRequest
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "forwardMessage"
-         :> ReqBody '[JSON] ForwardMessageRequest
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendPhoto"
-         :> MultipartFormDataReqBody (SendPhotoRequest FileUpload)
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendPhoto"
-         :> ReqBody '[JSON] (SendPhotoRequest Text)
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendAudio"
-         :> MultipartFormDataReqBody (SendAudioRequest FileUpload)
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendAudio"
-         :> ReqBody '[JSON] (SendAudioRequest Text)
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendDocument"
-         :> MultipartFormDataReqBody (SendDocumentRequest FileUpload)
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendDocument"
-         :> ReqBody '[JSON] (SendDocumentRequest Text)
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendSticker"
-         :> MultipartFormDataReqBody (SendStickerRequest FileUpload)
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendSticker"
-         :> ReqBody '[JSON] (SendStickerRequest Text)
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendVideo"
-         :> MultipartFormDataReqBody (SendVideoRequest FileUpload)
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendVideo"
-         :> ReqBody '[JSON] (SendVideoRequest Text)
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendVoice"
-         :> MultipartFormDataReqBody (SendVoiceRequest FileUpload)
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendVoice"
-         :> ReqBody '[JSON] (SendVoiceRequest Text)
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendLocation"
-         :> ReqBody '[JSON] SendLocationRequest
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendVenue"
-         :> ReqBody '[JSON] SendVenueRequest
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendContact"
-         :> ReqBody '[JSON] SendContactRequest
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "sendChatAction"
-         :> ReqBody '[JSON] SendChatActionRequest
-         :> Post '[JSON] ChatActionResponse
-    :<|> TelegramToken :> "sendGame"
-         :> ReqBody '[JSON] SendGameRequest
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "getUpdates"
-         :> QueryParam "offset" Int
-         :> QueryParam "limit" Int
-         :> QueryParam "timeout" Int
-         :> Get '[JSON] UpdatesResponse
-    :<|> TelegramToken :> "getFile"
-         :> QueryParam "file_id" Text
-         :> Get '[JSON] FileResponse
-    :<|> TelegramToken :> "getUserProfilePhotos"
-         :> QueryParam "user_id" Int
-         :> QueryParam "offset" Int
-         :> QueryParam "limit" Int
-         :> Get '[JSON] UserProfilePhotosResponse
-    :<|> TelegramToken :> "setWebhook"
-         :> QueryParam "url" Text
-         :> Get '[JSON] SetWebhookResponse
-    :<|> TelegramToken :> "setWebhook"
-         :> MultipartFormDataReqBody SetWebhookRequest
-         :> Post '[JSON] SetWebhookResponse
-    :<|> TelegramToken :> "deleteWebhook"
-         :> Post '[JSON] (Response Bool)
-    :<|> TelegramToken :> "getWebhookInfo"
-         :> Get '[JSON] GetWebhookInfoResponse
-    :<|> TelegramToken :> "answerInlineQuery"
-         :> ReqBody '[JSON] AnswerInlineQueryRequest
-         :> Post '[JSON] InlineQueryResponse
-    :<|> TelegramToken :> "answerCallbackQuery"
-         :> ReqBody '[JSON] AnswerCallbackQueryRequest
-         :> Post '[JSON] CallbackQueryResponse
-    :<|> TelegramToken :> "kickChatMember"
-         :> QueryParam "chat_id" Text
-         :> QueryParam "user_id" Int
-         :> Post '[JSON] KickChatMemberResponse
-    :<|> TelegramToken :> "leaveChat"
-         :> QueryParam "chat_id" Text
-         :> Post '[JSON] LeaveChatResponse
-    :<|> TelegramToken :> "unbanChatMember"
-         :> QueryParam "chat_id" Text
-         :> QueryParam "user_id" Int
-         :> Post '[JSON] UnbanChatMemberResponse
-    :<|> TelegramToken :> "getChat"
-         :> QueryParam "chat_id" Text
-         :> Post '[JSON] GetChatResponse
-    :<|> TelegramToken :> "getChatAdministrators"
-         :> QueryParam "chat_id" Text
-         :> Post '[JSON] GetChatAdministratorsResponse
-    :<|> TelegramToken :> "getChatMembersCount"
-         :> QueryParam "chat_id" Text
-         :> Post '[JSON] GetChatMembersCountResponse
-    :<|> TelegramToken :> "getChatMember"
-         :> QueryParam "chat_id" Text
-         :> QueryParam "user_id" Int
-         :> Post '[JSON] GetChatMemberResponse
-    :<|> TelegramToken :> "editMessageText"
-         :> ReqBody '[JSON] EditMessageTextRequest
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "editMessageCaption"
-         :> ReqBody '[JSON] EditMessageCaptionRequest
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "editMessageReplyMarkup"
-         :> ReqBody '[JSON] EditMessageReplyMarkupRequest
-         :> Post '[JSON] MessageResponse
-    :<|> TelegramToken :> "editMessageText"
-         :> ReqBody '[JSON] EditMessageTextRequest
-         :> Post '[JSON] (Response Bool)
-    :<|> TelegramToken :> "editMessageCaption"
-         :> ReqBody '[JSON] EditMessageCaptionRequest
-         :> Post '[JSON] (Response Bool)
-    :<|> TelegramToken :> "editMessageReplyMarkup"
-         :> ReqBody '[JSON] EditMessageReplyMarkupRequest
-         :> Post '[JSON] (Response Bool)
-
--- | Proxy for Thelegram Bot API
-api :: Proxy TelegramBotAPI
-api = Proxy
-
-getMe_                     :: Token -> ClientM GetMeResponse
-sendMessage_               :: Token -> SendMessageRequest -> ClientM MessageResponse
-forwardMessage_            :: Token -> ForwardMessageRequest -> ClientM MessageResponse
-uploadPhoto_               :: Token -> SendPhotoRequest FileUpload -> ClientM MessageResponse
-sendPhoto_                 :: Token -> SendPhotoRequest Text -> ClientM MessageResponse
-uploadAudio_               :: Token -> SendAudioRequest FileUpload -> ClientM MessageResponse
-sendAudio_                 :: Token -> SendAudioRequest Text -> ClientM MessageResponse
-uploadDocument_            :: Token -> SendDocumentRequest FileUpload -> ClientM MessageResponse
-sendDocument_              :: Token -> SendDocumentRequest Text -> ClientM MessageResponse
-uploadSticker_             :: Token -> SendStickerRequest FileUpload -> ClientM MessageResponse
-sendSticker_               :: Token -> SendStickerRequest Text -> ClientM MessageResponse
-uploadVideo_               :: Token -> SendVideoRequest FileUpload -> ClientM MessageResponse
-sendVideo_                 :: Token -> SendVideoRequest Text -> ClientM MessageResponse
-uploadVoice_               :: Token -> SendVoiceRequest FileUpload -> ClientM MessageResponse
-sendVoice_                 :: Token -> SendVoiceRequest Text -> ClientM MessageResponse
-sendLocation_              :: Token -> SendLocationRequest -> ClientM MessageResponse
-sendVenue_                 :: Token -> SendVenueRequest-> ClientM MessageResponse
-sendContact_               :: Token -> SendContactRequest -> ClientM MessageResponse
-sendChatAction_            :: Token -> SendChatActionRequest -> ClientM ChatActionResponse
-sendGame_                  :: Token -> SendGameRequest -> ClientM MessageResponse
-getUpdates_                :: Token -> Maybe Int -> Maybe Int -> Maybe Int -> ClientM UpdatesResponse
-getFile_                   :: Token -> Maybe Text -> ClientM FileResponse
-getUserProfilePhotos_      :: Token -> Maybe Int -> Maybe Int -> Maybe Int -> ClientM UserProfilePhotosResponse
-setWebhook_                :: Token -> Maybe Text -> ClientM SetWebhookResponse
-setWebhookWithCert_        :: Token -> SetWebhookRequest -> ClientM SetWebhookResponse
-deleteWebhook_             :: Token -> ClientM (Response Bool)
-getWebhookInfo_            :: Token -> ClientM GetWebhookInfoResponse
-answerInlineQuery_         :: Token -> AnswerInlineQueryRequest -> ClientM InlineQueryResponse
-answerCallbackQuery_       :: Token -> AnswerCallbackQueryRequest -> ClientM CallbackQueryResponse
-kickChatMember_            :: Token -> Maybe Text -> Maybe Int -> ClientM KickChatMemberResponse
-leaveChat_                 :: Token -> Maybe Text -> ClientM LeaveChatResponse
-unbanChatMember_           :: Token -> Maybe Text -> Maybe Int -> ClientM UnbanChatMemberResponse
-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
-editMessageText_           :: Token -> EditMessageTextRequest -> ClientM MessageResponse
-editMessageCaption_        :: Token -> EditMessageCaptionRequest -> ClientM MessageResponse
-editMessageReplyMarkup_    :: Token -> EditMessageReplyMarkupRequest -> ClientM MessageResponse
-editMessageText__          :: Token -> EditMessageTextRequest -> ClientM (Response Bool)
-editMessageCaption__       :: Token -> EditMessageCaptionRequest -> ClientM (Response Bool)
-editMessageReplyMarkup__   :: Token -> EditMessageReplyMarkupRequest -> ClientM (Response Bool)
-getMe_
-  :<|> sendMessage_
-  :<|> forwardMessage_
-  :<|> uploadPhoto_
-  :<|> sendPhoto_
-  :<|> uploadAudio_
-  :<|> sendAudio_
-  :<|> uploadDocument_
-  :<|> sendDocument_
-  :<|> uploadSticker_
-  :<|> sendSticker_
-  :<|> uploadVideo_
-  :<|> sendVideo_
-  :<|> uploadVoice_
-  :<|> sendVoice_
-  :<|> sendLocation_
-  :<|> sendVenue_
-  :<|> sendContact_
-  :<|> sendChatAction_
-  :<|> sendGame_
-  :<|> getUpdates_
-  :<|> getFile_
-  :<|> getUserProfilePhotos_
-  :<|> setWebhook_
-  :<|> setWebhookWithCert_
-  :<|> deleteWebhook_
-  :<|> getWebhookInfo_
-  :<|> answerInlineQuery_
-  :<|> answerCallbackQuery_
-  :<|> kickChatMember_
-  :<|> leaveChat_
-  :<|> unbanChatMember_
-  :<|> getChat_
-  :<|> getChatAdministrators_
-  :<|> getChatMembersCount_
-  :<|> getChatMember_
-  :<|> editMessageText_
-  :<|> editMessageCaption_
-  :<|> editMessageReplyMarkup_
-  :<|> editMessageText__
-  :<|> editMessageCaption__
-  :<|> editMessageReplyMarkup__ =
-      client api
-
--- | A simple method for testing your bot's auth token. Requires no parameters.
---   Returns basic information about the bot in form of a 'User' object.
-getMe :: Token -> Manager -> IO (Either ServantError GetMeResponse)
-getMe token manager = runClientM (getMe_ token) (ClientEnv manager telegramBaseUrl)
-
--- | Use this method to send text messages. On success, the sent 'Message' is returned.
-sendMessage :: Token -> SendMessageRequest -> Manager -> IO (Either ServantError MessageResponse)
-sendMessage = run telegramBaseUrl sendMessage_
-
--- | Use this method to forward messages of any kind. On success, the sent 'Message' is returned.
-forwardMessage :: Token -> ForwardMessageRequest -> Manager -> IO (Either ServantError MessageResponse)
-forwardMessage = run telegramBaseUrl forwardMessage_
-
--- | Use this method to upload and send photos. On success, the sent 'Message' is returned.
-uploadPhoto :: Token -> SendPhotoRequest FileUpload -> Manager -> IO (Either ServantError MessageResponse)
-uploadPhoto = run telegramBaseUrl uploadPhoto_
-
--- | Use this method to send photos that have already been uploaded. On success, the sent 'Message' is returned.
-sendPhoto :: Token -> SendPhotoRequest Text -> Manager -> IO (Either ServantError MessageResponse)
-sendPhoto = run telegramBaseUrl sendPhoto_
-
--- | Use this method to upload and send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. On success, the sent 'Message' is returned. Bots can currently send audio files of up to 50 MB in size, this limit may be changed in the future.
---
---       For backward compatibility, when the fields __title__ and __performer__ are both empty and the mime-type of the file to be sent is not _audio/mpeg_, the file will be sent as a playable voice message. For this to work, the audio must be in an .ogg file encoded with OPUS. This behavior will be phased out in the future. For sending voice messages, use the 'sendVoice' method instead.
-uploadAudio :: Token -> SendAudioRequest FileUpload -> Manager -> IO (Either ServantError MessageResponse)
-uploadAudio = run telegramBaseUrl uploadAudio_
-
--- | Use this method to send audio files that are already on the Telegram servers, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. On success, the sent 'Message' is returned. Bots can currently send audio files of up to 50 MB in size, this limit may be changed in the future.
---
---       For backward compatibility, when the fields __title__ and __performer__ are both empty and the mime-type of the file to be sent is not _audio/mpeg_, the file will be sent as a playable voice message. For this to work, the audio must be in an .ogg file encoded with OPUS. This behavior will be phased out in the future. For sending voice messages, use the 'sendVoice' method instead.
-sendAudio :: Token -> SendAudioRequest Text -> Manager -> IO (Either ServantError MessageResponse)
-sendAudio = run telegramBaseUrl sendAudio_
-
--- | Use this method to upload and send general files. On success, the sent 'Message' is returned. Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future.
-uploadDocument :: Token -> SendDocumentRequest FileUpload -> Manager -> IO (Either ServantError MessageResponse)
-uploadDocument = run telegramBaseUrl uploadDocument_
-
--- | Use this method to send general files that have already been uploaded. On success, the sent 'Message' is returned. Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future.
-sendDocument :: Token -> SendDocumentRequest Text -> Manager -> IO (Either ServantError MessageResponse)
-sendDocument = run telegramBaseUrl sendDocument_
-
--- | Use this method to upload and send .webp stickers. On success, the sent 'Message' is returned.
-uploadSticker :: Token -> SendStickerRequest FileUpload -> Manager -> IO (Either ServantError MessageResponse)
-uploadSticker = run telegramBaseUrl uploadSticker_
-
--- | Use this method to send .webp stickers that are already on the Telegram servers. On success, the sent 'Message' is returned.
-sendSticker :: Token -> SendStickerRequest Text -> Manager -> IO (Either ServantError MessageResponse)
-sendSticker = run telegramBaseUrl sendSticker_
-
--- | Use this method to upload and send video files. Telegram clients support mp4 videos (other formats may be sent as 'Document'). On success, the sent 'Message' is returned. Bots can currently send video files of up to 50 MB in size, this limit may be changed in the future.
-uploadVideo :: Token -> SendVideoRequest FileUpload -> Manager -> IO (Either ServantError MessageResponse)
-uploadVideo = run telegramBaseUrl uploadVideo_
-
--- | Use this method to send video files that are already on the Telegram servers. Telegram clients support mp4 videos (other formats may be sent as 'Document'). On success, the sent 'Message' is returned. Bots can currently send video files of up to 50 MB in size, this limit may be changed in the future.
-sendVideo :: Token -> SendVideoRequest Text -> Manager -> IO (Either ServantError MessageResponse)
-sendVideo = run telegramBaseUrl sendVideo_
-
--- | Use this method to upload and send audio files, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .ogg file encoded with OPUS (other formats may be sent as 'Audio' or 'Document'). On success, the sent 'Message' is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future.
-uploadVoice :: Token -> SendVoiceRequest FileUpload -> Manager -> IO (Either ServantError MessageResponse)
-uploadVoice = run telegramBaseUrl uploadVoice_
-
--- | Use this method to send audio files that are already on the telegram server, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .ogg file encoded with OPUS (other formats may be sent as 'Audio' or 'Document'). On success, the sent 'Message' is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future.
-sendVoice :: Token -> SendVoiceRequest Text -> Manager -> IO (Either ServantError MessageResponse)
-sendVoice = run telegramBaseUrl sendVoice_
-
--- | Use this method to send point on the map. On success, the sent 'Message' is returned.
-sendLocation :: Token -> SendLocationRequest -> Manager -> IO (Either ServantError MessageResponse)
-sendLocation = run telegramBaseUrl sendLocation_
-
--- | Use this method to send information about a venue. On success, the sent 'Message' is returned.
-sendVenue :: Token -> SendVenueRequest -> Manager -> IO (Either ServantError MessageResponse)
-sendVenue = run telegramBaseUrl sendVenue_
-
--- | Use this method to send information about a venue. On success, the sent 'Message' is returned.
-sendContact :: Token -> SendContactRequest -> Manager -> IO (Either ServantError MessageResponse)
-sendContact = run telegramBaseUrl sendContact_
-
--- | Use this method when you need to tell the user that something is happening on the bot's side.
---   The status is set for 5 seconds or less (when a message arrives from your bot,
---   Telegram clients clear its typing status).
-sendChatAction :: Token -> SendChatActionRequest -> Manager -> IO (Either ServantError ChatActionResponse)
-sendChatAction = run telegramBaseUrl sendChatAction_
-
--- | Use this method to send a game. On success, the sent 'Message' is returned.
-sendGame :: Token -> SendGameRequest -> Manager -> IO (Either ServantError MessageResponse)
-sendGame = run telegramBaseUrl sendGame_
-
--- | Use this method to receive incoming updates using long polling. An Array of 'Update' objects is returned.
-getUpdates
-    :: Token
-    -> Maybe Int -- ^ offset
-    -> Maybe Int -- ^ limit
-    -> Maybe Int -- ^ timeout
-    -> Manager
-    -> IO (Either ServantError UpdatesResponse)
-getUpdates token offset limit timeout manager = runClientM (getUpdates_ token offset limit timeout) $ ClientEnv manager telegramBaseUrl
-
--- | Use this method to get basic info about a file and prepare it for downloading. For the moment, bots can download files of up to 20MB in size. On success, a 'File' object is returned. The file can then be downloaded via the link @https://api.telegram.org/file/bot<token>/<file_path>@, where @<file_path>@ is taken from the response. It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling getFile again.
-getFile :: Token -> Text -> Manager -> IO (Either ServantError FileResponse)
-getFile token file_id manager = runClientM (getFile_ token (Just file_id)) $ ClientEnv manager telegramBaseUrl
-
--- | Use this method to get a list of profile pictures for a user. Returns a 'UserProfilePhotos' object.
-getUserProfilePhotos :: Token -> Int -> Maybe Int -> Maybe Int -> Manager -> IO (Either ServantError UserProfilePhotosResponse)
-getUserProfilePhotos token user_id offset limit manager = runClientM (getUserProfilePhotos_ token (Just user_id) offset limit) $ ClientEnv manager telegramBaseUrl
-
--- | Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized 'Update'. In case of an unsuccessful request, we will give up after a reasonable amount of attempts.
---
---       If you'd like to make sure that the Webhook request comes from Telegram, we recommend using a secret path in the URL, e.g. @https://www.example.com/<token>@. Since nobody else knows your bot‘s token, you can be pretty sure it’s us.
-setWebhook :: Token
-    -> Maybe Text -- ^ HTTPS url to send updates to. Use an empty string to remove webhook integration
-    -> Manager
-    -> IO (Either ServantError SetWebhookResponse)
-setWebhook token url manager = runClientM (setWebhook_ token url) $ ClientEnv manager telegramBaseUrl
-
--- | Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized 'Update'. In case of an unsuccessful request, we will give up after a reasonable amount of attempts.
---
---       If you'd like to make sure that the Webhook request comes from Telegram, we recommend using a secret path in the URL, e.g. @https://www.example.com/<token>@. Since nobody else knows your bot‘s token, you can be pretty sure it’s us.
-setWebhookWithCertificate :: Token -> SetWebhookRequest -> Manager -> IO (Either ServantError SetWebhookResponse)
-setWebhookWithCertificate = run telegramBaseUrl setWebhookWithCert_
-
-deleteWebhook :: Token -> Manager -> IO (Either ServantError (Response Bool))
-deleteWebhook token manager = runClientM (deleteWebhook_ token) $ ClientEnv manager telegramBaseUrl
-
--- | Contains information about the current status of a webhook.
-getWebhookInfo :: Token -> Manager -> IO (Either ServantError GetWebhookInfoResponse)
-getWebhookInfo token manager = runClientM (getWebhookInfo_ token) $ ClientEnv manager telegramBaseUrl
-
--- | Use this method to send answers to an inline query. No more than 50 results per query are allowed.
-answerInlineQuery :: Token -> AnswerInlineQueryRequest -> Manager -> IO (Either ServantError InlineQueryResponse)
-answerInlineQuery = run telegramBaseUrl answerInlineQuery_
-
--- | Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert.
-answerCallbackQuery :: Token -> AnswerCallbackQueryRequest -> Manager -> IO (Either ServantError CallbackQueryResponse)
-answerCallbackQuery = run telegramBaseUrl answerCallbackQuery_
-
--- | 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 chat_id user_id manager = runClientM (kickChatMember_ token (Just chat_id) (Just user_id)) $ ClientEnv manager telegramBaseUrl
-
--- | 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 token chat_id manager = runClientM (leaveChat_ token (Just chat_id)) $ ClientEnv manager telegramBaseUrl
-
--- | Use this method to unban a previously kicked user in a supergroup. The user will not return to the group automatically, but will be able to join via link, etc. The bot must be an administrator in the group for this to work.
-unbanChatMember :: Token -> Text -> Int -> Manager -> IO (Either ServantError UnbanChatMemberResponse)
-unbanChatMember token chat_id user_id manager = runClientM (unbanChatMember_ token (Just chat_id) (Just user_id)) $ ClientEnv manager telegramBaseUrl
-
--- | 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 token chat_id manager = runClientM (getChat_ token (Just chat_id)) $ ClientEnv manager telegramBaseUrl
-
--- | Use this method to get a list of administrators in a chat. On success, returns an Array of 'ChatMember' objects that contains information about all chat administrators except other bots. If the chat is a group or a supergroup and no administrators were appointed, only the creator will be returned.
-getChatAdministrators :: Token -> Text -> Manager -> IO (Either ServantError GetChatAdministratorsResponse)
-getChatAdministrators token chat_id manager = runClientM (getChatAdministrators_ token (Just chat_id)) $ ClientEnv manager telegramBaseUrl
-
--- | Use this method to get the number of members in a chat. Returns 'Int' on success.
-getChatMembersCount :: Token -> Text -> Manager -> IO (Either ServantError GetChatMembersCountResponse)
-getChatMembersCount token chat_id manager = runClientM (getChatMembersCount_ token (Just chat_id)) $ ClientEnv manager telegramBaseUrl
-
--- | Use this method to get information about a member of a chat. Returns a 'ChatMember' object on success.
-getChatMember :: Token -> Text -> Int -> Manager -> IO (Either ServantError GetChatMemberResponse)
-getChatMember token chat_id user_id manager = runClientM (getChatMember_ token (Just chat_id) (Just user_id)) $ ClientEnv manager telegramBaseUrl
-
--- | Use this method to edit text messages sent by the bot. On success, the edited 'Message' is returned, otherwise True is returned.
-editMessageText :: Token -> EditMessageTextRequest -> Manager -> IO (Either ServantError MessageResponse)
-editMessageText = run telegramBaseUrl editMessageText_
-
--- | Use this method to edit captions of messages sent by the bot. On success, the edited 'Message' is returned.
-editMessageCaption :: Token -> EditMessageCaptionRequest -> Manager -> IO (Either ServantError MessageResponse)
-editMessageCaption = run telegramBaseUrl editMessageCaption_
-
--- | Use this method to edit only the reply markup of messages sent by the bot. On success, the edited 'Message' is returned.
-editMessageReplyMarkup :: Token -> EditMessageReplyMarkupRequest -> Manager -> IO (Either ServantError MessageResponse)
-editMessageReplyMarkup = run telegramBaseUrl editMessageReplyMarkup_
-
--- | Use this method to edit text messages sent via the bot (for inline bots).
-editInlineMessageText :: Token -> EditMessageTextRequest -> Manager -> IO (Either ServantError (Response Bool))
-editInlineMessageText = run telegramBaseUrl editMessageText__
-
--- | Use this method to edit captions of messages sent via the bot (for inline bots).
-editInlineMessageCaption :: Token -> EditMessageCaptionRequest -> Manager -> IO (Either ServantError (Response Bool))
-editInlineMessageCaption = run telegramBaseUrl editMessageCaption__
-
--- | Use this method to edit only the reply markup of messages sent via the bot (for inline bots).
-editInlineMessageReplyMarkup :: Token -> EditMessageReplyMarkupRequest -> Manager -> IO (Either ServantError (Response Bool))
-editInlineMessageReplyMarkup = run telegramBaseUrl editMessageReplyMarkup__
-
-run :: BaseUrl -> (Token -> a -> ClientM b) -> Token -> a -> Manager -> IO (Either ServantError b)
-run b e t r m = runClientM (e t r) (ClientEnv m b)
+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators     #-}++module Web.Telegram.API.Bot.API+  ( -- * Functions+    module API+  , runClient+  , runClient'+  , runTelegramClient+    -- * API+  , TelegramBotAPI+  , api+    -- * Types+  , Token             (..)+  , TelegramClient+  ) where++import           Data.Proxy+import           Servant.API+import           Web.Telegram.API.Bot.API.Chats    as API+import           Web.Telegram.API.Bot.API.Core+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++type TelegramBotAPI =+       TelegramBotMessagesAPI+  :<|> TelegramBotUpdatesAPI+  :<|> TelegramBotChatsAPI+  :<|> TelegramBotEditAPI+  :<|> TelegramBotQueriesAPI+  :<|> TelegramBotGetAPI+  :<|> TelegramBotPaymentsAPI+  :<|> TelegramBotStickersAPI++-- | Proxy for Thelegram Bot API+api :: Proxy TelegramBotAPI+api = Proxy
+ src/Web/Telegram/API/Bot/API/Chats.hs view
@@ -0,0 +1,264 @@+{-# 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+  , getChatAdministratorsM+  , getChatMembersCount+  , getChatMembersCountM+  , getChatMember+  , getChatMemberM+  , setChatStickerSetM+  , deleteChatStickerSetM+    -- * API+  , TelegramBotChatsAPI+  , chatsApi+  ) where++import           Data.Proxy+import           Data.Text                        (Text)+import           Network.HTTP.Client              (Manager)+import           Servant.API+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+type TelegramBotChatsAPI =+         TelegramToken :> "kickChatMember"+         :> QueryParam "chat_id" Text+         :> QueryParam "user_id" Int+         :> QueryParam "until_date" Int+         :> Post '[JSON] KickChatMemberResponse+    :<|> TelegramToken :> "leaveChat"+         :> QueryParam "chat_id" Text+         :> Post '[JSON] LeaveChatResponse+    :<|> TelegramToken :> "unbanChatMember"+         :> 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+    :<|> TelegramToken :> "getChatAdministrators"+         :> QueryParam "chat_id" Text+         :> Post '[JSON] GetChatAdministratorsResponse+    :<|> TelegramToken :> "getChatMembersCount"+         :> QueryParam "chat_id" Text+         :> Post '[JSON] GetChatMembersCountResponse+    :<|> TelegramToken :> "getChatMember"+         :> 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 -> 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_+  :<|> 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 -- ^ 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) 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++-- | See 'leaveChat'+leaveChatM :: Text -> TelegramClient LeaveChatResponse+leaveChatM chatId = run_ leaveChat_ (Just chatId)++-- | Use this method to unban a previously kicked user in a supergroup. The user will not return to the group automatically, but will be able to join via link, etc. The bot must be an administrator in the group for this to work.+unbanChatMember :: Token -> Text -> Int -> Manager -> IO (Either ServantError UnbanChatMemberResponse)+unbanChatMember token chatId userId = runClient (unbanChatMemberM chatId userId) token++-- | See 'unbanChatMember'+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++-- | See 'getChat'+getChatM :: Text -> TelegramClient GetChatResponse+getChatM chatId = run_ getChat_ (Just chatId)++-- | Use this method to get a list of administrators in a chat. On success, returns an Array of 'ChatMember' objects that contains information about all chat administrators except other bots. If the chat is a group or a supergroup and no administrators were appointed, only the creator will be returned.+getChatAdministrators :: Token -> Text -> Manager -> IO (Either ServantError GetChatAdministratorsResponse)+getChatAdministrators = runM getChatAdministratorsM++-- | See 'getChatAdministrators'+getChatAdministratorsM :: Text -> TelegramClient GetChatAdministratorsResponse+getChatAdministratorsM chatId = run_ getChatAdministrators_ (Just chatId)++-- | Use this method to get the number of members in a chat. Returns 'Int' on success.+getChatMembersCount :: Token -> Text -> Manager -> IO (Either ServantError GetChatMembersCountResponse)+getChatMembersCount = runM getChatMembersCountM++-- | See 'getChatMembersCount'+getChatMembersCountM :: Text -> TelegramClient GetChatMembersCountResponse+getChatMembersCountM chatId = run_ getChatMembersCount_ (Just chatId)++-- | Use this method to get information about a member of a chat. Returns a 'ChatMember' object on success.+getChatMember :: Token -> Text -> Int -> Manager -> IO (Either ServantError GetChatMemberResponse)+getChatMember token chatId userId = runClient (getChatMemberM chatId userId) token++-- | 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
@@ -0,0 +1,63 @@+{-# LANGUAGE DataKinds                  #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings          #-}+{-# LANGUAGE TypeOperators              #-}++module Web.Telegram.API.Bot.API.Core+  (  -- * Types+    Token             (..)+  , TelegramToken+  , TelegramClient+  , run+  , run_+  , runM+  , asking+  , runClient+  , runClient'+  , runTelegramClient+  , telegramBaseUrl+  ) where++import           Control.Monad.Trans.Class+import           Control.Monad.Trans.Reader++import           Data.Text                  (Text)+import           Network.HTTP.Client        (Manager)+import           Servant.API+import           Servant.Client++-- | Telegram Bot's Token+newtype Token = Token Text+  deriving (Show, Eq, Ord, ToHttpApiData, FromHttpApiData)++-- | Type for token+type TelegramToken = Capture ":token" Token++type TelegramClient a = ReaderT Token ClientM a++telegramBaseUrl :: BaseUrl+telegramBaseUrl = BaseUrl Https "api.telegram.org" 443 ""++-- | Allows to run 'TelegramClient' against arbitrary url+runClient' :: TelegramClient a -> Token -> ClientEnv -> IO (Either ServantError a)+runClient' tcm token = runClientM (runReaderT tcm token)++-- | 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++run :: BaseUrl -> (Token -> a -> ClientM b) -> Token -> a -> Manager -> IO (Either ServantError b)+run b e t r m = runClientM (e t r) (ClientEnv m b)++run_ :: Monad m => (a -> b -> m c) -> b -> ReaderT a m c+run_ act request = asking $ flip act request++runM :: (r -> TelegramClient a) -> Token -> r -> Manager -> IO (Either ServantError a)+runM tcm token request = runClient (tcm request) token
+ src/Web/Telegram/API/Bot/API/Edit.hs view
@@ -0,0 +1,138 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators     #-}++module Web.Telegram.API.Bot.API.Edit+  ( -- * Functions+    editMessageText+  , editMessageTextM+  , editMessageCaption+  , editMessageCaptionM+  , editMessageReplyMarkup+  , editMessageReplyMarkupM+  , editInlineMessageText+  , editInlineMessageTextM+  , editInlineMessageCaption+  , editInlineMessageCaptionM+  , editInlineMessageReplyMarkup+  , editInlineMessageReplyMarkupM+  , editMessageLiveLocationM+  , stopMessageLiveLocationM+    -- * API+  , TelegramBotEditAPI+  , editApi+    -- * Types+  ) where++import           Data.Proxy+import           Network.HTTP.Client            (Manager)+import           Servant.API+import           Servant.Client          hiding (Response)+import           Web.Telegram.API.Bot.API.Core+import           Web.Telegram.API.Bot.Requests+import           Web.Telegram.API.Bot.Responses++-- | Telegram Bot API+type TelegramBotEditAPI =+         TelegramToken :> "editMessageText"+         :> ReqBody '[JSON] EditMessageTextRequest+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "editMessageCaption"+         :> ReqBody '[JSON] EditMessageCaptionRequest+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "editMessageReplyMarkup"+         :> ReqBody '[JSON] EditMessageReplyMarkupRequest+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "editMessageText"+         :> ReqBody '[JSON] EditMessageTextRequest+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "editMessageCaption"+         :> ReqBody '[JSON] EditMessageCaptionRequest+         :> Post '[JSON] (Response Bool)+    :<|> 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+editApi :: Proxy TelegramBotEditAPI+editApi = Proxy++editMessageText_           :: Token -> EditMessageTextRequest -> ClientM MessageResponse+editMessageCaption_        :: Token -> EditMessageCaptionRequest -> ClientM MessageResponse+editMessageReplyMarkup_    :: Token -> EditMessageReplyMarkupRequest -> ClientM MessageResponse+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.+editMessageText :: Token -> EditMessageTextRequest -> Manager -> IO (Either ServantError MessageResponse)+editMessageText = runM editMessageTextM++-- | See 'editMessageText'+editMessageTextM :: EditMessageTextRequest -> TelegramClient MessageResponse+editMessageTextM = run_ editMessageText_++-- | Use this method to edit captions of messages sent by the bot. On success, the edited 'Message' is returned.+editMessageCaption :: Token -> EditMessageCaptionRequest -> Manager -> IO (Either ServantError MessageResponse)+editMessageCaption = runM editMessageCaptionM++-- | See 'editMessageCaption'+editMessageCaptionM :: EditMessageCaptionRequest -> TelegramClient MessageResponse+editMessageCaptionM = run_ editMessageCaption_++-- | Use this method to edit only the reply markup of messages sent by the bot. On success, the edited 'Message' is returned.+editMessageReplyMarkup :: Token -> EditMessageReplyMarkupRequest -> Manager -> IO (Either ServantError MessageResponse)+editMessageReplyMarkup = runM editMessageReplyMarkupM++editMessageReplyMarkupM :: EditMessageReplyMarkupRequest -> TelegramClient MessageResponse+editMessageReplyMarkupM = run_ editMessageReplyMarkup_++-- | Use this method to edit text messages sent via the bot (for inline bots).+editInlineMessageText :: Token -> EditMessageTextRequest -> Manager -> IO (Either ServantError (Response Bool))+editInlineMessageText = runM editInlineMessageTextM++-- | See 'editInlineMessageText'+editInlineMessageTextM :: EditMessageTextRequest -> TelegramClient (Response Bool)+editInlineMessageTextM = run_ editMessageText__++-- | Use this method to edit captions of messages sent via the bot (for inline bots).+editInlineMessageCaption :: Token -> EditMessageCaptionRequest -> Manager -> IO (Either ServantError (Response Bool))+editInlineMessageCaption = runM editInlineMessageCaptionM++-- | See 'editInlineMessageCaption'+editInlineMessageCaptionM :: EditMessageCaptionRequest -> TelegramClient (Response Bool)+editInlineMessageCaptionM = run_ editMessageCaption__++-- | Use this method to edit only the reply markup of messages sent via the bot (for inline bots).+editInlineMessageReplyMarkup :: Token -> EditMessageReplyMarkupRequest -> Manager -> IO (Either ServantError (Response Bool))+editInlineMessageReplyMarkup = runM editInlineMessageReplyMarkupM++-- | 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/Get.hs view
@@ -0,0 +1,75 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators     #-}++module Web.Telegram.API.Bot.API.Get+  ( -- * Functions+    getMe+  , getMeM+  , getFile+  , getFileM+  , getUserProfilePhotos+  , getUserProfilePhotosM+    -- * API+  , TelegramBotGetAPI+  , getApi+    -- * Types+  ) where++import           Data.Proxy+import           Data.Text                      (Text)+import           Network.HTTP.Client            (Manager)+import           Servant.API+import           Servant.Client+import           Web.Telegram.API.Bot.API.Core+import           Web.Telegram.API.Bot.Responses++-- | Telegram Bot API+type TelegramBotGetAPI =+         TelegramToken :> "getMe"+         :> Get '[JSON] GetMeResponse+    :<|> TelegramToken :> "getFile"+         :> QueryParam "file_id" Text+         :> Get '[JSON] FileResponse+    :<|> TelegramToken :> "getUserProfilePhotos"+         :> QueryParam "user_id" Integer+         :> QueryParam "offset" Int+         :> QueryParam "limit" Int+         :> Get '[JSON] UserProfilePhotosResponse++-- | Proxy for Thelegram Bot API+getApi :: Proxy TelegramBotGetAPI+getApi = Proxy++getMe_                     :: Token -> ClientM GetMeResponse+getFile_                   :: Token -> Maybe Text -> ClientM FileResponse+getUserProfilePhotos_      :: Token -> Maybe Integer -> Maybe Int -> Maybe Int -> ClientM UserProfilePhotosResponse+getMe_+  :<|> getFile_+  :<|> getUserProfilePhotos_+     = client getApi++-- | A simple method for testing your bot's auth token. Requires no parameters.+--   Returns basic information about the bot in form of a 'User' object.+getMe :: Token -> Manager -> IO (Either ServantError GetMeResponse)+getMe = runClient getMeM++-- | See `getMe`+getMeM :: TelegramClient GetMeResponse+getMeM = asking getMe_++-- | Use this method to get basic info about a file and prepare it for downloading. For the moment, bots can download files of up to 20MB in size. On success, a 'File' object is returned. The file can then be downloaded via the link @https://api.telegram.org/file/bot<token>/<file_path>@, where @<file_path>@ is taken from the response. It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling getFile again.+getFile :: Token -> Text -> Manager -> IO (Either ServantError FileResponse)+getFile token fileId = runClient (getFileM fileId) token++-- | See 'getFile'+getFileM :: Text -> TelegramClient FileResponse+getFileM fileId = run_ getFile_ (Just fileId)++-- | Use this method to get a list of profile pictures for a user. Returns a 'UserProfilePhotos' object.+getUserProfilePhotos :: Token -> Integer -> Maybe Int -> Maybe Int -> Manager -> IO (Either ServantError UserProfilePhotosResponse)+getUserProfilePhotos token userId offset limit = runClient (getUserProfilePhotosM userId offset limit) token++-- | See 'getUserProfilePhotos'+getUserProfilePhotosM :: Integer -> Maybe Int -> Maybe Int -> TelegramClient UserProfilePhotosResponse+getUserProfilePhotosM userId offset limit = asking $ \t -> getUserProfilePhotos_ t (Just userId) offset limit
+ src/Web/Telegram/API/Bot/API/Messages.hs view
@@ -0,0 +1,361 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators     #-}++module Web.Telegram.API.Bot.API.Messages+  ( -- * Functions+    sendMessage+  , sendMessageM+  , forwardMessage+  , forwardMessageM+  , uploadPhoto+  , uploadPhotoM+  , sendPhoto+  , sendPhotoM+  , uploadAudio+  , uploadAudioM+  , sendAudio+  , sendAudioM+  , uploadDocument+  , uploadDocumentM+  , sendDocument+  , sendDocumentM+  , uploadSticker+  , uploadStickerM+  , sendSticker+  , sendStickerM+  , uploadVideo+  , uploadVideoM+  , sendVideo+  , sendVideoM+  , uploadVoice+  , uploadVoiceM+  , sendVoice+  , sendVoiceM+  , uploadVideoNote+  , uploadVideoNoteM+  , sendVideoNote+  , sendVideoNoteM+  , sendMediaGroupM+  , sendLocation+  , sendLocationM+  , sendVenue+  , sendVenueM+  , sendContact+  , sendContactM+  , sendChatAction+  , sendChatActionM+  , sendGame+  , sendGameM+    -- * API+  , TelegramBotMessagesAPI+  , messagesApi+    -- * Types+  ) where++import           Data.Proxy+import           Data.Text                        (Text)+import           Network.HTTP.Client              (Manager)+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++-- | Telegram Bot API+type TelegramBotMessagesAPI =+         TelegramToken :> "sendMessage"+         :> ReqBody '[JSON] SendMessageRequest+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "forwardMessage"+         :> ReqBody '[JSON] ForwardMessageRequest+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendPhoto"+         :> MultipartFormDataReqBody (SendPhotoRequest FileUpload)+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendPhoto"+         :> ReqBody '[JSON] (SendPhotoRequest Text)+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendAudio"+         :> MultipartFormDataReqBody (SendAudioRequest FileUpload)+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendAudio"+         :> ReqBody '[JSON] (SendAudioRequest Text)+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendDocument"+         :> MultipartFormDataReqBody (SendDocumentRequest FileUpload)+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendDocument"+         :> ReqBody '[JSON] (SendDocumentRequest Text)+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendSticker"+         :> MultipartFormDataReqBody (SendStickerRequest FileUpload)+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendSticker"+         :> ReqBody '[JSON] (SendStickerRequest Text)+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendVideo"+         :> MultipartFormDataReqBody (SendVideoRequest FileUpload)+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendVideo"+         :> ReqBody '[JSON] (SendVideoRequest Text)+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendVoice"+         :> MultipartFormDataReqBody (SendVoiceRequest FileUpload)+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendVoice"+         :> ReqBody '[JSON] (SendVoiceRequest Text)+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendVideoNote"+         :> MultipartFormDataReqBody (SendVideoNoteRequest FileUpload)+         :> Post '[JSON] MessageResponse+    :<|> 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+    :<|> TelegramToken :> "sendVenue"+         :> ReqBody '[JSON] SendVenueRequest+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendContact"+         :> ReqBody '[JSON] SendContactRequest+         :> Post '[JSON] MessageResponse+    :<|> TelegramToken :> "sendChatAction"+         :> ReqBody '[JSON] SendChatActionRequest+         :> Post '[JSON] ChatActionResponse+    :<|> TelegramToken :> "sendGame"+         :> ReqBody '[JSON] SendGameRequest+         :> Post '[JSON] MessageResponse++-- | Proxy for Thelegram Bot API+messagesApi :: Proxy TelegramBotMessagesAPI+messagesApi = Proxy++sendMessage_               :: Token -> SendMessageRequest -> ClientM MessageResponse+forwardMessage_            :: Token -> ForwardMessageRequest -> ClientM MessageResponse+uploadPhoto_               :: Token -> SendPhotoRequest FileUpload -> ClientM MessageResponse+sendPhoto_                 :: Token -> SendPhotoRequest Text -> ClientM MessageResponse+uploadAudio_               :: Token -> SendAudioRequest FileUpload -> ClientM MessageResponse+sendAudio_                 :: Token -> SendAudioRequest Text -> ClientM MessageResponse+uploadDocument_            :: Token -> SendDocumentRequest FileUpload -> ClientM MessageResponse+sendDocument_              :: Token -> SendDocumentRequest Text -> ClientM MessageResponse+uploadSticker_             :: Token -> SendStickerRequest FileUpload -> ClientM MessageResponse+sendSticker_               :: Token -> SendStickerRequest Text -> ClientM MessageResponse+uploadVideo_               :: Token -> SendVideoRequest FileUpload -> ClientM MessageResponse+sendVideo_                 :: Token -> SendVideoRequest Text -> ClientM MessageResponse+uploadVoice_               :: Token -> SendVoiceRequest FileUpload -> ClientM MessageResponse+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+sendChatAction_            :: Token -> SendChatActionRequest -> ClientM ChatActionResponse+sendGame_                  :: Token -> SendGameRequest -> ClientM MessageResponse+sendMessage_+  :<|> forwardMessage_+  :<|> uploadPhoto_+  :<|> sendPhoto_+  :<|> uploadAudio_+  :<|> sendAudio_+  :<|> uploadDocument_+  :<|> sendDocument_+  :<|> uploadSticker_+  :<|> sendSticker_+  :<|> uploadVideo_+  :<|> sendVideo_+  :<|> uploadVoice_+  :<|> sendVoice_+  :<|> uploadVideoNote_+  :<|> sendVideoNote_+  :<|> sendMediaGroup_+  :<|> sendLocation_+  :<|> sendVenue_+  :<|> sendContact_+  :<|> sendChatAction_+  :<|> sendGame_+     = client messagesApi++-- | Use this method to send text messages. On success, the sent 'Message' is returned.+sendMessage :: Token -> SendMessageRequest -> Manager -> IO (Either ServantError MessageResponse)+sendMessage = runM sendMessageM++-- | See 'sendMessage'+sendMessageM :: SendMessageRequest -> TelegramClient MessageResponse+sendMessageM = run_ sendMessage_++-- | Use this method to forward messages of any kind. On success, the sent 'Message' is returned.+forwardMessage :: Token -> ForwardMessageRequest -> Manager -> IO (Either ServantError MessageResponse)+forwardMessage = runM forwardMessageM++-- | See 'forwardMessage'+forwardMessageM :: ForwardMessageRequest -> TelegramClient MessageResponse+forwardMessageM = run_ forwardMessage_++-- | Use this method to upload and send photos. On success, the sent 'Message' is returned.+uploadPhoto :: Token -> SendPhotoRequest FileUpload -> Manager -> IO (Either ServantError MessageResponse)+uploadPhoto = runM uploadPhotoM++-- | See 'uploadPhoto'+uploadPhotoM :: SendPhotoRequest FileUpload -> TelegramClient MessageResponse+uploadPhotoM = run_ uploadPhoto_++-- | Use this method to send photos that have already been uploaded. On success, the sent 'Message' is returned.+sendPhoto :: Token -> SendPhotoRequest Text -> Manager -> IO (Either ServantError MessageResponse)+sendPhoto = runM sendPhotoM++-- | See 'sendPhoto'+sendPhotoM :: SendPhotoRequest Text -> TelegramClient MessageResponse+sendPhotoM = run_ sendPhoto_++-- | Use this method to upload and send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. On success, the sent 'Message' is returned. Bots can currently send audio files of up to 50 MB in size, this limit may be changed in the future.+--+--       For backward compatibility, when the fields __title__ and __performer__ are both empty and the mime-type of the file to be sent is not _audio/mpeg_, the file will be sent as a playable voice message. For this to work, the audio must be in an .ogg file encoded with OPUS. This behavior will be phased out in the future. For sending voice messages, use the 'sendVoice' method instead.+uploadAudio :: Token -> SendAudioRequest FileUpload -> Manager -> IO (Either ServantError MessageResponse)+uploadAudio = runM uploadAudioM++-- | See 'uploadAudio'+uploadAudioM :: SendAudioRequest FileUpload -> TelegramClient MessageResponse+uploadAudioM = run_ uploadAudio_++-- | Use this method to send audio files that are already on the Telegram servers, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. On success, the sent 'Message' is returned. Bots can currently send audio files of up to 50 MB in size, this limit may be changed in the future.+--+--       For backward compatibility, when the fields __title__ and __performer__ are both empty and the mime-type of the file to be sent is not _audio/mpeg_, the file will be sent as a playable voice message. For this to work, the audio must be in an .ogg file encoded with OPUS. This behavior will be phased out in the future. For sending voice messages, use the 'sendVoice' method instead.+sendAudio :: Token -> SendAudioRequest Text -> Manager -> IO (Either ServantError MessageResponse)+sendAudio = runM sendAudioM++-- | See 'sendAudio'+sendAudioM :: SendAudioRequest Text -> TelegramClient MessageResponse+sendAudioM = run_ sendAudio_++-- | Use this method to upload and send general files. On success, the sent 'Message' is returned. Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future.+uploadDocument :: Token -> SendDocumentRequest FileUpload -> Manager -> IO (Either ServantError MessageResponse)+uploadDocument = runM uploadDocumentM++-- | See 'uploadDocument'+uploadDocumentM :: SendDocumentRequest FileUpload -> TelegramClient MessageResponse+uploadDocumentM = run_ uploadDocument_++-- | Use this method to send general files that have already been uploaded. On success, the sent 'Message' is returned. Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future.+sendDocument :: Token -> SendDocumentRequest Text -> Manager -> IO (Either ServantError MessageResponse)+sendDocument = runM sendDocumentM++-- | See 'sendDocument'+sendDocumentM :: SendDocumentRequest Text -> TelegramClient MessageResponse+sendDocumentM = run_ sendDocument_++-- | Use this method to upload and send .webp stickers. On success, the sent 'Message' is returned.+uploadSticker :: Token -> SendStickerRequest FileUpload -> Manager -> IO (Either ServantError MessageResponse)+uploadSticker = runM uploadStickerM++-- | See 'uploadSticker'+uploadStickerM :: SendStickerRequest FileUpload -> TelegramClient MessageResponse+uploadStickerM = run_ uploadSticker_++-- | Use this method to send .webp stickers that are already on the Telegram servers. On success, the sent 'Message' is returned.+sendSticker :: Token -> SendStickerRequest Text -> Manager -> IO (Either ServantError MessageResponse)+sendSticker = runM sendStickerM++-- | See 'sendSticker'+sendStickerM :: SendStickerRequest Text -> TelegramClient MessageResponse+sendStickerM = run_ sendSticker_++-- | Use this method to upload and send video files. Telegram clients support mp4 videos (other formats may be sent as 'Document'). On success, the sent 'Message' is returned. Bots can currently send video files of up to 50 MB in size, this limit may be changed in the future.+uploadVideo :: Token -> SendVideoRequest FileUpload -> Manager -> IO (Either ServantError MessageResponse)+uploadVideo = runM uploadVideoM++-- | See 'uploadVideo'+uploadVideoM :: SendVideoRequest FileUpload -> TelegramClient MessageResponse+uploadVideoM = run_ uploadVideo_++-- | Use this method to send video files that are already on the Telegram servers. Telegram clients support mp4 videos (other formats may be sent as 'Document'). On success, the sent 'Message' is returned. Bots can currently send video files of up to 50 MB in size, this limit may be changed in the future.+sendVideo :: Token -> SendVideoRequest Text -> Manager -> IO (Either ServantError MessageResponse)+sendVideo = runM sendVideoM++-- | See 'sendVideo'+sendVideoM :: SendVideoRequest Text -> TelegramClient MessageResponse+sendVideoM = run_ sendVideo_++-- | Use this method to upload and send audio files, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .ogg file encoded with OPUS (other formats may be sent as 'Audio' or 'Document'). On success, the sent 'Message' is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future.+uploadVoice :: Token -> SendVoiceRequest FileUpload -> Manager -> IO (Either ServantError MessageResponse)+uploadVoice = runM uploadVoiceM++-- | See 'uploadVoice'+uploadVoiceM :: SendVoiceRequest FileUpload -> TelegramClient MessageResponse+uploadVoiceM = run_ uploadVoice_++-- | Use this method to send audio files that are already on the telegram server, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .ogg file encoded with OPUS (other formats may be sent as 'Audio' or 'Document'). On success, the sent 'Message' is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future.+sendVoice :: Token -> SendVoiceRequest Text -> Manager -> IO (Either ServantError MessageResponse)+sendVoice = runM sendVoiceM++-- | See 'sendVoice'+sendVoiceM :: SendVoiceRequest Text -> TelegramClient MessageResponse+sendVoiceM = run_ sendVoice_++-- | As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned.+uploadVideoNote :: Token -> SendVideoNoteRequest FileUpload -> Manager -> IO (Either ServantError MessageResponse)+uploadVideoNote = runM uploadVideoNoteM++-- | See 'uploadVideoNote'+uploadVideoNoteM :: SendVideoNoteRequest FileUpload -> TelegramClient MessageResponse+uploadVideoNoteM = run_ uploadVideoNote_++-- | As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent Message is returned.+sendVideoNote :: Token -> SendVideoNoteRequest Text -> Manager -> IO (Either ServantError MessageResponse)+sendVideoNote = runM sendVideoNoteM++-- | See 'sendVoice'+sendVideoNoteM :: SendVideoNoteRequest Text -> TelegramClient MessageResponse+sendVideoNoteM = run_ sendVideoNote_++-- | Use this method to send point on the map. On success, the sent 'Message' is returned.+sendLocation :: Token -> SendLocationRequest -> Manager -> IO (Either ServantError MessageResponse)+sendLocation = runM sendLocationM++-- | 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)+sendVenue = runM sendVenueM++-- | See 'sendVenue'+sendVenueM :: SendVenueRequest -> TelegramClient MessageResponse+sendVenueM = run_ sendVenue_++-- | Use this method to send information about a venue. On success, the sent 'Message' is returned.+sendContact :: Token -> SendContactRequest -> Manager -> IO (Either ServantError MessageResponse)+sendContact = runM sendContactM++-- | See 'sendContact'+sendContactM :: SendContactRequest -> TelegramClient MessageResponse+sendContactM = run_ sendContact_++-- | Use this method when you need to tell the user that something is happening on the bot's side.+--   The status is set for 5 seconds or less (when a message arrives from your bot,+--   Telegram clients clear its typing status).+sendChatAction :: Token -> SendChatActionRequest -> Manager -> IO (Either ServantError ChatActionResponse)+sendChatAction = runM sendChatActionM++-- | See 'sendChatAction'+sendChatActionM :: SendChatActionRequest -> TelegramClient ChatActionResponse+sendChatActionM = run_ sendChatAction_++-- | Use this method to send a game. On success, the sent 'Message' is returned.+sendGame :: Token -> SendGameRequest -> Manager -> IO (Either ServantError MessageResponse)+sendGame = runM sendGameM++-- | See 'sendGame'+sendGameM :: SendGameRequest -> TelegramClient MessageResponse+sendGameM = run_ sendGame_
+ src/Web/Telegram/API/Bot/API/Payments.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators     #-}++module Web.Telegram.API.Bot.API.Payments+  ( -- * Functions+    sendInvoiceM+  , answerShippingQueryM+  , answerPreCheckoutQueryM+    -- * API+  , TelegramBotPaymentsAPI+  , paymentsApi+    -- * Types+  ) where++import           Data.Proxy+import           Servant.API+import           Servant.Client+import           Web.Telegram.API.Bot.API.Core+import           Web.Telegram.API.Bot.Requests+import           Web.Telegram.API.Bot.Responses++type TelegramBotPaymentsAPI =+        TelegramToken :> "sendInvoice"+        :> ReqBody '[JSON] SendInvoiceRequest+        :> Post '[JSON] MessageResponse+   :<|> TelegramToken :> "answerShippingQuery"+        :> ReqBody '[JSON] AnswerShippingQueryRequest+        :> Post '[JSON] AnswerShippingQueryResponse+   :<|> TelegramToken :> "answerPreCheckoutQuery"+        :> ReqBody '[JSON] AnswerPreCheckoutQueryRequest+        :> Post '[JSON] AnswerPreCheckoutQueryResponse++paymentsApi :: Proxy TelegramBotPaymentsAPI+paymentsApi = Proxy++sendInvoice_ :: Token -> SendInvoiceRequest -> ClientM MessageResponse+answerShippingQuery_ :: Token -> AnswerShippingQueryRequest -> ClientM AnswerShippingQueryResponse+answerPreCheckoutQuery_ :: Token -> AnswerPreCheckoutQueryRequest -> ClientM AnswerPreCheckoutQueryResponse+sendInvoice_+  :<|> answerShippingQuery_+  :<|> answerPreCheckoutQuery_+     = client paymentsApi++sendInvoiceM :: SendInvoiceRequest -> TelegramClient MessageResponse+sendInvoiceM = run_ sendInvoice_++answerShippingQueryM :: AnswerShippingQueryRequest -> TelegramClient AnswerShippingQueryResponse+answerShippingQueryM = run_ answerShippingQuery_++answerPreCheckoutQueryM :: AnswerPreCheckoutQueryRequest -> TelegramClient AnswerPreCheckoutQueryResponse+answerPreCheckoutQueryM = run_ answerPreCheckoutQuery_
+ src/Web/Telegram/API/Bot/API/Queries.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators     #-}++module Web.Telegram.API.Bot.API.Queries+  ( -- * Functions+    answerInlineQuery+  , answerInlineQueryM+  , answerCallbackQuery+  , answerCallbackQueryM+    -- * API+  , TelegramBotQueriesAPI+  , queriesApi+    -- * Types+  ) where++import           Data.Proxy+import           Network.HTTP.Client            (Manager)+import           Servant.API+import           Servant.Client+import           Web.Telegram.API.Bot.API.Core+import           Web.Telegram.API.Bot.Requests+import           Web.Telegram.API.Bot.Responses++-- | Telegram Bot API+type TelegramBotQueriesAPI =+         TelegramToken :> "answerInlineQuery"+         :> ReqBody '[JSON] AnswerInlineQueryRequest+         :> Post '[JSON] InlineQueryResponse+    :<|> TelegramToken :> "answerCallbackQuery"+         :> ReqBody '[JSON] AnswerCallbackQueryRequest+         :> Post '[JSON] CallbackQueryResponse+++-- | Proxy for Thelegram Bot API+queriesApi :: Proxy TelegramBotQueriesAPI+queriesApi = Proxy++answerInlineQuery_         :: Token -> AnswerInlineQueryRequest -> ClientM InlineQueryResponse+answerCallbackQuery_       :: Token -> AnswerCallbackQueryRequest -> ClientM CallbackQueryResponse+answerInlineQuery_ :<|> answerCallbackQuery_+     = client queriesApi++-- | Use this method to send answers to an inline query. No more than 50 results per query are allowed.+answerInlineQuery :: Token -> AnswerInlineQueryRequest -> Manager -> IO (Either ServantError InlineQueryResponse)+answerInlineQuery = runM answerInlineQueryM++-- | See 'answerInlineQuery'+answerInlineQueryM :: AnswerInlineQueryRequest -> TelegramClient InlineQueryResponse+answerInlineQueryM = run_ answerInlineQuery_++-- | Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert.+answerCallbackQuery :: Token -> AnswerCallbackQueryRequest -> Manager -> IO (Either ServantError CallbackQueryResponse)+answerCallbackQuery = runM answerCallbackQueryM++-- | See 'answerCallbackQuery'+answerCallbackQueryM :: AnswerCallbackQueryRequest -> TelegramClient CallbackQueryResponse+answerCallbackQueryM = run_ answerCallbackQuery_
+ 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
@@ -0,0 +1,122 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators     #-}++module Web.Telegram.API.Bot.API.Updates+  ( -- * Functions+    getUpdates+  , getUpdatesM+  , getUpdatesM'+  , setWebhook+  , setWebhookM+  , setWebhookWithCertificate+  , setWebhookWithCertificateM+  , deleteWebhook+  , deleteWebhookM+  , getWebhookInfo+  , getWebhookInfoM+    -- * API+  , TelegramBotUpdatesAPI+  , updatesApi+  ) where++import           Data.Maybe+import           Data.Proxy+import           Data.Text                        (Text, empty)+import           Network.HTTP.Client              (Manager)+import           Servant.API+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+type TelegramBotUpdatesAPI =+         TelegramToken :> "getUpdates"+         :> ReqBody '[JSON] GetUpdatesRequest+         :> Get '[JSON] UpdatesResponse+    :<|> TelegramToken :> "setWebhook"+         :> ReqBody '[JSON] SetWebhookRequest+         :> Get '[JSON] SetWebhookResponse+    :<|> TelegramToken :> "setWebhook"+         :> MultipartFormDataReqBody SetWebhookRequest+         :> Post '[JSON] SetWebhookResponse+    :<|> TelegramToken :> "deleteWebhook"+         :> Post '[JSON] (Response Bool)+    :<|> TelegramToken :> "getWebhookInfo"+         :> Get '[JSON] GetWebhookInfoResponse++-- | Proxy for Thelegram Bot API to configure your bot+updatesApi :: Proxy TelegramBotUpdatesAPI+updatesApi = Proxy++getUpdates_                :: Token -> GetUpdatesRequest -> ClientM UpdatesResponse+setWebhook_                :: Token -> SetWebhookRequest -> ClientM SetWebhookResponse+setWebhookWithCert_        :: Token -> SetWebhookRequest -> ClientM SetWebhookResponse+deleteWebhook_             :: Token -> ClientM (Response Bool)+getWebhookInfo_            :: Token -> ClientM GetWebhookInfoResponse+getUpdates_+  :<|> setWebhook_+  :<|> setWebhookWithCert_+  :<|> deleteWebhook_+  :<|> getWebhookInfo_ = client updatesApi++-- | Use this method to receive incoming updates using long polling. A list of 'Update' objects is returned. Use `getUpdatesM` for more features+getUpdates+    :: Token+    -> Maybe Int -- ^ offset+    -> Maybe Int -- ^ limit+    -> Maybe Int -- ^ timeout+    -> Manager+    -> IO (Either ServantError UpdatesResponse)+getUpdates token offset limit timeout = runClient (getUpdatesM request) token+    where request = GetUpdatesRequest offset limit timeout Nothing++-- | Get update with default parameters See 'getUpdates' for details.+getUpdatesM' :: TelegramClient UpdatesResponse+getUpdatesM' = getUpdatesM getUpdatesRequest++-- | See 'getUpdates'+getUpdatesM :: GetUpdatesRequest -> TelegramClient UpdatesResponse+getUpdatesM = run_ getUpdates_++-- | Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized 'Update'. In case of an unsuccessful request, we will give up after a reasonable amount of attempts.+--+--       If you'd like to make sure that the Webhook request comes from Telegram, we recommend using a secret path in the URL, e.g. @https://www.example.com/<token>@. Since nobody else knows your bot‘s token, you can be pretty sure it’s us.+setWebhook :: Token+    -> Maybe Text -- ^ HTTPS url to send updates to. Use an empty string to remove webhook integration+    -> Manager+    -> IO (Either ServantError SetWebhookResponse)+setWebhook token url = runClient (setWebhookM request) token+    where request = setWebhookRequest' $ fromMaybe empty url++-- | See 'setWebhook'+setWebhookM :: SetWebhookRequest -> TelegramClient SetWebhookResponse+setWebhookM = run_ setWebhook_++-- | Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized 'Update'. In case of an unsuccessful request, we will give up after a reasonable amount of attempts.+--+--       If you'd like to make sure that the Webhook request comes from Telegram, we recommend using a secret path in the URL, e.g. @https://www.example.com/<token>@. Since nobody else knows your bot‘s token, you can be pretty sure it’s us.+setWebhookWithCertificate :: Token -> SetWebhookRequest -> Manager -> IO (Either ServantError SetWebhookResponse)+setWebhookWithCertificate = runM setWebhookWithCertificateM++-- | See 'setWebhookWithCertificate'+setWebhookWithCertificateM :: SetWebhookRequest -> TelegramClient SetWebhookResponse+setWebhookWithCertificateM = run_ setWebhookWithCert_++-- | Use this method to remove webhook integration if you decide to switch back to 'getUpdates'. Returns True on success.+deleteWebhook :: Token -> Manager -> IO (Either ServantError (Response Bool))+deleteWebhook = runClient deleteWebhookM++-- | See 'deleteWebhook'+deleteWebhookM :: TelegramClient (Response Bool)+deleteWebhookM = asking deleteWebhook_++-- | Contains information about the current status of a webhook.+getWebhookInfo :: Token -> Manager -> IO (Either ServantError GetWebhookInfoResponse)+getWebhookInfo = runClient getWebhookInfoM++-- | See 'getWebhookInfo'+getWebhookInfoM :: TelegramClient GetWebhookInfoResponse+getWebhookInfoM = asking getWebhookInfo_
src/Web/Telegram/API/Bot/Data.hs view
@@ -1,879 +1,1200 @@-{-# LANGUAGE DeriveGeneric     #-}
-{-# LANGUAGE DeriveAnyClass    #-}
-{-# LANGUAGE OverloadedStrings #-}
-
--- | This module contains objects which represent data of Telegram Bot API responses
-module Web.Telegram.API.Bot.Data
-    ( -- * Types
-      User                          (..)
-    , ChatMember                    (..)
-    , Chat                          (..)
-    , Message                       (..)
-    , MessageEntity                 (..)
-    , PhotoSize                     (..)
-    , Audio                         (..)
-    , Document                      (..)
-    , Game                          (..)
-    , Animation                     (..)
-    , Sticker                       (..)
-    , Video                         (..)
-    , Voice                         (..)
-    , Venue                         (..)
-    , Contact                       (..)
-    , Location                      (..)
-    , Update                        (..)
-    , File                          (..)
-    , UserProfilePhotos             (..)
-    , InlineQuery                   (..)
-    , ChosenInlineResult            (..)
-    , InlineQueryResult             (..)
-    , InlineKeyboardMarkup          (..)
-    , InlineKeyboardButton          (..)
-    , CallbackGame                  (..)
-    , CallbackQuery                 (..)
-    , ChatType                      (..)
-    , ParseMode                     (..)
-    , InputMessageContent           (..)
-    , KeyboardButton                (..)
-    , WebhookInfo                   (..)
-      -- * Functions
-    , inlineKeyboardButton
-    , keyboardButton
-    , inlineQueryResultArticle
-    , inlineQueryResultAudio
-    , inlineQueryResultContact
-    , inlineQueryResultDocument
-    , inlineQueryResultGif
-    , inlineQueryResultLocation
-    , inlineQueryResultMpeg4Gif
-    , inlineQueryResultPhoto
-    , inlineQueryResultVenue
-    , inlineQueryResultVideo
-    , inlineQueryResultVoice
-    , inlineQueryResultCachedAudio
-    , inlineQueryResultCachedDocument
-    , inlineQueryResultCachedGif
-    , inlineQueryResultCachedMpeg4Gif
-    , inlineQueryResultGame
-    , inlineQueryResultCachedPhoto
-    , inlineQueryResultCachedSticker
-    , inlineQueryResultCachedVideo
-    , inlineQueryResultCachedVoice
-
-    ) where
-
-import           Prelude hiding (id)
-
-import           Data.Aeson
-import           Data.Maybe()
-import           Data.Aeson.Types
-import           Data.Text (Text)
-import qualified Data.Char as Char
-import           GHC.Generics
-import           Data.List
-import           Web.Telegram.API.Bot.JsonExt
-
--- | This object represents a Telegram user or bot.
-data User = User
-  {
-    user_id :: Int                -- ^ Unique identifier for this user or 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
-  } deriving (Show, Generic)
-
-instance ToJSON User where
-  toJSON = toJsonDrop 5
-
-instance FromJSON User where
-  parseJSON = parseJsonDrop 5
-
--- | This object represents a phone contact.
-data Contact = Contact
-  {
-    contact_phone_number :: Text       -- ^ Contact's phone number
-  , contact_first_name   :: Text       -- ^ Contact's first name
-  , contact_last_name    :: Maybe Text -- ^ Contact's last name
-  , contact_user_id      :: Maybe Int  -- ^ Contact's user identifier in Telegram
-  } deriving (Show, Generic)
-
-instance ToJSON Contact where
-  toJSON = toJsonDrop 8
-
-instance FromJSON Contact where
-  parseJSON = parseJsonDrop 8
-
--- | This object represents a chat.
-data Chat = Chat
-  {
-    chat_id :: Int                -- ^ Unique identifier for this chat, not exceeding 1e13 by absolute value
-  , chat_type :: ChatType         -- ^ Type of chat, can be either 'Private', 'Group', 'Supergroup' or 'Channel'
-  , chat_title :: Maybe Text      -- ^ Title, for channels and group chats
-  , chat_username :: Maybe Text   -- ^ Username, for private chats and channels if available
-  , 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.
-  } deriving (Show, Generic)
-
-instance ToJSON Chat where
-  toJSON = toJsonDrop 5
-
-instance FromJSON Chat where
-  parseJSON = parseJsonDrop 5
-
--- | Type of chat.
-data ChatType = Private
-              | Group
-              | Supergroup
-              | Channel deriving (Show, Generic)
-
-instance ToJSON ChatType where
-  toJSON Private        = "private"
-  toJSON Group          = "group"
-  toJSON Supergroup     = "supergroup"
-  toJSON Channel        = "channel"
-
-instance FromJSON ChatType where
-  parseJSON "private"    = pure Private
-  parseJSON "group"      = pure Group
-  parseJSON "supergroup" = pure Supergroup
-  parseJSON "channel"    = pure Channel
-  parseJSON _            = fail "Failed to parse ChatType"
-
--- | Parse mode for text message
-data ParseMode = Markdown | HTML deriving (Show, Generic)
-
-instance ToJSON ParseMode where
-  toJSON Markdown = "Markdown"
-  toJSON HTML = "HTML"
-
-instance FromJSON ParseMode where
-  parseJSON "Markdown" = pure Markdown
-  parseJSON "HTML" = pure HTML
-  parseJSON _          = fail "Failed to parse ParseMode"
-
--- | This object represents one size of a photo or a 'File' / 'Sticker' thumbnail.
-data PhotoSize = PhotoSize
-  {
-    photo_file_id   :: Text       -- ^ Unique identifier for this file
-  , photo_width     :: Int        -- ^ Photo width
-  , photo_height    :: Int        -- ^ Photo height
-  , photo_file_size :: Maybe Int  -- ^ File size
-  } deriving (Show, Generic)
-
-instance ToJSON PhotoSize where
-  toJSON = toJsonDrop 6
-
-instance FromJSON PhotoSize where
-  parseJSON = parseJsonDrop 6
-
--- | This object represents an audio file to be treated as music by the Telegram clients.
-data Audio = Audio
-  {
-    audio_file_id   :: Text       -- ^ Unique identifier for this file
-  , audio_duration  :: Int        -- ^ Duration of the audio in seconds as defined by sender
-  , audio_performer :: Maybe Text -- ^ Performer of the audio as defined by sender or by audio tags
-  , audio_title     :: Maybe Text -- ^ Title of the audio as defined by sender or by audio tags
-  , audio_mime_type :: Maybe Text -- ^ MIME type of the file as defined by sender
-  , audio_file_size :: Maybe Int  -- ^ File size
-  } deriving (Show, Generic)
-
-instance ToJSON Audio where
-  toJSON = toJsonDrop 6
-
-instance FromJSON Audio where
-  parseJSON = parseJsonDrop 6
-
--- | This object represents a general file (as opposed to 'PhotoSize', 'Voice' messages and 'Audio' files).
-data Document = Document
-  {
-    doc_file_id   :: Text             -- ^ Unique file identifier
-  , doc_thumb     :: Maybe PhotoSize  -- ^ Document thumbnail as defined by sender
-  , doc_file_name :: Maybe Text       -- ^ Original filename as defined by sender
-  , doc_mime_type :: Maybe Text       -- ^ MIME type of the file as defined by sender
-  , doc_file_size :: Maybe Int        -- ^ File size
-  } deriving (Show, Generic)
-
-instance ToJSON Document where
-  toJSON = toJsonDrop 4
-
-instance FromJSON Document where
-  parseJSON = parseJsonDrop 4
-
--- | This object represents a game. Use BotFather to create and edit games, their short names will act as unique identifiers.
-data Game = Game
-  {
-    game_title :: Text -- ^ Title of the game
-  , game_description :: Text -- ^ Description of the game
-  , game_photo :: [PhotoSize] -- ^ Photo that will be displayed in the game message in chats.
-  , game_text :: Maybe Text -- ^ Brief description of the game or high scores included in the game message. Can be automatically edited to include current high scores for the game when the bot calls setGameScore, or manually edited using editMessageText. 0-4096 characters.
-  , game_text_entities :: Maybe [MessageEntity] -- ^ Special entities that appear in text, such as usernames, URLs, bot commands, etc.
-  , game_animation :: Maybe Animation -- ^ Animation that will be displayed in the game message in chats. Upload via BotFather
-  } deriving (Show, Generic)
-
-instance ToJSON Game where
-  toJSON = toJsonDrop 5
-
-instance FromJSON Game where
-  parseJSON = parseJsonDrop 5
-
--- | This object represents an animation file to be displayed in the message containing a game.
-data Animation = Animation
-  {
-    anim_file_id :: Text -- ^ Unique file identifier
-  , anim_thumb :: Maybe PhotoSize -- ^ Animation thumbnail as defined by sender
-  , anim_file_name :: Maybe Text -- ^ Original animation filename as defined by sender
-  , anim_mime_type :: Maybe Text -- ^ MIME type of the file as defined by sender
-  , anim_file_size :: Maybe Int -- ^ File size
-  } deriving (Show, Generic)
-
-instance ToJSON Animation where
-  toJSON = toJsonDrop 5
-
-instance FromJSON Animation where
-  parseJSON = parseJsonDrop 5
-
--- | 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
-  } deriving (Show, Generic)
-
-instance ToJSON Sticker where
-  toJSON = toJsonDrop 8
-
-instance FromJSON Sticker where
-  parseJSON = parseJsonDrop 8
-
--- | This object represents a video file.
-data Video = Video
-  {
-    video_file_id   :: Text             -- ^ Unique identifier for this file
-  , video_width     :: Int              -- ^ Video width as defined by sender
-  , video_height    :: Int              -- ^ Video height as defined by sender
-  , video_duration  :: Int              -- ^ Duration of the video in seconds as defined by sender
-  , video_thumb     :: Maybe PhotoSize  -- ^ Video thumbnail
-  , video_mime_type :: Maybe Text       -- ^ MIME type of a file as defined by sender
-  , video_file_size :: Maybe Int        -- ^ File size
-  } deriving (Show, Generic)
-
-instance ToJSON Video where
-  toJSON = toJsonDrop 6
-
-instance FromJSON Video where
-  parseJSON = parseJsonDrop 6
-
--- | This object represents a voice note.
-data Voice = Voice
-  {
-    voice_file_id   :: Text       -- ^ Unique identifier for this file
-  , voice_duration  :: Int        -- ^ Duration of the audio in seconds as defined by sender
-  , voice_mime_type :: Maybe Text -- ^ MIME type of the file as defined by sender
-  , voice_file_size :: Maybe Int  -- ^ File size
-  } deriving (Show, Generic)
-
-instance ToJSON Voice where
-  toJSON = toJsonDrop 6
-
-instance FromJSON Voice where
-  parseJSON = parseJsonDrop 6
-
--- | This object represents an incoming inline query. When the user sends an empty query, your bot could return some default or trending results.
-data InlineQuery = InlineQuery
-  {
-    query_id        :: Text -- ^ Unique identifier for this query
-  , query_from      :: User -- ^ Sender
-  , query_location  :: Maybe Location -- ^ Sender location, only for bots that request user location
-  , query_query     :: Text -- ^ Text of the query
-  , query_offset    :: Text -- ^ Offset of the results to be returned, can be controlled by the bot
-  } deriving (Show, Generic)
-
-instance ToJSON InlineQuery where
-  toJSON = toJsonDrop 6
-
-instance FromJSON InlineQuery where
-  parseJSON = parseJsonDrop 6
-
--- | This object represents a result of an inline query that was chosen by the user and sent to their chat partner.
-data ChosenInlineResult = ChosenInlineResult
-  {
-    chosen_result_id          :: Text -- ^ The unique identifier for the result that was chosen
-  , chosen_from               :: User -- ^ The user that chose the result
-  , chosen_location           :: Maybe Location -- ^ Sender location, only for bots that require user location
-  , chosen_inline_message_id  :: Maybe Text -- ^ Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message. Will be also received in callback queries and can be used to edit the message.
-  , chosen_query              :: Text -- ^ The query that was used to obtain the result
-  } deriving (Show, Generic)
-
-instance ToJSON ChosenInlineResult where
-  toJSON = toJsonDrop 7
-
-instance FromJSON ChosenInlineResult where
-  parseJSON = parseJsonDrop 7
-
--- | This object represents the content of a message to be sent as a result of an inline query.
-data InputMessageContent =
-  -- | Represents the content of a text message to be sent as the result of an inline query.
-  InputTextMessageContent
-  {
-    imc_message_text :: Text -- ^ Text of the message to be sent, 1-4096 characters
-  , imc_parse_mode :: Maybe ParseMode -- ^ Send 'Markdown' or 'HTML', if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message.
-  , imc_disable_web_page_preview :: Maybe Bool -- ^ Disables link previews for links in the sent message
-  }
-  -- | Represents the content of a location message to be sent as the result of an inline query.
-  | InputLocationMessageContent
-  {
-    imc_latitude :: Float -- ^ Latitude of the location in degrees
-  , imc_longitude :: Float -- ^ Longitude of the location in degrees
-  }
-  -- | Represents the content of a venue message to be sent as the result of an inline query.
-  | InputVenueMessageContent
-  {
-    imc_latitude :: Float -- ^ Latitude of the location in degrees
-  , imc_longitude :: Float -- ^ Longitude of the location in degrees
-  , imc_title :: Text -- ^ Name of the venue
-  , imc_address :: Text -- ^ Address of the venue
-  , imc_foursquare_id :: Maybe Text -- ^ Foursquare identifier of the venue, if known
-  }
-  -- | Represents the content of a contact message to be sent as the result of an inline query.
-  | InputContactMessageContent
-  {
-    imc_phone_number :: Text -- ^ Contact's phone number
-  , imc_first_name :: Text -- ^ Contact's first name
-  , imc_last_name :: Maybe Text -- ^ Contact's last name
-  } deriving (Show, Generic)
-
-instance ToJSON InputMessageContent where
-  toJSON = toJsonDrop 4
-
-instance FromJSON InputMessageContent where
-  parseJSON = parseJsonDrop 4
-
-data InlineQueryResult =
-  -- | Represents a link to an article or web page.
-  InlineQueryResultArticle
-  {
-    iq_res_id                              :: Text -- ^ Unique identifier for this result, 1-64 Bytes
-  , iq_res_title                           :: Maybe Text -- ^ Title of the result
-  , iq_res_input_message_content           :: Maybe InputMessageContent -- ^ Content of the message to be sent
-  , iq_res_reply_markup                    :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message
-  , iq_res_url                             :: Maybe Text -- ^ URL of the result
-  , iq_res_hide_url                        :: Maybe Bool -- ^ Pass True, if you don't want the URL to be shown in the message
-  , iq_res_description                     :: Maybe Text -- ^ Short description of the result
-  , iq_res_thumb_url                       :: Maybe Text -- ^ Url of the thumbnail for the result
-  , iq_res_thumb_width                     :: Maybe Int -- ^ Thumbnail width
-  , iq_res_thumb_height                    :: Maybe Int -- ^ Thumbnail height
-  }
-  -- | Represents a link to a photo. By default, this photo will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo.
-  | InlineQueryResultPhoto
-  {
-    iq_res_id                              :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_photo_url                       :: Text -- ^ A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB
-  , iq_res_thumb_url                       :: Maybe Text -- ^ URL of the thumbnail for the photo
-  , iq_res_photo_width                     :: Maybe Int -- ^ Optional. Width of the photo
-  , iq_res_photo_height                    :: Maybe Int -- ^ Optional. Height of the photo
-  , iq_res_title                           :: Maybe Text -- ^ Title for the result
-  , iq_res_description                     :: Maybe Text -- ^ Short description of the result
-  , iq_res_caption                         :: Maybe Text -- ^ Caption of the photo to be sent, 0-200 characters
-  , iq_res_reply_markup                    :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message
-  , iq_res_input_message_content           :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the photo
-  }
-  -- | Represents a link to an animated GIF file. By default, this animated GIF file will be sent by the user with optional caption. Alternatively, you can provide message_text to send it instead of the animation.
-  | InlineQueryResultGif
-  {
-    iq_res_id                              :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_gif_url                         :: Text -- ^ A valid URL for the GIF file. File size must not exceed 1MB
-  , iq_res_gif_width                       :: Maybe Int -- ^ Width of the GIF
-  , iq_res_gif_height                      :: Maybe Int -- ^ Height of the GIF
-  , iq_res_thumb_url                       :: Maybe Text -- ^ URL of the static thumbnail for the result (jpeg or gif)
-  , iq_res_title                           :: Maybe Text -- ^ Title for the result
-  , iq_res_caption                         :: Maybe Text -- ^ Caption of the GIF file to be sent, 0-200 characters
-  , iq_res_reply_markup                    :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message
-  , iq_res_input_message_content           :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the GIF animation
-  }
-  -- | Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default, this animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you can provide message_text to send it instead of the animation.
-  | InlineQueryResultMpeg4Gif
-  {
-    iq_res_id                              :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_mpeg4_url                       :: Text -- ^ A valid URL for the MP4 file. File size must not exceed 1MB
-  , iq_res_mpeg4_width                     :: Maybe Int -- ^ Video width
-  , iq_res_mpeg4_height                    :: Maybe Int -- ^ Video height
-  , iq_res_thumb_url                       :: Maybe Text -- ^ URL of the static thumbnail (jpeg or gif) for the result
-  , iq_res_title                           :: Maybe Text -- ^ Title for the result
-  , iq_res_caption                         :: Maybe Text -- ^ Caption of the MPEG-4 file to be sent, 0-200 characters
-  , iq_res_reply_markup                    :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message
-  , iq_res_input_message_content           :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the video animation
-  }
-  -- | Represents link to a page containing an embedded video player or a video file.
-  | InlineQueryResultVideo
-  {
-    iq_res_id                              :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_video_url                       :: Text -- ^ A valid URL for the embedded video player or video file
-  , iq_res_mime_type                       :: Text -- ^ Mime type of the content of video url, “text/html” or “video/mp4”
-  , iq_res_thumb_url                       :: Maybe Text -- ^ URL of the thumbnail (jpeg only) for the video
-  , iq_res_title                           :: Maybe Text -- ^ Title for the result
-  , iq_res_caption                         :: Maybe Text -- ^ Caption of the video to be sent, 0-200 characters
-  , iq_res_video_width                     :: Maybe Int -- ^ Video width
-  , iq_res_video_height                    :: Maybe Int -- ^ Video height
-  , iq_res_video_duration                  :: Maybe Int -- ^ Video duration in seconds
-  , iq_res_description                     :: Maybe Text -- ^ Short description of the result
-  , iq_res_reply_markup                    :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message
-  , iq_res_input_message_content           :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the video
-  }
-  -- | Represents a link to an mp3 audio file. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio.
-  | InlineQueryResultAudio
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_audio_url :: Text -- ^ A valid URL for the audio file
-  , iq_res_title :: Maybe Text -- ^ Title
-  , iq_res_caption :: Maybe Text -- ^ Caption, 0-200 characters
-  , iq_res_performer :: Maybe Text -- ^ Performer
-  , iq_res_audio_duration :: Maybe Int -- ^ Audio duration in seconds
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the audio
-  }
-  -- | Represents a link to a voice recording in an .ogg container encoded with OPUS. By default, this voice recording will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the the voice message.
-  | InlineQueryResultVoice
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_voice_url :: Text -- ^ A valid URL for the voice recording
-  , iq_res_title :: Maybe Text -- ^ Recording title
-  , iq_res_caption :: Maybe Text -- ^ Caption, 0-200 characters
-  , iq_res_voice_duration :: Maybe Int -- ^ Recording duration in seconds
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the voice recording
-  }
-  -- | Represents a link to a file. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. Currently, only .PDF and .ZIP files can be sent using this method.
-  | InlineQueryResultDocument
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_title :: Maybe Text -- ^ Title for the result
-  , iq_res_caption :: Maybe Text -- ^ Caption of the document to be sent, 0-200 characters
-  , iq_res_document_url :: Text -- ^ A valid URL for the file
-  , iq_res_mime_type :: Text -- ^ Mime type of the content of the file, either “application/pdf” or “application/zip”
-  , iq_res_description :: Maybe Text -- ^ Short description of the result
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the file
-  , iq_res_thumb_url :: Maybe Text -- ^ URL of the thumbnail (jpeg only) for the file
-  , iq_res_thumb_width :: Maybe Int -- ^ Thumbnail width
-  , iq_res_thumb_height :: Maybe Int -- ^ Thumbnail height
-  }
-  -- | Represents a location on a map. By default, the location will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the location.
-  | InlineQueryResultLocation
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 Bytes
-  , iq_res_latitude :: Float -- ^ Location latitude in degrees
-  , iq_res_longitude :: Float -- ^ Location longitude in degrees
-  , iq_res_title :: Maybe Text -- ^ Location title
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the location
-  , iq_res_thumb_url :: Maybe Text -- ^ Url of the thumbnail for the result
-  , iq_res_thumb_width :: Maybe Int -- ^ Thumbnail width
-  , iq_res_thumb_height :: Maybe Int -- ^ Thumbnail height
-  }
-  -- | Represents a venue. By default, the venue will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the venue.
-  | InlineQueryResultVenue
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 Bytes
-  , iq_res_latitude :: Float -- ^ Latitude of the venue location in degrees
-  , iq_res_longitude :: Float -- ^ Longitude of the venue location in degrees
-  , iq_res_title :: Maybe Text -- ^ Title of the venue
-  , iq_res_address :: Text -- ^ Address of the venue
-  , iq_res_foursquare_id :: Maybe Text -- ^ Foursquare identifier of the venue if known
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the venue
-  , iq_res_thumb_url :: Maybe Text -- ^ Url of the thumbnail for the result
-  , iq_res_thumb_width :: Maybe Int -- ^ Thumbnail width
-  , iq_res_thumb_height :: Maybe Int -- ^ Thumbnail height
-  }
-  -- | Represents a link to a photo stored on the Telegram servers. By default, this photo will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo.
-  | InlineQueryResultContact
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 Bytes
-  , iq_res_phone_number :: Text -- ^ Contact's phone number
-  , iq_res_first_name :: Text -- ^ Contact's first name
-  , iq_res_last_name :: Maybe Text -- ^ Contact's last name
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the contact
-  , iq_res_thumb_url :: Maybe Text -- ^ Url of the thumbnail for the result
-  , iq_res_thumb_width :: Maybe Int -- ^ Thumbnail width
-  , iq_res_thumb_height :: Maybe Int -- ^ Thumbnail height
-  }
-  -- | Represents a Game.
-  | InlineQueryResultGame
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_game_short_name :: Text -- ^ Short name of the game
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message
-  }
-  -- | Represents a link to a photo stored on the Telegram servers. By default, this photo will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo.
-  | InlineQueryResultCachedPhoto
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_photo_file_id :: Text -- ^ A valid file identifier of the photo
-  , iq_res_title :: Maybe Text -- ^ Title for the result
-  , iq_res_description :: Maybe Text -- ^ Short description of the result
-  , iq_res_caption :: Maybe Text -- ^ Caption of the photo to be sent, 0-200 characters
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the photo
-  }
-  -- | Represents a link to an animated GIF file stored on the Telegram servers. By default, this animated GIF file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with specified content instead of the animation.
-  | InlineQueryResultCachedGif
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_gif_file_id :: Text -- ^ A valid file identifier for the GIF file
-  , iq_res_title :: Maybe Text -- ^ Title for the result
-  , iq_res_caption :: Maybe Text -- ^ Caption of the GIF file to be sent, 0-200 characters
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the GIF animation
-  }
-  -- | Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation.
-  | InlineQueryResultCachedMpeg4Gif
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_mpeg4_file_id :: Text -- ^ A valid file identifier for the MP4 file
-  , iq_res_title :: Maybe Text -- ^ Title for the result
-  , iq_res_caption :: Maybe Text -- ^ Caption of the MPEG-4 file to be sent, 0-200 characters
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the video animation
-  }
-  -- | Represents a link to a sticker stored on the Telegram servers. By default, this sticker will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the sticker.
-  | InlineQueryResultCachedSticker
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_sticker_file_id :: Text -- ^ A valid file identifier of the sticker
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the sticker
-  }
-  -- | Represents a link to a file stored on the Telegram servers. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. Currently, only pdf-files and zip archives can be sent using this method.
-  | InlineQueryResultCachedDocument
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_title :: Maybe Text -- ^ Title for the result
-  , iq_res_document_file_id :: Text -- ^ A valid file identifier for the file
-  , iq_res_description :: Maybe Text -- ^ Short description of the result
-  , iq_res_caption :: Maybe Text -- ^ Caption of the document to be sent, 0-200 characters
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the file
-  }
-  -- | Represents a link to a video file stored on the Telegram servers. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the video.
-  | InlineQueryResultCachedVideo
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_video_file_id :: Text -- ^ A valid file identifier for the video file
-  , iq_res_title :: Maybe Text -- ^ Title for the result
-  , iq_res_description :: Maybe Text -- ^ Short description of the result
-  , iq_res_caption :: Maybe Text -- ^ Caption of the video to be sent, 0-200 characters
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the video
-  }
-  -- | Represents a link to a voice message stored on the Telegram servers. By default, this voice message will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the voice message.
-  | InlineQueryResultCachedVoice
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_voice_file_id :: Text -- ^ A valid file identifier for the voice message
-  , iq_res_title :: Maybe Text -- ^ Voice message title
-  , iq_res_caption :: Maybe Text -- ^ Caption, 0-200 characters
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ ontent of the message to be sent instead of the voice message
-  }
-  -- | Represents a link to an mp3 audio file stored on the Telegram servers. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio.
-  | InlineQueryResultCachedAudio
-  {
-    iq_res_id :: Text -- ^ Unique identifier for this result, 1-64 bytes
-  , iq_res_audio_file_id :: Text -- ^ A valid file identifier for the audio file
-  , iq_res_caption :: Maybe Text -- ^ Caption, 0-200 characters
-  , iq_res_reply_markup :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message
-  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ ontent of the message to be sent instead of the audio
-  } deriving (Show, Generic)
-
-dropCached :: String -> String
-dropCached name = if "Cached" `isPrefixOf` name then drop 6 name else name
-
-tagModifier :: String -> String
-tagModifier "InlineQueryResultMpeg4Gif" = "mpeg4_gif"
-tagModifier "InlineQueryResultCachedMpeg4Gif" = "mpeg4_gif"
-tagModifier x = (fmap Char.toLower . dropCached . drop 17) x
-
-inlineQueryJSONOptions :: Options
-inlineQueryJSONOptions = defaultOptions {
-    fieldLabelModifier     = drop 7
-  , omitNothingFields      = True
-  , sumEncoding            = TaggedObject { tagFieldName = "type", contentsFieldName = undefined }
-  , constructorTagModifier = tagModifier
-  }
-
-instance ToJSON InlineQueryResult where
-  toJSON = genericToJSON inlineQueryJSONOptions
-
-instance FromJSON InlineQueryResult where
-  parseJSON = genericParseJSON inlineQueryJSONOptions
-
-inlineQueryResultArticle :: Text -> Text -> InputMessageContent -> InlineQueryResult
-inlineQueryResultArticle id title content = InlineQueryResultArticle id (Just title) (Just content) Nothing Nothing Nothing Nothing Nothing Nothing Nothing
-
-inlineQueryResultPhoto :: Text -> Text -> Text -> InlineQueryResult
-inlineQueryResultPhoto id photoUrl thumbUlr = InlineQueryResultPhoto id photoUrl (Just thumbUlr) Nothing Nothing Nothing Nothing Nothing Nothing Nothing
-
-inlineQueryResultGif :: Text -> Text -> Text -> InlineQueryResult
-inlineQueryResultGif id gifUrl thumbUrl = InlineQueryResultGif id gifUrl Nothing Nothing (Just thumbUrl) Nothing Nothing Nothing Nothing
-
-inlineQueryResultMpeg4Gif :: Text -> Text -> Text -> InlineQueryResult
-inlineQueryResultMpeg4Gif id mpeg4Url thumbUrl = InlineQueryResultMpeg4Gif id mpeg4Url Nothing Nothing (Just thumbUrl) Nothing Nothing Nothing Nothing
-
-inlineQueryResultVideo :: Text -> Text -> Text -> Text -> Text -> InlineQueryResult
-inlineQueryResultVideo id videoUrl mimeType thumbUrl title = InlineQueryResultVideo id videoUrl mimeType (Just thumbUrl) (Just title) Nothing Nothing Nothing Nothing Nothing Nothing Nothing
-
-inlineQueryResultAudio :: Text -> Text -> Text -> InlineQueryResult
-inlineQueryResultAudio id audioUrl title = InlineQueryResultAudio id audioUrl (Just title) Nothing Nothing Nothing Nothing Nothing
-
-inlineQueryResultVoice :: Text -> Text -> Text -> InlineQueryResult
-inlineQueryResultVoice id voiceUrl title = InlineQueryResultVoice id voiceUrl (Just title) Nothing Nothing Nothing Nothing
-
-inlineQueryResultDocument :: Text -> Text -> Text -> Text -> InlineQueryResult
-inlineQueryResultDocument id title docUrl mimeType = InlineQueryResultDocument id (Just title) Nothing docUrl mimeType Nothing Nothing Nothing Nothing Nothing Nothing
-
-inlineQueryResultLocation :: Text -> Float -> Float -> Text -> InlineQueryResult
-inlineQueryResultLocation id lat lon title = InlineQueryResultLocation id lat lon (Just title) Nothing Nothing Nothing Nothing Nothing
-
-inlineQueryResultVenue :: Text -> Float -> Float -> Text -> Text -> InlineQueryResult
-inlineQueryResultVenue id lat lon title address = InlineQueryResultVenue id lat lon (Just title) address Nothing Nothing Nothing Nothing Nothing Nothing
-
-inlineQueryResultContact :: Text -> Text -> Text -> InlineQueryResult
-inlineQueryResultContact id phoneNumber firstName = InlineQueryResultContact id phoneNumber firstName Nothing Nothing Nothing Nothing Nothing Nothing
-
-inlineQueryResultGame :: Text -> Text -> InlineQueryResult
-inlineQueryResultGame id gameShortName = InlineQueryResultGame id gameShortName Nothing
-
-inlineQueryResultCachedPhoto :: Text -> Text -> InlineQueryResult
-inlineQueryResultCachedPhoto id fileId = InlineQueryResultCachedPhoto id fileId Nothing Nothing Nothing Nothing Nothing
-
-inlineQueryResultCachedGif :: Text -> Text -> InlineQueryResult
-inlineQueryResultCachedGif id fileId = InlineQueryResultCachedGif id fileId Nothing Nothing Nothing Nothing
-
-inlineQueryResultCachedMpeg4Gif :: Text -> Text -> InlineQueryResult
-inlineQueryResultCachedMpeg4Gif id fileId = InlineQueryResultCachedMpeg4Gif id fileId Nothing Nothing Nothing Nothing
-
-inlineQueryResultCachedSticker :: Text -> Text -> InlineQueryResult
-inlineQueryResultCachedSticker id fileId = InlineQueryResultCachedSticker id fileId Nothing Nothing
-
-inlineQueryResultCachedDocument :: Text -> Text -> Text -> InlineQueryResult
-inlineQueryResultCachedDocument id fileId title = InlineQueryResultCachedDocument id (Just title) fileId Nothing Nothing Nothing Nothing
-
-inlineQueryResultCachedVideo :: Text -> Text -> Text -> InlineQueryResult
-inlineQueryResultCachedVideo id fileId title = InlineQueryResultCachedVideo id fileId (Just title) Nothing Nothing Nothing Nothing
-
-inlineQueryResultCachedVoice :: Text -> Text -> Text -> InlineQueryResult
-inlineQueryResultCachedVoice id fileId title = InlineQueryResultCachedVoice id fileId (Just title) Nothing Nothing Nothing
-
-inlineQueryResultCachedAudio :: Text -> Text -> InlineQueryResult
-inlineQueryResultCachedAudio id fileId = InlineQueryResultCachedAudio id fileId Nothing Nothing Nothing
-
-data InlineKeyboardMarkup = InlineKeyboardMarkup
-  {
-    inline_keyboard :: [[InlineKeyboardButton]]
-  } deriving (FromJSON, ToJSON, Show, Generic)
-
-data InlineKeyboardButton = InlineKeyboardButton
-  {
-    ikb_text :: Text
-  , ikb_url :: Maybe Text
-  , ikb_callback_data :: Maybe Text
-  , ikb_switch_inline_query :: Maybe Text
-  , ikb_callback_game :: Maybe CallbackGame
-  , ikb_switch_inline_query_current_chat :: Maybe Text -- ^ If set, pressing the button will insert the bot‘s username and the specified inline query in the current chat's input field. Can be empty, in which case only the bot’s username will be inserted.
-  } deriving (Show, Generic)
-
-instance ToJSON InlineKeyboardButton where
-  toJSON = toJsonDrop 4
-
-instance FromJSON InlineKeyboardButton where
-  parseJSON = parseJsonDrop 4
-
-inlineKeyboardButton :: Text -> InlineKeyboardButton
-inlineKeyboardButton buttonText =
-  InlineKeyboardButton buttonText Nothing Nothing Nothing Nothing Nothing
-
-data CallbackGame = CallbackGame
-  {
-  } deriving (Show, Generic)
-
-instance ToJSON CallbackGame where
-  toJSON = toJsonDrop 3
-
-instance FromJSON CallbackGame where
-  parseJSON = parseJsonDrop 3
-
-data CallbackQuery = CallbackQuery
-  {
-    cq_id :: Text
-  , cq_from :: User
-  , cq_message :: Maybe Message
-  , cq_inline_message_id :: Maybe Text
-  , cq_chat_instance :: Text
-  , cq_data :: Maybe Text
-  , cq_game_short_name :: Maybe Text
-  } deriving (Show, Generic)
-
-instance ToJSON CallbackQuery where
-  toJSON = toJsonDrop 3
-
-instance FromJSON CallbackQuery where
-  parseJSON = parseJsonDrop 3
-
--- | This object represents an incoming update.
--- Only one of the optional parameters can be present in any given update.
-data Update = Update
-  {
-    update_id            :: Int   -- ^ The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. This ID becomes especially handy if you’re using 'setWebhooks', since it allows you to ignore repeated updates or to restore the correct update sequence, should they get out of order.
-  , message              :: Maybe Message -- ^ New incoming message of any kind — text, photo, sticker, etc.
-  , edited_message       :: Maybe Message -- ^ New version of a message that is known to the bot and was edited
-  , channel_post         :: Maybe Message -- ^ New incoming channel post of any kind — text, photo, sticker, etc.
-  , edited_channel_post  :: Maybe Message -- ^ New version of a channel post that is known to the bot and was edited
-  , inline_query         :: Maybe InlineQuery -- ^ New incoming inline query
-  , chosen_inline_result :: Maybe ChosenInlineResult -- ^ The result of a inline query that was chosen by a user and sent to their chat partner
-  , callback_query       :: Maybe CallbackQuery -- ^ This object represents an incoming callback query from a callback button in an inline keyboard. If the button that originated the query was attached to a message sent by the bot, the field message will be presented. If the button was attached to a message sent via the bot (in inline mode), the field inline_message_id will be presented.
-  } deriving (FromJSON, ToJSON, Show, Generic)
-
--- | This object represents a point on the map.
-data Location = Location
-  {
-    longitude :: Float -- ^ Longitude as defined by sender
-  , latitude  :: Float -- ^ Latitude as defined by sender
-  } deriving (FromJSON, ToJSON, Show, Generic)
-
--- | This object represents a file ready to be downloaded. The file can be downloaded via the link
---   @https://api.telegram.org/file/bot<token>/<file_path>@. It is guaranteed that the link will be valid
---   for at least 1 hour. When the link expires, a new one can be requested by calling 'getFile'.
---
---       Maximum file size to download is 20 MB
-data File = File
-  {
-    file_id :: Text         -- ^ Unique identifier for this file
-  , file_size :: Maybe Int  -- ^ File size, if known
-  , file_path :: Maybe Text -- ^ File path. Use @https://api.telegram.org/file/bot<token>/<file_path>@ to get the file.
-  } deriving (FromJSON, ToJSON, Show, Generic)
-
--- | This object represent a user's profile pictures.
-data UserProfilePhotos = UserProfilePhotos
-  {
-    total_count :: Int      -- ^ Total number of profile pictures the target user has
-  , photos :: [[PhotoSize]] -- ^ Requested profile pictures (in up to 4 sizes each)
-  } deriving (FromJSON, ToJSON, Show, Generic)
-
-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”
-  } deriving (Show, Generic)
-
-instance ToJSON ChatMember where
-  toJSON = toJsonDrop 3
-
-instance FromJSON ChatMember where
-  parseJSON = parseJsonDrop 3
-
--- | This object represents a message.
-data Message = Message
-  {
-    message_id :: Int                     -- ^ Unique message identifier
-  , from :: Maybe User                    -- ^ Sender, can be empty for messages sent to channels
-  , date :: Int                           -- ^ Date the message was sent in Unix time
-  , chat :: Chat                          -- ^ Conversation the message belongs to
-  , 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_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
-  , 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
-  , 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
-  , photo :: Maybe [PhotoSize]            -- ^ Message is a photo, available sizes of the photo
-  , 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
-  , 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)
-  , 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
-  , delete_chat_photo :: Maybe Bool       -- ^ Service message: the chat photo was deleted
-  , group_chat_created :: Maybe Bool      -- ^ Service message: the group has been created
-  , supergroup_chat_created :: Maybe Bool -- ^ Service message: the supergroup has been created
-  , channel_chat_created :: Maybe Bool    -- ^ Service message: the channel has been created
-  , migrate_to_chat_id :: Maybe Int       -- ^ The group has been migrated to a supergroup with the specified identifier, not exceeding 1e13 by absolute value
-  , migrate_from_chat_id :: Maybe Int     -- ^ The supergroup has been migrated from a group with the specified identifier, not exceeding 1e13 by absolute value
-  , 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.
-  } deriving (FromJSON, ToJSON, Show, Generic)
-
--- | This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.
-data MessageEntity = MessageEntity
-  {
-    me_type :: Text       -- ^ Type of the entity. Can be mention (@username), hashtag, bot_command, url, email, bold (bold text), italic (italic text), code (monowidth string), pre (monowidth block), text_link (for clickable text URLs), text_mention (for users without usernames)
-  , me_offset :: Int      -- ^ Offset in UTF-16 code units to the start of the entity
-  , me_length :: Int      -- ^ Length of the entity in UTF-16 code units
-  , me_url :: Maybe Text  -- ^ For “text_link” only, url that will be opened after user taps on the text
-  , me_user :: Maybe User -- ^ For “text_mention” only, the mentioned user
-  } deriving (Show, Generic)
-
-instance ToJSON MessageEntity where
-  toJSON = toJsonDrop 3
-
-instance FromJSON MessageEntity where
-  parseJSON = parseJsonDrop 3
-
--- | This object represents a venue.
-data Venue = Venue
-  {
-    venue_location :: Location          -- ^ Venue location
-  , venue_title :: Text                 -- ^ Name of the venue
-  , venue_address :: Text               -- ^ Address of the venue
-  , venue_foursquare_id :: Maybe Text   -- ^ Foursquare identifier of the venue
-  } deriving (Show, Generic)
-
-instance ToJSON Venue where
-  toJSON = toJsonDrop 6
-
-instance FromJSON Venue where
-  parseJSON = parseJsonDrop 6
-
-data KeyboardButton = KeyboardButton
-  {
-    kb_text             :: Text -- ^ Text of the button. If none of the optional fields are used, it will be sent to the bot as a message when the button is pressed
-  , kb_request_contact  :: Maybe Bool -- ^ If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only
-  , kb_request_location :: Maybe Bool -- ^ If True, the user's current location will be sent when the button is pressed. Available in private chats only
-  } deriving (Show, Generic)
-
-instance ToJSON KeyboardButton where
-  toJSON = toJsonDrop 3
-
-instance FromJSON KeyboardButton where
-  parseJSON = parseJsonDrop 3
-
-keyboardButton :: Text -> KeyboardButton
-keyboardButton buttonText = KeyboardButton buttonText Nothing Nothing
-
-data WebhookInfo = WebhookInfo
-  {
-    whi_url :: Text -- ^ Webhook URL, may be empty if webhook is not set up
-  , whi_has_custom_certificate :: Bool -- ^ True, if a custom certificate was provided for webhook certificate checks
-  , whi_pending_update_count :: Int -- ^ Number of updates awaiting delivery
-  , whi_last_error_date :: Maybe Int -- ^ Unix time for the most recent error that happened when trying to deliver an update via webhook
-  , whi_last_error_message :: Maybe Text -- ^ Error message in human-readable format for the most recent error that happened when trying to deliver an update via webhook
-  } deriving (Show, Generic)
-
-instance ToJSON WebhookInfo where
-  toJSON = toJsonDrop 4
-
-instance FromJSON WebhookInfo where
-  parseJSON = parseJsonDrop 4+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE OverloadedStrings #-}++-- | This module contains objects which represent data of Telegram Bot API responses+module Web.Telegram.API.Bot.Data+    ( -- * Types+      User                          (..)+    , LanguageCode                  (..)+    , ChatMember                    (..)+    , ChatPhoto                     (..)+    , Chat                          (..)+    , Message                       (..)+    , MessageEntity                 (..)+    , PhotoSize                     (..)+    , Audio                         (..)+    , Document                      (..)+    , Game                          (..)+    , Animation                     (..)+    , Sticker                       (..)+    , Video                         (..)+    , Voice                         (..)+    , VideoNote                     (..)+    , Venue                         (..)+    , Contact                       (..)+    , Location                      (..)+    , Update                        (..)+    , File                          (..)+    , UserProfilePhotos             (..)+    , InlineQuery                   (..)+    , ChosenInlineResult            (..)+    , InlineQueryResult             (..)+    , InlineKeyboardMarkup          (..)+    , InlineKeyboardButton          (..)+    , CallbackGame                  (..)+    , CallbackQuery                 (..)+    , ChatType                      (..)+    , ParseMode                     (..)+    , InputMessageContent           (..)+    , KeyboardButton                (..)+    , WebhookInfo                   (..)+    , LabeledPrice                  (..)+    , CurrencyCode                  (..)+    , Invoice                       (..)+    , ShippingAddress               (..)+    , OrderInfo                     (..)+    , ShippingOption                (..)+    , SuccessfulPayment             (..)+    , ShippingQuery                 (..)+    , PreCheckoutQuery              (..)+    , MaskPositionPoint             (..)+    , MaskPosition                  (..)+    , StickerSet                    (..)+    , InputMedia                    (..)+      -- * Functions+    , inlineKeyboardButton+    , keyboardButton+    , inlineQueryResultArticle+    , inlineQueryResultAudio+    , inlineQueryResultContact+    , inlineQueryResultDocument+    , inlineQueryResultGif+    , inlineQueryResultLocation+    , inlineQueryResultMpeg4Gif+    , inlineQueryResultPhoto+    , inlineQueryResultVenue+    , inlineQueryResultVideo+    , inlineQueryResultVoice+    , inlineQueryResultCachedAudio+    , inlineQueryResultCachedDocument+    , inlineQueryResultCachedGif+    , inlineQueryResultCachedMpeg4Gif+    , inlineQueryResultGame+    , inlineQueryResultCachedPhoto+    , inlineQueryResultCachedSticker+    , inlineQueryResultCachedVideo+    , inlineQueryResultCachedVoice+    , inputMediaPhoto+    , inputMediaVideo+    ) where++import           Prelude                      hiding (id)++import           Data.Aeson+import           Data.Aeson.Types+import qualified Data.Char                    as Char+import           Data.Int                     (Int64)+import           Data.List+import           Data.Text                    (Text)+import           GHC.Generics++import           Web.Telegram.API.Bot.JsonExt++-- | This object represents a Telegram user or bot.+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+  , user_language_code :: Maybe LanguageCode+  } deriving (Show, Generic)++instance ToJSON User where+  toJSON = toJsonDrop 5++instance FromJSON User where+  parseJSON = parseJsonDrop 5++newtype LanguageCode = LanguageCode Text+  deriving (Show, Eq, Ord)++instance ToJSON LanguageCode where+  toJSON (LanguageCode code) = toJSON code++instance FromJSON LanguageCode where+  parseJSON (String code) = pure $ LanguageCode code+  parseJSON _ = fail "Unable to parse LanguageCode"++data VideoNote = VideoNote+  {+    vid_note_file_id   :: Text -- ^ Unique identifier for this file+  , vid_note_length    :: Int -- ^ Video width and height as defined by sender+  , vid_note_duration  :: Int -- ^ Duration of the video in seconds as defined by sender+  , vid_note_thumb     :: Maybe PhotoSize -- ^ Video thumbnail+  , vid_note_file_size :: Maybe Int -- ^ File size+  } deriving (Show, Generic)++instance ToJSON VideoNote where+  toJSON = toJsonDrop 9++instance FromJSON VideoNote where+  parseJSON = parseJsonDrop 9++-- | This object represents a phone contact.+data Contact = Contact+  {+    contact_phone_number :: Text       -- ^ Contact's phone number+  , contact_first_name   :: Text       -- ^ Contact's first name+  , contact_last_name    :: Maybe Text -- ^ Contact's last name+  , contact_user_id      :: Maybe Int  -- ^ Contact's user identifier in Telegram+  } deriving (Show, Generic)++instance ToJSON Contact where+  toJSON = toJsonDrop 8++instance FromJSON Contact where+  parseJSON = parseJsonDrop 8++-- | This object represents a chat.+data Chat = Chat+  { chat_id                             :: Int64+    -- ^ Unique identifier for this chat.+    -- This number may be greater than 32 bits and some programming languages+    -- may have difficulty/silent defects in interpreting it.+    -- But it is smaller than 52 bits,+    -- so a signed 64 bit integer or double-precision float type are safe for+    -- storing this identifier.+  , chat_type                           :: ChatType   -- ^ Type of chat, can be either 'Private', 'Group', 'Supergroup' or 'Channel'+  , chat_title                          :: Maybe Text -- ^ Title, for channels and group chats+  , chat_username                       :: Maybe Text -- ^ Username, for private chats and channels if available+  , 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+  toJSON = toJsonDrop 5++instance FromJSON Chat where+  parseJSON = parseJsonDrop 5++-- | Type of chat.+data ChatType = Private+              | Group+              | Supergroup+              | Channel deriving (Show, Generic)++instance ToJSON ChatType where+  toJSON Private        = "private"+  toJSON Group          = "group"+  toJSON Supergroup     = "supergroup"+  toJSON Channel        = "channel"++instance FromJSON ChatType where+  parseJSON "private"    = pure Private+  parseJSON "group"      = pure Group+  parseJSON "supergroup" = pure Supergroup+  parseJSON "channel"    = pure Channel+  parseJSON _            = fail "Failed to parse ChatType"++-- | Parse mode for text message+data ParseMode = Markdown | HTML deriving (Show, Generic)++instance ToJSON ParseMode where+  toJSON Markdown = "Markdown"+  toJSON HTML = "HTML"++instance FromJSON ParseMode where+  parseJSON "Markdown" = pure Markdown+  parseJSON "HTML" = pure HTML+  parseJSON _          = fail "Failed to parse ParseMode"++-- | This object represents one size of a photo or a 'File' / 'Sticker' thumbnail.+data PhotoSize = PhotoSize+  {+    photo_file_id   :: Text       -- ^ Unique identifier for this file+  , photo_width     :: Int        -- ^ Photo width+  , photo_height    :: Int        -- ^ Photo height+  , photo_file_size :: Maybe Int  -- ^ File size+  } deriving (Show, Generic)++instance ToJSON PhotoSize where+  toJSON = toJsonDrop 6++instance FromJSON PhotoSize where+  parseJSON = parseJsonDrop 6++-- | This object represents an audio file to be treated as music by the Telegram clients.+data Audio = Audio+  {+    audio_file_id   :: Text       -- ^ Unique identifier for this file+  , audio_duration  :: Int        -- ^ Duration of the audio in seconds as defined by sender+  , audio_performer :: Maybe Text -- ^ Performer of the audio as defined by sender or by audio tags+  , audio_title     :: Maybe Text -- ^ Title of the audio as defined by sender or by audio tags+  , audio_mime_type :: Maybe Text -- ^ MIME type of the file as defined by sender+  , audio_file_size :: Maybe Int  -- ^ File size+  } deriving (Show, Generic)++instance ToJSON Audio where+  toJSON = toJsonDrop 6++instance FromJSON Audio where+  parseJSON = parseJsonDrop 6++-- | This object represents a general file (as opposed to 'PhotoSize', 'Voice' messages and 'Audio' files).+data Document = Document+  {+    doc_file_id   :: Text            -- ^ Unique file identifier+  , doc_thumb     :: Maybe PhotoSize -- ^ Document thumbnail as defined by sender+  , doc_file_name :: Maybe Text      -- ^ Original filename as defined by sender+  , doc_mime_type :: Maybe Text      -- ^ MIME type of the file as defined by sender+  , doc_file_size :: Maybe Int       -- ^ File size+  } deriving (Show, Generic)++instance ToJSON Document where+  toJSON = toJsonDrop 4++instance FromJSON Document where+  parseJSON = parseJsonDrop 4++-- | This object represents a game. Use BotFather to create and edit games, their short names will act as unique identifiers.+data Game = Game+  {+    game_title         :: Text -- ^ Title of the game+  , game_description   :: Text -- ^ Description of the game+  , game_photo         :: [PhotoSize] -- ^ Photo that will be displayed in the game message in chats.+  , game_text          :: Maybe Text -- ^ Brief description of the game or high scores included in the game message. Can be automatically edited to include current high scores for the game when the bot calls setGameScore, or manually edited using editMessageText. 0-4096 characters.+  , game_text_entities :: Maybe [MessageEntity] -- ^ Special entities that appear in text, such as usernames, URLs, bot commands, etc.+  , game_animation     :: Maybe Animation -- ^ Animation that will be displayed in the game message in chats. Upload via BotFather+  } deriving (Show, Generic)++instance ToJSON Game where+  toJSON = toJsonDrop 5++instance FromJSON Game where+  parseJSON = parseJsonDrop 5++-- | This object represents an animation file to be displayed in the message containing a game.+data Animation = Animation+  {+    anim_file_id   :: Text -- ^ Unique file identifier+  , anim_thumb     :: Maybe PhotoSize -- ^ Animation thumbnail as defined by sender+  , anim_file_name :: Maybe Text -- ^ Original animation filename as defined by sender+  , anim_mime_type :: Maybe Text -- ^ MIME type of the file as defined by sender+  , anim_file_size :: Maybe Int -- ^ File size+  } deriving (Show, Generic)++instance ToJSON Animation where+  toJSON = toJsonDrop 5++instance FromJSON Animation where+  parseJSON = parseJsonDrop 5++-- | 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_set_name      :: Maybe Text+  , sticker_mask_position :: Maybe MaskPosition+  , sticker_file_size     :: Maybe Int        -- ^ File size+  } deriving (Show, Generic)++instance ToJSON Sticker where+  toJSON = toJsonDrop 8++instance FromJSON Sticker where+  parseJSON = parseJsonDrop 8++-- | This object represents a video file.+data Video = Video+  {+    video_file_id   :: Text             -- ^ Unique identifier for this file+  , video_width     :: Int              -- ^ Video width as defined by sender+  , video_height    :: Int              -- ^ Video height as defined by sender+  , video_duration  :: Int              -- ^ Duration of the video in seconds as defined by sender+  , video_thumb     :: Maybe PhotoSize  -- ^ Video thumbnail+  , video_mime_type :: Maybe Text       -- ^ MIME type of a file as defined by sender+  , video_file_size :: Maybe Int        -- ^ File size+  } deriving (Show, Generic)++instance ToJSON Video where+  toJSON = toJsonDrop 6++instance FromJSON Video where+  parseJSON = parseJsonDrop 6++-- | This object represents a voice note.+data Voice = Voice+  {+    voice_file_id   :: Text       -- ^ Unique identifier for this file+  , voice_duration  :: Int        -- ^ Duration of the audio in seconds as defined by sender+  , voice_mime_type :: Maybe Text -- ^ MIME type of the file as defined by sender+  , voice_file_size :: Maybe Int  -- ^ File size+  } deriving (Show, Generic)++instance ToJSON Voice where+  toJSON = toJsonDrop 6++instance FromJSON Voice where+  parseJSON = parseJsonDrop 6++-- | This object represents an incoming inline query. When the user sends an empty query, your bot could return some default or trending results.+data InlineQuery = InlineQuery+  {+    query_id       :: Text -- ^ Unique identifier for this query+  , query_from     :: User -- ^ Sender+  , query_location :: Maybe Location -- ^ Sender location, only for bots that request user location+  , query_query    :: Text -- ^ Text of the query+  , query_offset   :: Text -- ^ Offset of the results to be returned, can be controlled by the bot+  } deriving (Show, Generic)++instance ToJSON InlineQuery where+  toJSON = toJsonDrop 6++instance FromJSON InlineQuery where+  parseJSON = parseJsonDrop 6++-- | This object represents a result of an inline query that was chosen by the user and sent to their chat partner.+data ChosenInlineResult = ChosenInlineResult+  {+    chosen_result_id         :: Text -- ^ The unique identifier for the result that was chosen+  , chosen_from              :: User -- ^ The user that chose the result+  , chosen_location          :: Maybe Location -- ^ Sender location, only for bots that require user location+  , chosen_inline_message_id :: Maybe Text -- ^ Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message. Will be also received in callback queries and can be used to edit the message.+  , chosen_query             :: Text -- ^ The query that was used to obtain the result+  } deriving (Show, Generic)++instance ToJSON ChosenInlineResult where+  toJSON = toJsonDrop 7++instance FromJSON ChosenInlineResult where+  parseJSON = parseJsonDrop 7++-- | This object represents the content of a message to be sent as a result of an inline query.+data InputMessageContent =+  -- | Represents the content of a text message to be sent as the result of an inline query.+  InputTextMessageContent+  {+    imc_message_text             :: Text -- ^ Text of the message to be sent, 1-4096 characters+  , imc_parse_mode               :: Maybe ParseMode -- ^ Send 'Markdown' or 'HTML', if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message.+  , imc_disable_web_page_preview :: Maybe Bool -- ^ Disables link previews for links in the sent message+  }+  -- | Represents the content of a location message to be sent as the result of an inline query.+  | InputLocationMessageContent+  {+    imc_latitude  :: Float -- ^ Latitude of the location in degrees+  , imc_longitude :: Float -- ^ Longitude of the location in degrees+  }+  -- | Represents the content of a venue message to be sent as the result of an inline query.+  | InputVenueMessageContent+  {+    imc_latitude      :: Float -- ^ Latitude of the location in degrees+  , imc_longitude     :: Float -- ^ Longitude of the location in degrees+  , imc_title         :: Text -- ^ Name of the venue+  , imc_address       :: Text -- ^ Address of the venue+  , imc_foursquare_id :: Maybe Text -- ^ Foursquare identifier of the venue, if known+  }+  -- | Represents the content of a contact message to be sent as the result of an inline query.+  | InputContactMessageContent+  {+    imc_phone_number :: Text -- ^ Contact's phone number+  , imc_first_name   :: Text -- ^ Contact's first name+  , imc_last_name    :: Maybe Text -- ^ Contact's last name+  } deriving (Show, Generic)++instance ToJSON InputMessageContent where+  toJSON = toJsonDrop 4++instance FromJSON InputMessageContent where+  parseJSON = parseJsonDrop 4++data InlineQueryResult =+  -- | Represents a link to an article or web page.+  InlineQueryResultArticle+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 Bytes+  , iq_res_title                 :: Maybe Text -- ^ Title of the result+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message+  , iq_res_url                   :: Maybe Text -- ^ URL of the result+  , iq_res_hide_url              :: Maybe Bool -- ^ Pass True, if you don't want the URL to be shown in the message+  , iq_res_description           :: Maybe Text -- ^ Short description of the result+  , iq_res_thumb_url             :: Maybe Text -- ^ Url of the thumbnail for the result+  , iq_res_thumb_width           :: Maybe Int -- ^ Thumbnail width+  , iq_res_thumb_height          :: Maybe Int -- ^ Thumbnail height+  }+  -- | Represents a link to a photo. By default, this photo will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo.+  | InlineQueryResultPhoto+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_photo_url             :: Text -- ^ A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB+  , iq_res_thumb_url             :: Maybe Text -- ^ URL of the thumbnail for the photo+  , iq_res_photo_width           :: Maybe Int -- ^ Optional. Width of the photo+  , iq_res_photo_height          :: Maybe Int -- ^ Optional. Height of the photo+  , iq_res_title                 :: Maybe Text -- ^ Title for the result+  , iq_res_description           :: Maybe Text -- ^ Short description of the result+  , iq_res_caption               :: Maybe Text -- ^ Caption of the photo to be sent, 0-200 characters+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the photo+  }+  -- | Represents a link to an animated GIF file. By default, this animated GIF file will be sent by the user with optional caption. Alternatively, you can provide message_text to send it instead of the animation.+  | InlineQueryResultGif+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_gif_url               :: Text -- ^ A valid URL for the GIF file. File size must not exceed 1MB+  , iq_res_gif_width             :: Maybe Int -- ^ Width of the GIF+  , iq_res_gif_height            :: Maybe Int -- ^ Height of the GIF+  , iq_res_thumb_url             :: Maybe Text -- ^ URL of the static thumbnail for the result (jpeg or gif)+  , iq_res_title                 :: Maybe Text -- ^ Title for the result+  , iq_res_caption               :: Maybe Text -- ^ Caption of the GIF file to be sent, 0-200 characters+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the GIF animation+  , iq_res_gif_duration          :: Maybe Int -- ^ Duration of the GIF+  }+  -- | Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default, this animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you can provide message_text to send it instead of the animation.+  | InlineQueryResultMpeg4Gif+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_mpeg4_url             :: Text -- ^ A valid URL for the MP4 file. File size must not exceed 1MB+  , iq_res_mpeg4_width           :: Maybe Int -- ^ Video width+  , iq_res_mpeg4_height          :: Maybe Int -- ^ Video height+  , iq_res_thumb_url             :: Maybe Text -- ^ URL of the static thumbnail (jpeg or gif) for the result+  , iq_res_title                 :: Maybe Text -- ^ Title for the result+  , iq_res_caption               :: Maybe Text -- ^ Caption of the MPEG-4 file to be sent, 0-200 characters+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the video animation+  , iq_res_mpeg4_duration        :: Maybe Int -- ^ Video duration+  }+  -- | Represents link to a page containing an embedded video player or a video file.+  | InlineQueryResultVideo+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_video_url             :: Text -- ^ A valid URL for the embedded video player or video file+  , iq_res_mime_type             :: Text -- ^ Mime type of the content of video url, “text/html” or “video/mp4”+  , iq_res_thumb_url             :: Maybe Text -- ^ URL of the thumbnail (jpeg only) for the video+  , iq_res_title                 :: Maybe Text -- ^ Title for the result+  , iq_res_caption               :: Maybe Text -- ^ Caption of the video to be sent, 0-200 characters+  , iq_res_video_width           :: Maybe Int -- ^ Video width+  , iq_res_video_height          :: Maybe Int -- ^ Video height+  , iq_res_video_duration        :: Maybe Int -- ^ Video duration in seconds+  , iq_res_description           :: Maybe Text -- ^ Short description of the result+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the video+  }+  -- | Represents a link to an mp3 audio file. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio.+  | InlineQueryResultAudio+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_audio_url             :: Text -- ^ A valid URL for the audio file+  , iq_res_title                 :: Maybe Text -- ^ Title+  , iq_res_caption               :: Maybe Text -- ^ Caption, 0-200 characters+  , iq_res_performer             :: Maybe Text -- ^ Performer+  , iq_res_audio_duration        :: Maybe Int -- ^ Audio duration in seconds+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the audio+  }+  -- | Represents a link to a voice recording in an .ogg container encoded with OPUS. By default, this voice recording will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the the voice message.+  | InlineQueryResultVoice+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_voice_url             :: Text -- ^ A valid URL for the voice recording+  , iq_res_title                 :: Maybe Text -- ^ Recording title+  , iq_res_caption               :: Maybe Text -- ^ Caption, 0-200 characters+  , iq_res_voice_duration        :: Maybe Int -- ^ Recording duration in seconds+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the voice recording+  }+  -- | Represents a link to a file. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. Currently, only .PDF and .ZIP files can be sent using this method.+  | InlineQueryResultDocument+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_title                 :: Maybe Text -- ^ Title for the result+  , iq_res_caption               :: Maybe Text -- ^ Caption of the document to be sent, 0-200 characters+  , iq_res_document_url          :: Text -- ^ A valid URL for the file+  , iq_res_mime_type             :: Text -- ^ Mime type of the content of the file, either “application/pdf” or “application/zip”+  , iq_res_description           :: Maybe Text -- ^ Short description of the result+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the file+  , iq_res_thumb_url             :: Maybe Text -- ^ URL of the thumbnail (jpeg only) for the file+  , iq_res_thumb_width           :: Maybe Int -- ^ Thumbnail width+  , iq_res_thumb_height          :: Maybe Int -- ^ Thumbnail height+  }+  -- | Represents a location on a map. By default, the location will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the location.+  | InlineQueryResultLocation+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 Bytes+  , iq_res_latitude              :: Float -- ^ Location latitude in degrees+  , iq_res_longitude             :: Float -- ^ Location longitude in degrees+  , iq_res_title                 :: Maybe Text -- ^ Location title+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the location+  , iq_res_thumb_url             :: Maybe Text -- ^ Url of the thumbnail for the result+  , iq_res_thumb_width           :: Maybe Int -- ^ Thumbnail width+  , iq_res_thumb_height          :: Maybe Int -- ^ Thumbnail height+  }+  -- | Represents a venue. By default, the venue will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the venue.+  | InlineQueryResultVenue+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 Bytes+  , iq_res_latitude              :: Float -- ^ Latitude of the venue location in degrees+  , iq_res_longitude             :: Float -- ^ Longitude of the venue location in degrees+  , iq_res_title                 :: Maybe Text -- ^ Title of the venue+  , iq_res_address               :: Text -- ^ Address of the venue+  , iq_res_foursquare_id         :: Maybe Text -- ^ Foursquare identifier of the venue if known+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the venue+  , iq_res_thumb_url             :: Maybe Text -- ^ Url of the thumbnail for the result+  , iq_res_thumb_width           :: Maybe Int -- ^ Thumbnail width+  , iq_res_thumb_height          :: Maybe Int -- ^ Thumbnail height+  }+  -- | Represents a link to a photo stored on the Telegram servers. By default, this photo will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo.+  | InlineQueryResultContact+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 Bytes+  , iq_res_phone_number          :: Text -- ^ Contact's phone number+  , iq_res_first_name            :: Text -- ^ Contact's first name+  , iq_res_last_name             :: Maybe Text -- ^ Contact's last name+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the contact+  , iq_res_thumb_url             :: Maybe Text -- ^ Url of the thumbnail for the result+  , iq_res_thumb_width           :: Maybe Int -- ^ Thumbnail width+  , iq_res_thumb_height          :: Maybe Int -- ^ Thumbnail height+  }+  -- | Represents a Game.+  | InlineQueryResultGame+  {+    iq_res_id              :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_game_short_name :: Text -- ^ Short name of the game+  , iq_res_reply_markup    :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message+  }+  -- | Represents a link to a photo stored on the Telegram servers. By default, this photo will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo.+  | InlineQueryResultCachedPhoto+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_photo_file_id         :: Text -- ^ A valid file identifier of the photo+  , iq_res_title                 :: Maybe Text -- ^ Title for the result+  , iq_res_description           :: Maybe Text -- ^ Short description of the result+  , iq_res_caption               :: Maybe Text -- ^ Caption of the photo to be sent, 0-200 characters+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the photo+  }+  -- | Represents a link to an animated GIF file stored on the Telegram servers. By default, this animated GIF file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with specified content instead of the animation.+  | InlineQueryResultCachedGif+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_gif_file_id           :: Text -- ^ A valid file identifier for the GIF file+  , iq_res_title                 :: Maybe Text -- ^ Title for the result+  , iq_res_caption               :: Maybe Text -- ^ Caption of the GIF file to be sent, 0-200 characters+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the GIF animation+  }+  -- | Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation.+  | InlineQueryResultCachedMpeg4Gif+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_mpeg4_file_id         :: Text -- ^ A valid file identifier for the MP4 file+  , iq_res_title                 :: Maybe Text -- ^ Title for the result+  , iq_res_caption               :: Maybe Text -- ^ Caption of the MPEG-4 file to be sent, 0-200 characters+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the video animation+  }+  -- | Represents a link to a sticker stored on the Telegram servers. By default, this sticker will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the sticker.+  | InlineQueryResultCachedSticker+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_sticker_file_id       :: Text -- ^ A valid file identifier of the sticker+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the sticker+  }+  -- | Represents a link to a file stored on the Telegram servers. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. Currently, only pdf-files and zip archives can be sent using this method.+  | InlineQueryResultCachedDocument+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_title                 :: Maybe Text -- ^ Title for the result+  , iq_res_document_file_id      :: Text -- ^ A valid file identifier for the file+  , iq_res_description           :: Maybe Text -- ^ Short description of the result+  , iq_res_caption               :: Maybe Text -- ^ Caption of the document to be sent, 0-200 characters+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the file+  }+  -- | Represents a link to a video file stored on the Telegram servers. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the video.+  | InlineQueryResultCachedVideo+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_video_file_id         :: Text -- ^ A valid file identifier for the video file+  , iq_res_title                 :: Maybe Text -- ^ Title for the result+  , iq_res_description           :: Maybe Text -- ^ Short description of the result+  , iq_res_caption               :: Maybe Text -- ^ Caption of the video to be sent, 0-200 characters+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ Content of the message to be sent instead of the video+  }+  -- | Represents a link to a voice message stored on the Telegram servers. By default, this voice message will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the voice message.+  | InlineQueryResultCachedVoice+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_voice_file_id         :: Text -- ^ A valid file identifier for the voice message+  , iq_res_title                 :: Maybe Text -- ^ Voice message title+  , iq_res_caption               :: Maybe Text -- ^ Caption, 0-200 characters+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ ontent of the message to be sent instead of the voice message+  }+  -- | Represents a link to an mp3 audio file stored on the Telegram servers. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio.+  | InlineQueryResultCachedAudio+  {+    iq_res_id                    :: Text -- ^ Unique identifier for this result, 1-64 bytes+  , iq_res_audio_file_id         :: Text -- ^ A valid file identifier for the audio file+  , iq_res_caption               :: Maybe Text -- ^ Caption, 0-200 characters+  , iq_res_reply_markup          :: Maybe InlineKeyboardMarkup -- ^ An Inline keyboard attached to the message+  , iq_res_input_message_content :: Maybe InputMessageContent -- ^ ontent of the message to be sent instead of the audio+  } deriving (Show, Generic)++dropCached :: String -> String+dropCached name = if "Cached" `isPrefixOf` name then drop 6 name else name++tagModifier :: String -> String+tagModifier "InlineQueryResultMpeg4Gif" = "mpeg4_gif"+tagModifier "InlineQueryResultCachedMpeg4Gif" = "mpeg4_gif"+tagModifier x = (fmap Char.toLower . dropCached . drop 17) x++inlineQueryJSONOptions :: Options+inlineQueryJSONOptions = defaultOptions {+    fieldLabelModifier     = drop 7+  , omitNothingFields      = True+  , sumEncoding            = TaggedObject { tagFieldName = "type", contentsFieldName = undefined }+  , constructorTagModifier = tagModifier+  }++instance ToJSON InlineQueryResult where+  toJSON = genericToJSON inlineQueryJSONOptions++instance FromJSON InlineQueryResult where+  parseJSON = genericParseJSON inlineQueryJSONOptions++inlineQueryResultArticle :: Text -> Text -> InputMessageContent -> InlineQueryResult+inlineQueryResultArticle id title content = InlineQueryResultArticle id (Just title) (Just content) Nothing Nothing Nothing Nothing Nothing Nothing Nothing++inlineQueryResultPhoto :: Text -> Text -> Text -> InlineQueryResult+inlineQueryResultPhoto id photoUrl thumbUlr = InlineQueryResultPhoto id photoUrl (Just thumbUlr) Nothing Nothing Nothing Nothing Nothing Nothing Nothing++inlineQueryResultGif :: Text -> Text -> Text -> InlineQueryResult+inlineQueryResultGif id gifUrl thumbUrl = InlineQueryResultGif id gifUrl Nothing Nothing (Just thumbUrl) Nothing Nothing Nothing Nothing Nothing++inlineQueryResultMpeg4Gif :: Text -> Text -> Text -> InlineQueryResult+inlineQueryResultMpeg4Gif id mpeg4Url thumbUrl = InlineQueryResultMpeg4Gif id mpeg4Url Nothing Nothing (Just thumbUrl) Nothing Nothing Nothing Nothing Nothing++inlineQueryResultVideo :: Text -> Text -> Text -> Text -> Text -> InlineQueryResult+inlineQueryResultVideo id videoUrl mimeType thumbUrl title = InlineQueryResultVideo id videoUrl mimeType (Just thumbUrl) (Just title) Nothing Nothing Nothing Nothing Nothing Nothing Nothing++inlineQueryResultAudio :: Text -> Text -> Text -> InlineQueryResult+inlineQueryResultAudio id audioUrl title = InlineQueryResultAudio id audioUrl (Just title) Nothing Nothing Nothing Nothing Nothing++inlineQueryResultVoice :: Text -> Text -> Text -> InlineQueryResult+inlineQueryResultVoice id voiceUrl title = InlineQueryResultVoice id voiceUrl (Just title) Nothing Nothing Nothing Nothing++inlineQueryResultDocument :: Text -> Text -> Text -> Text -> InlineQueryResult+inlineQueryResultDocument id title docUrl mimeType = InlineQueryResultDocument id (Just title) Nothing docUrl mimeType Nothing Nothing Nothing Nothing Nothing Nothing++inlineQueryResultLocation :: Text -> Float -> Float -> Text -> InlineQueryResult+inlineQueryResultLocation id lat lon title = InlineQueryResultLocation id lat lon (Just title) Nothing Nothing Nothing Nothing Nothing++inlineQueryResultVenue :: Text -> Float -> Float -> Text -> Text -> InlineQueryResult+inlineQueryResultVenue id lat lon title address = InlineQueryResultVenue id lat lon (Just title) address Nothing Nothing Nothing Nothing Nothing Nothing++inlineQueryResultContact :: Text -> Text -> Text -> InlineQueryResult+inlineQueryResultContact id phoneNumber firstName = InlineQueryResultContact id phoneNumber firstName Nothing Nothing Nothing Nothing Nothing Nothing++inlineQueryResultGame :: Text -> Text -> InlineQueryResult+inlineQueryResultGame id gameShortName = InlineQueryResultGame id gameShortName Nothing++inlineQueryResultCachedPhoto :: Text -> Text -> InlineQueryResult+inlineQueryResultCachedPhoto id fileId = InlineQueryResultCachedPhoto id fileId Nothing Nothing Nothing Nothing Nothing++inlineQueryResultCachedGif :: Text -> Text -> InlineQueryResult+inlineQueryResultCachedGif id fileId = InlineQueryResultCachedGif id fileId Nothing Nothing Nothing Nothing++inlineQueryResultCachedMpeg4Gif :: Text -> Text -> InlineQueryResult+inlineQueryResultCachedMpeg4Gif id fileId = InlineQueryResultCachedMpeg4Gif id fileId Nothing Nothing Nothing Nothing++inlineQueryResultCachedSticker :: Text -> Text -> InlineQueryResult+inlineQueryResultCachedSticker id fileId = InlineQueryResultCachedSticker id fileId Nothing Nothing++inlineQueryResultCachedDocument :: Text -> Text -> Text -> InlineQueryResult+inlineQueryResultCachedDocument id fileId title = InlineQueryResultCachedDocument id (Just title) fileId Nothing Nothing Nothing Nothing++inlineQueryResultCachedVideo :: Text -> Text -> Text -> InlineQueryResult+inlineQueryResultCachedVideo id fileId title = InlineQueryResultCachedVideo id fileId (Just title) Nothing Nothing Nothing Nothing++inlineQueryResultCachedVoice :: Text -> Text -> Text -> InlineQueryResult+inlineQueryResultCachedVoice id fileId title = InlineQueryResultCachedVoice id fileId (Just title) Nothing Nothing Nothing++inlineQueryResultCachedAudio :: Text -> Text -> InlineQueryResult+inlineQueryResultCachedAudio id fileId = InlineQueryResultCachedAudio id fileId Nothing Nothing Nothing++data InlineKeyboardMarkup = InlineKeyboardMarkup+  {+    inline_keyboard :: [[InlineKeyboardButton]]+  } deriving (FromJSON, ToJSON, Show, Generic)++data InlineKeyboardButton = InlineKeyboardButton+  {+    ikb_text                             :: Text -- ^ Label text on the button+  , ikb_url                              :: Maybe Text -- ^ HTTP url to be opened when button is pressed+  , ikb_callback_data                    :: Maybe Text -- ^ Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes+  , ikb_switch_inline_query              :: Maybe Text -- ^  If set, pressing the button will prompt the user to select one of their chats, open that chat and insert the bot‘s username and the specified inline query in the input field. Can be empty, in which case just the bot’s username will be inserted.+  , ikb_callback_game                    :: Maybe CallbackGame -- ^  Description of the game that will be launched when the user presses the button. NOTE: This type of button must always be the first button in the first row.+  , ikb_switch_inline_query_current_chat :: Maybe Text -- ^ If set, pressing the button will insert the bot‘s username and the specified inline query in the current chat's input field. Can be empty, in which case only the bot’s username will be inserted.+  , ikb_pay                              :: Maybe Bool -- ^ Specify True, to send a Pay button. NOTE: This type of button must always be the first button in the first row.+  } deriving (Show, Generic)++instance ToJSON InlineKeyboardButton where+  toJSON = toJsonDrop 4++instance FromJSON InlineKeyboardButton where+  parseJSON = parseJsonDrop 4++inlineKeyboardButton :: Text -> InlineKeyboardButton+inlineKeyboardButton buttonText =+  InlineKeyboardButton buttonText Nothing Nothing Nothing Nothing Nothing Nothing++data CallbackGame = CallbackGame+  {+  } deriving (Show, Generic)++instance ToJSON CallbackGame where+  toJSON = toJsonDrop 3++instance FromJSON CallbackGame where+  parseJSON = parseJsonDrop 3++data CallbackQuery = CallbackQuery+  {+    cq_id                :: Text+  , cq_from              :: User+  , cq_message           :: Maybe Message+  , cq_inline_message_id :: Maybe Text+  , cq_chat_instance     :: Text+  , cq_data              :: Maybe Text+  , cq_game_short_name   :: Maybe Text+  } deriving (Show, Generic)++instance ToJSON CallbackQuery where+  toJSON = toJsonDrop 3++instance FromJSON CallbackQuery where+  parseJSON = parseJsonDrop 3++-- | This object represents an incoming update.+-- Only one of the optional parameters can be present in any given update.+data Update = Update+  {+    update_id            :: Int   -- ^ The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. This ID becomes especially handy if you’re using 'setWebhooks', since it allows you to ignore repeated updates or to restore the correct update sequence, should they get out of order.+  , message              :: Maybe Message -- ^ New incoming message of any kind — text, photo, sticker, etc.+  , edited_message       :: Maybe Message -- ^ New version of a message that is known to the bot and was edited+  , channel_post         :: Maybe Message -- ^ New incoming channel post of any kind — text, photo, sticker, etc.+  , edited_channel_post  :: Maybe Message -- ^ New version of a channel post that is known to the bot and was edited+  , inline_query         :: Maybe InlineQuery -- ^ New incoming inline query+  , chosen_inline_result :: Maybe ChosenInlineResult -- ^ The result of a inline query that was chosen by a user and sent to their chat partner+  , callback_query       :: Maybe CallbackQuery -- ^ This object represents an incoming callback query from a callback button in an inline keyboard. If the button that originated the query was attached to a message sent by the bot, the field message will be presented. If the button was attached to a message sent via the bot (in inline mode), the field inline_message_id will be presented.+  , shipping_query       :: Maybe ShippingQuery -- ^  New incoming shipping query. Only for invoices with flexible price+  , pre_checkout_query   :: Maybe PreCheckoutQuery -- ^ New incoming pre-checkout query. Contains full information about checkout+  } deriving (FromJSON, ToJSON, Show, Generic)++-- | This object represents a point on the map.+data Location = Location+  {+    longitude :: Float -- ^ Longitude as defined by sender+  , latitude  :: Float -- ^ Latitude as defined by sender+  } deriving (FromJSON, ToJSON, Show, Generic)++-- | This object represents a file ready to be downloaded. The file can be downloaded via the link+--   @https://api.telegram.org/file/bot<token>/<file_path>@. It is guaranteed that the link will be valid+--   for at least 1 hour. When the link expires, a new one can be requested by calling 'getFile'.+--+--       Maximum file size to download is 20 MB+data File = File+  {+    file_id   :: Text       -- ^ Unique identifier for this file+  , file_size :: Maybe Int  -- ^ File size, if known+  , file_path :: Maybe Text -- ^ File path. Use @https://api.telegram.org/file/bot<token>/<file_path>@ to get the file.+  } deriving (FromJSON, ToJSON, Show, Generic)++-- | This object represent a user's profile pictures.+data UserProfilePhotos = UserProfilePhotos+  {+    total_count :: Int           -- ^ Total number of profile pictures the target user has+  , photos      :: [[PhotoSize]] -- ^ Requested profile pictures (in up to 4 sizes each)+  } deriving (FromJSON, ToJSON, Show, Generic)++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_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+  toJSON = toJsonDrop 3++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+  {+    message_id              :: Int -- ^ Unique message identifier+  , from                    :: Maybe User -- ^ Sender, can be empty for messages sent to channels+  , date                    :: Int -- ^ Date the message was sent in Unix time+  , chat                    :: Chat -- ^ Conversation the message belongs to+  , 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+  , photo                   :: Maybe [PhotoSize] -- ^ Message is a photo, available sizes of the photo+  , 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+  , delete_chat_photo       :: Maybe Bool -- ^ Service message: the chat photo was deleted+  , group_chat_created      :: Maybe Bool -- ^ Service message: the group has been created+  , supergroup_chat_created :: Maybe Bool -- ^ Service message: the supergroup has been created+  , channel_chat_created    :: Maybe Bool -- ^ Service message: the channel has been created+  , migrate_to_chat_id      :: Maybe Int64 -- ^ The group has been migrated to a supergroup with the specified identifier, not exceeding 1e13 by absolute value+  , migrate_from_chat_id    :: Maybe Int64 -- ^ The supergroup has been migrated from a group with the specified identifier, not exceeding 1e13 by absolute value+  , 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.+  } deriving (FromJSON, ToJSON, Show, Generic)++-- | This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.+data MessageEntity = MessageEntity+  {+    me_type   :: Text -- ^ Type of the entity. Can be mention (@username), hashtag, bot_command, url, email, bold (bold text), italic (italic text), code (monowidth string), pre (monowidth block), text_link (for clickable text URLs), text_mention (for users without usernames)+  , me_offset :: Int -- ^ Offset in UTF-16 code units to the start of the entity+  , me_length :: Int -- ^ Length of the entity in UTF-16 code units+  , me_url    :: Maybe Text -- ^ For “text_link” only, url that will be opened after user taps on the text+  , me_user   :: Maybe User -- ^ For “text_mention” only, the mentioned user+  } deriving (Show, Generic)++instance ToJSON MessageEntity where+  toJSON = toJsonDrop 3++instance FromJSON MessageEntity where+  parseJSON = parseJsonDrop 3++-- | This object represents a venue.+data Venue = Venue+  {+    venue_location      :: Location -- ^ Venue location+  , venue_title         :: Text -- ^ Name of the venue+  , venue_address       :: Text -- ^ Address of the venue+  , venue_foursquare_id :: Maybe Text -- ^ Foursquare identifier of the venue+  } deriving (Show, Generic)++instance ToJSON Venue where+  toJSON = toJsonDrop 6++instance FromJSON Venue where+  parseJSON = parseJsonDrop 6++data KeyboardButton = KeyboardButton+  {+    kb_text             :: Text -- ^ Text of the button. If none of the optional fields are used, it will be sent to the bot as a message when the button is pressed+  , kb_request_contact  :: Maybe Bool -- ^ If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only+  , kb_request_location :: Maybe Bool -- ^ If True, the user's current location will be sent when the button is pressed. Available in private chats only+  } deriving (Show, Generic)++instance ToJSON KeyboardButton where+  toJSON = toJsonDrop 3++instance FromJSON KeyboardButton where+  parseJSON = parseJsonDrop 3++keyboardButton :: Text -> KeyboardButton+keyboardButton buttonText = KeyboardButton buttonText Nothing Nothing++data WebhookInfo = WebhookInfo+  {+    whi_url                    :: Text -- ^ Webhook URL, may be empty if webhook is not set up+  , whi_has_custom_certificate :: Bool -- ^ True, if a custom certificate was provided for webhook certificate checks+  , whi_pending_update_count   :: Int -- ^ Number of updates awaiting delivery+  , whi_last_error_date        :: Maybe Int -- ^ Unix time for the most recent error that happened when trying to deliver an update via webhook+  , whi_last_error_message     :: Maybe Text -- ^ Error message in human-readable format for the most recent error that happened when trying to deliver an update via webhook+  , whi_max_connections        :: Maybe Int -- ^ Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery+  , whi_allowed_updates        :: Maybe [Text] -- ^ A list of update types the bot is subscribed to. Defaults to all update types+  } deriving (Show, Generic)++instance ToJSON WebhookInfo where+  toJSON = toJsonDrop 4++instance FromJSON WebhookInfo where+  parseJSON = parseJsonDrop 4++-- Payments++data LabeledPrice+  -- | This object represents a portion of the price for goods or services.+  = LabeledPrice+  {+    lp_label  :: Text -- ^ Portion label+  , lp_amount :: Int -- ^ Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in <https://core.telegram.org/bots/payments/currencies.json currencies.json>, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).+  } deriving (Show, Generic)++instance ToJSON LabeledPrice where+  toJSON = toJsonDrop 3++instance FromJSON LabeledPrice where+  parseJSON = parseJsonDrop 3++newtype CurrencyCode = CurrencyCode Text+  deriving (Show, Eq, Ord)++instance ToJSON CurrencyCode where+  toJSON (CurrencyCode code) = toJSON code++instance FromJSON CurrencyCode where+  parseJSON (String code) = pure $ CurrencyCode code+  parseJSON _ = fail "Unable to parse CurrencyCode"++data Invoice+  -- | This object contains basic information about an invoice.+  = Invoice+  {+    inv_title           :: Text -- ^ Product name+  , inv_description     :: Text -- ^ Product description+  , inv_start_parameter :: Text -- ^ Unique bot deep-linking parameter that can be used to generate this invoice+  , inv_currency        :: CurrencyCode -- ^ Three-letter ISO 4217 <https://core.telegram.org/bots/payments#supported-currencies currency> code+  , inv_total_amount    :: Int -- ^ Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in <https://core.telegram.org/bots/payments/currencies.json currencies.json>, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).+  } deriving (Show, Generic)++instance ToJSON Invoice where+  toJSON = toJsonDrop 4++instance FromJSON Invoice where+  parseJSON = parseJsonDrop 4++data ShippingAddress = ShippingAddress+  {+    ship_addr_country_code :: Text -- ^ ISO 3166-1 alpha-2 country code+  , ship_addr_state        :: Maybe Text -- ^ State, if applicable+  , ship_addr_city         :: Text -- ^ City+  , ship_addr_street_line1 :: Text -- ^ First line for the address+  , ship_addr_street_line2 :: Text -- ^ Second line for the address+  , ship_addr_post_code    :: Text -- ^ Address post code+  } deriving (Show, Generic)++instance ToJSON ShippingAddress where+  toJSON = toJsonDrop 10++instance FromJSON ShippingAddress where+  parseJSON = parseJsonDrop 10++data OrderInfo = OrderInfo+  {+    ord_info_name             :: Maybe Text -- ^ User name+  , ord_info_phone_number     :: Maybe Text -- ^ User's phone number+  , ord_info_email            :: Maybe Text -- ^ User email+  , ord_info_shipping_address :: Maybe ShippingAddress -- ^ User shipping address+  } deriving (Show, Generic)++instance ToJSON OrderInfo where+  toJSON = toJsonDrop 9++instance FromJSON OrderInfo where+  parseJSON = parseJsonDrop 9++data ShippingOption = ShippingOption+  {+    ship_opt_id     :: Text -- ^ Shipping option identifier+  , ship_opt_title  :: Text -- ^ Option title+  , ship_opt_prices :: [LabeledPrice] -- ^ List of price portions+  } deriving (Show, Generic)++instance ToJSON ShippingOption where+  toJSON = toJsonDrop 9++instance FromJSON ShippingOption where+  parseJSON = parseJsonDrop 9++data SuccessfulPayment = SuccessfulPayment+  {+    suc_pmnt_currency                   :: CurrencyCode -- ^ Three-letter ISO 4217 <https://core.telegram.org/bots/payments#supported-currencies currency> code+  , suc_pmnt_total_amount               :: Int -- ^ Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in <https://core.telegram.org/bots/payments/currencies.json currencies.json>, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).+  , suc_pmnt_invoice_payload            :: Text -- ^ Bot specified invoice payload+  , suc_pmnt_shipping_option_id         :: Maybe Text -- ^  Identifier of the shipping option chosen by the user+  , suc_pmnt_order_info                 :: Maybe OrderInfo -- ^ Order info provided by the user+  , suc_pmnt_telegram_payment_charge_id :: Text -- ^ Telegram payment identifier+  , suc_pmnt_provider_payment_charge_id :: Text -- ^ Provider payment identifier+  } deriving (Show, Generic)++instance ToJSON SuccessfulPayment where+  toJSON = toJsonDrop 9++instance FromJSON SuccessfulPayment where+  parseJSON = parseJsonDrop 9++data ShippingQuery = ShippingQuery+  {+    ship_q_id               :: Text -- ^ Unique query identifier+  , ship_q_from             :: User -- ^ User who sent the query+  , ship_q_invoice_payload  :: Text -- ^ Bot specified invoice payload+  , ship_q_shipping_address :: ShippingAddress -- ^ User specified shipping address+  } deriving (Show, Generic)+++instance ToJSON ShippingQuery where+  toJSON = toJsonDrop 7++instance FromJSON ShippingQuery where+  parseJSON = parseJsonDrop 7++data PreCheckoutQuery = PreCheckoutQuery+  {+    pre_che_id                 :: Text -- ^ Unique query identifier+  , pre_che_from               :: User -- ^ User who sent the query+  , pre_che_currency           :: CurrencyCode -- ^ Three-letter ISO 4217 <https://core.telegram.org/bots/payments#supported-currencies currency> code+  , pre_che_total_amount       :: Int -- ^ Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in <https://core.telegram.org/bots/payments/currencies.json currencies.json>, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).+  , pre_che_invoice_payload    :: Text -- ^ Bot specified invoice payload+  , pre_che_shipping_option_id :: Maybe Text -- ^ Identifier of the shipping option chosen by the user+  , pre_che_order_info         :: Maybe OrderInfo -- ^ Order info provided by the user+  } deriving (Show, Generic)++instance ToJSON PreCheckoutQuery where+  toJSON = toJsonDrop 8++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/JsonExt.hs view
@@ -1,24 +1,24 @@-{-# LANGUAGE FlexibleContexts  #-}
-{-# LANGUAGE RankNTypes  #-}
-
--- | This module contains helper functions to work with JSON
-module Web.Telegram.API.Bot.JsonExt
-    (
-      toJsonDrop,
-      parseJsonDrop
-    ) where
-
-import           Data.Aeson
-import           Data.Aeson.Types
-import           GHC.Generics
-
--- | Method used to drop prefix from field name during serialization
-toJsonDrop :: forall a.(Generic a, GToJSON Zero (Rep a)) => Int -> a -> Value
-toJsonDrop prefix = genericToJSON defaultOptions {
-    fieldLabelModifier = drop prefix
-  , omitNothingFields = True
-  }
-
--- | Method used to drop prefix from field name during deserialization
-parseJsonDrop :: forall a.(Generic a, GFromJSON Zero (Rep a)) => Int -> Value -> Parser a
-parseJsonDrop prefix = genericParseJSON defaultOptions { fieldLabelModifier = drop prefix }
+{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE RankNTypes  #-}++-- | This module contains helper functions to work with JSON+module Web.Telegram.API.Bot.JsonExt+    (+      toJsonDrop,+      parseJsonDrop+    ) where++import           Data.Aeson+import           Data.Aeson.Types+import           GHC.Generics++-- | Method used to drop prefix from field name during serialization+toJsonDrop :: forall a.(Generic a, GToJSON Zero (Rep a)) => Int -> a -> Value+toJsonDrop prefix = genericToJSON defaultOptions {+    fieldLabelModifier = drop prefix+  , omitNothingFields = True+  }++-- | Method used to drop prefix from field name during deserialization+parseJsonDrop :: forall a.(Generic a, GFromJSON Zero (Rep a)) => Int -> Value -> Parser a+parseJsonDrop prefix = genericParseJSON defaultOptions { fieldLabelModifier = drop prefix }
src/Web/Telegram/API/Bot/Requests.hs view
@@ -1,667 +1,1077 @@-{-# LANGUAGE DeriveGeneric     #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE FlexibleInstances #-}
-
--- | This module contains data objects which represents requests to Telegram Bot API
-module Web.Telegram.API.Bot.Requests
-    ( -- * Types
-      SendMessageRequest             (..)
-    , ForwardMessageRequest          (..)
-    , FileUpload                     (..)
-    , FileUploadContent              (..)
-    , SetWebhookRequest              (..)
-    , SendPhotoRequest               (..)
-    , SendAudioRequest               (..)
-    , SendDocumentRequest            (..)
-    , SendStickerRequest             (..)
-    , SendVideoRequest               (..)
-    , SendVoiceRequest               (..)
-    , SendLocationRequest            (..)
-    , SendVenueRequest               (..)
-    , SendContactRequest             (..)
-    , SendChatActionRequest          (..)
-    , SendGameRequest                (..)
-    , ChatAction                     (..)
-    , AnswerInlineQueryRequest       (..)
-    , AnswerCallbackQueryRequest     (..)
-    , ReplyKeyboard                  (..)
-    , EditMessageTextRequest         (..)
-    , EditMessageCaptionRequest      (..)
-    , EditMessageReplyMarkupRequest  (..)
-     -- * Functions
-    , localFileUpload
-    , setWebhookRequest
-    , sendMessageRequest
-    , forwardMessageRequest
-    , sendPhotoRequest
-    , uploadPhotoRequest
-    , sendAudioRequest
-    , uploadAudioRequest
-    , sendDocumentRequest
-    , uploadDocumentRequest
-    , sendStickerRequest
-    , uploadStickerRequest
-    , sendVideoRequest
-    , uploadVideoRequest
-    , sendVoiceRequest
-    , uploadVoiceRequest
-    , sendLocationRequest
-    , sendVenueRequest
-    , sendContactRequest
-    , sendChatActionRequest
-    , sendGameRequest
-    , answerInlineQueryRequest
-    , answerCallbackQueryRequest
-    , inlineKeyboardMarkup
-    , replyKeyboardMarkup
-    , replyKeyboardHide
-    , forceReply
-    , editMessageTextRequest
-    , editInlineMessageTextRequest
-    , editMessageCaptionRequest
-    , editInlineMessageCaptionRequest
-    , editMessageReplyMarkupRequest
-    , editInlineMessageReplyMarkupRequest
-    ) where
-
-import           Data.Aeson
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Lazy as LBS
-import           Data.Maybe
-import           Data.Text (Text)
-import qualified Data.Text as T
-import qualified Data.Text.Encoding as T
-import           GHC.Generics
-import           Network.HTTP.Client.MultipartFormData
-import           Network.Mime
-import           Servant.Client.MultipartFormData (ToMultipartFormData (..))
-import           Web.Telegram.API.Bot.Data (InlineKeyboardButton,
-                                            InlineKeyboardMarkup,
-                                            InlineQueryResult, KeyboardButton,
-                                            ParseMode)
-import           Web.Telegram.API.Bot.JsonExt
-
-
--- | This object represents data (image, video, ...) to upload.
-data FileUploadContent =
-    FileUploadFile FilePath
-  | FileUploadBS BS.ByteString
-  | FileUploadLBS LBS.ByteString
-
--- | This object represents data (image, video, ...) with mime type to upload.
-data FileUpload = FileUpload
-  {
-    fileUpload_type    :: Maybe MimeType    -- ^ Mime type of the upload.
-  , fileUpload_content :: FileUploadContent -- ^ The payload/source to upload.
-  }
-
-localFileUpload :: FilePath -> FileUpload
-localFileUpload path = FileUpload
-  { fileUpload_type = Nothing
-  , fileUpload_content = FileUploadFile path
-  }
-
-fileUploadToPart :: Text -> FileUpload -> Part
-fileUploadToPart inputName fileUpload =
-  let part =
-        case fileUpload_content fileUpload of
-          FileUploadFile path -> partFileSource inputName path
-          FileUploadBS bs -> partBS inputName bs
-          FileUploadLBS lbs -> partLBS inputName lbs
-  in part { partContentType = fileUpload_type fileUpload }
-
-utf8Part :: Text -> Text -> Part
-utf8Part inputName = partBS inputName . T.encodeUtf8
-
--- | This object represents request for 'setWebhookWithCertificate'
-data SetWebhookRequest = SetWebhookRequest
-  {
-    webhook_url         :: Text -- ^ HTTPS url to send updates to. Use `setWebhook` function and an empty string to remove webhook integration
-  , webhook_certificate :: FileUpload -- ^ Upload your public key certificate so that the root certificate in use can be checked.
-  }
-
-instance ToMultipartFormData SetWebhookRequest where
-  toMultipartFormData req =
-    [ utf8Part         "url"         $ webhook_url req
-    , fileUploadToPart "certificate" $ webhook_certificate req
-    ]
-
-setWebhookRequest :: Text -> FileUpload -> SetWebhookRequest
-setWebhookRequest = SetWebhookRequest
-
--- | This object represents request for 'sendMessage'
-data SendMessageRequest = SendMessageRequest
-  {
-    message_chat_id                  :: Text -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)
-  , message_text                     :: Text -- ^ Text of the message to be sent
-  , message_parse_mode               :: Maybe ParseMode -- ^ Send 'Markdown', if you want Telegram apps to show bold, italic and inline URLs in your bot's message
-  , message_disable_web_page_preview :: Maybe Bool -- ^ Disables link previews for links in this message
-  , message_disable_notification     :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.
-  , message_reply_to_message_id      :: Maybe Int -- ^ If the message is a reply, ID of the original message
-  , message_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.
-  } deriving (Show, Generic)
-
-instance ToJSON SendMessageRequest where
-  toJSON = toJsonDrop 8
-
-instance FromJSON SendMessageRequest where
-  parseJSON = parseJsonDrop 8
-
-sendMessageRequest :: Text -> Text -> SendMessageRequest
-sendMessageRequest chatId text = SendMessageRequest chatId text Nothing Nothing Nothing Nothing Nothing
-
--- | This object represents request for 'forwardMessage'
-data ForwardMessageRequest = ForwardMessageRequest
-  {
-    forward_chat_id :: Text -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)
-  , forward_from_chat_id :: Text -- ^ Unique identifier for the chat where the original message was sent (or channel username in the format @@channelusername@)
-  , forward_disable_notification     :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.
-  , forward_message_id :: Int -- ^ Unique message identifier
-  } deriving (Show, Generic)
-
-instance ToJSON ForwardMessageRequest where
-  toJSON = toJsonDrop 8
-
-instance FromJSON ForwardMessageRequest where
-  parseJSON = parseJsonDrop 8
-
-forwardMessageRequest :: Text -> Text -> Int -> ForwardMessageRequest
-forwardMessageRequest chatId fromChatId forwardMessageId = ForwardMessageRequest chatId fromChatId Nothing forwardMessageId
-
--- | This object represents request for 'sendPhoto'
-data SendPhotoRequest payload = SendPhotoRequest
-  {
-    photo_chat_id              :: Text -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)
-  , photo_photo                :: payload -- ^ Photo to send. You can either pass a file_id as String to resend a photo that is already on the Telegram servers, or upload a new photo.
-  , photo_caption              :: Maybe Text -- ^ Photo caption (may also be used when resending photos by file_id), 0-200 characters.
-  , photo_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.
-  , photo_reply_to_message_id  :: Maybe Int -- ^ If the message is a reply, ID of the original message
-  , photo_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.
-  } deriving (Show, Generic)
-
-instance ToJSON (SendPhotoRequest Text) where
-  toJSON = toJsonDrop 6
-
-instance FromJSON (SendPhotoRequest Text) where
-  parseJSON = parseJsonDrop 6
-
-sendPhotoRequest :: Text -> Text -> SendPhotoRequest Text
-sendPhotoRequest chatId photo = SendPhotoRequest chatId photo Nothing Nothing Nothing Nothing
-
-uploadPhotoRequest :: Text -> FileUpload -> SendPhotoRequest FileUpload
-uploadPhotoRequest chatId photo = SendPhotoRequest chatId photo Nothing Nothing Nothing Nothing
-
-instance ToMultipartFormData (SendPhotoRequest FileUpload) where
-  toMultipartFormData req =
-    [ utf8Part "chat_id" (photo_chat_id req) ] ++
-    catMaybes
-    [ utf8Part "caption" <$> photo_caption req
-    , partLBS "disable_notification" . encode <$> photo_disable_notification req
-    , utf8Part "reply_to_message_id" . T.pack . show <$> photo_reply_to_message_id req
-    , partLBS "reply_markup" . encode <$> photo_reply_markup req
-    ] ++
-    [ fileUploadToPart "photo" (photo_photo req) ]
-
--- | This object represents request for 'sendAudio'
-data SendAudioRequest payload = SendAudioRequest
-  {
-    _audio_chat_id              :: Text -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)
-  , _audio_audio                :: payload -- ^ Audio to send. You can either pass a file_id as String to resend an audio that is already on the Telegram servers, or upload a new audio file.
-  , _audio_caption              :: Maybe Text -- ^ Audio caption, 0-200 characters
-  , _audio_duration             :: Maybe Int -- ^ Duration of the audio in seconds
-  , _audio_performer            :: Maybe Text -- ^ Performer
-  , _audio_title                :: Maybe Text -- ^ Track name
-  , _audio_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.
-  , _audio_reply_to_message_id  :: Maybe Int -- ^ If the message is a reply, ID of the original message
-  , _audio_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.
-  } deriving (Show, Generic)
-
-instance ToJSON (SendAudioRequest Text) where
-  toJSON = toJsonDrop 7
-
-instance FromJSON (SendAudioRequest Text) where
-  parseJSON = parseJsonDrop 7
-
-instance ToMultipartFormData (SendAudioRequest FileUpload) where
-  toMultipartFormData req =
-    [ utf8Part "chat_id" (_audio_chat_id req) ] ++
-    catMaybes
-    [ utf8Part "duration" . T.pack . show <$> _audio_duration req
-    , utf8Part "performer" <$> _audio_performer req
-    , utf8Part "title" <$> _audio_title req
-    , partLBS "disable_notification" . encode <$> _audio_disable_notification req
-    , utf8Part "reply_to_message_id" . T.pack . show <$> _audio_reply_to_message_id req
-    , partLBS "reply_markup" . encode <$> _audio_reply_markup req
-    ] ++
-    [ fileUploadToPart "audio" (_audio_audio req) ]
-
-sendAudioRequest :: Text -> Text -> SendAudioRequest Text
-sendAudioRequest chatId audio = SendAudioRequest chatId audio Nothing Nothing Nothing Nothing Nothing Nothing Nothing
-
-uploadAudioRequest :: Text -> FileUpload -> SendAudioRequest FileUpload
-uploadAudioRequest chatId audio = SendAudioRequest chatId audio Nothing Nothing Nothing Nothing Nothing Nothing Nothing
-
--- | This object represents request for 'sendSticker'
-data SendStickerRequest payload = SendStickerRequest
-  {
-    sticker_chat_id                  :: Text -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)
-  , sticker_sticker                  :: payload -- ^ Sticker to send. You can either pass a file_id as String to resend a sticker that is already on the Telegram servers, or upload a new sticker.
-  , sticker_disable_notification     :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.
-  , sticker_reply_to_message_id      :: Maybe Int -- ^ If the message is a reply, ID of the original message
-  , sticker_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.
-  } deriving (Show, Generic)
-
-instance ToJSON (SendStickerRequest Text) where
-  toJSON = toJsonDrop 8
-
-instance FromJSON (SendStickerRequest Text) where
-  parseJSON = parseJsonDrop 8
-
-instance ToMultipartFormData (SendStickerRequest FileUpload) where
-  toMultipartFormData req =
-    [ utf8Part "chat_id" (sticker_chat_id req) ] ++
-    catMaybes
-    [ partLBS "disable_notification" . encode <$> sticker_disable_notification req
-    , utf8Part "reply_to_message_id" . T.pack . show <$> sticker_reply_to_message_id req
-    , partLBS "reply_markup" . encode <$> sticker_reply_markup req
-    ] ++
-    [ fileUploadToPart "sticker" (sticker_sticker req) ]
-
-sendStickerRequest :: Text -> Text -> SendStickerRequest Text
-sendStickerRequest chatId sticker = SendStickerRequest chatId sticker Nothing Nothing Nothing
-
-uploadStickerRequest :: Text -> FileUpload -> SendStickerRequest FileUpload
-uploadStickerRequest chatId sticker = SendStickerRequest chatId sticker Nothing Nothing Nothing
-
--- | This object represents request for 'sendDocument'
-data SendDocumentRequest payload = SendDocumentRequest
-  {
-    document_chat_id                  :: Text -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)
-  , document_document                 :: payload -- ^ File to send. You can either pass a file_id as String to resend a file that is already on the Telegram servers, or upload a new file.
-  , document_caption                  :: Maybe Text -- ^ Document caption (may also be used when resending documents by file_id), 0-200 characters
-  , document_disable_notification     :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.
-  , document_reply_to_message_id      :: Maybe Int -- ^ If the message is a reply, ID of the original message
-  , document_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.
-  } deriving (Show, Generic)
-
-instance ToJSON (SendDocumentRequest Text) where
-  toJSON = toJsonDrop 9
-
-instance FromJSON (SendDocumentRequest Text) where
-  parseJSON = parseJsonDrop 9
-
-instance ToMultipartFormData (SendDocumentRequest FileUpload) where
-  toMultipartFormData req =
-    [ utf8Part "chat_id" (document_chat_id req) ] ++
-    catMaybes
-    [ utf8Part "caption" <$> document_caption req
-    , partLBS "disable_notification" . encode <$> document_disable_notification req
-    , utf8Part "reply_to_message_id" . T.pack . show <$> document_reply_to_message_id req
-    , partLBS "reply_markup" . encode <$> document_reply_markup req
-    ] ++
-    [ fileUploadToPart "document" (document_document req) ]
-
-sendDocumentRequest :: Text -> Text -> SendDocumentRequest Text
-sendDocumentRequest chatId document = SendDocumentRequest chatId document Nothing Nothing Nothing Nothing
-
-uploadDocumentRequest :: Text -> FileUpload -> SendDocumentRequest FileUpload
-uploadDocumentRequest chatId document = SendDocumentRequest chatId document Nothing Nothing Nothing Nothing
-
--- | This object represents request for 'sendVideo'
-data SendVideoRequest payload = SendVideoRequest
-  {
-    _video_chat_id                  :: Text -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)
-  , _video_video                    :: payload -- ^ Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram servers, or upload a new video.
-  , _video_duration                 :: Maybe Int -- ^ Duration of sent video in seconds
-  , _video_caption                  :: Maybe Text -- ^ Video caption, 0-200 characters.
-  , _video_disable_notification     :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.
-  , _video_reply_to_message_id      :: Maybe Int -- ^ If the message is a reply, ID of the original message
-  , _video_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.
-  } deriving (Show, Generic)
-
-instance ToJSON (SendVideoRequest Text) where
-  toJSON = toJsonDrop 7
-
-instance FromJSON (SendVideoRequest Text) where
-  parseJSON = parseJsonDrop 7
-
-instance ToMultipartFormData (SendVideoRequest FileUpload) where
-  toMultipartFormData req =
-    [ utf8Part "chat_id" (_video_chat_id req) ] ++
-    catMaybes
-    [ partLBS "duration" . encode <$> _video_duration req
-    , utf8Part "caption" <$> _video_caption req
-    , partLBS "disable_notification" . encode <$> _video_disable_notification req
-    , utf8Part "reply_to_message_id" . T.pack . show <$> _video_reply_to_message_id req
-    , partLBS "reply_markup" . encode <$> _video_reply_markup req
-    ] ++
-    [ fileUploadToPart "video" (_video_video req) ]
-
-sendVideoRequest :: Text -> Text -> SendVideoRequest Text
-sendVideoRequest chatId video = SendVideoRequest chatId video Nothing Nothing Nothing Nothing Nothing
-
-uploadVideoRequest :: Text -> FileUpload -> SendVideoRequest FileUpload
-uploadVideoRequest chatId video = SendVideoRequest chatId video Nothing Nothing Nothing Nothing Nothing
-
--- | This object represents request for 'sendVoice'
-data SendVoiceRequest payload = SendVoiceRequest
-  {
-    _voice_chat_id                  :: Text -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)
-  , _voice_voice                    :: payload -- ^ Audio file to send. You can either pass a file_id as String to resend an audio that is already on the Telegram servers, or upload a new audio file.
-  , _voice_caption                  :: Maybe Text -- ^ Voice message caption, 0-200 characters
-  , _voice_duration                 :: Maybe Int -- ^ Duration of sent audio in seconds
-  , _voice_disable_notification     :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.
-  , _voice_reply_to_message_id      :: Maybe Int -- ^ If the message is a reply, ID of the original message
-  , _voice_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.
-  } deriving (Show, Generic)
-
-instance ToJSON (SendVoiceRequest Text) where
-  toJSON = toJsonDrop 7
-
-instance FromJSON (SendVoiceRequest Text) where
-  parseJSON = parseJsonDrop 7
-
-instance ToMultipartFormData (SendVoiceRequest FileUpload) where
-  toMultipartFormData req =
-    [ utf8Part "chat_id" (_voice_chat_id req) ] ++
-    catMaybes
-    [ partLBS "duration" . encode <$> _voice_duration req
-    , partLBS "disable_notification" . encode <$> _voice_disable_notification req
-    , utf8Part "reply_to_message_id" . T.pack . show <$> _voice_reply_to_message_id req
-    , partLBS "reply_markup" . encode <$> _voice_reply_markup req
-    ] ++
-    [ fileUploadToPart "voice" (_voice_voice req) ]
-
-sendVoiceRequest :: Text -> Text -> SendVoiceRequest Text
-sendVoiceRequest chatId voice = SendVoiceRequest chatId voice Nothing Nothing Nothing Nothing Nothing
-
-uploadVoiceRequest :: Text -> FileUpload -> SendVoiceRequest FileUpload
-uploadVoiceRequest chatId voice = SendVoiceRequest chatId voice Nothing Nothing Nothing Nothing Nothing
-
--- | This object represents request for 'sendLocation'
-data SendLocationRequest = SendLocationRequest
-  {
-    location_chat_id                :: Text -- ^ 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_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.
-  } deriving (Show, Generic)
-
-instance ToJSON SendLocationRequest where
-  toJSON = toJsonDrop 9
-
-instance FromJSON SendLocationRequest where
-  parseJSON = parseJsonDrop 9
-
-sendLocationRequest :: Text -> Float -> Float -> SendLocationRequest
-sendLocationRequest chatId latitude longitude = SendLocationRequest chatId latitude longitude Nothing Nothing Nothing
-
--- | This object represents request for 'sendVenue'
-data SendVenueRequest = SendVenueRequest
-  {
-    _venue_chat_id               :: Text -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)
-  , _venue_latitude              :: Float -- ^ Latitude of the venue
-  , _venue_longitude             :: Float -- ^ Longitude of the venue
-  , _venue_title                 :: Text -- ^ Name of the venue
-  , _venue_address               :: Text -- ^ Address of the venue
-  , _venue_foursquare_id         :: Maybe Text -- ^ Foursquare identifier of the venue
-  , _venue_disable_notification  :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.
-  , _venue_reply_to_message_id   :: Maybe Int -- ^ If the message is a reply, ID of the original message
-  , _venue_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.
-  } deriving (Show, Generic)
-
-instance ToJSON SendVenueRequest where
-  toJSON = toJsonDrop 7
-
-instance FromJSON SendVenueRequest where
-  parseJSON = parseJsonDrop 7
-
-sendVenueRequest :: Text -> Float -> Float -> Text -> Text -> SendVenueRequest
-sendVenueRequest chatId latitude longitude title address = SendVenueRequest chatId latitude longitude title address Nothing Nothing Nothing Nothing
-
--- | This object represents request for 'sendContact'
-data SendContactRequest = SendContactRequest
-  {
-    _contact_chat_id              :: Text -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)
-  , _contact_phone_number         :: Text       -- ^ Contact's phone number
-  , _contact_first_name           :: Text       -- ^ Contact's first name
-  , _contact_last_name            :: Maybe Text -- ^ Contact's last name
-  , _contact_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.
-  , _contact_reply_to_message_id  :: Maybe Int -- ^ If the message is a reply, ID of the original message
-  , _contact_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.
-  } deriving (Show, Generic)
-
-instance ToJSON SendContactRequest where
-  toJSON = toJsonDrop 9
-
-instance FromJSON SendContactRequest where
-  parseJSON = parseJsonDrop 9
-
-sendContactRequest :: Text -> Text -> Text -> SendContactRequest
-sendContactRequest chatId phoneNumber firstName = SendContactRequest chatId phoneNumber firstName Nothing Nothing Nothing Nothing
-
--- | Type of action to broadcast.
-data ChatAction = Typing
-                | UploadPhoto
-                | RecordVideo
-                | UploadVideo
-                | RecordAudio
-                | UploadAudio
-                | UploadDocument
-                | FindLocation deriving (Show, Generic)
-
-instance ToJSON ChatAction where
-  toJSON Typing         = "typing"
-  toJSON UploadPhoto    = "upload_photo"
-  toJSON RecordVideo    = "record_video"
-  toJSON UploadVideo    = "upload_video"
-  toJSON RecordAudio    = "record_audio"
-  toJSON UploadAudio    = "upload_audio"
-  toJSON UploadDocument = "upload_document"
-  toJSON FindLocation   = "find_location"
-
-instance FromJSON ChatAction where
-  parseJSON "typing"          = pure Typing
-  parseJSON "upload_photo"    = pure UploadPhoto
-  parseJSON "record_video"    = pure RecordVideo
-  parseJSON "upload_video"    = pure UploadVideo
-  parseJSON "record_audio"    = pure RecordAudio
-  parseJSON "upload_audio"    = pure UploadAudio
-  parseJSON "upload_document" = pure UploadDocument
-  parseJSON "find_location"   = pure FindLocation
-  parseJSON _                 = fail "Failed to parse ChatAction"
-
--- | This object represents request for 'sendChatAction'
-data SendChatActionRequest = SendChatActionRequest
-  {
-    action_chat_id :: Text -- ^ Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
-  , action_action :: ChatAction -- ^ Type of action to broadcast.
-  } deriving (Show, Generic)
-
-instance ToJSON SendChatActionRequest where
-  toJSON = toJsonDrop 7
-
-instance FromJSON SendChatActionRequest where
-  parseJSON = parseJsonDrop 7
-
-sendChatActionRequest :: Text -> ChatAction -> SendChatActionRequest
-sendChatActionRequest = SendChatActionRequest
-
--- | This object represents request for 'sendGame'
-data SendGameRequest = SendGameRequest
-  {
-    game_chat_id :: Text -- ^ Unique identifier for the target chat
-  , game_game_short_name :: Text -- ^ Short name of the game, serves as the unique identifier for the game. Set up your games via Botfather.
-  , game_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.
-  , game_reply_to_message_id :: Maybe Int -- ^  If the message is a reply, ID of the original message
-  , game_reply_markup :: Maybe InlineKeyboardMarkup -- ^ A JSON-serialized object for an inline keyboard. If empty, one ‘Play game_title’ button will be shown. If not empty, the first button must launch the game.
-  } deriving (Show, Generic)
-
-instance ToJSON SendGameRequest where
-  toJSON = toJsonDrop 5
-
-instance FromJSON SendGameRequest where
-  parseJSON = parseJsonDrop 5
-
-sendGameRequest :: Text -> Text -> SendGameRequest
-sendGameRequest chatId shortName = SendGameRequest chatId shortName Nothing Nothing Nothing
-
-data AnswerInlineQueryRequest = AnswerInlineQueryRequest
-  {
-    query_inline_query_id     :: Text -- ^ Unique identifier for the answered query
-  , query_results             :: [InlineQueryResult] -- ^ A JSON-serialized array of results for the inline query
-  , query_cache_time          :: Maybe Int -- ^ The maximum amount of time in seconds that the result of the inline query may be cached on the server. Defaults to 300.
-  , query_is_personal         :: Maybe Bool -- ^ Pass True, if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query
-  , query_next_offset         :: Maybe Text -- ^ Pass the offset that a client should send in the next query with the same text to receive more results. Pass an empty string if there are no more results or if you don‘t support pagination. Offset length can’t exceed 64 bytes.
-  , query_switch_pm_text      :: Maybe Text -- ^ If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter
-  , query_switch_pm_parameter :: Maybe Text -- ^ Parameter for the start message sent to the bot when user presses the switch button
-                                            --
-                                            -- Example: An inline bot that sends YouTube videos can ask the user to connect the bot to
-                                            -- their YouTube account to adapt search results accordingly. To do this, it displays a
-                                            -- ‘Connect your YouTube account’ button above the results, or even before showing any.
-                                            -- The user presses the button, switches to a private chat with the bot and, in doing so,
-                                            -- passes a start parameter that instructs the bot to return an oauth link. Once done,
-                                            -- the bot can offer a switch_inline button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.
-  } deriving (Show, Generic)
-
-instance ToJSON AnswerInlineQueryRequest where
-  toJSON = toJsonDrop 6
-
-instance FromJSON AnswerInlineQueryRequest where
-  parseJSON = parseJsonDrop 6
-
-answerInlineQueryRequest :: Text -> [InlineQueryResult] -> AnswerInlineQueryRequest
-answerInlineQueryRequest queryId results = AnswerInlineQueryRequest queryId results Nothing Nothing Nothing Nothing Nothing
-
-data AnswerCallbackQueryRequest = AnswerCallbackQueryRequest
-  {
-    cq_callback_query_id :: Text -- ^ Unique identifier for the query to be answered
-  , cq_text :: Maybe Text -- ^ Text of the notification. If not specified, nothing will be shown to the user
-  , cq_show_alert :: Maybe Bool -- ^ If true, an alert will be shown by the client instead of a notification at the top of the chat screen. Defaults to false.
-  , cq_url :: Maybe Text -- ^ URL that will be opened by the user's client. If you have created a `Game` and accepted the conditions via `@Botfather`, specify the URL that opens your game – note that this will only work if the query comes from a `callback_game` button. Otherwise, you may use links like telegram.me/your_bot?start=XXXX that open your bot with a parameter.
-  , cq_cache_time :: Maybe Int -- ^ The maximum amount of time in seconds that the result of the callback query may be cached client-side. Telegram apps will support caching starting in version 3.14. Defaults to 0.
-  } deriving (Show, Generic)
-
-instance ToJSON AnswerCallbackQueryRequest where
-  toJSON = toJsonDrop 3
-
-instance FromJSON AnswerCallbackQueryRequest where
-  parseJSON = parseJsonDrop 3
-
-answerCallbackQueryRequest :: Text -> AnswerCallbackQueryRequest
-answerCallbackQueryRequest queryId = AnswerCallbackQueryRequest queryId Nothing Nothing Nothing Nothing
-
-data ReplyKeyboard =
-  -- | This object represents a custom keyboard with reply options
-  ReplyInlineKeyboardMarkup
-  {
-    reply_inline_keyboard      :: [[InlineKeyboardButton]] -- ^ Array of button rows, each represented by an Array of InlineKeyboardButton objects
-  }
-  | ReplyKeyboardMarkup
-  {
-    reply_keyboard             :: [[KeyboardButton]] -- ^ Array of button rows, each represented by an Array of 'KeyboardButton' objects
-  , reply_resize_keyboard      :: Maybe Bool -- ^ Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard.
-  , reply_one_time_keyboard    :: Maybe Bool -- ^ Requests clients to hide the keyboard as soon as it's been used. Defaults to false.
-  , reply_selective            :: Maybe Bool -- ^ Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.    Example: A user requests to change the bot‘s language, bot replies to the request with a keyboard to select the new language. Other users in the group don’t see the keyboard.
-  }
-  -- | Upon receiving a message with this object, Telegram clients will hide the current custom keyboard and display the default letter-keyboard. By default, custom keyboards are displayed until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are hidden immediately after the user presses a button
-  | ReplyKeyboardHide
-  {
-    reply_hide_keyboard        :: Bool -- ^ Requests clients to hide the custom keyboard
-  , reply_selective            :: Maybe Bool -- ^ Use this parameter if you want to hide keyboard for specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.    Example: A user votes in a poll, bot returns confirmation message in reply to the vote and hides keyboard for that user, while still showing the keyboard with poll options to users who haven't voted yet.
-  }
-  | ReplyKeyboardRemove
-  {
-    reply_remove_keyboard      :: Bool -- ^ Requests clients to remove the custom keyboard (user will not be able to summon this keyboard; if you want to hide the keyboard from sight but keep it accessible, use one_time_keyboard in 'ReplyKeyboardMarkup')
-  , reply_selective            :: Maybe Bool -- ^ Use this parameter if you want to hide keyboard for specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.    Example: A user votes in a poll, bot returns confirmation message in reply to the vote and hides keyboard for that user, while still showing the keyboard with poll options to users who haven't voted yet.
-  }
-  -- | Upon receiving a message with this object, Telegram clients will display a reply interface to the user (act as if the user has selected the bot‘s message and tapped ’Reply'). This can be extremely useful if you want to create user-friendly step-by-step interfaces without having to sacrifice privacy mode.
-  | ForceReply
-  {
-    reply_force_reply          :: Bool -- ^ Shows reply interface to the user, as if they manually selected the bot‘s message and tapped ’Reply'
-  , reply_selective            :: Maybe Bool -- ^ Use this parameter if you want to force reply from specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
-  } deriving (Show, Generic)
-
-instance ToJSON ReplyKeyboard where
-  toJSON = toJsonDrop 6
-
-instance FromJSON ReplyKeyboard where
-  parseJSON = parseJsonDrop 6
-
-inlineKeyboardMarkup :: [[InlineKeyboardButton]] -> ReplyKeyboard
-inlineKeyboardMarkup = ReplyInlineKeyboardMarkup
-
-replyKeyboardMarkup :: [[KeyboardButton]] -> ReplyKeyboard
-replyKeyboardMarkup keyboard = ReplyKeyboardMarkup keyboard Nothing Nothing Nothing
-
-replyKeyboardHide :: ReplyKeyboard
-replyKeyboardHide = ReplyKeyboardHide True Nothing
-
-forceReply :: ReplyKeyboard
-forceReply = ForceReply True Nothing
-
-data EditMessageTextRequest = EditMessageTextRequest
-  {
-    emt_chat_id :: Maybe Text -- ^ Required if `inline_message_id` is not specified. Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
-  , emt_message_id :: Maybe Int -- ^ if `inline_message_id` is not specified. Unique identifier of the sent message
-  , emt_inline_message_id :: Maybe Text -- ^ Required if chat_id and message_id are not specified. Identifier of the inline message
-  , emt_text :: Text -- ^ New text of the message
-  , emt_parse_mode :: Maybe ParseMode -- ^ Send `Markdown` or `HTML`, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message.
-  , emt_disable_web_page_preview :: Maybe Bool -- ^ Disables link previews for links in this message
-  , emt_reply_markup :: Maybe InlineKeyboardMarkup -- ^ A JSON-serialized object for an inline keyboard.
-  } deriving (Show, Generic)
-
-instance ToJSON EditMessageTextRequest where
-  toJSON = toJsonDrop 4
-
-instance FromJSON EditMessageTextRequest where
-  parseJSON = parseJsonDrop 4
-
-editMessageTextRequest :: Text -> Int -> Text -> EditMessageTextRequest
-editMessageTextRequest chatId messageId text = EditMessageTextRequest (Just chatId) (Just messageId) Nothing text Nothing Nothing Nothing
-
-editInlineMessageTextRequest :: Text -> Text -> EditMessageTextRequest
-editInlineMessageTextRequest inlineMessageId text = EditMessageTextRequest Nothing Nothing (Just inlineMessageId) text Nothing Nothing Nothing
-
-data EditMessageCaptionRequest = EditMessageCaptionRequest
-  {
-    emc_chat_id :: Maybe Text -- ^ Required if `inline_message_id` is not specified. Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
-  , emc_message_id :: Maybe Int -- ^ Required if `inline_message_id` is not specified. Unique identifier of the sent message
-  , emc_inline_message_id :: Maybe Text -- ^ Required if `chat_id` and `message_id` are not specified. Identifier of the inline message
-  , emc_caption :: Maybe Text -- ^ New caption of the message
-  , emc_reply_markup :: Maybe InlineKeyboardMarkup -- ^ A JSON-serialized object for an inline keyboard.
-  } deriving (Show, Generic)
-
-instance ToJSON EditMessageCaptionRequest where
-  toJSON = toJsonDrop 4
-
-instance FromJSON EditMessageCaptionRequest where
-  parseJSON = parseJsonDrop 4
-
-editMessageCaptionRequest :: Text -> Int -> Maybe Text -> EditMessageCaptionRequest
-editMessageCaptionRequest chatId messageId caption = EditMessageCaptionRequest (Just chatId) (Just messageId) Nothing caption Nothing
-
-editInlineMessageCaptionRequest :: Text -> Maybe Text -> EditMessageCaptionRequest
-editInlineMessageCaptionRequest inlineMessageId caption = EditMessageCaptionRequest Nothing Nothing (Just inlineMessageId) caption Nothing
-
-data EditMessageReplyMarkupRequest = EditMessageReplyMarkupRequest
-  {
-    emrm_chat_id :: Maybe Text -- ^ Required if `inline_message_id` is not specified. Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
-  , emrm_message_id :: Maybe Int -- ^ Required if `inline_message_id` is not specified. Unique identifier of the sent message
-  , emrm_inline_message_id :: Maybe Text -- ^ Required if `chat_id` and `message_id` are not specified. Identifier of the inline message
-  , emrm_reply_markup :: Maybe InlineKeyboardMarkup -- ^ A JSON-serialized object for an inline keyboard.
-  } deriving (Show, Generic)
-
-instance ToJSON EditMessageReplyMarkupRequest where
-  toJSON = toJsonDrop 5
-
-instance FromJSON EditMessageReplyMarkupRequest where
-  parseJSON = parseJsonDrop 5
-
-editMessageReplyMarkupRequest :: Text -> Int -> Maybe InlineKeyboardMarkup -> EditMessageReplyMarkupRequest
-editMessageReplyMarkupRequest chatId messageId keyboard = EditMessageReplyMarkupRequest (Just chatId) (Just messageId) Nothing keyboard
-
-editInlineMessageReplyMarkupRequest :: Text -> Maybe InlineKeyboardMarkup -> EditMessageReplyMarkupRequest
-editInlineMessageReplyMarkupRequest inlineMessageId keyboard = EditMessageReplyMarkupRequest Nothing Nothing (Just inlineMessageId) keyboard
+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}++-- | This module contains data objects which represents requests to Telegram Bot API+module Web.Telegram.API.Bot.Requests+    ( -- * Types+      ChatId                         (..)+    , SendMessageRequest             (..)+    , ForwardMessageRequest          (..)+    , FileUpload                     (..)+    , FileUploadContent              (..)+    , SetWebhookRequest              (..)+    , GetUpdatesRequest              (..)+    , SendPhotoRequest               (..)+    , SendAudioRequest               (..)+    , SendDocumentRequest            (..)+    , SendStickerRequest             (..)+    , SendVideoRequest               (..)+    , SendVoiceRequest               (..)+    , SendVideoNoteRequest           (..)+    , SendMediaGroupRequest          (..)+    , SendLocationRequest            (..)+    , SendVenueRequest               (..)+    , SendContactRequest             (..)+    , SendChatActionRequest          (..)+    , SendGameRequest                (..)+    , ChatAction                     (..)+    , AnswerInlineQueryRequest       (..)+    , AnswerCallbackQueryRequest     (..)+    , ReplyKeyboard                  (..)+    , EditMessageTextRequest         (..)+    , EditMessageCaptionRequest      (..)+    , EditMessageReplyMarkupRequest  (..)+    , SendInvoiceRequest             (..)+    , AnswerShippingQueryRequest     (..)+    , AnswerPreCheckoutQueryRequest  (..)+    , RestrictChatMemberRequest      (..)+    , PromoteChatMemberRequest       (..)+    , SetChatPhotoRequest            (..)+    , UploadStickerFileRequest       (..)+    , CreateNewStickerSetRequest     (..)+    , AddStickerToSetRequest         (..)+    , EditMessageLiveLocationRequest (..)+    , StopMessageLiveLocationRequest (..)+     -- * Functions+    , localFileUpload+    , setWebhookRequest+    , setWebhookRequest'+    , getUpdatesRequest+    , sendMessageRequest+    , forwardMessageRequest+    , sendPhotoRequest+    , uploadPhotoRequest+    , sendAudioRequest+    , uploadAudioRequest+    , sendDocumentRequest+    , uploadDocumentRequest+    , sendStickerRequest+    , uploadStickerRequest+    , sendVideoRequest+    , uploadVideoRequest+    , sendVoiceRequest+    , uploadVoiceRequest+    , sendVideoNoteRequest+    , uploadVideoNoteRequest+    , sendMediaGroupRequest+    , sendLocationRequest+    , sendVenueRequest+    , sendContactRequest+    , sendChatActionRequest+    , sendGameRequest+    , answerInlineQueryRequest+    , answerCallbackQueryRequest+    , inlineKeyboardMarkup+    , replyKeyboardMarkup+    , replyKeyboardHide+    , forceReply+    , editMessageTextRequest+    , editInlineMessageTextRequest+    , editMessageCaptionRequest+    , editInlineMessageCaptionRequest+    , editMessageReplyMarkupRequest+    , editInlineMessageReplyMarkupRequest+    , sendInvoiceRequest+    , okShippingQueryRequest+    , errorShippingQueryRequest+    , okAnswerPrecheckoutQueryRequest+    , errorAnswerPrecheckoutQueryRequest+    , restrictChatMemberRequest+    , promoteChatMemberRequest+    ) where++import           Data.Aeson+import           Data.Aeson.Types                      (typeMismatch)+import qualified Data.ByteString                       as BS+import qualified Data.ByteString.Lazy                  as LBS+import           Data.Int                              (Int64)+import           Data.Maybe+import           Data.Text                             (Text)+import qualified Data.Text                             as T+import qualified Data.Text.Encoding                    as T+import           GHC.Generics+import           Network.HTTP.Client.MultipartFormData+import           Network.Mime+import           Servant.Client.MultipartFormData      (ToMultipartFormData (..))+import           Web.Telegram.API.Bot.Data             (CurrencyCode,+                                                        InlineKeyboardButton,+                                                        InlineKeyboardMarkup,+                                                        InlineQueryResult,+                                                        InputMedia,+                                                        KeyboardButton,+                                                        LabeledPrice,+                                                        MaskPosition, ParseMode,+                                                        ShippingOption)+import           Web.Telegram.API.Bot.JsonExt+++-- | This object represents data (image, video, ...) to upload.+data FileUploadContent =+    FileUploadFile FilePath+  | FileUploadBS BS.ByteString+  | FileUploadLBS LBS.ByteString++-- | This object represents data (image, video, ...) with mime type to upload.+data FileUpload = FileUpload+  {+    fileUpload_type    :: Maybe MimeType    -- ^ Mime type of the upload.+  , fileUpload_content :: FileUploadContent -- ^ The payload/source to upload.+  }++localFileUpload :: FilePath -> FileUpload+localFileUpload path = FileUpload+  { fileUpload_type = Nothing+  , fileUpload_content = FileUploadFile path+  }++fileUploadToPart :: Text -> FileUpload -> Part+fileUploadToPart inputName fileUpload =+  let part =+        case fileUpload_content fileUpload of+          FileUploadFile path -> partFileSource inputName path+          FileUploadBS bs     -> partBS inputName bs+          FileUploadLBS lbs   -> partLBS inputName lbs+  in part { partContentType = fileUpload_type fileUpload }++utf8Part :: Text -> Text -> Part+utf8Part inputName = partBS inputName . T.encodeUtf8++-- | This object represents request for 'setWebhookWithCertificate'+data SetWebhookRequest = SetWebhookRequest+  {+    webhook_url             :: Text -- ^ HTTPS url to send updates to. Use `setWebhook` function and an empty string to remove webhook integration+  , webhook_certificate     :: FileUpload -- ^ Upload your public key certificate so that the root certificate in use can be checked.+  , webhook_max_connections :: Maybe Int+-- TODO: implement , webhook_allowed_updates :: Maybe [Text]+  }+  | SetWebhookWithoutCertRequest+  {+    webhook_url             :: Text+  , webhook_max_connections :: Maybe Int+  , webhook_allowed_updates :: Maybe [Text]+  } deriving (Generic)++instance ToJSON SetWebhookRequest where+  toJSON SetWebhookRequest{} = undefined+  toJSON (SetWebhookWithoutCertRequest url maxConnections allowedUpdates) = object $+    ("url" .= url) : catMaybes [+    ("max_connections" .=) <$> maxConnections,+    ("allowed_updates" .=) <$> allowedUpdates]++instance ToMultipartFormData SetWebhookRequest where+  toMultipartFormData req =+    [ utf8Part         "url"         $ webhook_url req+    , fileUploadToPart "certificate" $ webhook_certificate req ] +++    catMaybes+    [ utf8Part         "max_connections" . tshow <$> webhook_max_connections req+    -- TODO: , ??? "allowed_updates" ??? <$> webhook_allowed_updates req+    ]++setWebhookRequest :: Text -> FileUpload -> SetWebhookRequest+setWebhookRequest url certificate = SetWebhookRequest url certificate Nothing++setWebhookRequest' :: Text -> SetWebhookRequest+setWebhookRequest' url = SetWebhookWithoutCertRequest url Nothing Nothing++data GetUpdatesRequest = GetUpdatesRequest+  {+    updates_offset          :: Maybe Int -- ^ Identifier of the first update to be returned. Must be greater by one than the highest among the identifiers of previously received updates. By default, updates starting with the earliest unconfirmed update are returned. An update is considered confirmed as soon as 'getUpdates' is called with an offset higher than its update_id. The negative offset can be specified to retrieve updates starting from -offset update from the end of the updates queue. All previous updates will forgotten.+  , updates_limit           :: Maybe Int -- ^ Limits the number of updates to be retrieved. Values between 1—100 are accepted. Defaults to 100.+  , updates_timeout         :: Maybe Int -- ^ Timeout in seconds for long polling. Defaults to 0, i.e. usual short polling. Should be positive, short polling should be used for testing purposes only.+  , updates_allowed_updates :: Maybe [Text] -- ^ List the types of updates you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See 'Update' for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used.+  } deriving (Show, Generic)++instance ToJSON GetUpdatesRequest where+  toJSON = toJsonDrop 8++instance FromJSON GetUpdatesRequest where+  parseJSON = parseJsonDrop 8++getUpdatesRequest :: GetUpdatesRequest+getUpdatesRequest = GetUpdatesRequest Nothing Nothing Nothing Nothing++-- | Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)+data ChatId = ChatId Int64 | ChatChannel Text+  deriving (Show)++instance ToJSON ChatId where+  toJSON (ChatId integer)   = toJSON integer+  toJSON (ChatChannel text) = String text++instance FromJSON ChatId where+  parseJSON value@Number{} = ChatId <$> parseJSON value+  parseJSON (String text)  = pure $ ChatChannel text+  parseJSON wat            = typeMismatch "Int64 or String" wat++chatIdToPart :: ChatId -> Text+chatIdToPart (ChatId integer)   = tshow integer+chatIdToPart (ChatChannel text) = text++-- | This object represents request for 'sendMessage'+data SendMessageRequest = SendMessageRequest+  {+    message_chat_id                  :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)+  , message_text                     :: Text -- ^ Text of the message to be sent+  , message_parse_mode               :: Maybe ParseMode -- ^ Send 'Markdown', if you want Telegram apps to show bold, italic and inline URLs in your bot's message+  , message_disable_web_page_preview :: Maybe Bool -- ^ Disables link previews for links in this message+  , message_disable_notification     :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.+  , message_reply_to_message_id      :: Maybe Int -- ^ If the message is a reply, ID of the original message+  , message_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.+  } deriving (Show, Generic)++instance ToJSON SendMessageRequest where+  toJSON = toJsonDrop 8++instance FromJSON SendMessageRequest where+  parseJSON = parseJsonDrop 8++sendMessageRequest :: ChatId -> Text -> SendMessageRequest+sendMessageRequest chatId text = SendMessageRequest chatId text Nothing Nothing Nothing Nothing Nothing++-- | This object represents request for 'forwardMessage'+data ForwardMessageRequest = ForwardMessageRequest+  {+    forward_chat_id              :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)+  , forward_from_chat_id         :: ChatId -- ^ Unique identifier for the chat where the original message was sent (or channel username in the format @@channelusername@)+  , forward_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.+  , forward_message_id           :: Int -- ^ Unique message identifier+  } deriving (Show, Generic)++instance ToJSON ForwardMessageRequest where+  toJSON = toJsonDrop 8++instance FromJSON ForwardMessageRequest where+  parseJSON = parseJsonDrop 8++forwardMessageRequest :: ChatId -> ChatId -> Int -> ForwardMessageRequest+forwardMessageRequest chatId fromChatId forwardMessageId = ForwardMessageRequest chatId fromChatId Nothing forwardMessageId++-- | This object represents request for 'sendPhoto'+data SendPhotoRequest payload = SendPhotoRequest+  {+    photo_chat_id              :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)+  , photo_photo                :: payload -- ^ Photo to send. You can either pass a file_id as String to resend a photo that is already on the Telegram servers, or upload a new photo.+  , photo_caption              :: Maybe Text -- ^ Photo caption (may also be used when resending photos by file_id), 0-200 characters.+  , photo_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.+  , photo_reply_to_message_id  :: Maybe Int -- ^ If the message is a reply, ID of the original message+  , photo_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.+  } deriving (Show, Generic)++instance ToJSON (SendPhotoRequest Text) where+  toJSON = toJsonDrop 6++instance FromJSON (SendPhotoRequest Text) where+  parseJSON = parseJsonDrop 6++sendPhotoRequest :: ChatId -> Text -> SendPhotoRequest Text+sendPhotoRequest chatId photo = SendPhotoRequest chatId photo Nothing Nothing Nothing Nothing++uploadPhotoRequest :: ChatId -> FileUpload -> SendPhotoRequest FileUpload+uploadPhotoRequest chatId photo = SendPhotoRequest chatId photo Nothing Nothing Nothing Nothing++instance ToMultipartFormData (SendPhotoRequest FileUpload) where+  toMultipartFormData req =+    [ utf8Part "chat_id" (chatIdToPart $ photo_chat_id req) ] +++    catMaybes+    [ utf8Part "caption" <$> photo_caption req+    , partLBS "disable_notification" . encode <$> photo_disable_notification req+    , utf8Part "reply_to_message_id" . tshow <$> photo_reply_to_message_id req+    , partLBS "reply_markup" . encode <$> photo_reply_markup req+    ] +++    [ fileUploadToPart "photo" (photo_photo req) ]++-- | This object represents request for 'sendAudio'+data SendAudioRequest payload = SendAudioRequest+  {+    _audio_chat_id              :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)+  , _audio_audio                :: payload -- ^ Audio to send. You can either pass a file_id as String to resend an audio that is already on the Telegram servers, or upload a new audio file.+  , _audio_caption              :: Maybe Text -- ^ Audio caption, 0-200 characters+  , _audio_duration             :: Maybe Int -- ^ Duration of the audio in seconds+  , _audio_performer            :: Maybe Text -- ^ Performer+  , _audio_title                :: Maybe Text -- ^ Track name+  , _audio_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.+  , _audio_reply_to_message_id  :: Maybe Int -- ^ If the message is a reply, ID of the original message+  , _audio_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.+  } deriving (Show, Generic)++instance ToJSON (SendAudioRequest Text) where+  toJSON = toJsonDrop 7++instance FromJSON (SendAudioRequest Text) where+  parseJSON = parseJsonDrop 7++instance ToMultipartFormData (SendAudioRequest FileUpload) where+  toMultipartFormData req =+    [ utf8Part "chat_id" (chatIdToPart $ _audio_chat_id req) ] +++    catMaybes+    [ utf8Part "duration" . tshow <$> _audio_duration req+    , utf8Part "performer" <$> _audio_performer req+    , utf8Part "title" <$> _audio_title req+    , partLBS "disable_notification" . encode <$> _audio_disable_notification req+    , utf8Part "reply_to_message_id" . tshow <$> _audio_reply_to_message_id req+    , partLBS "reply_markup" . encode <$> _audio_reply_markup req+    ] +++    [ fileUploadToPart "audio" (_audio_audio req) ]++sendAudioRequest :: ChatId -> Text -> SendAudioRequest Text+sendAudioRequest chatId audio = SendAudioRequest chatId audio Nothing Nothing Nothing Nothing Nothing Nothing Nothing++uploadAudioRequest :: ChatId -> FileUpload -> SendAudioRequest FileUpload+uploadAudioRequest chatId audio = SendAudioRequest chatId audio Nothing Nothing Nothing Nothing Nothing Nothing Nothing++-- | This object represents request for 'sendSticker'+data SendStickerRequest payload = SendStickerRequest+  {+    sticker_chat_id              :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)+  , sticker_sticker              :: payload -- ^ Sticker to send. You can either pass a file_id as String to resend a sticker that is already on the Telegram servers, or upload a new sticker.+  , sticker_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.+  , sticker_reply_to_message_id  :: Maybe Int -- ^ If the message is a reply, ID of the original message+  , sticker_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.+  } deriving (Show, Generic)++instance ToJSON (SendStickerRequest Text) where+  toJSON = toJsonDrop 8++instance FromJSON (SendStickerRequest Text) where+  parseJSON = parseJsonDrop 8++instance ToMultipartFormData (SendStickerRequest FileUpload) where+  toMultipartFormData req =+    [ utf8Part "chat_id" (chatIdToPart $ sticker_chat_id req) ] +++    catMaybes+    [ partLBS "disable_notification" . encode <$> sticker_disable_notification req+    , utf8Part "reply_to_message_id" . tshow <$> sticker_reply_to_message_id req+    , partLBS "reply_markup" . encode <$> sticker_reply_markup req+    ] +++    [ fileUploadToPart "sticker" (sticker_sticker req) ]++sendStickerRequest :: ChatId -> Text -> SendStickerRequest Text+sendStickerRequest chatId sticker = SendStickerRequest chatId sticker Nothing Nothing Nothing++uploadStickerRequest :: ChatId -> FileUpload -> SendStickerRequest FileUpload+uploadStickerRequest chatId sticker = SendStickerRequest chatId sticker Nothing Nothing Nothing++-- | This object represents request for 'sendDocument'+data SendDocumentRequest payload = SendDocumentRequest+  {+    document_chat_id              :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)+  , document_document             :: payload -- ^ File to send. You can either pass a file_id as String to resend a file that is already on the Telegram servers, or upload a new file.+  , document_caption              :: Maybe Text -- ^ Document caption (may also be used when resending documents by file_id), 0-200 characters+  , document_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.+  , document_reply_to_message_id  :: Maybe Int -- ^ If the message is a reply, ID of the original message+  , document_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.+  } deriving (Show, Generic)++instance ToJSON (SendDocumentRequest Text) where+  toJSON = toJsonDrop 9++instance FromJSON (SendDocumentRequest Text) where+  parseJSON = parseJsonDrop 9++instance ToMultipartFormData (SendDocumentRequest FileUpload) where+  toMultipartFormData req =+    [ utf8Part "chat_id" (chatIdToPart $ document_chat_id req) ] +++    catMaybes+    [ utf8Part "caption" <$> document_caption req+    , partLBS "disable_notification" . encode <$> document_disable_notification req+    , utf8Part "reply_to_message_id" . tshow <$> document_reply_to_message_id req+    , partLBS "reply_markup" . encode <$> document_reply_markup req+    ] +++    [ fileUploadToPart "document" (document_document req) ]++sendDocumentRequest :: ChatId -> Text -> SendDocumentRequest Text+sendDocumentRequest chatId document = SendDocumentRequest chatId document Nothing Nothing Nothing Nothing++uploadDocumentRequest :: ChatId -> FileUpload -> SendDocumentRequest FileUpload+uploadDocumentRequest chatId document = SendDocumentRequest chatId document Nothing Nothing Nothing Nothing++-- | This object represents request for 'sendVideo'+data SendVideoRequest payload = SendVideoRequest+  {+    _video_chat_id              :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)+  , _video_video                :: payload -- ^ Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram servers, or upload a new video.+  , _video_duration             :: Maybe Int -- ^ Duration of sent video in seconds+  , _video_caption              :: Maybe Text -- ^ Video caption, 0-200 characters.+  , _video_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.+  , _video_reply_to_message_id  :: Maybe Int -- ^ If the message is a reply, ID of the original message+  , _video_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.+  } deriving (Show, Generic)++instance ToJSON (SendVideoRequest Text) where+  toJSON = toJsonDrop 7++instance FromJSON (SendVideoRequest Text) where+  parseJSON = parseJsonDrop 7++instance ToMultipartFormData (SendVideoRequest FileUpload) where+  toMultipartFormData req =+    [ utf8Part "chat_id" (chatIdToPart $ _video_chat_id req) ] +++    catMaybes+    [ partLBS "duration" . encode <$> _video_duration req+    , utf8Part "caption" <$> _video_caption req+    , partLBS "disable_notification" . encode <$> _video_disable_notification req+    , utf8Part "reply_to_message_id" . tshow <$> _video_reply_to_message_id req+    , partLBS "reply_markup" . encode <$> _video_reply_markup req+    ] +++    [ fileUploadToPart "video" (_video_video req) ]++sendVideoRequest :: ChatId -> Text -> SendVideoRequest Text+sendVideoRequest chatId video = SendVideoRequest chatId video Nothing Nothing Nothing Nothing Nothing++uploadVideoRequest :: ChatId -> FileUpload -> SendVideoRequest FileUpload+uploadVideoRequest chatId video = SendVideoRequest chatId video Nothing Nothing Nothing Nothing Nothing++-- | This object represents request for 'sendVoice'+data SendVoiceRequest payload = SendVoiceRequest+  {+    _voice_chat_id              :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)+  , _voice_voice                :: payload -- ^ Audio file to send. You can either pass a file_id as String to resend an audio that is already on the Telegram servers, or upload a new audio file.+  , _voice_caption              :: Maybe Text -- ^ Voice message caption, 0-200 characters+  , _voice_duration             :: Maybe Int -- ^ Duration of sent audio in seconds+  , _voice_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.+  , _voice_reply_to_message_id  :: Maybe Int -- ^ If the message is a reply, ID of the original message+  , _voice_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.+  } deriving (Show, Generic)++instance ToJSON (SendVoiceRequest Text) where+  toJSON = toJsonDrop 7++instance FromJSON (SendVoiceRequest Text) where+  parseJSON = parseJsonDrop 7++instance ToMultipartFormData (SendVoiceRequest FileUpload) where+  toMultipartFormData req =+    [ utf8Part "chat_id" (chatIdToPart $ _voice_chat_id req) ] +++    catMaybes+    [ partLBS "duration" . encode <$> _voice_duration req+    , partLBS "disable_notification" . encode <$> _voice_disable_notification req+    , utf8Part "reply_to_message_id" . tshow <$> _voice_reply_to_message_id req+    , partLBS "reply_markup" . encode <$> _voice_reply_markup req+    ] +++    [ fileUploadToPart "voice" (_voice_voice req) ]++sendVoiceRequest :: ChatId -> Text -> SendVoiceRequest Text+sendVoiceRequest chatId voice = SendVoiceRequest chatId voice Nothing Nothing Nothing Nothing Nothing++uploadVoiceRequest :: ChatId -> FileUpload -> SendVoiceRequest FileUpload+uploadVoiceRequest chatId voice = SendVoiceRequest chatId voice Nothing Nothing Nothing Nothing Nothing++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_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.+  } deriving (Show, Generic)++instance ToJSON (SendVideoNoteRequest Text) where+  toJSON = toJsonDrop 9++instance FromJSON (SendVideoNoteRequest Text) where+  parseJSON = parseJsonDrop 9++instance ToMultipartFormData (SendVideoNoteRequest FileUpload) where+  toMultipartFormData req =+    [ utf8Part "chat_id" (chatIdToPart $ _vid_note_chat_id req) ] +++    catMaybes+    [ partLBS "duration" . encode <$> _vid_note_duration req+    , partLBS "length" . encode <$> _vid_note_length req+    , partLBS "disable_notification" . encode <$> _vid_note_disable_notification req+    , utf8Part "reply_to_message_id" . tshow <$> _vid_note_reply_to_message_id req+    , partLBS "reply_markup" . encode <$> _vid_note_reply_markup req+    ] +++    [ fileUploadToPart "video_note" (_vid_note_video_note req) ]++sendVideoNoteRequest :: ChatId -> Text -> SendVideoNoteRequest Text+sendVideoNoteRequest chatId videoNote = SendVideoNoteRequest chatId videoNote Nothing Nothing Nothing Nothing Nothing++uploadVideoNoteRequest :: ChatId -> FileUpload -> SendVideoNoteRequest FileUpload+uploadVideoNoteRequest chatId videoNote = SendVideoNoteRequest chatId videoNote Nothing Nothing Nothing Nothing Nothing++-- | This object represents request for 'sendLocation'+data SendLocationRequest = SendLocationRequest+  {+    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.+  } deriving (Show, Generic)++instance ToJSON SendLocationRequest where+  toJSON = toJsonDrop 9++instance FromJSON SendLocationRequest where+  parseJSON = parseJsonDrop 9++sendLocationRequest :: ChatId -> Float -> Float -> SendLocationRequest+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+  {+    _venue_chat_id              :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)+  , _venue_latitude             :: Float -- ^ Latitude of the venue+  , _venue_longitude            :: Float -- ^ Longitude of the venue+  , _venue_title                :: Text -- ^ Name of the venue+  , _venue_address              :: Text -- ^ Address of the venue+  , _venue_foursquare_id        :: Maybe Text -- ^ Foursquare identifier of the venue+  , _venue_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.+  , _venue_reply_to_message_id  :: Maybe Int -- ^ If the message is a reply, ID of the original message+  , _venue_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.+  } deriving (Show, Generic)++instance ToJSON SendVenueRequest where+  toJSON = toJsonDrop 7++instance FromJSON SendVenueRequest where+  parseJSON = parseJsonDrop 7++sendVenueRequest :: ChatId -> Float -> Float -> Text -> Text -> SendVenueRequest+sendVenueRequest chatId latitude longitude title address = SendVenueRequest chatId latitude longitude title address Nothing Nothing Nothing Nothing++-- | This object represents request for 'sendContact'+data SendContactRequest = SendContactRequest+  {+    _contact_chat_id              :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format @@channelusername@)+  , _contact_phone_number         :: Text       -- ^ Contact's phone number+  , _contact_first_name           :: Text       -- ^ Contact's first name+  , _contact_last_name            :: Maybe Text -- ^ Contact's last name+  , _contact_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.+  , _contact_reply_to_message_id  :: Maybe Int -- ^ If the message is a reply, ID of the original message+  , _contact_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.+  } deriving (Show, Generic)++instance ToJSON SendContactRequest where+  toJSON = toJsonDrop 9++instance FromJSON SendContactRequest where+  parseJSON = parseJsonDrop 9++sendContactRequest :: ChatId -> Text -> Text -> SendContactRequest+sendContactRequest chatId phoneNumber firstName = SendContactRequest chatId phoneNumber firstName Nothing Nothing Nothing Nothing++-- | Type of action to broadcast.+data ChatAction = Typing+                | UploadPhoto+                | RecordVideo+                | UploadVideo+                | RecordAudio+                | UploadAudio+                | UploadDocument+                | FindLocation+                | RecordVideoNote+                | UploadVideoNote deriving (Show, Generic)++instance ToJSON ChatAction where+  toJSON Typing          = "typing"+  toJSON UploadPhoto     = "upload_photo"+  toJSON RecordVideo     = "record_video"+  toJSON UploadVideo     = "upload_video"+  toJSON RecordAudio     = "record_audio"+  toJSON UploadAudio     = "upload_audio"+  toJSON UploadDocument  = "upload_document"+  toJSON FindLocation    = "find_location"+  toJSON RecordVideoNote = "record_video_note"+  toJSON UploadVideoNote = "upload_video_note"++instance FromJSON ChatAction where+  parseJSON "typing"            = pure Typing+  parseJSON "upload_photo"      = pure UploadPhoto+  parseJSON "record_video"      = pure RecordVideo+  parseJSON "upload_video"      = pure UploadVideo+  parseJSON "record_audio"      = pure RecordAudio+  parseJSON "upload_audio"      = pure UploadAudio+  parseJSON "upload_document"   = pure UploadDocument+  parseJSON "find_location"     = pure FindLocation+  parseJSON "record_video_note" = pure RecordVideoNote+  parseJSON "upload_video_note" = pure UploadVideoNote+  parseJSON _                   = fail "Failed to parse ChatAction"++-- | This object represents request for 'sendChatAction'+data SendChatActionRequest = SendChatActionRequest+  {+    action_chat_id :: ChatId -- ^ Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)+  , action_action  :: ChatAction -- ^ Type of action to broadcast.+  } deriving (Show, Generic)++instance ToJSON SendChatActionRequest where+  toJSON = toJsonDrop 7++instance FromJSON SendChatActionRequest where+  parseJSON = parseJsonDrop 7++sendChatActionRequest :: ChatId -> ChatAction -> SendChatActionRequest+sendChatActionRequest = SendChatActionRequest++-- | This object represents request for 'sendGame'+data SendGameRequest = SendGameRequest+  {+    game_chat_id              :: Integer -- ^ Unique identifier for the target chat+  , game_game_short_name      :: Text -- ^ Short name of the game, serves as the unique identifier for the game. Set up your games via Botfather.+  , game_disable_notification :: Maybe Bool -- ^ Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.+  , game_reply_to_message_id  :: Maybe Int -- ^  If the message is a reply, ID of the original message+  , game_reply_markup         :: Maybe InlineKeyboardMarkup -- ^ A JSON-serialized object for an inline keyboard. If empty, one ‘Play game_title’ button will be shown. If not empty, the first button must launch the game.+  } deriving (Show, Generic)++instance ToJSON SendGameRequest where+  toJSON = toJsonDrop 5++instance FromJSON SendGameRequest where+  parseJSON = parseJsonDrop 5++sendGameRequest :: Integer -> Text -> SendGameRequest+sendGameRequest chatId shortName = SendGameRequest chatId shortName Nothing Nothing Nothing++data AnswerInlineQueryRequest = AnswerInlineQueryRequest+  {+    query_inline_query_id     :: Text -- ^ Unique identifier for the answered query+  , query_results             :: [InlineQueryResult] -- ^ A JSON-serialized array of results for the inline query+  , query_cache_time          :: Maybe Int -- ^ The maximum amount of time in seconds that the result of the inline query may be cached on the server. Defaults to 300.+  , query_is_personal         :: Maybe Bool -- ^ Pass True, if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query+  , query_next_offset         :: Maybe Text -- ^ Pass the offset that a client should send in the next query with the same text to receive more results. Pass an empty string if there are no more results or if you don‘t support pagination. Offset length can’t exceed 64 bytes.+  , query_switch_pm_text      :: Maybe Text -- ^ If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter+  , query_switch_pm_parameter :: Maybe Text -- ^ Parameter for the start message sent to the bot when user presses the switch button+                                            --+                                            -- Example: An inline bot that sends YouTube videos can ask the user to connect the bot to+                                            -- their YouTube account to adapt search results accordingly. To do this, it displays a+                                            -- ‘Connect your YouTube account’ button above the results, or even before showing any.+                                            -- The user presses the button, switches to a private chat with the bot and, in doing so,+                                            -- passes a start parameter that instructs the bot to return an oauth link. Once done,+                                            -- the bot can offer a switch_inline button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.+  } deriving (Show, Generic)++instance ToJSON AnswerInlineQueryRequest where+  toJSON = toJsonDrop 6++instance FromJSON AnswerInlineQueryRequest where+  parseJSON = parseJsonDrop 6++answerInlineQueryRequest :: Text -> [InlineQueryResult] -> AnswerInlineQueryRequest+answerInlineQueryRequest queryId results = AnswerInlineQueryRequest queryId results Nothing Nothing Nothing Nothing Nothing++data AnswerCallbackQueryRequest = AnswerCallbackQueryRequest+  {+    cq_callback_query_id :: Text -- ^ Unique identifier for the query to be answered+  , cq_text              :: Maybe Text -- ^ Text of the notification. If not specified, nothing will be shown to the user+  , cq_show_alert        :: Maybe Bool -- ^ If true, an alert will be shown by the client instead of a notification at the top of the chat screen. Defaults to false.+  , cq_url               :: Maybe Text -- ^ URL that will be opened by the user's client. If you have created a `Game` and accepted the conditions via `@Botfather`, specify the URL that opens your game – note that this will only work if the query comes from a `callback_game` button. Otherwise, you may use links like telegram.me/your_bot?start=XXXX that open your bot with a parameter.+  , cq_cache_time        :: Maybe Int -- ^ The maximum amount of time in seconds that the result of the callback query may be cached client-side. Telegram apps will support caching starting in version 3.14. Defaults to 0.+  } deriving (Show, Generic)++instance ToJSON AnswerCallbackQueryRequest where+  toJSON = toJsonDrop 3++instance FromJSON AnswerCallbackQueryRequest where+  parseJSON = parseJsonDrop 3++answerCallbackQueryRequest :: Text -> AnswerCallbackQueryRequest+answerCallbackQueryRequest queryId = AnswerCallbackQueryRequest queryId Nothing Nothing Nothing Nothing++data ReplyKeyboard =+  -- | This object represents a custom keyboard with reply options+  ReplyInlineKeyboardMarkup+  {+    reply_inline_keyboard :: [[InlineKeyboardButton]] -- ^ Array of button rows, each represented by an Array of InlineKeyboardButton objects+  }+  | ReplyKeyboardMarkup+  {+    reply_keyboard          :: [[KeyboardButton]] -- ^ Array of button rows, each represented by an Array of 'KeyboardButton' objects+  , reply_resize_keyboard   :: Maybe Bool -- ^ Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard.+  , reply_one_time_keyboard :: Maybe Bool -- ^ Requests clients to hide the keyboard as soon as it's been used. Defaults to false.+  , reply_selective         :: Maybe Bool -- ^ Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.    Example: A user requests to change the bot‘s language, bot replies to the request with a keyboard to select the new language. Other users in the group don’t see the keyboard.+  }+  -- | Upon receiving a message with this object, Telegram clients will hide the current custom keyboard and display the default letter-keyboard. By default, custom keyboards are displayed until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are hidden immediately after the user presses a button+  | ReplyKeyboardHide+  {+    reply_hide_keyboard :: Bool -- ^ Requests clients to hide the custom keyboard+  , reply_selective     :: Maybe Bool -- ^ Use this parameter if you want to hide keyboard for specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.    Example: A user votes in a poll, bot returns confirmation message in reply to the vote and hides keyboard for that user, while still showing the keyboard with poll options to users who haven't voted yet.+  }+  | ReplyKeyboardRemove+  {+    reply_remove_keyboard :: Bool -- ^ Requests clients to remove the custom keyboard (user will not be able to summon this keyboard; if you want to hide the keyboard from sight but keep it accessible, use one_time_keyboard in 'ReplyKeyboardMarkup')+  , reply_selective       :: Maybe Bool -- ^ Use this parameter if you want to hide keyboard for specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.    Example: A user votes in a poll, bot returns confirmation message in reply to the vote and hides keyboard for that user, while still showing the keyboard with poll options to users who haven't voted yet.+  }+  -- | Upon receiving a message with this object, Telegram clients will display a reply interface to the user (act as if the user has selected the bot‘s message and tapped ’Reply'). This can be extremely useful if you want to create user-friendly step-by-step interfaces without having to sacrifice privacy mode.+  | ForceReply+  {+    reply_force_reply :: Bool -- ^ Shows reply interface to the user, as if they manually selected the bot‘s message and tapped ’Reply'+  , reply_selective   :: Maybe Bool -- ^ Use this parameter if you want to force reply from specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.+  } deriving (Show, Generic)++instance ToJSON ReplyKeyboard where+  toJSON = toJsonDrop 6++instance FromJSON ReplyKeyboard where+  parseJSON = parseJsonDrop 6++inlineKeyboardMarkup :: [[InlineKeyboardButton]] -> ReplyKeyboard+inlineKeyboardMarkup = ReplyInlineKeyboardMarkup++replyKeyboardMarkup :: [[KeyboardButton]] -> ReplyKeyboard+replyKeyboardMarkup keyboard = ReplyKeyboardMarkup keyboard Nothing Nothing Nothing++replyKeyboardHide :: ReplyKeyboard+replyKeyboardHide = ReplyKeyboardHide True Nothing++forceReply :: ReplyKeyboard+forceReply = ForceReply True Nothing++data EditMessageTextRequest = EditMessageTextRequest+  {+    emt_chat_id                  :: Maybe ChatId -- ^ Required if `inline_message_id` is not specified. Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)+  , emt_message_id               :: Maybe Int -- ^ if `inline_message_id` is not specified. Unique identifier of the sent message+  , emt_inline_message_id        :: Maybe Text -- ^ Required if chat_id and message_id are not specified. Identifier of the inline message+  , emt_text                     :: Text -- ^ New text of the message+  , emt_parse_mode               :: Maybe ParseMode -- ^ Send `Markdown` or `HTML`, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message.+  , emt_disable_web_page_preview :: Maybe Bool -- ^ Disables link previews for links in this message+  , emt_reply_markup             :: Maybe InlineKeyboardMarkup -- ^ A JSON-serialized object for an inline keyboard.+  } deriving (Show, Generic)++instance ToJSON EditMessageTextRequest where+  toJSON = toJsonDrop 4++instance FromJSON EditMessageTextRequest where+  parseJSON = parseJsonDrop 4++editMessageTextRequest :: ChatId -> Int -> Text -> EditMessageTextRequest+editMessageTextRequest chatId messageId text = EditMessageTextRequest (Just chatId) (Just messageId) Nothing text Nothing Nothing Nothing++editInlineMessageTextRequest :: Text -> Text -> EditMessageTextRequest+editInlineMessageTextRequest inlineMessageId text = EditMessageTextRequest Nothing Nothing (Just inlineMessageId) text Nothing Nothing Nothing++data EditMessageCaptionRequest = EditMessageCaptionRequest+  {+    emc_chat_id           :: Maybe ChatId -- ^ Required if `inline_message_id` is not specified. Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)+  , emc_message_id        :: Maybe Int -- ^ Required if `inline_message_id` is not specified. Unique identifier of the sent message+  , emc_inline_message_id :: Maybe Text -- ^ Required if `chat_id` and `message_id` are not specified. Identifier of the inline message+  , emc_caption           :: Maybe Text -- ^ New caption of the message+  , emc_reply_markup      :: Maybe InlineKeyboardMarkup -- ^ A JSON-serialized object for an inline keyboard.+  } deriving (Show, Generic)++instance ToJSON EditMessageCaptionRequest where+  toJSON = toJsonDrop 4++instance FromJSON EditMessageCaptionRequest where+  parseJSON = parseJsonDrop 4++editMessageCaptionRequest :: ChatId -> Int -> Maybe Text -> EditMessageCaptionRequest+editMessageCaptionRequest chatId messageId caption = EditMessageCaptionRequest (Just chatId) (Just messageId) Nothing caption Nothing++editInlineMessageCaptionRequest :: Text -> Maybe Text -> EditMessageCaptionRequest+editInlineMessageCaptionRequest inlineMessageId caption = EditMessageCaptionRequest Nothing Nothing (Just inlineMessageId) caption Nothing++data EditMessageReplyMarkupRequest = EditMessageReplyMarkupRequest+  {+    emrm_chat_id           :: Maybe ChatId -- ^ Required if `inline_message_id` is not specified. Unique identifier for the target chat or username of the target channel (in the format `@channelusername`)+  , emrm_message_id        :: Maybe Int -- ^ Required if `inline_message_id` is not specified. Unique identifier of the sent message+  , emrm_inline_message_id :: Maybe Text -- ^ Required if `chat_id` and `message_id` are not specified. Identifier of the inline message+  , emrm_reply_markup      :: Maybe InlineKeyboardMarkup -- ^ A JSON-serialized object for an inline keyboard.+  } deriving (Show, Generic)++instance ToJSON EditMessageReplyMarkupRequest where+  toJSON = toJsonDrop 5++instance FromJSON EditMessageReplyMarkupRequest where+  parseJSON = parseJsonDrop 5++editMessageReplyMarkupRequest :: ChatId -> Int -> Maybe InlineKeyboardMarkup -> EditMessageReplyMarkupRequest+editMessageReplyMarkupRequest chatId messageId keyboard = EditMessageReplyMarkupRequest (Just chatId) (Just messageId) Nothing keyboard++editInlineMessageReplyMarkupRequest :: Text -> Maybe InlineKeyboardMarkup -> EditMessageReplyMarkupRequest+editInlineMessageReplyMarkupRequest inlineMessageId keyboard = EditMessageReplyMarkupRequest Nothing Nothing (Just inlineMessageId) keyboard++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_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+  toJSON = toJsonDrop 8++instance FromJSON SendInvoiceRequest where+  parseJSON = parseJsonDrop 8++sendInvoiceRequest :: Int64 -- ^ Unique identifier for the target private chat+  -> Text -- ^ Product name+  -> Text -- ^ Product description+  -> Text -- ^ Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.+  -> Text -- ^ Payments provider token, obtained via Botfather+  -> Text -- ^ Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter+  -> CurrencyCode -- ^ Three-letter ISO 4217 <https://core.telegram.org/bots/payments#supported-currencies currency> code+  -> [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 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.+  = AnswerShippingQueryRequest+  {+    asq_shipping_query_id :: Text -- ^ Unique identifier for the query to be answered+  , asq_ok                :: Bool -- ^ Specify True if delivery to the specified address is possible and False if there are any problems (for example, if delivery to the specified address is not possible)+  , asq_shipping_options  :: Maybe [ShippingOption] -- ^ Required if ok is True. A JSON-serialized array of available shipping options.+  , asq_error_message     :: Maybe Text -- ^ Required if ok is False. Error message in human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user.+  } deriving (Show, Generic)++instance ToJSON AnswerShippingQueryRequest where+  toJSON = toJsonDrop 4++instance FromJSON AnswerShippingQueryRequest where+  parseJSON = parseJsonDrop 4++okShippingQueryRequest :: Text -> [ShippingOption] -> AnswerShippingQueryRequest+okShippingQueryRequest queryId options = AnswerShippingQueryRequest queryId True (Just options) Nothing++errorShippingQueryRequest :: Text -> Text -> AnswerShippingQueryRequest+errorShippingQueryRequest queryId errorMsg = AnswerShippingQueryRequest queryId False Nothing (Just errorMsg)++data AnswerPreCheckoutQueryRequest+  -- | Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an `Update` with the field pre_checkout_query. Use this method to respond to such pre-checkout queries. On success, True is returned. Note: The Bot API must receive an answer within 10 seconds after the pre-checkout query was sent.+  = AnswerPreCheckoutQueryRequest+  {+    apc_pre_checkout_query_id :: Text -- ^ 	Unique identifier for the query to be answered+  , apc_ok                    :: Bool -- ^ Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems.+  , apc_error_message         :: Maybe Text -- ^ Required if ok is False. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user.+  } deriving (Show, Generic)++instance ToJSON AnswerPreCheckoutQueryRequest where+  toJSON = toJsonDrop 4++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
src/Web/Telegram/API/Bot/Responses.hs view
@@ -1,92 +1,99 @@-{-# LANGUAGE DeriveGeneric     #-}
-{-# LANGUAGE DeriveAnyClass    #-}
-{-# LANGUAGE OverloadedStrings #-}
-
--- | This module contains responses from Telegram Bot API
-module Web.Telegram.API.Bot.Responses
-    ( -- * Types
-      Response                        (..)
-    , ResponseParameters              (..)
-    , GetMeResponse
-    , MessageResponse
-    , ChatActionResponse
-    , UpdatesResponse
-    , FileResponse
-    , UserProfilePhotosResponse
-    , SetWebhookResponse
-    , InlineQueryResponse
-    , CallbackQueryResponse
-    , KickChatMemberResponse
-    , LeaveChatResponse
-    , UnbanChatMemberResponse
-    , GetChatResponse
-    , GetChatAdministratorsResponse
-    , GetChatMembersCountResponse
-    , GetChatMemberResponse
-    , GetWebhookInfoResponse
-    ) where
-
-import           Data.Aeson
-import           GHC.Generics
-import           Web.Telegram.API.Bot.Data
-import           Web.Telegram.API.Bot.JsonExt
-
-data Response a = Response
-  {
-    result :: a
-  , parameters :: Maybe ResponseParameters
-  } deriving (Show, Generic, FromJSON)
-
-data ResponseParameters = ResponseParameters
-  {
-    res_migrate_to_chat_id :: Maybe Int -- ^ The group has been migrated to a supergroup with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.
-  , res_retry_after :: Maybe Int -- ^ In case of exceeding flood control, the number of seconds left to wait before the request can be repeated
-  } deriving (Show, Generic)
-
-instance FromJSON ResponseParameters where
-  parseJSON = parseJsonDrop 4
-
--- | This object represents 'getMe' response
-type GetMeResponse = Response User
-
--- | This object represents message response
-type MessageResponse = Response Message
-
--- | This object represents 'sendChatAction' response
-type ChatActionResponse = Response Bool
-
--- | This object represents 'getUpdates' response
-type UpdatesResponse = Response [Update]
-
--- | This object represents file response
-type FileResponse = Response File
-
--- | This object represents user profile photos response
-type UserProfilePhotosResponse = Response UserProfilePhotos
-
--- | This object represents 'setWebhook' response
-type SetWebhookResponse = Response Bool
-
--- | This object represents 'answerInlineQuery' response
-type InlineQueryResponse = Response Bool
-
--- | This object represents 'answerCallbackQuery' response
-type CallbackQueryResponse = Response Bool
-
--- | This object represents 'kickChatMember' response
-type KickChatMemberResponse = Response Bool
-
-type LeaveChatResponse = Response Bool
-
--- | This object represents 'unbanChatMember' response
-type UnbanChatMemberResponse = Response Bool
-
-type GetChatResponse = Response Chat
-
-type GetChatAdministratorsResponse = Response [ChatMember]
-
-type GetChatMembersCountResponse = Response Int
-
-type GetChatMemberResponse = Response ChatMember
-
+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE OverloadedStrings #-}++-- | This module contains responses from Telegram Bot API+module Web.Telegram.API.Bot.Responses+    ( -- * Types+      Response                        (..)+    , ResponseParameters              (..)+    , GetMeResponse+    , MessageResponse+    , ChatActionResponse+    , UpdatesResponse+    , FileResponse+    , UserProfilePhotosResponse+    , SetWebhookResponse+    , InlineQueryResponse+    , CallbackQueryResponse+    , KickChatMemberResponse+    , LeaveChatResponse+    , UnbanChatMemberResponse+    , GetChatResponse+    , GetChatAdministratorsResponse+    , GetChatMembersCountResponse+    , GetChatMemberResponse+    , GetWebhookInfoResponse+    , AnswerShippingQueryResponse+    , AnswerPreCheckoutQueryResponse+    ) where++import           Data.Aeson+import           Data.Int                     (Int64)+import           GHC.Generics+import           Web.Telegram.API.Bot.Data+import           Web.Telegram.API.Bot.JsonExt++data Response a = Response+  {+    result     :: a+  , parameters :: Maybe ResponseParameters+  } deriving (Show, Generic, FromJSON)++data ResponseParameters = ResponseParameters+  {+    res_migrate_to_chat_id :: Maybe Int64 -- ^ The group has been migrated to a supergroup with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.+  , res_retry_after        :: Maybe Int   -- ^ In case of exceeding flood control, the number of seconds left to wait before the request can be repeated+  } deriving (Show, Generic)++instance FromJSON ResponseParameters where+  parseJSON = parseJsonDrop 4++-- | This object represents 'getMe' response+type GetMeResponse = Response User++-- | This object represents message response+type MessageResponse = Response Message++-- | This object represents 'sendChatAction' response+type ChatActionResponse = Response Bool++-- | This object represents 'getUpdates' response+type UpdatesResponse = Response [Update]++-- | This object represents file response+type FileResponse = Response File++-- | This object represents user profile photos response+type UserProfilePhotosResponse = Response UserProfilePhotos++-- | This object represents 'setWebhook' response+type SetWebhookResponse = Response Bool++-- | This object represents 'answerInlineQuery' response+type InlineQueryResponse = Response Bool++-- | This object represents 'answerCallbackQuery' response+type CallbackQueryResponse = Response Bool++-- | This object represents 'kickChatMember' response+type KickChatMemberResponse = Response Bool++type LeaveChatResponse = Response Bool++-- | This object represents 'unbanChatMember' response+type UnbanChatMemberResponse = Response Bool++type GetChatResponse = Response Chat++type GetChatAdministratorsResponse = Response [ChatMember]++type GetChatMembersCountResponse = Response Int++type GetChatMemberResponse = Response ChatMember+ type GetWebhookInfoResponse = Response WebhookInfo++type AnswerShippingQueryResponse = Response Bool++type AnswerPreCheckoutQueryResponse = Response Bool
telegram-api.cabal view
@@ -1,82 +1,102 @@-name:                telegram-api
-version:             0.5.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
-license:             BSD3
-license-file:        LICENSE
-author:              Alexey Rodiontsev
-maintainer:          alex.rodiontsev@gmail.com
-copyright:           Alexey Rodiontsev (c) 2016
-category:            Web
-build-type:          Simple
--- extra-source-files:
-cabal-version:       >=1.10
-data-files:          test-data/christmas-cat.jpg
-                   , test-data/cert.pem
-                   , test-data/haskell-logo.webp
-                   , test-data/lego-video.mp4
-                   , 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
-
-extra-source-files:
-  README.md
-  CHANGELOG.md
-
-library
-  hs-source-dirs:      src
-  exposed-modules:     Web.Telegram.API.Bot
-                     , Web.Telegram.API.Bot.API
-                     , Web.Telegram.API.Bot.Data
-                     , Web.Telegram.API.Bot.Responses
-                     , Web.Telegram.API.Bot.Requests
-  other-modules:       Web.Telegram.API.Bot.JsonExt
-                     , Servant.Client.MultipartFormData
-  build-depends:       base >= 4.7 && < 5
-                     , aeson == 1.0.*
-                     , http-api-data
-                     , http-client >= 0.5 && < 0.6
-                     , servant >= 0.9 && <0.10
-                     , servant-client >= 0.9 && <0.10
-                     , mtl >= 2.2 && < 2.3
-                     , text
-                     , transformers
-                     , http-media
-                     , http-types
-                     , mime-types
-                     , bytestring
-                     , string-conversions
-  default-language:    Haskell2010
-  ghc-options:         -Wall -fno-warn-name-shadowing -fno-warn-unused-binds
-
-test-suite telegram-api-test
-  type:                exitcode-stdio-1.0
-  hs-source-dirs:      test
-  main-is:             Spec.hs
-  other-modules:       MainSpec
-                     , InlineSpec
-                     , JsonSpec
-  build-depends:       base
-                     , aeson == 1.0.*
-                     , hjpath
-                     , ansi-wl-pprint
-                     , http-client >= 0.5 && < 0.6
-                     , http-client-tls
-                     , http-types
-                     , hspec
-                     , optparse-applicative
-                     , servant >= 0.9 && <0.10
-                     , servant-client >= 0.9 && <0.10
-                     , telegram-api
-                     , http-types
-                     , filepath
-                     , text
-                     , transformers
-                     , utf8-string
-  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall -fno-warn-name-shadowing
-  default-language:    Haskell2010
-
-source-repository head
-  type:     git
-  location: https://github.com/klappvisor/haskell-telegram-api
+name:                telegram-api+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+license:             BSD3+license-file:        LICENSE+author:              Alexey Rodiontsev+maintainer:          alex.rodiontsev@gmail.com+copyright:           Alexey Rodiontsev (c) 2016+category:            Web+build-type:          Simple+-- extra-source-files:+cabal-version:       >=1.10+data-files:          test-data/christmas-cat.jpg+                   , test-data/cert.pem+                   , test-data/haskell-logo.webp+                   , test-data/lego-video.mp4+                   , test-data/lego-square.mp4+                   , 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+  CHANGELOG.md++library+  hs-source-dirs:      src+  exposed-modules:     Web.Telegram.API.Bot+                     , Web.Telegram.API.Bot.API+                     , Web.Telegram.API.Bot.API.Messages+                     , Web.Telegram.API.Bot.API.Edit+                     , Web.Telegram.API.Bot.API.Queries+                     , Web.Telegram.API.Bot.API.Get+                     , 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+  other-modules:       Web.Telegram.API.Bot.JsonExt+                     , Web.Telegram.API.Bot.API.Core+                     , 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.12 && < 0.13+                     , servant-client >= 0.12 && < 0.13+                     , servant-client-core >= 0.12 && < 0.13+                     , mtl >= 2.2 && < 2.3+                     , text+                     , transformers+                     , http-media+                     , http-types+                     , mime-types+                     , bytestring+                     , string-conversions+  default-language:    Haskell2010+  ghc-options:         -Wall -fno-warn-name-shadowing -fno-warn-unused-binds++test-suite telegram-api-test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  other-modules:       MainSpec+                     , InlineSpec+                     , PaymentsSpec+                     , JsonSpec+                     , UpdatesSpec+                     , StickersSpec+                     , TestCore+  build-depends:       base+                     , aeson >= 1.0 && < 1.3+                     , hjpath+                     , ansi-wl-pprint+                     , http-client >= 0.5 && < 0.6+                     , http-client-tls+                     , http-types+                     , hspec+                     , optparse-applicative+                     , 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++source-repository head+  type:     git+  location: https://github.com/klappvisor/haskell-telegram-api
test-data/cert.pem view
@@ -1,22 +1,22 @@------BEGIN CERTIFICATE-----
-MIIDqzCCApOgAwIBAgIJALwlJJK1qK3DMA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNV
-BAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazERMA8GA1UEBwwIQnJvb2tseW4xITAf
-BgNVBAoMGEV4YW1wbGUgQnJvb2tseW4gQ29tcGFueTEUMBIGA1UEAwwLZXhhbXBs
-ZS5jb20wHhcNMTYwNzA5MTMyMzMxWhcNMTcwNzA5MTMyMzMxWjBsMQswCQYDVQQG
-EwJVUzERMA8GA1UECAwITmV3IFlvcmsxETAPBgNVBAcMCEJyb29rbHluMSEwHwYD
-VQQKDBhFeGFtcGxlIEJyb29rbHluIENvbXBhbnkxFDASBgNVBAMMC2V4YW1wbGUu
-Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2NmLUaOzaUC/OE29
-hMkraWcCUu3EKVTQK8xF0CGEoOSE4kGSELT7i9o8eFRaEYeYoM5dI27VSAM1L7nO
-11y1vp08XOMaN8mNKl12FlxCdV6UCb0aIjOFxqYbWnLyEW8b8lOnnX21cLI1fDsn
-lO2OSpZXBhOhttG5u6JOhnyu2tWBu8rAjm/0DV7wgxJKqR/yLe5purWjAoRpKFEf
-hXloN7LTLlb30xmVlVE7VR3dpK44LfXaW1hBfZRlAalwmpgl9+BW0b4Fyj+OKyyx
-X9XpbSYbv2N8l7fRpI/UjNzoand2EVGUPy1WOnoVjHNWkqbpgkAYWeBXZorTyGh4
-Vf/uQwIDAQABo1AwTjAdBgNVHQ4EFgQUeyEkNLWp1//1Ko4iysRyRM9FeV8wHwYD
-VR0jBBgwFoAUeyEkNLWp1//1Ko4iysRyRM9FeV8wDAYDVR0TBAUwAwEB/zANBgkq
-hkiG9w0BAQsFAAOCAQEASQ5idy2Ii3MfL38aHhc/cblgJ9SU3anKuOMnW3dDjlle
-MLVpf09ccWsaObLAp3CNLgf/KdWVIZUSHUX37fNgy1IMVUcEKdl1U9nrsH47IwAn
-kNZ1uBROhcW+Vf1jzrxFkHd81eGmkqQseE+uqKpiHpiAvSPYPhBNgOf6xOSS57PG
-Q5pMoUTdZytJsOzRPzNDkJ32BXE/ysm39wnWyk1rz7AJudDnxMKg6bsjBPApKs7I
-ktTY3d0/NJ9Ie2TB7OISQtPWwo5iSrFeBoBDo0ybuCVVBVYo0YPUdcwMIPO8pH3z
-N6kaYyC1G67cCmLp4lOuvOKKKkrRgmscZht9aoNCFw==
+-----BEGIN CERTIFICATE-----+MIIDqzCCApOgAwIBAgIJALwlJJK1qK3DMA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNV+BAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazERMA8GA1UEBwwIQnJvb2tseW4xITAf+BgNVBAoMGEV4YW1wbGUgQnJvb2tseW4gQ29tcGFueTEUMBIGA1UEAwwLZXhhbXBs+ZS5jb20wHhcNMTYwNzA5MTMyMzMxWhcNMTcwNzA5MTMyMzMxWjBsMQswCQYDVQQG+EwJVUzERMA8GA1UECAwITmV3IFlvcmsxETAPBgNVBAcMCEJyb29rbHluMSEwHwYD+VQQKDBhFeGFtcGxlIEJyb29rbHluIENvbXBhbnkxFDASBgNVBAMMC2V4YW1wbGUu+Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2NmLUaOzaUC/OE29+hMkraWcCUu3EKVTQK8xF0CGEoOSE4kGSELT7i9o8eFRaEYeYoM5dI27VSAM1L7nO+11y1vp08XOMaN8mNKl12FlxCdV6UCb0aIjOFxqYbWnLyEW8b8lOnnX21cLI1fDsn+lO2OSpZXBhOhttG5u6JOhnyu2tWBu8rAjm/0DV7wgxJKqR/yLe5purWjAoRpKFEf+hXloN7LTLlb30xmVlVE7VR3dpK44LfXaW1hBfZRlAalwmpgl9+BW0b4Fyj+OKyyx+X9XpbSYbv2N8l7fRpI/UjNzoand2EVGUPy1WOnoVjHNWkqbpgkAYWeBXZorTyGh4+Vf/uQwIDAQABo1AwTjAdBgNVHQ4EFgQUeyEkNLWp1//1Ko4iysRyRM9FeV8wHwYD+VR0jBBgwFoAUeyEkNLWp1//1Ko4iysRyRM9FeV8wDAYDVR0TBAUwAwEB/zANBgkq+hkiG9w0BAQsFAAOCAQEASQ5idy2Ii3MfL38aHhc/cblgJ9SU3anKuOMnW3dDjlle+MLVpf09ccWsaObLAp3CNLgf/KdWVIZUSHUX37fNgy1IMVUcEKdl1U9nrsH47IwAn+kNZ1uBROhcW+Vf1jzrxFkHd81eGmkqQseE+uqKpiHpiAvSPYPhBNgOf6xOSS57PG+Q5pMoUTdZytJsOzRPzNDkJ32BXE/ysm39wnWyk1rz7AJudDnxMKg6bsjBPApKs7I+ktTY3d0/NJ9Ie2TB7OISQtPWwo5iSrFeBoBDo0ybuCVVBVYo0YPUdcwMIPO8pH3z+N6kaYyC1G67cCmLp4lOuvOKKKkrRgmscZht9aoNCFw== -----END CERTIFICATE-----
+ test-data/lego-square.mp4 view

binary file changed (absent → 182242 bytes)

+ 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-data/wikipedia-telegram.txt view
@@ -1,7 +1,7 @@-From the English Wikipedia about Telegram,
-https://en.wikipedia.org/wiki/Telegram_(software)
-
-Telegram is a cloud-based instant messaging service. Telegram clients exist for both mobile (Android, iOS, Windows Phone, Ubuntu Touch) and desktop systems (Windows, OS X, Linux). Users can send messages and exchange photos, videos, stickers and files of any type. Telegram also provides optional end-to-end encrypted messaging with self-destruct timers, but Telegram's implementation of these features has been contested by security researchers and cryptography experts.
-
-Telegram is supported by the Russian-born entrepreneur Pavel Durov, who is now a citizen of Saint Kitts and Nevis, travelling the world in self-imposed exile. Its client-side code is open-source software, whereas its server-side code is closed-sourced and proprietary. The service also provides APIs to independent developers.
-
+From the English Wikipedia about Telegram,+https://en.wikipedia.org/wiki/Telegram_(software)++Telegram is a cloud-based instant messaging service. Telegram clients exist for both mobile (Android, iOS, Windows Phone, Ubuntu Touch) and desktop systems (Windows, OS X, Linux). Users can send messages and exchange photos, videos, stickers and files of any type. Telegram also provides optional end-to-end encrypted messaging with self-destruct timers, but Telegram's implementation of these features has been contested by security researchers and cryptography experts.++Telegram is supported by the Russian-born entrepreneur Pavel Durov, who is now a citizen of Saint Kitts and Nevis, travelling the world in self-imposed exile. Its client-side code is open-source software, whereas its server-side code is closed-sourced and proprietary. The service also provides APIs to independent developers.+
test/InlineSpec.hs view
@@ -1,53 +1,53 @@-{-# LANGUAGE OverloadedStrings #-}
-
-module InlineSpec (spec) where
-
-import           Web.Telegram.API.Bot
-import           Test.Hspec
-import           Data.Text (Text)
-import           Network.HTTP.Client      (newManager)
-import           Network.HTTP.Client.TLS  (tlsManagerSettings)
-
-spec :: Token -> Text -> Spec
-spec token chatId = do
-  let inline_query_id = ""
-  manager <- runIO $ newManager tlsManagerSettings
-  -- Create the tls connection manager
-  describe "/answerInlineQuery" $ do
-    it "should answer with article" $ do
-      Right Response { result = res } <-
-        answerInlineQuery token (answerInlineQueryRequest inline_query_id [inline_article]) manager
-      res `shouldBe` True
-    it "should answer with photo" $ do
-      Right Response { result = res } <-
-        answerInlineQuery token (answerInlineQueryRequest inline_query_id [inline_photo]) manager
-      res `shouldBe` True
-    it "should answer with gif" $ do
-      Right Response { result = res } <-
-        answerInlineQuery token (answerInlineQueryRequest inline_query_id [inline_gif]) manager
-      res `shouldBe` True
-    it "should answer with mpeg gif" $ do
-      Right Response { result = res } <-
-        answerInlineQuery token (answerInlineQueryRequest inline_query_id [inline_mpeg]) manager
-      res `shouldBe` True
-    it "should answer with video" $ do
-      Right Response { result = res } <-
-        answerInlineQuery token (answerInlineQueryRequest inline_query_id [inline_video]) manager
-      res `shouldBe` True
-
-  describe "/answerInlineQuery" $
-    it "should get updates and answer" $ do
-      Right Response { result = updates} <-
-        getUpdates token Nothing Nothing Nothing manager
-      Update { inline_query = Just (InlineQuery { query_id = id } ) } <- pure (last updates)
-      e <-
-        answerInlineQuery token (answerInlineQueryRequest id [inline_video]) manager
-      putStrLn (show e)
-
-message_content = InputTextMessageContent "test message content" Nothing Nothing
-
-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_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
+{-# LANGUAGE OverloadedStrings #-}++module InlineSpec (spec) where++import           Data.Text               (Text)+import           Network.HTTP.Client     (newManager)+import           Network.HTTP.Client.TLS (tlsManagerSettings)+import           Test.Hspec+import           Web.Telegram.API.Bot++spec :: Token -> Text -> Spec+spec token chatId = do+  let inline_query_id = ""+  manager <- runIO $ newManager tlsManagerSettings+  -- Create the tls connection manager+  describe "/answerInlineQuery" $ do+    it "should answer with article" $ do+      Right Response { result = res } <-+        answerInlineQuery token (answerInlineQueryRequest inline_query_id [inline_article]) manager+      res `shouldBe` True+    it "should answer with photo" $ do+      Right Response { result = res } <-+        answerInlineQuery token (answerInlineQueryRequest inline_query_id [inline_photo]) manager+      res `shouldBe` True+    it "should answer with gif" $ do+      Right Response { result = res } <-+        answerInlineQuery token (answerInlineQueryRequest inline_query_id [inline_gif]) manager+      res `shouldBe` True+    it "should answer with mpeg gif" $ do+      Right Response { result = res } <-+        answerInlineQuery token (answerInlineQueryRequest inline_query_id [inline_mpeg]) manager+      res `shouldBe` True+    it "should answer with video" $ do+      Right Response { result = res } <-+        answerInlineQuery token (answerInlineQueryRequest inline_query_id [inline_video]) manager+      res `shouldBe` True++  describe "/answerInlineQuery" $+    it "should get updates and answer" $ do+      Right Response { result = updates} <-+        getUpdates token Nothing Nothing Nothing manager+      Update { inline_query = Just (InlineQuery { query_id = id } ) } <- pure (last updates)+      e <-+        answerInlineQuery token (answerInlineQueryRequest id [inline_video]) manager+      putStrLn (show e)++message_content = InputTextMessageContent "test message content" Nothing Nothing++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 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/JsonSpec.hs view
@@ -1,109 +1,109 @@-{-# LANGUAGE OverloadedStrings #-}
-
-module JsonSpec (spec) where
-
-import           Web.Telegram.API.Bot
-import           Test.Hspec
-import qualified Data.Text as T
-import           Data.Aeson
-import           Text.JSON.JPath
-import           Data.ByteString.Lazy.UTF8
-
-spec :: Spec
-spec = do
-  let getType q = jPath (T.unpack "type") (toString (encode q))
-  describe "type of serialized inline query result" $ do
-    it "should be article" $
-      getType iq_article `shouldBe` ["\"article\""]
-    it "should be photo" $
-      getType iq_photo `shouldBe` ["\"photo\""]
-    it "should be gif" $
-      getType iq_gif `shouldBe` ["\"gif\""]
-    it "should be mpeg4_gif" $
-      getType iq_mpeg `shouldBe` ["\"mpeg4_gif\""]
-    it "should be video" $
-      getType iq_video `shouldBe` ["\"video\""]
-    it "should be audio" $
-      getType iq_audio `shouldBe` ["\"audio\""]
-    it "should be contact" $
-      getType iq_contact `shouldBe` ["\"contact\""]
-    it "should be document" $
-      getType iq_document `shouldBe` ["\"document\""]
-    it "should be location" $
-      getType iq_location `shouldBe` ["\"location\""]
-    it "should be venue" $
-      getType iq_venue `shouldBe` ["\"venue\""]
-  describe "type of serialized cached inline query result" $ do
-    it "should be audio" $
-      getType cached_audio `shouldBe` ["\"audio\""]
-    it "should be voice" $
-      getType cached_voice `shouldBe` ["\"voice\""]
-    it "should be video" $
-      getType cached_video `shouldBe` ["\"video\""]
-    it "should be document" $
-      getType cached_doc `shouldBe` ["\"document\""]
-    it "should be sticker" $
-      getType cached_sticker `shouldBe` ["\"sticker\""]
-    it "should be mpeg4_gif" $
-      getType cached_mpeg `shouldBe` ["\"mpeg4_gif\""]
-    it "should be gif" $
-      getType cached_gif `shouldBe` ["\"gif\""]
-    it "should be photo" $
-      getType cached_photo `shouldBe` ["\"photo\""]
-
-message_content :: InputMessageContent
-message_content = InputTextMessageContent "test message content" Nothing Nothing
-
-iq_article :: InlineQueryResult
-iq_article = inlineQueryResultArticle "" "text article content" message_content
-
-iq_photo :: InlineQueryResult
-iq_photo = inlineQueryResultPhoto "" "" ""
-
-iq_gif :: InlineQueryResult
-iq_gif = inlineQueryResultGif "" "" ""
-
-iq_mpeg :: InlineQueryResult
-iq_mpeg = inlineQueryResultMpeg4Gif "" "" ""
-
-iq_video :: InlineQueryResult
-iq_video = inlineQueryResultVideo "" "" "video/mpeg" "" ""
-
-iq_audio :: InlineQueryResult
-iq_audio = inlineQueryResultAudio "" "" ""
-
-iq_contact :: InlineQueryResult
-iq_contact = inlineQueryResultContact "" "" ""
-
-iq_document :: InlineQueryResult
-iq_document = inlineQueryResultDocument "" "" "" ""
-
-iq_location :: InlineQueryResult
-iq_location = inlineQueryResultLocation "" 0.0 0.0 ""
-
-iq_venue :: InlineQueryResult
-iq_venue = inlineQueryResultVenue "" 0.0 0.0 "" ""
-
-cached_audio :: InlineQueryResult
-cached_audio = inlineQueryResultCachedAudio "" ""
-
-cached_voice :: InlineQueryResult
-cached_voice = inlineQueryResultCachedVoice "" "" ""
-
-cached_video :: InlineQueryResult
-cached_video = inlineQueryResultCachedVideo "" "" ""
-
-cached_doc :: InlineQueryResult
-cached_doc = inlineQueryResultCachedDocument "" "" ""
-
-cached_sticker :: InlineQueryResult
-cached_sticker = inlineQueryResultCachedSticker "" ""
-
-cached_mpeg :: InlineQueryResult
-cached_mpeg = inlineQueryResultCachedMpeg4Gif "" ""
-
-cached_gif :: InlineQueryResult
-cached_gif = inlineQueryResultCachedGif "" ""
-
-cached_photo :: InlineQueryResult
-cached_photo = inlineQueryResultCachedPhoto "" ""
+{-# LANGUAGE OverloadedStrings #-}++module JsonSpec (spec) where++import           Data.Aeson+import           Data.ByteString.Lazy.UTF8+import qualified Data.Text                 as T+import           Test.Hspec+import           Text.JSON.JPath+import           Web.Telegram.API.Bot++spec :: Spec+spec = do+  let getType q = jPath (T.unpack "type") (toString (encode q))+  describe "type of serialized inline query result" $ do+    it "should be article" $+      getType iq_article `shouldBe` ["\"article\""]+    it "should be photo" $+      getType iq_photo `shouldBe` ["\"photo\""]+    it "should be gif" $+      getType iq_gif `shouldBe` ["\"gif\""]+    it "should be mpeg4_gif" $+      getType iq_mpeg `shouldBe` ["\"mpeg4_gif\""]+    it "should be video" $+      getType iq_video `shouldBe` ["\"video\""]+    it "should be audio" $+      getType iq_audio `shouldBe` ["\"audio\""]+    it "should be contact" $+      getType iq_contact `shouldBe` ["\"contact\""]+    it "should be document" $+      getType iq_document `shouldBe` ["\"document\""]+    it "should be location" $+      getType iq_location `shouldBe` ["\"location\""]+    it "should be venue" $+      getType iq_venue `shouldBe` ["\"venue\""]+  describe "type of serialized cached inline query result" $ do+    it "should be audio" $+      getType cached_audio `shouldBe` ["\"audio\""]+    it "should be voice" $+      getType cached_voice `shouldBe` ["\"voice\""]+    it "should be video" $+      getType cached_video `shouldBe` ["\"video\""]+    it "should be document" $+      getType cached_doc `shouldBe` ["\"document\""]+    it "should be sticker" $+      getType cached_sticker `shouldBe` ["\"sticker\""]+    it "should be mpeg4_gif" $+      getType cached_mpeg `shouldBe` ["\"mpeg4_gif\""]+    it "should be gif" $+      getType cached_gif `shouldBe` ["\"gif\""]+    it "should be photo" $+      getType cached_photo `shouldBe` ["\"photo\""]++message_content :: InputMessageContent+message_content = InputTextMessageContent "test message content" Nothing Nothing++iq_article :: InlineQueryResult+iq_article = inlineQueryResultArticle "" "text article content" message_content++iq_photo :: InlineQueryResult+iq_photo = inlineQueryResultPhoto "" "" ""++iq_gif :: InlineQueryResult+iq_gif = inlineQueryResultGif "" "" ""++iq_mpeg :: InlineQueryResult+iq_mpeg = inlineQueryResultMpeg4Gif "" "" ""++iq_video :: InlineQueryResult+iq_video = inlineQueryResultVideo "" "" "video/mpeg" "" ""++iq_audio :: InlineQueryResult+iq_audio = inlineQueryResultAudio "" "" ""++iq_contact :: InlineQueryResult+iq_contact = inlineQueryResultContact "" "" ""++iq_document :: InlineQueryResult+iq_document = inlineQueryResultDocument "" "" "" ""++iq_location :: InlineQueryResult+iq_location = inlineQueryResultLocation "" 0.0 0.0 ""++iq_venue :: InlineQueryResult+iq_venue = inlineQueryResultVenue "" 0.0 0.0 "" ""++cached_audio :: InlineQueryResult+cached_audio = inlineQueryResultCachedAudio "" ""++cached_voice :: InlineQueryResult+cached_voice = inlineQueryResultCachedVoice "" "" ""++cached_video :: InlineQueryResult+cached_video = inlineQueryResultCachedVideo "" "" ""++cached_doc :: InlineQueryResult+cached_doc = inlineQueryResultCachedDocument "" "" ""++cached_sticker :: InlineQueryResult+cached_sticker = inlineQueryResultCachedSticker "" ""++cached_mpeg :: InlineQueryResult+cached_mpeg = inlineQueryResultCachedMpeg4Gif "" ""++cached_gif :: InlineQueryResult+cached_gif = inlineQueryResultCachedGif "" ""++cached_photo :: InlineQueryResult+cached_photo = inlineQueryResultCachedPhoto "" ""
test/MainSpec.hs view
@@ -1,322 +1,347 @@-{-# LANGUAGE DataKinds         #-}
-{-# LANGUAGE DeriveAnyClass    #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TypeOperators     #-}
-
-module MainSpec (spec) where
-
-import           Control.Monad
-import           Data.Monoid
-import           Web.Telegram.API.Bot
-import           Test.Hspec
-import           Data.Either (isRight, isLeft)
-import           Data.Text (Text)
-import qualified Data.Text as T
-import           Network.HTTP.Client      (newManager)
-import           Network.HTTP.Client.TLS  (tlsManagerSettings)
-import           Servant.Client
-import           Network.HTTP.Types.Status
-import           System.FilePath
-import           Control.Concurrent
-
-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 -> Text -> Text -> Spec
-spec token chatId botName = do
-  manager <- runIO $ newManager tlsManagerSettings
-  dataDir <- runIO getDataDir
-  let testFile name = dataDir </> "test-data" </> name
-  describe "/getMe" $
-    it "responds with correct bot's name" $ do
-      Right Response { result = u } <-
-        getMe token manager
-      user_first_name u `shouldBe` botName -- f.e. "TelegramAPIBot"
-
-  describe "/sendMessage" $ do
-    it "should send message" $ do
-      res <- sendMessage token (sendMessageRequest chatId "test message") manager
-      success res
-      let Right Response { result = m } = res
-      text m `shouldBe` Just "test message"
-
-    it "should return error message" $ do
-      res <- sendMessage token (sendMessageRequest "" "test message") manager
-      nosuccess res
-      let Left FailureResponse { responseStatus = Status { statusMessage = msg } } = res
-      msg `shouldBe` "Bad Request"
-
-    it "should send message markdown" $ do
-      let request = (sendMessageRequest chatId "text *bold* _italic_ [github](github.com/klappvisor/telegram-api)") {
-          message_parse_mode = Just Markdown
-        }
-      res <- sendMessage token request manager
-      success res
-      let Right Response { result = m } = res
-      text m `shouldBe` Just "text bold italic github"
-
-    it "should set keyboard" $ do
-      let kbA = keyboardButton "A"
-          kbB = keyboardButton "B"
-          kbC = keyboardButton "C"
-      let message = (sendMessageRequest chatId "set keyboard") {
-        message_reply_markup = Just $ replyKeyboardMarkup [[kbA, kbB, kbC]]
-      }
-      res <- sendMessage token message manager
-      success res
-      let Right Response { result = m } = res
-      text m `shouldBe` Just "set keyboard"
-
-    it "should remove keyboard" $ do
-      let message = (sendMessageRequest chatId "remove keyboard") {
-        message_reply_markup = Just replyKeyboardHide
-      }
-      res <- sendMessage token message manager
-      success res
-      let Right Response { result = m } = res
-      text m `shouldBe` Just "remove keyboard"
-
-    it "should send message with inline keyboard" $ do
-      let kbA = (inlineKeyboardButton "A") { ikb_callback_data = Just "A" }
-          kbB = (inlineKeyboardButton "B") { ikb_callback_data = Just "B" }
-          kbC = (inlineKeyboardButton "C") { ikb_callback_data = Just "C" }
-      let message = (sendMessageRequest chatId "set inline keyboard") {
-        message_reply_markup = Just $ inlineKeyboardMarkup [[kbA, kbB, kbC]]
-      }
-      res <- sendMessage token message manager
-      success res
-      let Right Response { result = m } = res
-      text m `shouldBe` Just "set inline keyboard"
-
-    it "should force reply" $ do
-      let message = (sendMessageRequest chatId "force reply") {
-        message_reply_markup = Just forceReply
-      }
-      res <- sendMessage token message manager
-      success res
-      let Right Response { result = m } = res
-      text m `shouldBe` Just "force reply"
-
-  describe "/forwardMessage" $
-    it "should forward message" $ do
-      res <- forwardMessage token (forwardMessageRequest chatId chatId 123000) manager
-      nosuccess res
-      let Left FailureResponse { responseStatus = Status { statusMessage = msg } } = res
-      msg `shouldBe` "Bad Request"
-
-  describe "/sendPhoto" $ do
-    it "should return error message" $ do
-      let photo = (sendPhotoRequest "" "photo_id") {
-        photo_caption = Just "photo caption"
-      }
-      Left FailureResponse { responseStatus = 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 upload = (uploadPhotoRequest chatId fileUpload) {
-        photo_caption = Just "uploaded photo"
-      }
-      Right Response { result = Message { caption = Just cpt, photo = Just photos } } <-
-        uploadPhoto token upload manager
-      cpt `shouldBe` "uploaded photo"
-      -- resend by id
-      let id = (photo_file_id . last) photos
-      let photo = (sendPhotoRequest chatId id) {
-        photo_caption = Just "photo caption"
-      }
-      Right Response { result = Message { caption = Just cpt } } <-
-        sendPhoto token photo manager
-      cpt `shouldBe` "photo caption"
-
-  describe "/sendAudio" $ do
-    it "should return error message" $ do
-      let audio = (sendAudioRequest "" "audio_id") {
-        _audio_performer = Just "performer"
-      , _audio_title = Just "title"
-      }
-      Left FailureResponse { responseStatus = 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")
-          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 {
-        result = Message {
-          audio = Just Audio {
-            audio_file_id = file_id, audio_title = Just title, audio_performer = Just performer
-          }
-        }
-      } <- uploadAudio token audio manager
-      title `shouldBe` audioTitle
-      performer `shouldBe` audioPerformer
-      let audio = sendAudioRequest chatId file_id
-      Right Response { result = Message { audio = Just Audio { audio_title = Just title' } } } <-
-        sendAudio token audio manager
-      title' `shouldBe` audioTitle
-
-  describe "/sendSticker" $ do
-    it "should send sticker" $ do
-      let sticker = sendStickerRequest chatId "BQADAgADGgADkWgMAAGXlYGBiM_d2wI"
-      Right Response { result = Message { sticker = Just sticker } } <-
-        sendSticker token sticker manager
-      sticker_file_id sticker `shouldBe` "BQADAgADGgADkWgMAAGXlYGBiM_d2wI"
-    it "should upload sticker" $ do
-      let fileUpload = localFileUpload (testFile "haskell-logo.webp")
-          stickerReq = uploadStickerRequest chatId fileUpload
-      Right Response { result = Message { sticker = Just sticker } } <-
-        uploadSticker token stickerReq manager
-      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")
-          voiceReq = (uploadVoiceRequest chatId fileUpload) { _voice_duration = Just 10 }
-      Right Response { result = Message { voice = Just voice } } <-
-        uploadVoice token voiceReq manager
-      voice_duration voice `shouldBe` 10
-
-  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")
-          videoReq = uploadVideoRequest chatId fileUpload
-      Right Response { result = Message { video = Just video } } <-
-        uploadVideo token videoReq manager
-      video_width video `shouldBe` 560
-
-  describe "/sendDocument" $
-    it "should upload document" $ do
-      let fileUpload = localFileUpload (testFile "wikipedia-telegram.txt")
-          documentReq = uploadDocumentRequest chatId fileUpload
-      Right Response { result = Message { document = Just document } } <-
-        uploadDocument token documentReq manager
-      doc_mime_type document `shouldBe` Just "text/plain"
-      doc_file_name document `shouldBe` Just "wikipedia-telegram.txt"
-
-  describe "/sendLocation" $
-    it "should send location" $ do
-      let location = sendLocationRequest chatId 52.38 4.9
-      Right Response { result = Message { location = Just loc } } <-
-        sendLocation token location manager
-      latitude loc `shouldSatisfy` liftM2 (&&) (> 52) (< 52.4)
-      longitude loc `shouldSatisfy` liftM2 (&&) (> 4.89) (< 5)
-
-  describe "/sendVenue" $
-    it "should send a venue" $ do
-      let venue = sendVenueRequest chatId 52.38 4.9 "Amsterdam Centraal" "Amsterdam"
-      Right Response { result = Message { location = Just loc } } <-
-        sendVenue token venue manager
-      latitude loc `shouldSatisfy` liftM2 (&&) (> 52) (< 52.4)
-      longitude loc `shouldSatisfy` liftM2 (&&) (> 4.89) (< 5)
-
-  describe "/sendContact" $
-    it "should send a contact" $ do
-      let contact = sendContactRequest chatId "06-18035176" "Hilbert"
-      Right Response { result = Message { contact = Just con } } <-
-        sendContact token contact manager
-      -- Telegram seems to remove any non numeric characters from the sent phone number (at least it removed my '-')
-      contact_phone_number con `shouldBe` "0618035176"
-      contact_first_name con `shouldBe` "Hilbert"
-
-  describe "/sendChatAction" $ do
-    it "should set typing action" $ do
-      Right Response { result = res} <-
-        sendChatAction token (SendChatActionRequest chatId Typing) manager
-      res `shouldBe` True
-    it "should set find location action" $ do
-      Right Response { result = res} <-
-        sendChatAction token (SendChatActionRequest chatId FindLocation) manager
-      res `shouldBe` True
-    it "should set upload photo action" $ do
-      Right Response { result = res} <-
-        sendChatAction token (SendChatActionRequest chatId UploadPhoto) manager
-      res `shouldBe` True
-
-  describe "/getUpdates" $
-    it "should get all messages" $ do
-      Right Response { result = updates} <-
-        getUpdates token Nothing Nothing Nothing manager
-      length updates `shouldSatisfy` (>= 0)
-
-  describe "/getFile" $ do
-    it "should get file" $ do
-      Right Response { result = file } <-
-        getFile token "AAQEABMXDZEwAARC0Kj3twkzNcMkAAIC" manager
-      fmap (T.take 10) (file_path file) `shouldBe` Just "thumb/file"
-
-    it "should return error" $ do
-      Left FailureResponse { responseStatus = Status { statusMessage = msg } } <-
-        getFile token "AAQEABMXDZEwAARC0Kj3twkzNcMkAmm" manager
-      msg `shouldBe` "Bad Request"
-
-  describe "/getUserProfilePhotos" $
-   it "should get user profile photos" $ do
-     Right Response { result = photos } <-
-       getUserProfilePhotos token (read (T.unpack chatId)) Nothing Nothing manager
-     total_count photos `shouldSatisfy` (>= 0)
-
-  describe "/setWebhook and /getWebhookInfo" $ do
-    it "should set webhook with certificate" $ do
-      let cert = localFileUpload $ testFile "cert.pem"
-          req = SetWebhookRequest "https://example.com/secret_token" cert
-      res <- setWebhookWithCertificate token req manager
-      success res
-      let Right Response { result = val } = res
-      val `shouldBe` True
-
-    it "should set webhook" $ do
-      threadDelay $ 2 * 1000 * 1000
-      Right Response { result = res } <-
-        setWebhook token (Just "https://example.com/secret_token") manager
-      res `shouldBe` True
-
-    it "should get webhook info" $ do
-      Right Response { result = WebhookInfo { whi_url = url } } <- getWebhookInfo token manager
-      url `shouldBe` "https://example.com/secret_token"
-
-    it "should remove webhook" $ do
-      Right Response { result = res } <-
-        setWebhook token Nothing manager
-      res `shouldBe` True
-
-    it "should remove webhood with deleteWebhook" $ do
-      threadDelay $ 2 * 1000 * 1000
-      _ <- setWebhook token (Just "https://example.com/secret_token") manager
-      res <- deleteWebhook token manager
-      success res
-      let Right Response { result = val } = res
-      val `shouldBe` True
-
-  describe "/editTextMessage" $ do
-    it "should edit message" $ do
-      let originalMessage = sendMessageRequest chatId "veritas"
-      Right Response { result = Message { message_id = msg_id, text = Just txt } } <-
-        sendMessage token originalMessage manager
-      let editRequest = editMessageTextRequest chatId msg_id $ "edited " <> txt
-      Right Response { result = Message { text = txt' } } <-
-        editMessageText token editRequest manager
-      txt' `shouldBe` Just "edited veritas"
-
-    it "should edit caption" $ do
-      let fileUpload = localFileUpload (testFile "christmas-cat.jpg")
-      let originalMessage = (uploadPhotoRequest chatId fileUpload) {
-        photo_caption = Just "cat picture"
-      }
-      Right Response { result = Message { message_id = msg_id, caption = Just cpt } } <-
-        uploadPhoto token originalMessage manager
-      let editRequest = editMessageCaptionRequest chatId msg_id $ Just $ "edited " <> cpt
-      Right Response { result = Message { caption = Just cpt' } } <-
-        editMessageCaption token editRequest manager
-      cpt' `shouldBe` "edited cat picture"
-
-    -- it "should edit caption" $ do ... after inline query tests are on place
+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators     #-}++module MainSpec (spec) where++import           Control.Concurrent+import           Control.Monad+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           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++spec :: Token -> ChatId -> Text -> Spec+spec token chatId botName = do+  manager <- runIO $ newManager tlsManagerSettings+  dataDir <- runIO getDataDir+  let testFile name = dataDir </> "test-data" </> name+  describe "/getMe" $+    it "responds with correct bot's name" $ do+      Right Response { result = u } <-+        getMe token manager+      user_first_name u `shouldBe` botName -- f.e. "TelegramAPIBot"++  describe "/sendMessage" $ do+    it "should send message" $ do+      res <- sendMessage token (sendMessageRequest chatId "test message") manager+      success res+      let Right Response { result = m } = res+      text m `shouldBe` Just "test message"++    it "should return error message" $ do+      res <- sendMessage token (sendMessageRequest (ChatChannel "") "test message") manager+      nosuccess res+      let Left (FailureResponse Core.Response { responseStatusCode = Status { statusMessage = msg } }) = res+      msg `shouldBe` "Bad Request"++    it "should send message markdown" $ do+      let request = (sendMessageRequest chatId "text *bold* _italic_ [github](github.com/klappvisor/telegram-api)") {+          message_parse_mode = Just Markdown+        }+      res <- sendMessage token request manager+      success res+      let Right Response { result = m } = res+      text m `shouldBe` Just "text bold italic github"++    it "should set keyboard" $ do+      let kbA = keyboardButton "A"+          kbB = keyboardButton "B"+          kbC = keyboardButton "C"+      let message = (sendMessageRequest chatId "set keyboard") {+        message_reply_markup = Just $ replyKeyboardMarkup [[kbA, kbB, kbC]]+      }+      res <- sendMessage token message manager+      success res+      let Right Response { result = m } = res+      text m `shouldBe` Just "set keyboard"++    it "should remove keyboard" $ do+      let message = (sendMessageRequest chatId "remove keyboard") {+        message_reply_markup = Just replyKeyboardHide+      }+      res <- sendMessage token message manager+      success res+      let Right Response { result = m } = res+      text m `shouldBe` Just "remove keyboard"++    it "should send message with inline keyboard" $ do+      let kbA = (inlineKeyboardButton "A") { ikb_callback_data = Just "A" }+          kbB = (inlineKeyboardButton "B") { ikb_callback_data = Just "B" }+          kbC = (inlineKeyboardButton "C") { ikb_callback_data = Just "C" }+      let message = (sendMessageRequest chatId "set inline keyboard") {+        message_reply_markup = Just $ inlineKeyboardMarkup [[kbA, kbB, kbC]]+      }+      res <- sendMessage token message manager+      success res+      let Right Response { result = m } = res+      text m `shouldBe` Just "set inline keyboard"++    it "should force reply" $ do+      let message = (sendMessageRequest chatId "force reply") {+        message_reply_markup = Just forceReply+      }+      res <- sendMessage token message manager+      success res+      let Right Response { result = m } = res+      text m `shouldBe` Just "force reply"++  describe "/forwardMessage" $+    it "should forward message" $ do+      res <- forwardMessage token (forwardMessageRequest chatId chatId 123000) manager+      nosuccess res+      let Left (FailureResponse Core.Response { responseStatusCode = Status { statusMessage = msg } }) = res+      msg `shouldBe` "Bad Request"++  describe "/sendPhoto" $ do+    it "should return error message" $ do+      let photo = (sendPhotoRequest (ChatChannel "") "photo_id") {+        photo_caption = Just "photo caption"+      }+      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 upload = (uploadPhotoRequest chatId fileUpload) {+        photo_caption = Just "uploaded photo"+      }+      Right Response { result = Message { caption = Just cpt, photo = Just photos } } <-+        uploadPhoto token upload manager+      cpt `shouldBe` "uploaded photo"+      -- resend by id+      let id = (photo_file_id . last) photos+      let photo = (sendPhotoRequest chatId id) {+        photo_caption = Just "photo caption"+      }+      Right Response { result = Message { caption = Just cpt } } <-+        sendPhoto token photo manager+      cpt `shouldBe` "photo caption"++  describe "/sendAudio" $ do+    it "should return error message" $ do+      let audio = (sendAudioRequest (ChatChannel "") "audio_id") {+        _audio_performer = Just "performer"+      , _audio_title = Just "title"+      }+      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"+          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+          }+      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+          }+        }+      } = res+      title `shouldBe` audioTitle+      performer `shouldBe` audioPerformer+      let audio = sendAudioRequest chatId file_id+      Right Response { result = Message { audio = Just Audio { audio_title = Just title' } } } <-+        sendAudio token audio manager+      title' `shouldBe` audioTitle++  describe "/sendSticker" $ do+    it "should send sticker" $ do+      let sticker = sendStickerRequest chatId "BQADAgADGgADkWgMAAGXlYGBiM_d2wI"+      Right Response { result = Message { sticker = Just sticker } } <-+        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"+          stickerReq = uploadStickerRequest chatId fileUpload+      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"+          voiceReq = (uploadVoiceRequest chatId fileUpload) { _voice_duration = Just 10 }+      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+      let fileUpload = localFileUpload $ testFile "lego-square.mp4"+          videoNoteReq = (uploadVideoNoteRequest chatId fileUpload)+            { _vid_note_length = Just 320 }+      res <- uploadVideoNote token videoNoteReq manager+      success res+      let Right Response { result = Message { video_note = Just video } } = res+      vid_note_duration video `shouldBe` 6++  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"+          videoReq = uploadVideoRequest chatId fileUpload+      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"+          documentReq = uploadDocumentRequest chatId fileUpload+      Right Response { result = Message { document = Just document } } <-+        uploadDocument token documentReq manager+      doc_mime_type document `shouldBe` Just "text/plain"+      doc_file_name document `shouldBe` Just "wikipedia-telegram.txt"++  describe "/sendLocation" $+    it "should send location" $ do+      let location = sendLocationRequest chatId 52.38 4.9+      Right Response { result = Message { location = Just loc } } <-+        sendLocation token location manager+      latitude loc `shouldSatisfy` liftM2 (&&) (> 52) (< 52.4)+      longitude loc `shouldSatisfy` liftM2 (&&) (> 4.89) (< 5)++  describe "/sendVenue" $+    it "should send a venue" $ do+      let venue = sendVenueRequest chatId 52.38 4.9 "Amsterdam Centraal" "Amsterdam"+      Right Response { result = Message { location = Just loc } } <-+        sendVenue token venue manager+      latitude loc `shouldSatisfy` liftM2 (&&) (> 52) (< 52.4)+      longitude loc `shouldSatisfy` liftM2 (&&) (> 4.89) (< 5)++  describe "/sendContact" $+    it "should send a contact" $ do+      let contact = sendContactRequest chatId "06-18035176" "Hilbert"+      Right Response { result = Message { contact = Just con } } <-+        sendContact token contact manager+      -- Telegram seems to remove any non numeric characters from the sent phone number (at least it removed my '-')+      contact_phone_number con `shouldBe` "0618035176"+      contact_first_name con `shouldBe` "Hilbert"++  describe "/sendChatAction" $ do+    it "should set typing action" $ do+      Right Response { result = res} <-+        sendChatAction token (SendChatActionRequest chatId Typing) manager+      res `shouldBe` True+    it "should set find location action" $ do+      Right Response { result = res} <-+        sendChatAction token (SendChatActionRequest chatId FindLocation) manager+      res `shouldBe` True+    it "should set upload photo action" $ do+      Right Response { result = res} <-+        sendChatAction token (SendChatActionRequest chatId UploadPhoto) manager+      res `shouldBe` True++  describe "/getUpdates" $+    it "should get all messages" $ do+      _ <- deleteWebhook token manager+      Right Response { result = updates} <-+        getUpdates token Nothing Nothing Nothing manager+      length updates `shouldSatisfy` (>= 0)++  describe "/getFile" $ do+    it "should get file" $ do+      Right Response { result = file } <-+        getFile token "AAQEABMXDZEwAARC0Kj3twkzNcMkAAIC" manager+      fmap (T.take 10) (file_path file) `shouldBe` Just "thumbnails"++    it "should return error" $ do+      Left (FailureResponse Core.Response { responseStatusCode = Status { statusMessage = msg } }) <-+        getFile token "AAQEABMXDZEwAARC0Kj3twkzNcMkAmm" manager+      msg `shouldBe` "Bad Request"++  describe "/getUserProfilePhotos" $+   it "should get user profile photos" $ do+     Right Response { result = photos } <- do+       let ChatId userId = chatId+       getUserProfilePhotos token (fromIntegral userId) Nothing Nothing manager+     total_count photos `shouldSatisfy` (>= 0)++  describe "/setWebhook and /getWebhookInfo" $ do+    it "should set webhook with certificate" $ do+      let cert = localFileUpload $ testFile "cert.pem"+          req = setWebhookRequest "https://example.com/secret_token" cert+      res <- setWebhookWithCertificate token req manager+      success res+      let Right Response { result = val } = res+      val `shouldBe` True++    it "should set webhook" $ do+      threadDelay $ 2 * 1000 * 1000+      Right Response { result = res } <-+        setWebhook token (Just "https://example.com/secret_token") manager+      res `shouldBe` True++    it "should get webhook info" $ do+      Right Response { result = WebhookInfo { whi_url = url } } <- getWebhookInfo token manager+      url `shouldBe` "https://example.com/secret_token"++    it "should remove webhook" $ do+      Right Response { result = res } <-+        setWebhook token Nothing manager+      res `shouldBe` True++    it "should remove webhood with deleteWebhook" $ do+      threadDelay $ 2 * 1000 * 1000+      _ <- setWebhook token (Just "https://example.com/secret_token") manager+      res <- deleteWebhook token manager+      success res+      let Right Response { result = val } = res+      val `shouldBe` True++  describe "/editTextMessage" $ do+    it "should edit message" $ do+      let originalMessage = sendMessageRequest chatId "veritas"+      Right Response { result = Message { message_id = msg_id, text = Just txt } } <-+        sendMessage token originalMessage manager+      let editRequest = editMessageTextRequest chatId msg_id $ "edited " <> txt+      Right Response { result = Message { text = txt' } } <-+        editMessageText token editRequest manager+      txt' `shouldBe` Just "edited veritas"++    it "should edit caption" $ do+      let fileUpload = localFileUpload (testFile "christmas-cat.jpg")+      let originalMessage = (uploadPhotoRequest chatId fileUpload) {+        photo_caption = Just "cat picture"+      }+      Right Response { result = Message { message_id = msg_id, caption = Just cpt } } <-+        uploadPhoto token originalMessage manager+      let editRequest = editMessageCaptionRequest chatId msg_id $ Just $ "edited " <> cpt+      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
@@ -0,0 +1,36 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators     #-}++module PaymentsSpec (spec) where++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++spec :: Token -> ChatId -> Text -> Text -> Spec+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"+          currencyCode = CurrencyCode "USD"+          prices = [ LabeledPrice "Cannon price" 59999+                   , LabeledPrice "Time-space penetration tax" 2100+                   , LabeledPrice "Portal Invention Day discount" (-1200)+                   , LabeledPrice "Portal delivery cost" 100+                   ]+          invoiceRequest = (sendInvoiceRequest chatId "Portal cannon" description "test_payload" paymentToken "deep_link" currencyCode prices)+                   { snd_inv_photo_url = Just "http://farm4.staticflickr.com/3560/3576111171_66c1fc2462_z.jpg"+                   , snd_inv_is_flexible = Just True+                   }+      res <- runClient (sendInvoiceM invoiceRequest) token manager+      success res+      let Right Response { result = m } = res+      inv_title <$> (invoice m) `shouldBe` Just "Portal cannon"+
test/Spec.hs view
@@ -1,84 +1,93 @@-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE RecordWildCards   #-}
-
-module Main (main) where
-
-import           Data.Monoid                  ((<>))
-import           Data.Maybe                   (fromMaybe)
-import           Data.Text                    (Text)
-import qualified Data.Text                    as T
-import qualified MainSpec
-import qualified JsonSpec
-import           Options.Applicative
-import           System.Environment           (withArgs)
-import           Test.Hspec
-import qualified Text.PrettyPrint.ANSI.Leijen as PP
-import           Web.Telegram.API.Bot
-
--- | Command line options for test suite
-data Options = Options
-  {
-    opt_integration :: Bool   -- ^ Run integration tests
-  , opt_token       :: Maybe String   -- ^ Bot token from BotFather
-  , opt_chatId      :: Maybe String   -- ^ Id of a chat or of your bot
-  , opt_botName     :: Maybe String   -- ^ Bot name
-  , opt_hSpecOpts   :: Maybe [String] -- ^ Command line options to pass to hSpec
-  }
-
-options :: Parser Options
-options = Options
-    <$> switch
-        ( long "integration"
-       <> help "Run integration tests" )
-    <*> optional ( strOption
-         ( long "token"
-        <> short 't'
-        <> metavar "BOT_TOKEN"
-        <> help "Bot Token" ))
-    <*> optional ( strOption
-         ( long "chatid"
-        <> short 'c'
-        <> metavar "CHAT_ID"
-        <> help "Chat Id" ))
-    <*> optional ( strOption
-         ( long "botname"
-        <> short 'b'
-        <> metavar "BOT_NAME"
-        <> help "Bot Name" ))
-    <*> optional ( some ( argument str
-         ( metavar "HSPEC_ARGS"
-        <> help "Hspec arguments")))
-
-main :: IO ()
-main = do
-    Options{..} <- execParser opts
-    let integration = opt_integration
-        token = fmap (\x -> Token ("bot" <> T.pack x)) opt_token
-        chatId = T.pack <$> opt_chatId
-        botName = T.pack <$> opt_botName
-        hspecArgs = fromMaybe [] opt_hSpecOpts
-    withArgs hspecArgs $ hspec (runSpec' integration token chatId botName)
-    where opts = info (helper <*> options)
-            ( fullDesc
-           <> progDescDoc description)
-runSpec' :: Bool -> Maybe Token -> Maybe Text -> Maybe Text -> SpecWith ()
-runSpec' integration token chatId botName = do
-    describe "Unit tests" $ do
-      describe "Json tests" $ JsonSpec.spec
-    if integration then runIntegrationSpec token chatId botName
-    else describe "Integration tests" $ it "skipping..." $
-        pendingWith "Use --integration switch to run integration tests"
-
-
-runIntegrationSpec :: Maybe Token -> Maybe Text -> Maybe Text -> SpecWith ()
-runIntegrationSpec (Just token) (Just chatId) (Just botName) = do
-        describe "Main integration tests" $ MainSpec.spec token chatId botName
-            --describe "Inline integration tests" $ InlineSpec.spec token chatId botName
-runIntegrationSpec _ _ _ = describe "Integration tests" $ do
-        fail "Missing required arguments for integration tests. Run stack test --test-arguments \"--help\" for more info"
-
-description ::  Maybe PP.Doc
-description = Just $
-           (PP.text  "Run the haskell-telegram-api tests")
-    PP.<$> ((PP.text "Running with stack: ") PP.<> (PP.text "stack test --test-arguments=\"--integration -t asd128903uiasbfì1023u -c 1235122 -b MyTeleBot -- -m send\""))
-    PP.<$> ((PP.red . PP.text $ "WARNING: ") PP.<> (PP.text "the HSPEC_ARGS are optional but if present MUST be at the end and seperated from the other options with a -- "))
+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards   #-}++module Main (main) where++import           Data.Char                    (isDigit)+import           Data.Maybe                   (fromMaybe)+import           Data.Monoid                  ((<>))+import           Data.Text                    (Text)+import qualified Data.Text                    as T+import qualified JsonSpec+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+import qualified UpdatesSpec+import           Web.Telegram.API.Bot++-- | Command line options for test suite+data Options = Options+  {+    opt_integration :: Bool   -- ^ Run integration tests+  , opt_chatId      :: Maybe String   -- ^ Id of a chat or of your bot+  , opt_botName     :: Maybe String   -- ^ Bot name+  , opt_hSpecOpts   :: Maybe [String] -- ^ Command line options to pass to hSpec+  }++options :: Parser Options+options = Options+    <$> switch+        ( long "integration"+       <> help "Run integration tests" )+    <*> optional ( strOption+         ( long "chatid"+        <> short 'c'+        <> metavar "CHAT_ID"+        <> help "Chat Id" ))+    <*> optional ( strOption+         ( long "botname"+        <> short 'b'+        <> metavar "BOT_NAME"+        <> help "Bot Name" ))+    <*> optional ( some ( argument str+         ( metavar "HSPEC_ARGS"+        <> help "Hspec arguments")))++main :: IO ()+main = do+    tokenEnv <- lookupEnv "BOT_TOKEN"+    paymentTokenEnv <- lookupEnv "PAYMENT_TOKEN"+    Options{..} <- execParser opts+    let integration = opt_integration+        token = (\x -> Token ("bot" <> T.pack x)) <$> tokenEnv+        paymentToken = T.pack <$> paymentTokenEnv+        chatId = readChatId <$> opt_chatId+        botName = T.pack <$> opt_botName+        hspecArgs = fromMaybe [] opt_hSpecOpts+    withArgs hspecArgs $ hspec (runSpec' integration token chatId botName paymentToken)+    where opts = info (helper <*> options)+            ( fullDesc+           <> progDescDoc description)+runSpec' :: Bool -> Maybe Token -> Maybe ChatId -> Maybe Text -> Maybe Text -> SpecWith ()+runSpec' integration token chatId botName paymentToken = do+    describe "Unit tests" $+      describe "Json tests" JsonSpec.spec+    if integration then runIntegrationSpec token chatId botName paymentToken+    else describe "Integration tests" $ it "skipping..." $+        pendingWith "Use --integration switch to run integration tests"+++runIntegrationSpec :: Maybe Token -> Maybe ChatId -> Maybe Text -> Maybe Text -> SpecWith ()+runIntegrationSpec (Just token) (Just chatId) (Just botName) (Just paymentToken) = do+        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"++description ::  Maybe PP.Doc+description = Just $+           (PP.text "Run the haskell-telegram-api tests")+    PP.<$> ((PP.text "Running with stack: ") PP.<> (PP.text "stack test --test-arguments=\"--integration -c 1235122 -b MyTeleBot -- -m send\""))+    PP.<$> ((PP.red . PP.text $ "WARNING: ") PP.<> (PP.text "the HSPEC_ARGS are optional but if present MUST be at the end and seperated from the other options with a -- "))++readChatId :: String -> ChatId+readChatId s@('@':_) = ChatChannel $ T.pack s+readChatId s         | all isDigit s = ChatId (read s)+readChatId _         = error "ChatId must be either Integer or String in form '@channel'"
+ 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
@@ -0,0 +1,71 @@+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators     #-}++module UpdatesSpec (spec) where++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++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 <- runTelegramClient token manager $ do+          b <- getMeM+          info <- getWebhookInfoM+          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 <- runTelegramClient token manager $ do+          info <- getWebhookInfoM+          liftIO $ (whi_url . result) info `shouldBe` ""+          liftIO $ threadDelay $ 2 * 1000 * 1000 -- to avoid Too many request error+          set <- setWebhookM $ setWebhookRequest' url+          liftIO $ result set `shouldBe` True+          info <- getWebhookInfoM+          liftIO $ (whi_url . result) info `shouldBe` url+          liftIO $ threadDelay $ 2 * 1000 * 1000 -- to avoid Too many request error+          del <- deleteWebhookM+          liftIO $ result del `shouldBe` True+          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 <- runTelegramClient token manager $ do+          let request = (setWebhookRequest' url) {+              webhook_allowed_updates = Just allowedUpdates+          }+          set <- setWebhookM request+          liftIO $ result set `shouldBe` True+          getWebhookInfoM+      let Right Response { result = whi } = res+      whi_allowed_updates whi `shouldBe` Just allowedUpdates++    it "should set max connections" $ 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+      let Right Response { result = whi } = res+      whi_max_connections whi `shouldBe` Just 5++