{-# LANGUAGE RecordWildCards #-}
module Telegram.Bot.Simple.BotApp (
  BotApp(..),
  BotJob(..),
  WebhookConfig(..),

  startBot,
  startBot_,

  startBotAsync,
  startBotAsync_,

  startBotWebhook,
  startBotWebhook_,

  getEnvToken,
) where

import           Control.Concurrent                  (forkIO)
import           Control.Monad                       (void)
import           Data.String                         (fromString)
import           Servant.Client
import           System.Environment                  (getEnv)

import           Control.Exception                   (finally)
import           Data.Either                         (isLeft)
import           Network.Wai.Handler.Warp
import           Network.Wai.Handler.WarpTLS
import qualified Telegram.Bot.API                    as Telegram
import           Telegram.Bot.API.Webhook            (SetWebhookRequest,
                                                      deleteWebhook,
                                                      setUpWebhook, webhookApp)
import           Telegram.Bot.Simple.BotApp.Internal

-- | Start bot with asynchronous polling.
-- The result is a function that allows you to send actions
-- directly to the bot.
startBotAsync :: BotApp model action -> ClientEnv -> IO (action -> IO ())
startBotAsync :: forall model action.
BotApp model action -> ClientEnv -> IO (action -> IO ())
startBotAsync BotApp model action
bot ClientEnv
env = do
  BotEnv model action
botEnv <- forall model action.
BotApp model action -> ClientEnv -> IO (BotEnv model action)
startBotEnv BotApp model action
bot ClientEnv
env
  forall {a}. ClientM a -> IO ()
fork_ forall a b. (a -> b) -> a -> b
$ forall model action.
BotApp model action -> BotEnv model action -> ClientM ()
startBotPolling BotApp model action
bot BotEnv model action
botEnv
  forall (m :: * -> *) a. Monad m => a -> m a
return (forall model action.
BotEnv model action -> Maybe Update -> Maybe action -> IO ()
issueAction BotEnv model action
botEnv forall a. Maybe a
Nothing forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> Maybe a
Just)
  where
    fork_ :: ClientM a -> IO ()
fork_ = forall (f :: * -> *) a. Functor f => f a -> f ()
void forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO () -> IO ThreadId
forkIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a. Functor f => f a -> f ()
void forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a. ClientM a -> ClientEnv -> IO (Either ClientError a)
runClientM ClientEnv
env

-- | Like 'startBotAsync', but ignores result.
startBotAsync_ :: BotApp model action -> ClientEnv -> IO ()
startBotAsync_ :: forall model action. BotApp model action -> ClientEnv -> IO ()
startBotAsync_ BotApp model action
bot ClientEnv
env = forall (f :: * -> *) a. Functor f => f a -> f ()
void (forall model action.
BotApp model action -> ClientEnv -> IO (action -> IO ())
startBotAsync BotApp model action
bot ClientEnv
env)

-- | Start bot with update polling in the main thread.
startBot :: BotApp model action -> ClientEnv -> IO (Either ClientError ())
startBot :: forall model action.
BotApp model action -> ClientEnv -> IO (Either ClientError ())
startBot BotApp model action
bot ClientEnv
env = do
  BotEnv model action
botEnv <- forall model action.
BotApp model action -> ClientEnv -> IO (BotEnv model action)
startBotEnv BotApp model action
bot ClientEnv
env
  forall a. ClientM a -> ClientEnv -> IO (Either ClientError a)
runClientM (forall model action.
BotApp model action -> BotEnv model action -> ClientM ()
startBotPolling BotApp model action
bot BotEnv model action
botEnv) ClientEnv
env

-- | Like 'startBot', but ignores result.
startBot_ :: BotApp model action -> ClientEnv -> IO ()
startBot_ :: forall model action. BotApp model action -> ClientEnv -> IO ()
startBot_ BotApp model action
bot = forall (f :: * -> *) a. Functor f => f a -> f ()
void forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall model action.
BotApp model action -> ClientEnv -> IO (Either ClientError ())
startBot BotApp model action
bot

data WebhookConfig = WebhookConfig
  { WebhookConfig -> TLSSettings
webhookConfigTlsSettings       :: TLSSettings,
    WebhookConfig -> Settings
webhookConfigTlsWarpSettings   :: Settings,
    WebhookConfig -> SetWebhookRequest
webhookConfigSetWebhookRequest :: SetWebhookRequest
  }

-- | Start bot with webhook on update in the main thread.
-- Port must be one of 443, 80, 88, 8443
-- certPath must be provided if using self signed certificate.
startBotWebhook :: BotApp model action -> WebhookConfig -> ClientEnv -> IO (Either ClientError ())
startBotWebhook :: forall model action.
BotApp model action
-> WebhookConfig -> ClientEnv -> IO (Either ClientError ())
startBotWebhook BotApp model action
bot (WebhookConfig{SetWebhookRequest
Settings
TLSSettings
webhookConfigSetWebhookRequest :: SetWebhookRequest
webhookConfigTlsWarpSettings :: Settings
webhookConfigTlsSettings :: TLSSettings
webhookConfigSetWebhookRequest :: WebhookConfig -> SetWebhookRequest
webhookConfigTlsWarpSettings :: WebhookConfig -> Settings
webhookConfigTlsSettings :: WebhookConfig -> TLSSettings
..}) ClientEnv
env = do
  BotEnv model action
botEnv <- forall model action.
BotApp model action -> ClientEnv -> IO (BotEnv model action)
startBotEnv BotApp model action
bot ClientEnv
env
  Either ClientError ()
res <- SetWebhookRequest -> ClientEnv -> IO (Either ClientError ())
setUpWebhook SetWebhookRequest
webhookConfigSetWebhookRequest ClientEnv
env
  if forall a b. Either a b -> Bool
isLeft Either ClientError ()
res
    then forall (m :: * -> *) a. Monad m => a -> m a
return Either ClientError ()
res
    else forall a b. b -> Either a b
Right forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TLSSettings -> Settings -> Application -> IO ()
runTLS TLSSettings
webhookConfigTlsSettings Settings
webhookConfigTlsWarpSettings (forall model action.
BotApp model action -> BotEnv model action -> Application
webhookApp BotApp model action
bot BotEnv model action
botEnv)
  forall a b. IO a -> IO b -> IO a
`finally`
    ClientEnv -> IO (Either ClientError ())
deleteWebhook ClientEnv
env


-- | Like 'startBotWebhook', but ignores result.
startBotWebhook_ :: BotApp model action -> WebhookConfig -> ClientEnv -> IO ()
startBotWebhook_ :: forall model action.
BotApp model action -> WebhookConfig -> ClientEnv -> IO ()
startBotWebhook_ BotApp model action
bot WebhookConfig
webhookConfig = forall (f :: * -> *) a. Functor f => f a -> f ()
void forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall model action.
BotApp model action
-> WebhookConfig -> ClientEnv -> IO (Either ClientError ())
startBotWebhook BotApp model action
bot WebhookConfig
webhookConfig

-- | Get a 'Telegram.Token' from environment variable.
--
-- Common use:
--
-- @
-- 'getEnvToken' "TELEGRAM_BOT_TOKEN"
-- @
getEnvToken :: String -> IO Telegram.Token
getEnvToken :: String -> IO Token
getEnvToken String
varName = forall a. IsString a => String -> a
fromString forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO String
getEnv String
varName

startBotEnv :: BotApp model action -> ClientEnv -> IO (BotEnv model action)
startBotEnv :: forall model action.
BotApp model action -> ClientEnv -> IO (BotEnv model action)
startBotEnv BotApp model action
bot ClientEnv
env = do
  BotEnv model action
botEnv <- forall model action.
BotApp model action -> ClientEnv -> IO (BotEnv model action)
defaultBotEnv BotApp model action
bot ClientEnv
env
  [ThreadId]
_jobThreadIds <- forall model action.
BotEnv model action -> [BotJob model action] -> IO [ThreadId]
scheduleBotJobs BotEnv model action
botEnv (forall model action. BotApp model action -> [BotJob model action]
botJobs BotApp model action
bot)
  ThreadId
_actionsThreadId <- forall model action.
BotApp model action -> BotEnv model action -> IO ThreadId
processActionsIndefinitely BotApp model action
bot BotEnv model action
botEnv
  forall (m :: * -> *) a. Monad m => a -> m a
return BotEnv model action
botEnv