marvin-0.2.0: src/Marvin/Adapter/Telegram/Poll.hs
{-|
Module : $Header$
Description : Adapter for communicating with Telegram via its http poll API.
Copyright : (c) Justus Adam, 2017
License : BSD3
Maintainer : dev@justus.science
Stability : experimental
Portability : POSIX
=== Caveats:
'resolveUser' and 'resolveChannel' resolving are not yet supported in this adapter and always returns 'Nothing'. See <https://github.com/JustusAdam/marvin/issues/10 #10>.
-}
{-# LANGUAGE CPP #-}
module Marvin.Adapter.Telegram.Poll
( TelegramAdapter, Poll
, TelegramChat(..), ChatType(..)
, TelegramUser(..)
, MkTelegram
, HasUsername(username), HasLastName(lastName), HasId_(id_), HasFirstName(firstName), HasType_(type_)
) where
import Control.Concurrent.Chan.Lifted
import Control.Concurrent.Lifted
import Control.Lens
import Control.Monad
import Control.Monad.Logger
import Data.Aeson hiding (Error, Success)
import Data.Aeson.Types hiding (Error, Success)
import Data.IORef.Lifted
import Marvin.Adapter
import Marvin.Adapter.Telegram.Common
import Marvin.Interpolate.Text
import Network.HTTP.Client (managerResponseTimeout)
import Network.HTTP.Client.TLS (tlsManagerSettings)
import Network.Wreq
#if MIN_VERSION_http_client(0,5,0)
import Network.HTTP.Client (responseTimeoutMicro)
#else
responseTimeoutMicro = Just
#endif
data UpdateWithId = UpdateWithId {updateId :: Integer, updateContent :: TelegramUpdate Poll }
instance FromJSON UpdateWithId where
parseJSON = withObject "expected object" $ \o -> UpdateWithId <$> o .: "update_id" <*> parseJSON (Object o)
pollEventGetter :: Chan (TelegramUpdate Poll) -> AdapterM (TelegramAdapter Poll) ()
pollEventGetter msgChan = do
idRef <- newIORef Nothing
forever $ do
timeout <- lookupFromAdapterConfig "polling-timeout" >>= readTimeout
let defParams = ["timeout" := (timeout :: Int) ]
nextId <- readIORef idRef
let pollSettings = defaults & manager . _Left .~ tlsManagerSettings { managerResponseTimeout = responseTimeoutMicro ((timeout + 3) * 1000)}
response <- execAPIMethodWith pollSettings parseJSON "getUpdates" $ maybe defParams ((:defParams) . ("offset" :=)) nextId
case response of
Left err -> do
logErrorN $(isT "Unable to parse json: #{err}")
threadDelay 30000
Right (Error code desc) -> do
logErrorN $(isT "Sending message failed with #{code}: #{desc}")
threadDelay 30000
Right Success {result=[]} -> return ()
Right Success {result=updates} -> do
writeIORef idRef $ Just $ succ $ maximum $ map updateId updates
logDebugN "Writing messages"
writeList2Chan msgChan $ map updateContent updates
where
defaultTimeout = 120
readTimeout Nothing = return defaultTimeout
readTimeout (Just n)
| n < 0 = do
logErrorN $(isT "Telegram adapter poll timeout must be positive, was #{n} (using default timeout instead)")
return defaultTimeout
| otherwise = return n
-- | Use the telegram API by fetching updates via HTTP
data Poll
instance MkTelegram Poll where
mkAdapterId = "telegram-poll"
mkEventGetter = pollEventGetter