{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
module Lambdabot.Plugin.Telegram.Bot.Generic where
import Data.Char
import Data.Text (Text)
import Data.Proxy
import qualified Data.Text as Text
import GHC.Generics
import GHC.TypeLits
import Lambdabot.Plugin.Telegram.Shared
-- | Helper type class used to derive 'FromCommand' via DeriveGeneric extension.
class GFromCommand command where
gGetMessage :: command proxy -> Msg
gGetPrefix :: command proxy -> Text
-- Empty data decl
instance GFromCommand V1 where
gGetMessage x = case x of { }
gGetPrefix x = case x of { }
instance (FromCommand c) => GFromCommand (K1 i c) where
gGetMessage (K1 x) = getMessage x
gGetPrefix (K1 x) = getPrefix x
instance (Constructor t, GFromCommand f) => GFromCommand (M1 C t f) where
gGetMessage (M1 x) = gGetMessage x
gGetPrefix m@(M1 _) = Text.cons '@' $ toKebabCase $ Text.pack $ conName m
instance (GFromCommand f) => GFromCommand (M1 S t f) where
gGetMessage (M1 x) = gGetMessage x
gGetPrefix (M1 x) = gGetPrefix x
instance (GFromCommand f) => GFromCommand (M1 D t f) where
gGetMessage (M1 x) = gGetMessage x
gGetPrefix (M1 x) = gGetPrefix x
instance (GFromCommand f, GFromCommand g) => GFromCommand (f :+: g) where
gGetMessage (L1 x) = gGetMessage x
gGetMessage (R1 x) = gGetMessage x
gGetPrefix (L1 x) = gGetPrefix x
gGetPrefix (R1 x) = gGetPrefix x
instance (GFromCommand f, GFromCommand g) => GFromCommand (f :*: g) where
gGetMessage (x :*: _y) = gGetMessage x
gGetPrefix (x :*: _y) = gGetPrefix x
class FromCommand command where
getMessage :: command -> Msg
default getMessage :: (Generic command, GFromCommand (Rep command)) => command -> Msg
getMessage x = gGetMessage (from x)
getPrefix :: command -> Text
default getPrefix :: (Generic command, GFromCommand (Rep command)) => command -> Text
getPrefix x = gGetPrefix (from x)
-- | Type class to identify the essence of incoming command and transform it to 'Msg' transport type.
instance FromCommand Msg where
getMessage = id
getPrefix = const ""
-- | Transform incoming telegram command into 'Msg'.
fromCommand :: FromCommand command => command -> Msg
fromCommand cmd = old { msgMessage = getPrefix cmd <> " " <> msgMessage old }
where
old = getMessage cmd
-- ** Helpers
-- | Helper to transform text into kebab case.
toKebabCase :: Text -> Text
toKebabCase txt =
let str = Text.unpack txt
uppers = isUpper <$> str
indices = zip [0..] uppers :: [(Int, Bool)]
onlyUpperIndices = filter (/= 0) $ fmap fst $ filter snd indices
go ix txt' =
let (begin, end) = Text.splitAt ix txt'
in Text.concat [ begin, "-", Text.toLower end ]
in Text.toLower $ foldr go txt onlyUpperIndices
data MaybeWith (modifier :: Modifier) a = MaybeWith a
data Modifier = AtEnd Symbol
instance (KnownSymbol postfix, FromCommand a, modifier ~ 'AtEnd postfix) =>
FromCommand (MaybeWith modifier a) where
getMessage (MaybeWith x) = getMessage x
getPrefix (MaybeWith x) = getPrefix x <> Text.pack (symbolVal (Proxy @postfix))