packages feed

telegram-api 0.1.0.0 → 0.1.0.1

raw patch · 9 files changed

+390/−223 lines, 9 filesdep ~basedep ~eitherdep ~servantsetup-changed

Dependency ranges changed: base, either, servant

Files

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
@@ -0,0 +1,96 @@+# telegram-api
+
+![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 Bots API][telegram-bot-api] based on [servant][servant] library. 
+Currently supports only one way of receiving updates based on [`getUpdates`](https://core.telegram.org/bots/api#getupdates) method.
+Uploading stickers, documents, video, etc is not supported yet, so you can only send items which are already uploaded on the Telegram servers.
+See list of supported methods below in TODO section.
+
+## Usage
+
+`getMe` example
+
+```haskell
+import Control.Monad
+import qualified Data.Text.IO as T
+import Data.Maybe
+import Telegram.API.Data
+import Telegram.API.Responses
+import Telegram.API.Bot
+
+main :: IO ()
+main = do
+  Right GetMeResponse { user_result = u } <-
+    getMe token
+    T.putStrLn (user_first_name u)
+  where token = Token "bot<token>" -- entire Token should be bot123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
+```
+
+`sendMessage` example
+
+```haskell
+import Control.Monad
+import qualified Data.Text.IO as T
+import Data.Maybe
+import Telegram.API.Data
+import Telegram.API.Requests
+import Telegram.API.Responses
+import Telegram.API.Bot
+
+main :: IO ()
+main = do
+  Right MessageResponse { message_result = m } <-
+    sendMessage token (SendMessageRequest chatId message (Just Markdown) Nothing Nothing)
+    T.putStrLn (message_id m)
+    T.putStrLn (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)"
+```
+
+## TODO
+
+### General
+
+* `reply_markup` is skipped for all methods 
+* Inline mode is not supported yet
+* Uploading of Files, Documents, Stickers, etc
+
+### Methods
+
+##### Currently supported:
+
+* `getMe`
+* `sendMessage`
+* `forwardMessage`
+* `sendPhoto` - without upload
+* `sendAudio` - without upload
+* `sendDocument` - without upload
+* `sendSticker` - without upload
+* `sendVideo` - without upload
+* `sendVoice` - without upload
+* `sendLocation`
+* `getUpdates`
+* `getFile`
+* `sendChatAction`
+* `getUserProfilePhotos`
+* `setWebhook` - without uploading certificate
+
+##### To be done:
+
+* `sendPhoto` - upload photo
+* `sendAudio` - upload audio
+* `sendDocument` - upload documents
+* `sendSticker` - upload stickers
+* `sendVideo` - upload video
+* `sendVoice` - upload voice
+* `setWebhook` - upload certificate
+* `answerInlineQuery` inline bots
+
+[telegram-bot-api]: https://core.telegram.org/bots/api
+[servant]: https://haskell-servant.github.io/
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple
+main = defaultMain
src/Web/Telegram/API/Bot.hs view
@@ -1,20 +1,20 @@-{-# LANGUAGE DataKinds         #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeOperators     #-}-{-# LANGUAGE TemplateHaskell   #-}---- | This module provides Telegram Bot API-module Web.Telegram.API.Bot-  (-    module Web.Telegram.API.Bot.API-  , module Web.Telegram.API.Bot.Data-  , module Web.Telegram.API.Bot.Responses-  , module Web.Telegram.API.Bot.Requests-  ) where--import           Web.Telegram.API.Bot.API-import           Web.Telegram.API.Bot.Data-import           Web.Telegram.API.Bot.Responses+{-# LANGUAGE DataKinds         #-}
+{-# LANGUAGE DeriveGeneric     #-}
+{-# LANGUAGE DeriveAnyClass    #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeOperators     #-}
+{-# LANGUAGE TemplateHaskell   #-}
+
+-- | This module provides Telegram Bot API
+module Web.Telegram.API.Bot
+  (
+    module Web.Telegram.API.Bot.API
+  , module Web.Telegram.API.Bot.Data
+  , module Web.Telegram.API.Bot.Responses
+  , module Web.Telegram.API.Bot.Requests
+  ) where
+
+import           Web.Telegram.API.Bot.API
+import           Web.Telegram.API.Bot.Data
+import           Web.Telegram.API.Bot.Responses
 import           Web.Telegram.API.Bot.Requests
src/Web/Telegram/API/Bot/API.hs view
@@ -21,6 +21,7 @@   , getUpdates
   , getFile
   , getUserProfilePhotos
+  , setWebhook
     -- * API
   , TelegramBotAPI
   , api
@@ -104,6 +105,10 @@          :> QueryParam "offset" Int
          :> QueryParam "limit" Int
          :> Get '[JSON] UserProfilePhotosResponse
+    :<|> TelegramToken :> "setWebhook"
+         :> QueryParam "url" Text
+         :> Get '[JSON]
+         SetWebhookResponse
 
 -- | Proxy for Thelegram Bot API
 api :: Proxy TelegramBotAPI
@@ -123,6 +128,7 @@ getUpdates_           :: Token -> Maybe Int -> Maybe Int -> Maybe Int -> EitherT ServantError IO UpdatesResponse
 getFile_              :: Token -> Maybe Text -> EitherT ServantError IO FileResponse
 getUserProfilePhotos_ :: Token -> Maybe Int -> Maybe Int -> Maybe Int -> EitherT ServantError IO UserProfilePhotosResponse
+setWebhook_           :: Token -> Maybe Text -> EitherT ServantError IO SetWebhookResponse
 getMe_
   :<|> sendMessage_
   :<|> forwardMessage_
@@ -136,7 +142,8 @@   :<|> sendChatAction_
   :<|> getUpdates_
   :<|> getFile_
-  :<|> getUserProfilePhotos_ =
+  :<|> getUserProfilePhotos_
+  :<|> setWebhook_ =
       client api
           (BaseUrl Https "api.telegram.org" 443)
 -- | A simple method for testing your bot's auth token. Requires no parameters.
@@ -197,4 +204,12 @@ 
 -- | Use this method to get a list of profile pictures for a user. Returns a 'UserProfilePhotos' object.
 getUserProfilePhotos :: Token -> Int -> Maybe Int -> Maybe Int -> IO (Either ServantError UserProfilePhotosResponse)
-getUserProfilePhotos token user_id offset limit = runEitherT $ getUserProfilePhotos_ token (Just user_id) offset limit+getUserProfilePhotos token user_id offset limit = runEitherT $ getUserProfilePhotos_ token (Just user_id) offset limit
+
+-- | 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
+    -> IO (Either ServantError SetWebhookResponse)
+setWebhook token url = runEitherT $ setWebhook_ token url
+ src/Web/Telegram/API/Bot/JsonExt.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE DataKinds         #-}
+{-# LANGUAGE DeriveGeneric     #-}
+{-# LANGUAGE DeriveAnyClass    #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeOperators     #-}
+{-# LANGUAGE TemplateHaskell   #-}
+{-# LANGUAGE FlexibleContexts  #-}
+
+-- | 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
+import           GHC.TypeLits
+
+-- | Method used to drop prefix from field name during serialization
+toJsonDrop prefix = genericToJSON defaultOptions {
+    fieldLabelModifier = drop prefix
+  , omitNothingFields = True
+  }
+
+-- | Method used to drop prefix from field name during deserialization
+parseJsonDrop prefix = genericParseJSON defaultOptions { fieldLabelModifier = drop prefix }
src/Web/Telegram/API/Bot/Responses.hs view
@@ -15,6 +15,7 @@     , UpdatesResponse           (..)
     , FileResponse              (..)
     , UserProfilePhotosResponse (..)
+    , SetWebhookResponse        (..)
     ) where
 
 import           Data.Aeson
@@ -98,4 +99,16 @@   toJSON = toJsonDrop 7
 
 instance FromJSON UserProfilePhotosResponse where
-  parseJSON = parseJsonDrop 7+  parseJSON = parseJsonDrop 7
+
+-- | This object represents 'setWebhook' response
+data SetWebhookResponse = SetWebhookResponse
+  {
+    webhook_result :: Bool
+  } deriving (Show, Generic)
+
+instance ToJSON SetWebhookResponse where
+  toJSON = toJsonDrop 8
+
+instance FromJSON SetWebhookResponse where
+  parseJSON = parseJsonDrop 8
telegram-api.cabal view
@@ -1,47 +1,51 @@-name:                telegram-api-version:             0.1.0.0-synopsis:            Telegram Bot API bindings-description:         High-level bindings to the Telegram Bots API-homepage:            http://github.com/klappvisor/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--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-  build-depends:       base >= 4.7 && < 5-                     , aeson-                     , servant-                     , servant-client-                     , text-                     , either-  default-language:    Haskell2010--test-suite telegram-api-test-  type:                exitcode-stdio-1.0-  hs-source-dirs:      test-  main-is:             Spec.hs-  build-depends:       base-                     , text-                     , hspec-                     , servant-                     , servant-client-                     , telegram-api-                     , http-types-  ghc-options:         -threaded -rtsopts -with-rtsopts=-N-  default-language:    Haskell2010--source-repository head-  type:     git-  location: https://github.com/klappvisor/telegram-api+name:                telegram-api
+version:             0.1.0.1
+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
+
+extra-source-files:
+  README.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
+  build-depends:       base >= 4.7 && < 5
+                     , aeson
+                     , servant
+                     , servant-client
+                     , text
+                     , either
+  default-language:    Haskell2010
+
+test-suite telegram-api-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  build-depends:       base
+                     , text
+                     , hspec
+                     , servant
+                     , servant-client
+                     , telegram-api
+                     , http-types
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/klappvisor/haskell-telegram-api
test/Spec.hs view
@@ -1,124 +1,135 @@-{-# LANGUAGE DataKinds         #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeOperators     #-}-{-# LANGUAGE TemplateHaskell   #-}--module Main (main) where--import           Control.Monad-import           Web.Telegram.API.Bot-import           Test.Hspec-import           Data.Text (Text)-import qualified Data.Text as T-import           Servant.Client-import           Servant.API-import           Network.HTTP.Types.Status-import           System.Environment--main :: IO ()-main = do-    [token, chatId] <- getArgs-    withArgs [] $ hspec (spec (Token (T.pack token)) (T.pack chatId))--spec :: Token -> Text -> Spec-spec token chatId = do-  describe "/getMe" $ do-    it "responds with correct bot's name" $ do-      Right GetMeResponse { user_result = u } <--        getMe token-      (user_first_name u) `shouldBe` "TelegramAPIBot"--  describe "/sendMessage" $ do-    it "should send message" $ do-      Right MessageResponse { message_result = m } <--        sendMessage token (SendMessageRequest chatId "test message" Nothing Nothing Nothing)-      (text m) `shouldBe` (Just "test message")--    it "should return error message" $ do-      Left FailureResponse { responseStatus = Status { statusMessage = msg } } <--        sendMessage token (SendMessageRequest "" "test message" Nothing Nothing Nothing)-      msg `shouldBe` "Bad Request"--    it "should send message markdown" $ do-      Right MessageResponse { message_result = m } <--        sendMessage token (SendMessageRequest chatId "text *bold* _italic_ [github](github.com/klappvisor/telegram-api)" (Just Markdown) Nothing Nothing)-      (text m) `shouldBe` (Just "text bold italic github")--  describe "/forwardMessage" $ do-    it "should forward message" $ do-      Left FailureResponse { responseStatus = Status { statusMessage = msg } } <--        forwardMessage token (ForwardMessageRequest chatId chatId 123)-      msg `shouldBe` "Bad Request"--  describe "/sendPhoto" $ do-    it "should return error message" $ do-      Left FailureResponse { responseStatus = Status { statusMessage = msg } } <--        sendPhoto token (SendPhotoRequest "" "photo_id" (Just "photo caption") Nothing)-      msg `shouldBe` "Bad Request"-    it "should send photo" $ do-      Right MessageResponse { message_result = Message { caption = Just cpt } } <--        sendPhoto token (SendPhotoRequest chatId "AgADBAADv6cxGybVMgABtZ_EOpBSdxYD5xwZAAQ4ElUVMAsbbBqFAAIC" (Just "photo caption") Nothing)-      cpt `shouldBe` "photo caption"--  describe "/sendAudio" $ do-    it "should return error message" $ do-      Left FailureResponse { responseStatus = Status { statusMessage = msg } } <--        sendAudio token (SendAudioRequest "" "audio_id" Nothing (Just "performer") (Just "title") Nothing)-      msg `shouldBe` "Bad Request"---         it "should send audio" $ do---           Right MessageResponse { message_result = Message { audio = Just Audio { audio_title = Just title } } } <----             sendAudio token (SendAudioRequest chatId "audio_id" Nothing (Just "performer") (Just "my title 1") Nothing)---           title `shouldBe` "my title 1"--  describe "/sendSticker" $ do-    it "should send sticker" $ do-      Right MessageResponse { message_result = Message { sticker = Just sticker } } <--        sendSticker token (SendStickerRequest chatId "BQADAgADGgADkWgMAAGXlYGBiM_d2wI" Nothing)-      (sticker_file_id sticker) `shouldBe` "BQADAgADGgADkWgMAAGXlYGBiM_d2wI"--  describe "/sendLocation" $ do-    it "should send location" $ do-      Right MessageResponse { message_result = Message { location = Just loc } } <--        sendLocation token (SendLocationRequest chatId 52.38 4.9 Nothing)-      (latitude loc) `shouldSatisfy` (liftM2 (&&) (> 52) (< 52.4))-      (longitude loc) `shouldSatisfy` (liftM2 (&&) (> 4.89) (< 5))--  describe "/sendChatAction" $ do-    it "should set typing action" $ do-      Right ChatActionResponse { action_result = res} <--        sendChatAction token (SendChatActionRequest chatId Typing)-      res `shouldBe` True-    it "should set find location action" $ do-      Right ChatActionResponse { action_result = res} <--        sendChatAction token (SendChatActionRequest chatId FindLocation)-      res `shouldBe` True-    it "should set upload photo action" $ do-      Right ChatActionResponse { action_result = res} <--        sendChatAction token (SendChatActionRequest chatId UploadPhoto)+{-# LANGUAGE DataKinds         #-}
+{-# LANGUAGE DeriveGeneric     #-}
+{-# LANGUAGE DeriveAnyClass    #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeOperators     #-}
+{-# LANGUAGE TemplateHaskell   #-}
+
+module Main (main) where
+
+import           Control.Monad
+import           Web.Telegram.API.Bot
+import           Test.Hspec
+import           Data.Text (Text)
+import qualified Data.Text as T
+import           Servant.Client
+import           Servant.API
+import           Network.HTTP.Types.Status
+import           System.Environment
+
+main :: IO ()
+main = do
+    [token, chatId] <- getArgs
+    withArgs [] $ hspec (spec (Token (T.pack token)) (T.pack chatId))
+
+spec :: Token -> Text -> Spec
+spec token chatId = do
+  describe "/getMe" $ do
+    it "responds with correct bot's name" $ do
+      Right GetMeResponse { user_result = u } <-
+        getMe token
+      (user_first_name u) `shouldBe` "TelegramAPIBot"
+
+  describe "/sendMessage" $ do
+    it "should send message" $ do
+      Right MessageResponse { message_result = m } <-
+        sendMessage token (SendMessageRequest chatId "test message" Nothing Nothing Nothing)
+      (text m) `shouldBe` (Just "test message")
+
+    it "should return error message" $ do
+      Left FailureResponse { responseStatus = Status { statusMessage = msg } } <-
+        sendMessage token (SendMessageRequest "" "test message" Nothing Nothing Nothing)
+      msg `shouldBe` "Bad Request"
+
+    it "should send message markdown" $ do
+      Right MessageResponse { message_result = m } <-
+        sendMessage token (SendMessageRequest chatId "text *bold* _italic_ [github](github.com/klappvisor/telegram-api)" (Just Markdown) Nothing Nothing)
+      (text m) `shouldBe` (Just "text bold italic github")
+
+  describe "/forwardMessage" $ do
+    it "should forward message" $ do
+      Left FailureResponse { responseStatus = Status { statusMessage = msg } } <-
+        forwardMessage token (ForwardMessageRequest chatId chatId 123)
+      msg `shouldBe` "Bad Request"
+
+  describe "/sendPhoto" $ do
+    it "should return error message" $ do
+      Left FailureResponse { responseStatus = Status { statusMessage = msg } } <-
+        sendPhoto token (SendPhotoRequest "" "photo_id" (Just "photo caption") Nothing)
+      msg `shouldBe` "Bad Request"
+    it "should send photo" $ do
+      Right MessageResponse { message_result = Message { caption = Just cpt } } <-
+        sendPhoto token (SendPhotoRequest chatId "AgADBAADv6cxGybVMgABtZ_EOpBSdxYD5xwZAAQ4ElUVMAsbbBqFAAIC" (Just "photo caption") Nothing)
+      cpt `shouldBe` "photo caption"
+
+  describe "/sendAudio" $ do
+    it "should return error message" $ do
+      Left FailureResponse { responseStatus = Status { statusMessage = msg } } <-
+        sendAudio token (SendAudioRequest "" "audio_id" Nothing (Just "performer") (Just "title") Nothing)
+      msg `shouldBe` "Bad Request"
+--         it "should send audio" $ do
+--           Right MessageResponse { message_result = Message { audio = Just Audio { audio_title = Just title } } } <-
+--             sendAudio token (SendAudioRequest chatId "audio_id" Nothing (Just "performer") (Just "my title 1") Nothing)
+--           title `shouldBe` "my title 1"
+
+  describe "/sendSticker" $ do
+    it "should send sticker" $ do
+      Right MessageResponse { message_result = Message { sticker = Just sticker } } <-
+        sendSticker token (SendStickerRequest chatId "BQADAgADGgADkWgMAAGXlYGBiM_d2wI" Nothing)
+      (sticker_file_id sticker) `shouldBe` "BQADAgADGgADkWgMAAGXlYGBiM_d2wI"
+
+  describe "/sendLocation" $ do
+    it "should send location" $ do
+      Right MessageResponse { message_result = Message { location = Just loc } } <-
+        sendLocation token (SendLocationRequest chatId 52.38 4.9 Nothing)
+      (latitude loc) `shouldSatisfy` (liftM2 (&&) (> 52) (< 52.4))
+      (longitude loc) `shouldSatisfy` (liftM2 (&&) (> 4.89) (< 5))
+
+  describe "/sendChatAction" $ do
+    it "should set typing action" $ do
+      Right ChatActionResponse { action_result = res} <-
+        sendChatAction token (SendChatActionRequest chatId Typing)
+      res `shouldBe` True
+    it "should set find location action" $ do
+      Right ChatActionResponse { action_result = res} <-
+        sendChatAction token (SendChatActionRequest chatId FindLocation)
+      res `shouldBe` True
+    it "should set upload photo action" $ do
+      Right ChatActionResponse { action_result = res} <-
+        sendChatAction token (SendChatActionRequest chatId UploadPhoto)
+      res `shouldBe` True
+
+  describe "/getUpdates" $ do
+    it "should get all messages" $ do
+      Right UpdatesResponse { update_result = updates} <-
+        getUpdates token Nothing Nothing Nothing
+      (length updates) `shouldSatisfy` (> 0)
+      ((message_id . message) (head updates)) `shouldSatisfy` (> 0)
+
+  describe "/getFile" $ do
+    it "should get file" $ do
+      Right FileResponse { file_result = file } <-
+        getFile token "AAQEABMXDZEwAARC0Kj3twkzNcMkAAIC"
+      (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"
+      msg `shouldBe` "Bad Request"
+
+  describe "/getUserProfilePhotos" $ do
+    it "should get user profile photos" $ do
+      Right UserProfilePhotosResponse { photos_result = photos } <-
+        getUserProfilePhotos token (read (T.unpack chatId)) Nothing Nothing
+      (total_count photos) `shouldSatisfy` (> 0)
+
+  describe "/setWebhook" $ do
+    it "should set webhook" $ do
+      Right SetWebhookResponse { webhook_result = res } <-
+        setWebhook token (Just "https://example.com/secret_token")
+      res `shouldBe` True
+
+    it "should remove webhook" $ do
+      Right SetWebhookResponse { webhook_result = res } <-
+        setWebhook token Nothing
       res `shouldBe` True--  describe "/getUpdates" $ do-    it "should get all messages" $ do-      Right UpdatesResponse { update_result = updates} <--        getUpdates token Nothing Nothing Nothing-      (length updates) `shouldSatisfy` (> 0)-      ((message_id . message) (head updates)) `shouldSatisfy` (> 0)--  describe "/getFile" $ do-    it "should get file" $ do-      Right FileResponse { file_result = file } <--        getFile token "AAQEABMXDZEwAARC0Kj3twkzNcMkAAIC"-      (file_path file) `shouldBe` (Just "thumb/file_2")--    it "should return error" $ do-      Left FailureResponse { responseStatus = Status { statusMessage = msg } } <--        getFile token "AAQEABMXDZEwAARC0Kj3twkzNcMkAmm"-      msg `shouldBe` "Bad Request"--  describe "/getUserProfilePhotos" $ do-    it "should get user profile photos" $ do-      Right UserProfilePhotosResponse { photos_result = photos } <--        getUserProfilePhotos token (read (T.unpack chatId)) Nothing Nothing-      (total_count photos) `shouldSatisfy` (> 0)