{-# LANGUAGE DeriveFunctor              #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RankNTypes                 #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE GADTs                      #-}
{-# LANGUAGE FlexibleInstances          #-}
module Telegram.Bot.Simple.Eff where

import           Control.Monad.Reader
import           Control.Monad.Writer
import           Data.Bifunctor
import           Servant.Client

import qualified Telegram.Bot.API     as Telegram

-- | Bot handler context.
--
-- The context may include an 'Update' the bot is handling at the moment.
newtype BotM a = BotM { forall a. BotM a -> ReaderT BotContext ClientM a
_runBotM :: ReaderT BotContext ClientM a }
  deriving (forall a b. a -> BotM b -> BotM a
forall a b. (a -> b) -> BotM a -> BotM b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> BotM b -> BotM a
$c<$ :: forall a b. a -> BotM b -> BotM a
fmap :: forall a b. (a -> b) -> BotM a -> BotM b
$cfmap :: forall a b. (a -> b) -> BotM a -> BotM b
Functor, Functor BotM
forall a. a -> BotM a
forall a b. BotM a -> BotM b -> BotM a
forall a b. BotM a -> BotM b -> BotM b
forall a b. BotM (a -> b) -> BotM a -> BotM b
forall a b c. (a -> b -> c) -> BotM a -> BotM b -> BotM c
forall (f :: * -> *).
Functor f
-> (forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: forall a b. BotM a -> BotM b -> BotM a
$c<* :: forall a b. BotM a -> BotM b -> BotM a
*> :: forall a b. BotM a -> BotM b -> BotM b
$c*> :: forall a b. BotM a -> BotM b -> BotM b
liftA2 :: forall a b c. (a -> b -> c) -> BotM a -> BotM b -> BotM c
$cliftA2 :: forall a b c. (a -> b -> c) -> BotM a -> BotM b -> BotM c
<*> :: forall a b. BotM (a -> b) -> BotM a -> BotM b
$c<*> :: forall a b. BotM (a -> b) -> BotM a -> BotM b
pure :: forall a. a -> BotM a
$cpure :: forall a. a -> BotM a
Applicative, Applicative BotM
forall a. a -> BotM a
forall a b. BotM a -> BotM b -> BotM b
forall a b. BotM a -> (a -> BotM b) -> BotM b
forall (m :: * -> *).
Applicative m
-> (forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
return :: forall a. a -> BotM a
$creturn :: forall a. a -> BotM a
>> :: forall a b. BotM a -> BotM b -> BotM b
$c>> :: forall a b. BotM a -> BotM b -> BotM b
>>= :: forall a b. BotM a -> (a -> BotM b) -> BotM b
$c>>= :: forall a b. BotM a -> (a -> BotM b) -> BotM b
Monad, MonadReader BotContext, Monad BotM
forall a. IO a -> BotM a
forall (m :: * -> *).
Monad m -> (forall a. IO a -> m a) -> MonadIO m
liftIO :: forall a. IO a -> BotM a
$cliftIO :: forall a. IO a -> BotM a
MonadIO)

data BotContext = BotContext
  { BotContext -> User
botContextUser   :: Telegram.User
  , BotContext -> Maybe Update
botContextUpdate :: Maybe Telegram.Update
  }

liftClientM :: ClientM a -> BotM a
liftClientM :: forall a. ClientM a -> BotM a
liftClientM = forall a. ReaderT BotContext ClientM a -> BotM a
BotM forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift

runBotM :: BotContext -> BotM a -> ClientM a
runBotM :: forall a. BotContext -> BotM a -> ClientM a
runBotM BotContext
update = forall a b c. (a -> b -> c) -> b -> a -> c
flip forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT BotContext
update forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. BotM a -> ReaderT BotContext ClientM a
_runBotM

newtype Eff action model = Eff { forall action model.
Eff action model -> Writer [BotM (Maybe action)] model
_runEff :: Writer [BotM (Maybe action)] model }
  deriving (forall a b. a -> Eff action b -> Eff action a
forall a b. (a -> b) -> Eff action a -> Eff action b
forall action a b. a -> Eff action b -> Eff action a
forall action a b. (a -> b) -> Eff action a -> Eff action b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Eff action b -> Eff action a
$c<$ :: forall action a b. a -> Eff action b -> Eff action a
fmap :: forall a b. (a -> b) -> Eff action a -> Eff action b
$cfmap :: forall action a b. (a -> b) -> Eff action a -> Eff action b
Functor, forall action. Functor (Eff action)
forall a. a -> Eff action a
forall action a. a -> Eff action a
forall a b. Eff action a -> Eff action b -> Eff action a
forall a b. Eff action a -> Eff action b -> Eff action b
forall a b. Eff action (a -> b) -> Eff action a -> Eff action b
forall action a b. Eff action a -> Eff action b -> Eff action a
forall action a b. Eff action a -> Eff action b -> Eff action b
forall action a b.
Eff action (a -> b) -> Eff action a -> Eff action b
forall a b c.
(a -> b -> c) -> Eff action a -> Eff action b -> Eff action c
forall action a b c.
(a -> b -> c) -> Eff action a -> Eff action b -> Eff action c
forall (f :: * -> *).
Functor f
-> (forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: forall a b. Eff action a -> Eff action b -> Eff action a
$c<* :: forall action a b. Eff action a -> Eff action b -> Eff action a
*> :: forall a b. Eff action a -> Eff action b -> Eff action b
$c*> :: forall action a b. Eff action a -> Eff action b -> Eff action b
liftA2 :: forall a b c.
(a -> b -> c) -> Eff action a -> Eff action b -> Eff action c
$cliftA2 :: forall action a b c.
(a -> b -> c) -> Eff action a -> Eff action b -> Eff action c
<*> :: forall a b. Eff action (a -> b) -> Eff action a -> Eff action b
$c<*> :: forall action a b.
Eff action (a -> b) -> Eff action a -> Eff action b
pure :: forall a. a -> Eff action a
$cpure :: forall action a. a -> Eff action a
Applicative, forall action. Applicative (Eff action)
forall a. a -> Eff action a
forall action a. a -> Eff action a
forall a b. Eff action a -> Eff action b -> Eff action b
forall a b. Eff action a -> (a -> Eff action b) -> Eff action b
forall action a b. Eff action a -> Eff action b -> Eff action b
forall action a b.
Eff action a -> (a -> Eff action b) -> Eff action b
forall (m :: * -> *).
Applicative m
-> (forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
return :: forall a. a -> Eff action a
$creturn :: forall action a. a -> Eff action a
>> :: forall a b. Eff action a -> Eff action b -> Eff action b
$c>> :: forall action a b. Eff action a -> Eff action b -> Eff action b
>>= :: forall a b. Eff action a -> (a -> Eff action b) -> Eff action b
$c>>= :: forall action a b.
Eff action a -> (a -> Eff action b) -> Eff action b
Monad)

-- | The idea behind following type class is
--   to allow you defining the type 'ret' you want to return from 'BotM' action.
--   You can create your own return-types via new instances.
--   Here 'action' is a 'botAction'
--   type, that will be used further in 'botHandler' function.
--   If you don't want to return action use 'Nothing' instead.
--
--   See "Telegram.Bot.Simple.Instances" for more commonly useful instances.
--   - @GetAction a a@ - for simple making finite automata of
--   BotM actions. (For example you can log every update
--   and then return new 'action' to answer at message/send sticker/etc)
--   - @GetAction () a@ - to use @pure ()@ instead of dealing with @Nothing@.
--   - @GetAction Text a@ - to add some sugar over the 'replyText' function.
--   'OverloadedStrings' breaks type inference,
--   so we advise to use @replyText \"message\"@
--   instead of @pure \@_ \@Text \"message\"@.
class GetAction return action where
  getNextAction :: BotM return -> BotM (Maybe action)

instance Bifunctor Eff where
  bimap :: forall a b c d. (a -> b) -> (c -> d) -> Eff a c -> Eff b d
bimap a -> b
f c -> d
g = forall action model.
Writer [BotM (Maybe action)] model -> Eff action model
Eff forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a w b w'. ((a, w) -> (b, w')) -> Writer w a -> Writer w' b
mapWriter (forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap c -> d
g (forall a b. (a -> b) -> [a] -> [b]
map forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a -> b) -> a -> b
$ a -> b
f)) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall action model.
Eff action model -> Writer [BotM (Maybe action)] model
_runEff

runEff :: Eff action model -> (model, [BotM (Maybe action)])
runEff :: forall action model.
Eff action model -> (model, [BotM (Maybe action)])
runEff = forall w a. Writer w a -> (a, w)
runWriter forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall action model.
Eff action model -> Writer [BotM (Maybe action)] model
_runEff

eff :: GetAction a b => BotM a -> Eff b ()
eff :: forall a b. GetAction a b => BotM a -> Eff b ()
eff BotM a
e = forall action model.
Writer [BotM (Maybe action)] model -> Eff action model
Eff (forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [forall return action.
GetAction return action =>
BotM return -> BotM (Maybe action)
getNextAction BotM a
e])

withEffect :: GetAction a action => BotM a -> model -> Eff action model
withEffect :: forall a action model.
GetAction a action =>
BotM a -> model -> Eff action model
withEffect BotM a
effect model
model = forall a b. GetAction a b => BotM a -> Eff b ()
eff BotM a
effect forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall (f :: * -> *) a. Applicative f => a -> f a
pure model
model

(<#) :: GetAction a action => model -> BotM a -> Eff action model
<# :: forall a action model.
GetAction a action =>
model -> BotM a -> Eff action model
(<#) = forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a action model.
GetAction a action =>
BotM a -> model -> Eff action model
withEffect

-- | Set a specific 'Telegram.Update' in a 'BotM' context.
setBotMUpdate :: Maybe Telegram.Update -> BotM a -> BotM a
setBotMUpdate :: forall a. Maybe Update -> BotM a -> BotM a
setBotMUpdate Maybe Update
update (BotM ReaderT BotContext ClientM a
m) =  forall a. ReaderT BotContext ClientM a -> BotM a
BotM (forall r (m :: * -> *) a. MonadReader r m => (r -> r) -> m a -> m a
local BotContext -> BotContext
f ReaderT BotContext ClientM a
m)
  where
    f :: BotContext -> BotContext
f BotContext
botContext = BotContext
botContext { botContextUpdate :: Maybe Update
botContextUpdate = Maybe Update
update }

-- | Set a specific 'Telegram.Update' in every effect of 'Eff' context.
setEffUpdate :: Maybe Telegram.Update -> Eff action model -> Eff action model
setEffUpdate :: forall action model.
Maybe Update -> Eff action model -> Eff action model
setEffUpdate Maybe Update
update (Eff Writer [BotM (Maybe action)] model
m) = forall action model.
Writer [BotM (Maybe action)] model -> Eff action model
Eff (forall w (m :: * -> *) a. MonadWriter w m => (w -> w) -> m a -> m a
censor (forall a b. (a -> b) -> [a] -> [b]
map (forall a. Maybe Update -> BotM a -> BotM a
setBotMUpdate Maybe Update
update)) Writer [BotM (Maybe action)] model
m)