packages feed

dbmonitor-0.1.0: src/Monitor/Telegram.hs

{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ImplicitParams #-}
module Monitor.Telegram where

import Data.ByteString (ByteString)
import Data.Text (pack, Text)
import Data.Text.Encoding (decodeUtf8)

import Telegram.Bot.API.MakingRequests
import Telegram.Bot.API.Methods
import Telegram.Bot.API.Types
import Telegram.Bot.Simple.BotApp (getEnvToken)

import Monitor.DataModel

standardRequest :: SomeChatId -> Text -> SendMessageRequest
standardRequest chan txt = SendMessageRequest
  { sendMessageChatId                   = chan
  , sendMessageText                     = txt
  , sendMessageParseMode                = Just Markdown
  , sendMessageDisableWebPagePreview    = Just True
  , sendMessageDisableNotification      = Just False
  , sendMessageReplyToMessageId         = Nothing
  , sendMessageReplyMarkup              = Nothing
  , sendMessageEntities                 = Nothing
  , sendMessageProtectContent           = Nothing
  , sendMessageAllowSendingWithoutReply = Nothing
  }

postAlert :: SendMessageRequest -> Monitor ()
postAlert msg = asks telegramTokenVar >>= \tgvar -> liftIO $ do
  token <- getEnvToken tgvar
  resp <- defaultRunBot token (sendMessage msg)
  print resp

broadcast :: (SomeChatId -> SendMessageRequest) -> Monitor ()
broadcast f = do
  chans <- asks channels
  mapM_ (postAlert . f. SomeChatId . ChatId) chans

deathNote :: FilePath -> SomeChatId -> SendMessageRequest
deathNote dir chan = standardRequest chan msg
  where
    msg = "*Monitor at " <> (pack dir) <> " has stopped by deleting or moving it's working directory*"

alertThreadDeath :: (?mutex :: Mutexes) => Monitor ()
alertThreadDeath = do
  dir <- asks databaseDirectory
  broadcast $ deathNote dir
  logMessage ("Death alert sent for monitor at " <> dir)

connectionErrorMessage :: String -> FilePath -> SomeChatId -> SendMessageRequest
connectionErrorMessage err dir chan = standardRequest chan msg
  where
    msg = "*Cannot connect to database at " <> (pack dir) <> ".* \n\
          \It may indicate cluster restart, check all applications.\n\
          \_Error message_: " <> (pack err)

alertConnectionError :: (?mutex :: Mutexes) => String -> Monitor ()
alertConnectionError err = do
  dir <- asks databaseDirectory
  broadcast $ connectionErrorMessage err dir
  logMessage ("Database connection problem alert sent for monitor at " <> dir)

queryErrorMessage :: FilePath -> String -> ByteString -> SomeChatId -> SendMessageRequest
queryErrorMessage path err sql chan = standardRequest chan msg
  where
    msg = "*Query error while executing check `"  <> (pack path) <> "`.* \n\
          \*Error message: *\n```" <> (pack err) <> "```\n\
          \*SQL text*: ```\n" <> (decodeUtf8 sql) <> "```\n\
          \It means incorrect assertion (parse errors are treated as 'not null') or error in query."

alertQueryError :: (?mutex :: Mutexes) => FilePath -> String -> ByteString -> Monitor ()
alertQueryError path err sql = do
  broadcast $ queryErrorMessage path err sql
  logMessage ("Query error alert sent for " <> path)

assertionMessage :: FilePath -> Assertion -> ByteString -> String -> SomeChatId -> SendMessageRequest
assertionMessage path assertion sql desc chan = standardRequest chan msg
  where
    msg = "*Assertion failed*:\nCheck `" <> (pack path) <> "`. \n\n\
          \*Assertion: *" <> (pack (show assertion)) <> "\n\n\
          \*SQL text*: ```\n" <> (decodeUtf8 sql) <> "```\n\
          \_Check description_:\n" <> (pack desc)

alertFailedAssertion :: (?mutex :: Mutexes) => FilePath -> PureJob -> Monitor ()
alertFailedAssertion path PureJob{..} = do
  broadcast (assertionMessage path pureJobAssertion pureJobSQL pureJobDescription)
  logMessage ("Failed assertion alert sent for " <> path)