在 Servant 的 API 端点内发出请求

Making a request inside an API endpoint in Servant

我正在尝试使用 telegram-api. I haven't had an issues with that so far since I could read the tests to understand how things worked, but I've had a lot of trouble when it comes to building a webhook endpoint with Servant 构建 Telegram 机器人。总体思路是,当我从 webhook 收到 Update 时,我会发回回复。

问题与我的 postWebhook 代码有关,它期望收到 Message 但收到的却是 IO Message。我认为这是因为 Servant 不希望我在该函数内发出请求,因为我有类型 EitherT ServantError IO (IO Message)(部分由 BotHandler 应用),而实际上它应该是 EitherT ServantError IO Message

我还在学习 Haskell 但我知道我必须以某种方式从 IO monad 中获取消息?将 BotAPI 更新为 return a Post '[JSON] (IO Message) 给了我这个:No instance for (Foldable IO) arising from a use of ‘serve’,这超出了我的初学者知识范围,我可以看到摆弄类型只是解决了同样的问题到代码的不同部分。我只是不知道如何解决它。

以下是删除了敏感字符串的代码:

{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE DeriveAnyClass    #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators     #-}

module Main where

import           Control.Monad
import           Control.Monad.Trans.Either
import           Data.Proxy
import           Data.Text                  (Text, pack)
import           Network.Wai.Handler.Warp
import           Servant
import           Web.Telegram.API.Bot

reply :: Token -> Message -> Text -> IO Message
reply token msg response = do
  Right MessageResponse { message_result = m } <-
    sendMessage token $ SendMessageRequest chatId response (Just Markdown) Nothing Nothing Nothing
  return m
  where chatId = pack . show $ chat_id (chat msg)

type BotAPI = "webhook" :> ReqBody '[JSON] Update :> Post '[JSON] Message
type BotHandler a = EitherT ServantErr IO a

botApi :: Proxy BotAPI
botApi = Proxy

initWebhook :: Token -> IO Bool
initWebhook token = do
  Right SetWebhookResponse { webhook_result = w } <-
    setWebhook token $ Just "https://example.com/webhook"
  return w

postWebhook :: Token -> Update -> BotHandler (IO Message)
postWebhook token update = case message update of
  Just msg -> return $ reply token msg "Testing!"
  Nothing -> left err400

server :: Token -> Server BotAPI
server = postWebhook

main :: IO ()
main = do
  initWebhook token
  run port $ serve botApi (server token)
  where token = Token "<token>"
        port = 8080

对于可能不理想的 Haskell 代码表示歉意。提前谢谢你:)

你的错误在

Just msg → return $ reply token msg "Testing!"

你在

EitherT ServantErr IO Message

monad 但 reply 有类型

reply :: Token → Message → Text → IO Message

然后简单地 lift 那个 IO 动作到你的 monad 中,它起作用了

postWebhook :: Token → Update → BotHandler Message
postWebhook token update = case message update of
  Just msg → lift $ reply token msg "Testing!"
  Nothing  → left err400

(对我来说解释这里涉及的所有事情并不容易)我认为你应该在这些复杂的例子之前多练习一下 monads,transformers,......但你很勇敢! :)