为 Slack API 使用 Firebase onRequest() 或 Express app.use()

Use Firebase onRequest() or Express app.use() for the Slack API

目标

使用 @slack/interactive-message 包和 firebase-functions 来收听和响应 Slack 消息和对话。

问题

我不确定如何将 @slack/interactive-message 侦听器与 firebase 一起使用。

1) 我是否使用 Firebase 的 functions.https.onRequest(),并且 以某种方式req 从 Slack 传递到 slackInteractions.action()

2) 我是否使用 app.use("/app", slackInteractions.expressMiddleware()); 如果是,slackInteractions.action()s go 去哪里?

3) 还有别的吗?

代码

// Express
import express = require("express");
const app = express();
const cors = require("cors")({
  origin: "*"
});
app.use("*", cors);

// Firebase Functions SDK
import functions = require("firebase-functions");

const slackbotConfig = functions.config().slackbot;
const { createMessageAdapter } = require("@slack/interactive-messages");
const slackInteractions = createMessageAdapter(slackbotConfig.signing_secret);

app.use("/app", slackInteractions.expressMiddleware());

// Express route
app.post("/go", (req, res) => {
  console.log("Hello from Express!");
  res
    .status(200)
    .send("Hello from Express!")
    .end();
});

exports.app = functions.https.onRequest(app);

exports.helloWorld = functions.https.onRequest((_req, res) => {
  console.log("Hello from Firebase!");
  res
    .status(200)
    .send("Hello from Firebase!")
    .end();
});

tl;博士

我不熟悉 Express 的细节和使用中间件。 @slack/interactive-message 的示例显示...

slackInteractions.start(port).then(() => {
  console.log(`server listening on port ${port}`);
});

...对于 Firebase Cloud Functions,此位不相关。我不确定 Firebase 和 @slack/interactive-message

之间的侦听器、请求和响应是如何集成的

@slack/interactive-messages 的创建者

简而言之,您的解决方案 2 对我来说似乎是正确的。虽然我没有使用 Firebase 函数的经验,但我对 express 有很好的理解,我会提供更多细节。

什么是快速中间件?

Express 中间件是一种处理传入 HTTP 请求的函数的名称。所有中间件功能都可以在逐个请求的基础上,选择预处理请求(通常通过在 req 参数中添加 属性),响应请求,或 post-处理一个请求(比如计算请求和响应之间的时间)。它可以做任何一件事情或这些事情的组合,这取决于它试图完成的事情。 Express 应用程序管理一堆中间件。您可以将其视为请求在响应准备好之前可能完成的步骤列表。该列表中的每个步骤都可以决定提供响应,以便该请求甚至不会到达下一步。

您的代码示例中的 cors 值是一个中间件函数。它应用了一些关于您的 Firebase 函数应该接受来自哪些来源的请求的规则。它将这些规则应用于传入请求,并且当不允许来源时,它将立即响应错误。否则,它允许请求由堆栈中的下一个中间件处理。

您的示例中还有另一个中间件,那就是路由器。路由器只是一种中间件,它知道如何根据传入请求中的路径(URL 的一部分)将应用程序拆分为单独的处理程序。每个 express app 都带有一个内置路由器,您在示例中使用 app.post("/go", () => {}); 代码行将处理程序附加到它。路由器通常是堆栈中的最后一个中间件。它们确实具有人们通常没有意识到的特殊功能。这些路由处理程序是什么?它们只是更多的中间件功能。所以总的来说,您可以将路由器视为一种中间件,它可以帮助您根据请求的路径划分应用程序行为。

这对 slackInteractions 意味着什么?

您可以将代码中的 slackInteractions 对象视为 始终 处理请求的路由器 - 它从不将请求传递到堆栈中的下一个中间件.关键区别在于,它不是通过请求路径来划分应用程序行为,而是使用 Slack 交互的各种属性来划分行为。您 describe which properties exactly you care about by passing in constraints.action() 方法。典型路由器与 slackInteractions 之间的唯一显着区别是值本身不是 express 中间件,您可以通过调用 .expressMiddleware() 方法生成一个 express 中间件。它像这样分开,因此它也可以在 express 应用程序之外工作(那时您可能会使用 .start() 方法)。

放在一起

就像我说的,我没有具体使用 Firebase 函数的经验,但我认为对于仅处理 Slack 交互的函数,您应该至少从以下开始。

// Firebase Functions SDK
import functions = require("firebase-functions");
const slackbotConfig = functions.config().slackbot;

// Slack Interactive Messages Adapter
const { createMessageAdapter } = require("@slack/interactive-messages");
const slackInteractions = createMessageAdapter(slackbotConfig.signing_secret);

// Action handlers
slackInteractions.action('welcome_agree_button', (payload, respond) => {
  // `payload` is an object that describes the interaction
  console.log(`The user ${payload.user.name} in team ${payload.team.domain} pressed a button`);

  // Your app does some asynchronous work using information in the payload
  setTimeout(() => {
    respond({ text: 'Thanks for accepting the code of conduct for our workspace' });
  }, 0)

  // Before the work completes, return a message object that is the same as the original but with
  // the interactive elements removed.
  const reply = payload.original_message;
  delete reply.attachments[0].actions;
  return reply;
});

// Express
import express = require("express");
const app = express();
app.use("/", slackInteractions.expressMiddleware());

exports.slackActions = functions.https.onRequest(app);