Skip to content

Commit

Permalink
Ignore updates that produce parse failures, allowing bots to partiall…
Browse files Browse the repository at this point in the history
…y work after API changes on the side of telegram
  • Loading branch information
s-and-witch committed May 15, 2024
1 parent 02bd350 commit 9c55a4b
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 6 deletions.
16 changes: 13 additions & 3 deletions telegram-bot-api/src/Telegram/Bot/API/GettingUpdates.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{-# LANGUAGE TypeOperators #-}
module Telegram.Bot.API.GettingUpdates where

import Data.Aeson (FromJSON (..), ToJSON (..))
import Data.Aeson (FromJSON (..), ToJSON (..), Value)
import Data.Foldable (asum)
import Data.Proxy
import GHC.Generics (Generic)
Expand Down Expand Up @@ -70,8 +70,10 @@ extractUpdateMessage Update{..} = asum

-- ** 'getUpdates'

type GetUpdates
= "getUpdates" :> ReqBody '[JSON] GetUpdatesRequest :> Get '[JSON] (Response [Update])
type GetUpdatesAs a
= "getUpdates" :> ReqBody '[JSON] GetUpdatesRequest :> Get '[JSON] (Response [a])

type GetUpdates = GetUpdatesAs Update

-- | Use this method to receive incoming updates using long polling.
-- An list of 'Update' objects is returned.
Expand All @@ -82,6 +84,14 @@ type GetUpdates
getUpdates :: GetUpdatesRequest -> ClientM (Response [Update])
getUpdates = client (Proxy @GetUpdates)

-- | More liberal version of `getUpdates` funcion.
--
-- It's useful when you aren't sure that you can
-- parse all the types of updates, so you would recieve
-- a list of Value with updates, that you can parse later.
getUpdatesAsValue :: GetUpdatesRequest -> ClientM (Response [Value])
getUpdatesAsValue = client (Proxy @(GetUpdatesAs Value))

-- | Request parameters for 'getUpdates'.
data GetUpdatesRequest = GetUpdatesRequest
{ getUpdatesOffset :: Maybe UpdateId -- ^ 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.
Expand Down
18 changes: 15 additions & 3 deletions telegram-bot-simple/src/Telegram/Bot/Simple/BotApp/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import qualified System.Cron as Cron

import qualified Telegram.Bot.API as Telegram
import Telegram.Bot.Simple.Eff
import Data.Either (partitionEithers)
import Data.Aeson.Types (parseEither, parseJSON)

-- | A bot application.
data BotApp model action = BotApp
Expand Down Expand Up @@ -141,7 +143,7 @@ startPolling handleUpdate = go Nothing
let inc (Telegram.UpdateId n) = Telegram.UpdateId (n + 1)
offset = fmap inc lastUpdateId
res <-
(Right <$> Telegram.getUpdates
(Right <$> Telegram.getUpdatesAsValue
(Telegram.GetUpdatesRequest offset Nothing (Just 25) Nothing))
`catchError` (pure . Left)

Expand All @@ -150,19 +152,29 @@ startPolling handleUpdate = go Nothing
liftIO (print servantErr)
pure lastUpdateId
Right result -> do
let updates = Telegram.responseResult result
let updateValues = Telegram.responseResult result
(errors, updates) = parseUpdates updateValues
updateIds = map Telegram.updateUpdateId updates
maxUpdateId = maximum (Nothing : map Just updateIds)
mapM_ reportParseError errors
mapM_ handleUpdate updates
pure maxUpdateId
liftIO $ threadDelay 1000000
go nextUpdateId

parseUpdates updates =
partitionEithers (map (parseEither parseJSON) updates)

reportParseError err =
liftIO $ putStrLn $
"Failed to parse an update! Please, make sure you have the latest version of `telegram-bot-api`\
\ library and consider opening an issue if so. Error message: " <> err

-- ** Helpers

-- | Instead of 'forkIO' which hides exceptions,
-- allow users to handle those exceptions separately.
--
--
-- See <https://github.com/fizruk/telegram-bot-simple/issues/159>.
asyncLink :: IO a -> IO (Async a)
asyncLink action = do
Expand Down

0 comments on commit 9c55a4b

Please sign in to comment.