never executed always true always false
    1 module PureClaw.Channels.Telegram
    2   ( -- * Telegram channel
    3     TelegramChannel (..)
    4   , TelegramConfig (..)
    5   , mkTelegramChannel
    6     -- * Message parsing
    7   , parseTelegramUpdate
    8   , TelegramUpdate (..)
    9   , TelegramMessage (..)
   10   , TelegramChat (..)
   11   , TelegramUser (..)
   12   ) where
   13 
   14 import Control.Concurrent.STM
   15 import Data.Aeson
   16 import Data.Text (Text)
   17 import Data.Text qualified as T
   18 
   19 import PureClaw.Channels.Class
   20 import PureClaw.Core.Errors
   21 import PureClaw.Core.Types
   22 import PureClaw.Handles.Channel
   23 import PureClaw.Handles.Log
   24 import PureClaw.Handles.Network
   25 
   26 -- | Configuration for Telegram channel.
   27 data TelegramConfig = TelegramConfig
   28   { _tc_botToken :: Text
   29   , _tc_apiBase  :: Text
   30   }
   31   deriving stock (Show, Eq)
   32 
   33 -- | A Telegram channel backed by a message queue. Updates are pushed
   34 -- into the queue (e.g. from a webhook endpoint) and the agent loop
   35 -- pulls them out via 'receive'. Responses are sent via the Telegram
   36 -- Bot API using the provided 'NetworkHandle'.
   37 data TelegramChannel = TelegramChannel
   38   { _tch_config  :: TelegramConfig
   39   , _tch_inbox   :: TQueue TelegramUpdate
   40   , _tch_network :: NetworkHandle
   41   , _tch_log     :: LogHandle
   42   }
   43 
   44 -- | Create a Telegram channel with an empty inbox.
   45 mkTelegramChannel :: TelegramConfig -> NetworkHandle -> LogHandle -> IO TelegramChannel
   46 mkTelegramChannel config nh lh = do
   47   inbox <- newTQueueIO
   48   pure TelegramChannel
   49     { _tch_config  = config
   50     , _tch_inbox   = inbox
   51     , _tch_network = nh
   52     , _tch_log     = lh
   53     }
   54 
   55 instance Channel TelegramChannel where
   56   toHandle tc = ChannelHandle
   57     { _ch_receive   = receiveUpdate tc
   58     , _ch_send      = sendMessage tc
   59     , _ch_sendError = sendTelegramError tc
   60     }
   61 
   62 -- | Block until a Telegram update arrives in the queue.
   63 receiveUpdate :: TelegramChannel -> IO IncomingMessage
   64 receiveUpdate tc = do
   65   update <- atomically $ readTQueue (_tch_inbox tc)
   66   let msg = _tu_message update
   67       userId = T.pack (show (_tu_id (_tm_from msg)))
   68       content = _tm_text msg
   69   pure IncomingMessage
   70     { _im_userId  = UserId userId
   71     , _im_content = content
   72     }
   73 
   74 -- | Send a message to the chat that the last update came from.
   75 -- In a full implementation this would POST to the Telegram sendMessage API.
   76 -- For now, logs the outgoing message.
   77 sendMessage :: TelegramChannel -> OutgoingMessage -> IO ()
   78 sendMessage tc msg =
   79   _lh_logInfo (_tch_log tc) $ "Telegram send: " <> _om_content msg
   80 
   81 -- | Send an error to the Telegram chat.
   82 sendTelegramError :: TelegramChannel -> PublicError -> IO ()
   83 sendTelegramError tc err =
   84   _lh_logWarn (_tch_log tc) $ "Telegram error: " <> T.pack (show err)
   85 
   86 -- | A Telegram Update object (simplified).
   87 data TelegramUpdate = TelegramUpdate
   88   { _tu_updateId :: Int
   89   , _tu_message  :: TelegramMessage
   90   }
   91   deriving stock (Show, Eq)
   92 
   93 -- | A Telegram Message object (simplified).
   94 data TelegramMessage = TelegramMessage
   95   { _tm_messageId :: Int
   96   , _tm_from      :: TelegramUser
   97   , _tm_chat      :: TelegramChat
   98   , _tm_text      :: Text
   99   }
  100   deriving stock (Show, Eq)
  101 
  102 -- | A Telegram Chat object (simplified).
  103 data TelegramChat = TelegramChat
  104   { _tcht_id   :: Int
  105   , _tcht_type :: Text
  106   }
  107   deriving stock (Show, Eq)
  108 
  109 -- | A Telegram User object (simplified).
  110 data TelegramUser = TelegramUser
  111   { _tu_id        :: Int
  112   , _tu_firstName :: Text
  113   }
  114   deriving stock (Show, Eq)
  115 
  116 instance FromJSON TelegramUpdate where
  117   parseJSON = withObject "TelegramUpdate" $ \o ->
  118     TelegramUpdate <$> o .: "update_id" <*> o .: "message"
  119 
  120 instance FromJSON TelegramMessage where
  121   parseJSON = withObject "TelegramMessage" $ \o ->
  122     TelegramMessage <$> o .: "message_id" <*> o .: "from" <*> o .: "chat" <*> o .: "text"
  123 
  124 instance FromJSON TelegramChat where
  125   parseJSON = withObject "TelegramChat" $ \o ->
  126     TelegramChat <$> o .: "id" <*> o .: "type"
  127 
  128 instance FromJSON TelegramUser where
  129   parseJSON = withObject "TelegramUser" $ \o ->
  130     TelegramUser <$> o .: "id" <*> o .: "first_name"
  131 
  132 -- | Parse a JSON value as a Telegram update.
  133 parseTelegramUpdate :: Value -> Either String TelegramUpdate
  134 parseTelegramUpdate v = case fromJSON v of
  135   Error err   -> Left err
  136   Success upd -> Right upd