尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

使用 Encore.ts 构建生产级 Slack Bot:从零到云端部署的完整实战指南

发布时间:2026/9/15 21:27:26

资讯中心
01
ARTICLE

使用 Encore.ts 构建生产级 Slack Bot:从零到云端部署的完整实战指南

使用 Encore.ts 构建生产级 Slack Bot:从零到云端部署的完整实战指南
使用 Encore.ts 构建生产级 Slack Bot从零到云端部署的完整实战指南【免费下载链接】encoreThe infrastructure platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/encor/encore导读本教程将带你用 Encore.ts当前开源仓库 encore 的 TypeScript 后端框架从零构建一个把经典cowsay工具带到 Slack 的聊天机器人用户只需在 Slack 中敲下/cowsay your message就能收获一只 ASCII 奶牛的说教。文章完整覆盖了从创建 Encore 应用、配置 Slack App Manifest、实现 Webhook 端点、本地调试、云端部署到使用 Encore 内置密钥管理 HMAC 校验请求签名、让端点达到生产级安全标准的全过程。学完本教程你将掌握 Encore.ts 的 Service 定义、Raw Endpoint 与内置 Secrets 三大核心能力并能在几分钟内交付一个可运行在云端的真实集成。全程约需 15 分钟。文中所有代码与命令均基于当前仓库的 Encore.ts 运行时见 runtimes/js/encore.dev。1. 整体架构预览在动手之前先理清这个 Slack Bot 的数据流它将帮助我们理解为什么选择 Raw Endpoint用户在 Slack 工作区输入/cowsay Hello thereSlack 平台向你在 App Manifest 中配置的 URL 发起一个application/x-www-form-urlencoded的 POST 请求并携带x-slack-request-timestamp、x-slack-signature等自定义请求头你的 Encore 应用端点解析表单中的text参数套上 cowsay 的 ASCII 外壳返回 JSON 响应Slack 收到响应后把文案以in_channel的形式公开渲染到频道里。由于 Slack 发送的是自定义 HTTP 头与表单编码的 bodyEncore.ts 默认的类型化 API 解析并不适用这正是教程选择Raw Endpoint原始端点的原因——它直接暴露 Node.js 的http请求/响应对象让你拥有对 HTTP 层的完全控制权。2. 创建 Encore 应用 首先创建 Encore 应用$ encore app create在模板选择中选中Empty app空应用。创建完成后请记下你的 app id下一步配置 Slack App 时会用到。Encore 应用是 Encore 平台上的一个逻辑单元包含源码、环境、密钥等一切资源。后续所有部署、密钥设置命令都会以当前目录下由encore app create生成的encore.app文件为上下文可在 pkg/appfile/appfile.go 中查看其结构定义。3. 创建 Slack 应用 接下来到 Slack 侧创建一个应用打开 Slack 的 API 站点点击Create New App在选择创建方式时选择From an app manifest从应用清单创建选择一个工作区用于安装该应用。 输入以下 Manifest请把 URL 中的$APP_ID替换成上一步记下的 Encore 应用 id_metadata: major_version: 1 display_information: name: Encore Bot description: Cowsay for the cloud age. features: slash_commands: - command: /cowsay # Replace $APP_ID below url: https://staging-$APP_ID.encr.app/cowsay description: Say things with a flair! usage_hint: your message here should_escape: false bot_user: display_name: encore-bot always_online: true oauth_config: scopes: bot: - commands - chat:write - chat:write.public settings: org_deploy_enabled: false socket_mode_enabled: false token_rotation_enabled: false几个关键配置项说明slash_commands.urlSlash Command 的 Webhook 回调地址。staging-$APP_ID.encr.app是 Encore 为每个应用自动生成的staging 环境域名/cowsay即稍后我们要实现的端点路径should_escape: false告知 Slack 不要对命令参数做额外转义避免text内容被破坏oauth_config.scopescommands用于注册斜杠命令chat:write与chat:write.public允许机器人向频道写消息socket_mode_enabled: false本教程走传统的 HTTP Webhook 模式而非 Socket Mode因为我们要演示如何用 Encore 的 Raw Endpoint 接收并校验 Webhook 请求。保存并安装应用后就可以进入编码环节了。4. 实现 Slack 端点4.1 定义服务Encore.ts 以目录为单位组织服务一个目录 一个encore.service.ts文件即定义一个后端服务该目录及其所有子目录中的文件都属于这个服务。 在 Encore 应用中创建slack服务$ mkdir slack $ touch slack/encore.service.ts 向slack/encore.service.ts写入以下内容-- slack/encore.service.ts -- import { Service } from encore.dev/service; export default new Service(slack);从源码看Service类要求name作为构造参数且必须在名为encore.service.ts的文件中调用这样 Encore 编译器才能高效地识别出服务定义见 runtimes/js/encore.dev/service/mod.ts。此外它还支持可选的cfg参数目前可配置服务级middlewares本教程暂不需要。4.2 用 Raw Endpoint 接收 Webhook 创建slack/slack.ts写入以下内容-- slack/slack.ts -- import { api } from encore.dev/api; import type { IncomingMessage } from node:http; // cowart is the formatting string for printing the cow art. const cowart (msg: string) Moo! ${msg} ; export const cowsay api.raw( { expose: true, path: /cowsay, method: * }, async (req, resp) { const body await getBody(req); const text new URLSearchParams(body).get(text); const msg cowart(text || Moo!); resp.setHeader(Content-Type, application/json); resp.end(JSON.stringify({ response_type: in_channel, text: msg })); }, ); // Extract the body from an incoming request. function getBody(req: IncomingMessage): Promisestring { return new Promise((resolve) { const bodyParts: any[] []; req .on(data, (chunk) { bodyParts.push(chunk); }) .on(end, () { resolve(Buffer.concat(bodyParts).toString()); }); }); }这段代码涉及 Encore.ts 的api.raw原始端点 API。在 runtimes/js/encore.dev/api/mod.ts 中可以看到它的签名api.raw(options, fn)其中fn接收两个参数——Node.js 的http.IncomingMessage请求与ServerResponse响应写者与 Express.js 的中间件风格类似。options类型为APIOptions支持以下关键字段字段说明默认值path请求路径支持:id单段参数与*通配如/files/*path未指定时为/service.endpointmethod匹配的 HTTP 方法可用*匹配任意方法*expose是否对外开放。false时仅内部网络可访问falseauth是否要求合法认证凭据为true时未认证请求返回 401falsebodyLimit请求体大小上限字节null表示不限2 MiBtags客户端生成与中间件过滤用的标签无本教程设置method: *是因为 Slack 平台可能以不同方式发送回调用通配符确保任何请求都能被接收。解析逻辑上Slack 的 Slash Command 回调 body 是application/x-www-form-urlencoded格式因此用new URLSearchParams(body).get(text)提取用户输入缺失时回退到Moo!。response_type: in_channel表示回复将公开显示在频道中而非仅对触发者可见。4.3 本地运行验证 启动本地开发环境$ encore run然后在另一个终端调用它$ curl http://localhost:4000/cowsay -d textEat your greens! {response_type:in_channel,text:Moo! Eat your greens!}encore run会启动本地运行时其实现位于 cli/daemon/run默认监听localhost:4000并自动热重载代码。看到上面的输出即代表端点工作正常。4.4 首次部署到云端 把代码提交并推送到 Encore$ git add -A . $ git commit -m Initial commit $ git push encoreencore是encore app create时自动配置的 Git remotegit push encore会触发云端构建与部署到 staging 环境。部署完成后回到 Slack 工作区输入/cowsay Hello there你就能看到奶牛开始说话了——至此一个可用的 Slack 集成已经完成。5. 加固验证 Webhook 请求签名上一节为了快速跑通忽略了一个生产级 Slack 应用必不可少的安全环节验证请求确实来自 Slack而不是攻击者伪造的。现在来补上它。Slack 的签名验证机制官方称之为Verifying requests from Slack核心分两步保存 Slack 提供的共享密钥Signing Secret用该密钥基于 HMAC-SHA256 计算签名与请求头x-slack-signature比对。5.1 保存共享密钥 在slack.ts顶部引入 Encore 的secretAPI-- slack/slack.ts -- import { secret } from encore.dev/config; const slackSigningSecret secret(SlackSigningSecret);在 runtimes/js/encore.dev/config/secrets.ts 中secret(name)返回一个可调用对象调用它如slackSigningSecret()即可解析出当前密钥值。源码还揭示了一个重要行为本地开发时若密钥未设置会返回空字符串并只给警告但部署到云环境时所有 secret 必须已设置否则部署会失败。更详细的行为可参考 docs/ts/primitives/secrets.md如密钥名在整个应用内全局唯一、可通过.secrets.local.cue文件覆盖本机值等。 在 Slack 的Basic Information页面复制Signing Secret然后设置各环境的密钥$ encore secret set --type prod SlackSigningSecret $ encore secret set --type dev,local,pr SlackSigningSecret第一条设置生产环境第二条设置开发/本地/预览环境本地可用占位值。--type支持production/development/preview/local及其简写prod/dev/pr也支持--env name为特定环境单独设置特定环境的值优先于环境类型值。命令的具体实现包括交互式密码输入、环境选择冲突检测、secret 创建后自动向本地运行时刷新位于 cli/cmd/encore/secrets/set.go。5.2 实现签名校验 补充 crypto 相关导入-- slack/slack.ts -- import { createHmac, timingSafeEqual } from node:crypto; import type { IncomingHttpHeaders } from http; 添加verifySignature函数-- slack/slack.ts -- // Verifies the signature of an incoming request from Slack. const verifySignature async function ( body: string, headers: IncomingHttpHeaders, ) { const requestTimestampSec parseInt( headers[x-slack-request-timestamp] as string, ); const signature headers[x-slack-signature] as string; if (Number.isNaN(requestTimestampSec)) { throw new Error( Failed to verify authenticity: header x-slack-request-timestamp did not have the expected type (${requestTimestampSec}), ); } // Calculate time-dependent values const nowMs Date.now(); const requestTimestampMaxDeltaMin 5; const fiveMinutesAgoSec Math.floor(nowMs / 1000) - 60 * requestTimestampMaxDeltaMin; // Enforce verification rules // Rule 1: Check staleness if (requestTimestampSec fiveMinutesAgoSec) { throw new Error( Failed to verify authenticity: x-slack-request-timestamp must differ from system time by no more than ${requestTimestampMaxDeltaMin} minutes or request is stale, ); } // Rule 2: Check signature // Separate parts of signature const [signatureVersion, signatureHash] signature.split(); // Only handle known versions if (signatureVersion ! v0) { throw new Error(Failed to verify authenticity: unknown signature version); } // Compute our own signature hash const hmac createHmac(sha256, slackSigningSecret()); hmac.update(${signatureVersion}:${requestTimestampSec}:${body}); const ourSignatureHash hmac.digest(hex); if ( !signatureHash || !timingSafeEqual( Buffer.from(signatureHash, utf8), Buffer.from(ourSignatureHash, utf8), ) ) { throw new Error(Failed to verify authenticity: signature mismatch); } };这段校验逻辑实现了 Slack 官方规范的两个规则规则 1防重放请求头x-slack-request-timestamp必须与系统时间相差不超过 5 分钟否则视为过期请求直接拒绝防止攻击者重放捕获到的旧请求规则 2验签以v0:{timestamp}:{body}为消息、以 Signing Secret 为密钥计算 HMAC-SHA256十六进制结果与x-slack-signature头中v0之后的部分比对。这里有两个容易被忽略但至关重要的细节消息串中的{body}必须是未经任何解析/改动的原始请求体因此必须在getBody拿到原始字符串后就立即计算签名比对哈希时使用timingSafeEqual而非普通字符串比较避免时序侧信道攻击——这正是node:crypto提供该函数的意义。5.3 接入端点 更新cowsay函数在解析参数前先校验签名-- slack/slack.ts -- export const cowsay api.raw( { expose: true, path: /cowsay, method: * }, async (req, resp) { const body await getBody(req); try { await verifySignature(body, req.headers); } catch (err) { const e err as Error; resp.statusCode 500; resp.end(e.message); return; } const text new URLSearchParams(body).get(text); const msg cowart(text || Moo!); resp.setHeader(Content-Type, application/json); resp.end(JSON.stringify({ response_type: in_channel, text: msg })); }, );至此任何无法通过签名校验的请求都会在业务逻辑执行前被拦截并以 500 拒绝端点达到生产级安全水准。6. 完整成品与最终部署6.1 完善奶牛艺术字 把最初的简化版cowart替换为完整的 ASCII 奶牛-- slack/slack.ts -- const cowart (msg: string) \\\ -${-.repeat(msg.length)}- | ${msg} | -${-.repeat(msg.length)}- \\ __n__n__ .------\-\\00/- / ## ## (oo) / \\## __ ./ |//YY \\|/ ||| ||| \\\ ;6.2 提交并部署 最后提交改动并部署$ git add -A . $ git commit -m Verify webhook requests and improve art $ git push encore 部署完成后回到 Slack 运行/cowsay Hello there如果一切顺利频道里会出现一只用---边框框住消息的完整奶牛。整个 Slack Bot 的代码量不到 100 行。6.3 用烟火庆祝应用已在云端运行。在 Encore Cloud Dashboard 中按Cmd KMac或Ctrl KWindows/Linux打开 Command Menu——在这里你可以快速访问 Cloud Dashboard 的全部功能例如跳转到 Service Catalog 查看服务或针对某个端点查看 Traces 追踪。输入fireworks并回车欣赏一场庆祝烟火。7. 延伸阅读与原理小结本教程实际用到的 Encore.ts 核心能力均可在当前仓库中找到一手实现Raw Endpointapi.raw的完整类型定义与APIOptions各字段语义见 runtimes/js/encore.dev/api/mod.ts更系统的用法与类型化api的差异、适用场景见 docs/ts/primitives/raw-endpoints.mdx其中明确指出原始端点常用于接收 Webhook等需要低层 HTTP 访问的场景服务定义Service类的目录即服务边界规则见 runtimes/js/encore.dev/service/mod.ts密钥管理secret()的运行时行为、本地缺失密钥的降级策略见 runtimes/js/encore.dev/config/secrets.ts密钥的完整使用与覆盖机制见 docs/ts/primitives/secrets.mdencore secret set命令支持的环境类型与参数解析见 cli/cmd/encore/secrets/set.go。回顾整条链路Encore Service 目录约定把slack服务与文件系统天然绑定Raw Endpoint让类型化框架也能无缝接收 Slack 这种自定义格式的 Webhook内置 Secrets在不把密钥写进代码库的前提下实现了开发与生产环境的差异化注入。三者组合正是 Encore.ts 让Webhook 类集成也能获得与类型化 API 同等级别的部署、追踪与密钥管理能力的关键所在。现在你已经掌握了这套模式完全可以照葫芦画瓢去对接 GitHub Webhook、Stripe 回调或任意需要验签的第三方集成。【免费下载链接】encoreThe infrastructure platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/encor/encore创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。