packages feed

proof-assistant-bot-0.2.2: src/Proof/Assistant/Response.hs

{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE OverloadedStrings #-}
module Proof.Assistant.Response where

import Data.ByteString (ByteString)
import Data.Text.Encoding (decodeUtf8)
import Telegram.Bot.API
  (ChatId, ContentType, MessageId
  , InputFile (..), ParseMode (..), PhotoFile (..)
  , SendAnimationRequest (..), SendMessageRequest (..), SendPhotoRequest (..), SomeChatId (..)
  , defSendAnimation, defSendMessage, defSendPhoto
  )

import Proof.Assistant.Request

-- | Bot response: either text message or picture (filepath and its content type).
data BotResponse
  = TextResponse !ByteString
  | ImageResponse
      { imgResponseCType :: !ContentType
      , imgResponsePath :: !FilePath
      , imgResponeWidth :: !(Maybe Int)
      , imgResponseHeight :: !(Maybe Int)
      }

-- | Response for Telegram.
data InterpreterResponse = InterpreterResponse
  { interpreterResponseTelegramChatId :: !ChatId -- ^ Telegram ChatId (for reply).
  , interpreterResponseTelegramMessageId :: !MessageId -- ^ Telegram MessageId (for reply).
  , interpreterResponseResponse :: !BotResponse -- ^ output data.
  }

data TelegramMessage
  = TgMsg SendMessageRequest
  | TgPng SendPhotoRequest
  | TgGif SendAnimationRequest

-- | Cast 'InterpreterResponse' to either 'SendMessageRequest' or 'SendPhotoRequest'.
-- For text messages:
-- - If first argument is 'True'
-- then it will wrap message in Monospace font and mark it with @MarkdownV2@ parse mode.
-- - Otherwise, text message will be sent.
-- - For all responses from Backends 'True' should be specified.
-- 
-- For images content type should be specified.
toMessageRequest
  :: Bool -> InterpreterResponse -> TelegramMessage
toMessageRequest isMonospace InterpreterResponse{..} = case interpreterResponseResponse of
  TextResponse bytes -> TgMsg $ SendMessageRequest
    { sendMessageChatId                   = SomeChatId interpreterResponseTelegramChatId
    , sendMessageMessageThreadId          = Nothing
    , sendMessageText
        = if isMonospace
          then "```\n" <> decodeUtf8 bytes <> "\n```\n"
          else decodeUtf8 bytes <> "\n"
    , sendMessageParseMode                = if isMonospace then Just MarkdownV2 else Nothing
    , sendMessageEntities                 = Nothing
    , sendMessageReplyParameters          = Nothing
    , sendMessageLinkPreviewOptions       = Nothing
    , sendMessageDisableNotification      = Nothing
    , sendMessageProtectContent           = Nothing
    , sendMessageReplyToMessageId         = Just interpreterResponseTelegramMessageId
    , sendMessageReplyMarkup              = Nothing
    }
  ImageResponse ctype imgPath mwidth mheight -> case ctype of
    "image/png" ->
      let reply = defSendPhoto (SomeChatId interpreterResponseTelegramChatId)
            $ MakePhotoFile $ InputFile imgPath ctype
      in TgPng (reply { sendPhotoReplyToMessageId = Just interpreterResponseTelegramMessageId })
    "image/gif" ->
      let reply = defSendAnimation (SomeChatId interpreterResponseTelegramChatId)
            $ InputFile imgPath "image/gif"
      in TgGif
        (reply { sendAnimationReplyToMessageId = Just interpreterResponseTelegramMessageId
               , sendAnimationWidth = mwidth
               , sendAnimationHeight = mheight
               })

    _ ->
      let reply =
            defSendMessage (SomeChatId interpreterResponseTelegramChatId) "Unsupported request"
      in TgMsg (reply { sendMessageReplyToMessageId = Just interpreterResponseTelegramMessageId })

-- | Cast 'InterpreterRequest' and output data to 'InterpreterResponse'.
makeTelegramResponse :: InterpreterRequest -> BotResponse -> InterpreterResponse
makeTelegramResponse InterpreterRequest{..} response =
  InterpreterResponse
    { interpreterResponseTelegramChatId    = interpreterRequestTelegramChatId
    , interpreterResponseTelegramMessageId = interpreterRequestTelegramMessageId
    , interpreterResponseResponse          = response
    }