ToolJet Slack 数据源接入指南:OAuth 授权、三种操作与源码级原理解析
【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet
ToolJet 允许你通过内置的 Slack 插件将工作区连接到 Slack,从而在应用内发送消息、列出成员、读取频道历史消息,并可将这些能力编排进按钮事件、定时任务与工作流。本文以 docs/versioned_docs/version-3.0.0-LTS/data-sources/slack.md 为主线,结合仓库中的插件实现(plugins/packages/slack)与前端授权组件(frontend/src/_components/Slack.jsx),完整讲解从连接、授权到执行查询的每一步,并剖析底层 OAuth 流程与三个操作背后的 Slack Web API 调用,让你既能照做,也能看懂原理。
前提:在 Slack 侧准备一个可用的 App
在 ToolJet 中建立连接之前,你需要在 Slack 侧有一个已创建并安装到目标工作区的 App。仓库文档明确给出了一个关键约束:
提供凭据的 App 必须已经安装在工作区中,并且需要被添加到你想发送消息的频道里。
这意味着:
- App 需要完成 OAuth 授权并安装(install)到目标工作区;
- 对于Send Message操作,App 还必须被加入目标频道(在 Slack 频道中执行
/invite @your-app-name,或在 App 管理页配置 Bot 并邀请进频道),否则chat.postMessage会返回not_in_channel之类的错误; - 用于授权回传的Redirect URI / OAuth callback URL必须与 ToolJet 展示的值一致(详见下文“获取 Redirect URI”小节)。
一、建立连接:添加数据源与 OAuth 授权
连接 Slack 数据源有两种入口,任选其一:
- 在查询面板(query panel)点击+ Add new Data source按钮;
- 通过 ToolJet 仪表盘进入Data Sources页面后添加。
进入 Slack 连接界面后,你会看到授权说明、默认权限范围、chat:write权限开关、Slack App 选择以及Connect to Slack按钮。
权限范围(Scopes)与chat:write开关
前端组件 frontend/src/_components/Slack.jsx 定义了默认权限范围,共 9 个:
users:read, channels:read, groups:read, im:read, mpim:read, channels:history, groups:history, im:history, mpim:history这些范围分别支撑三种操作:
users:read→ 支撑List Members;channels:history/groups:history/im:history/mpim:history→ 支撑List Messages(可读公共频道、私密群组、单聊 IM、多人私聊 MPIM 的历史);- 若要发送消息,需要额外勾选
chat:write开关(见 Slack.jsx:勾选后会在 scope 字符串末尾追加chat:write)。
获取 Redirect URI(回调地址)
连接界面底部会展示一个Redirect URI,由前端根据当前部署地址动态生成(Slack.jsx):
const redirectUri = `${getHostURL()}/oauth2/authorize`;在 Slack App 管理后台配置 OAuth 回调时,必须填写该地址,否则授权回调将无法命中 ToolJet。对应地,插件服务端在拼接授权链接时同样使用TOOLJET_HOST(及可选SUB_PATH)构造redirect_uri(plugins/packages/slack/lib/index.ts)。
两种凭据来源:环境变量 vs 自定义 Slack App
连接界面中Slack app下拉框决定凭据来源(Slack.jsx):
| 选项(ToolJet Cloud) | 选项(自托管) | 含义 |
|---|---|---|
| ToolJet slack app | Use environment variables | 使用部署环境中的SLACK_CLIENT_ID/SLACK_CLIENT_SECRET,即“from_env” |
| Custom slack app | Custom slack app | 手动填写 Client ID 与 Client Secret,即“from_datasource_configuration” |
选择Custom slack app后,界面会额外出现Client ID与Client Secret两个输入框(Slack.jsx)。这两个值最终会落入数据源配置的client_id、client_secret字段(在 manifest.json 中client_secret被标记为encrypted: true,即以加密形式存储)。从源码结构看,自托管部署若不配置环境变量,使用“Use environment variables”选项时插件会读取不到凭据,因此自托管用户通常选择自定义 App 方式填写凭据。
授权流程(OAuth 2.0)
点击Connect to Slack后,前端通过datasourceService.fetchOauth2BaseUrl('slack', ...)请求后端生成授权 URL,并附加scope、access_type=offline、prompt=select_account参数后弹出新窗口(Slack.jsx)。服务端插件负责拼接 Slack 的授权端点(plugins/packages/slack/lib/index.ts):
https://slack.com/oauth/v2/authorize?response_type=code&client_id=<CLIENT_ID>&redirect_uri=<TOOLJET_HOST><SUB_PATH/>oauth2/authorize在 Slack 弹出的授权页面上(见下图),确认 ToolJet 将能读取频道/对话内容与工作区信息,点击允许后,Slack 会携带授权码(code)回调到 ToolJet。随后插件调用 Slack 的令牌端点换取访问令牌(index.ts):
POST https://slack.com/api/oauth.v2.access body: code=<AUTH_CODE>&client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>&redirect_uri=<REDIRECT_URI>响应中的access_token(以及可能的refresh_token)会被保存为数据源的安全字段,此后所有查询都通过Authorization: Bearer <access_token>调用 Slack API(index.ts)。
二、查询 Slack:创建查询的通用步骤
连接建立后,即可在查询管理器中编写查询:
- 点击编辑器底部查询管理器的+ Add按钮;
- 选择上一步添加的Slack数据源;
- 在Operation下拉框中选择目标操作(List members / Send message / List messages from a channel);
- 点击Preview预览输出,或点击Run创建并触发查询。
操作下拉框由插件的操作描述文件 operations.json 定义,其内部值与显示名称的对应关系如下:
| 显示名称 | 内部值(operation) | 底层 Slack API |
|---|---|---|
| List members | list_users | users.list |
| Send message | send_message | chat.postMessage |
| List messages from a channel | list_messages | conversations.history |
从 index.ts 的run()方法可以看到,插件根据queryOptions.operation走switch分支执行对应请求,成功后统一返回{ status: 'ok', data: result };若网络/接口调用抛出异常,则包装为QueryError('Query could not be completed')。查询结果会暴露为查询变量的data与rawData字段(见 manifest.json 中的exposedVariables),可在前端表达式中引用。
三、支持的三种操作详解
1. List Members(列出工作区成员)
功能:返回当前 Slack 工作区所有成员的数据。
参数:无。
底层实现:插件以 GET 方式调用https://slack.com/api/users.list(index.ts),响应中的members数组即成员列表(含id、name、real_name、profile等字段)。从源码实现看,该操作未透传分页参数,会一次性拉取接口返回的全部成员;成员较多的工作区注意响应体大小。
2. Send Message(发送消息)
功能:向指定频道或私信(DM / IM)发送消息。
必填参数:
- Channel:频道 ID 或用户 ID(占位提示为
Enter channel id or user id)。注意 Slack 要求传ID 而非名称——可在 Slack 中通过右键频道“Copy link”获取C...开头的频道 ID,或直接使用users.list查询返回的用户 ID 来发私信; - Message:要发送的消息正文。
权限前提:执行该操作前,必须勾选连接界面中的chat:write开关(Slack.jsx),授权时才会在 scope 中追加chat:write。这一点与插件运行逻辑强耦合——插件在run()中会检查sourceOptions.access_type === 'chat:write',不满足则直接返回(index.ts):
{ "ok": false, "error": "You do not have the required permissions to perform this operation" }底层实现:通过chat.postMessage发送(index.ts),请求体如下:
{ channel: queryOptions.channel, text: queryOptions.message, as_user: queryOptions.sendAsUser // 可选:是否以应用(bot)身份发送 }其中as_user由查询参数sendAsUser透传(类型定义见 types.ts)。消息正文支持 Slack 的 mrkdwn 富文本语法(如*加粗*、\代码``)。
常见报错:not_in_channel表示 App 未被邀请进目标频道;missing_scope表示授权时未勾选chat:write。
3. List Messages(列出频道消息)
功能:获取指定频道的消息历史。
必填参数:
- Channel:频道 ID(占位提示为
Enter channel id); - Limit:本次返回的最大消息条数;
- Next Cursor:分页游标,用于获取下一页数据。
底层实现:插件以 POST 表单方式调用https://slack.com/api/conversations.history(index.ts):
{ channel: queryOptions.channel, limit: queryOptions.limit || 100, // 默认 100 条 cursor: queryOptions.cursor || '' // 空串表示第一页 }关于 Limit 与 Next Cursor 的说明:limit不填时插件默认取 100(Slack API 的返回条数上限);Slack 的分页采用游标(cursor)机制——第一页响应中的response_metadata.next_cursor即为下一页的游标值,将其填入Next Cursor即可继续拉取后续消息,循环该过程即可遍历整个频道历史。limit与cursor均声明为codehinter类型(operations.json),意味着它们支持 JS 表达式,可动态传入(例如从上一查询结果中取next_cursor)。
四、参数表达式与查询联动
三个操作的参数(Channel、Message、Limit、Next Cursor)在 operations.json 中均被定义为codehinter类型,支持在输入框中编写 JavaScript 表达式引用其他查询的返回值。典型的联动场景:
- 发消息给最近列出的成员:用 List Members 查询的
data.members[0].id作为 Send Message 的 Channel 参数; - 分页读取历史消息:第一次执行 List Messages 后,将返回结果中的
data.response_metadata.next_cursor填入第二次查询的 Next Cursor,逐页滚动抓取; - 用消息内容触发后续逻辑:在 List Messages 结果上继续编写
{{queries.slack1.data.messages}}之类的表达式完成过滤、统计或写入数据库。
五、从源码看整体链路
把上文串起来,一次 Slack 查询的完整调用链如下:
前端查询管理器(选择操作与参数) → 插件 run() 方法(switch 分发,见 plugins/packages/slack/lib/index.ts) → Slack Web API(users.list / chat.postMessage / conversations.history) → 返回 { status: 'ok', data: result } 给查询面板 → 结果可被其他组件、查询或 JS 表达式引用授权链路则分两段:前端组件 Slack.jsx 负责展示界面、收集 scope 与凭据来源;插件服务端 index.ts 负责构造授权 URL、用授权码换 token,并将 token 作为数据源安全字段持久化。credential_source的from_env/from_datasource_configuration两种取值对应读取SLACK_CLIENT_ID/SLACK_CLIENT_SECRET环境变量或界面填写的 Client ID/Secret(index.ts)。
整个插件以@tooljet-plugins/common的QueryService接口为契约实现(index.ts),与 ToolJet 的其他数据源插件保持一致的接入模式;数据源的属性结构由 manifest.json 声明,操作参数结构由 operations.json 声明,两者是驱动动态表单渲染的元数据来源。
常见问题速查
| 现象 | 排查方向 |
|---|---|
| 授权页未出现或回调失败 | 检查 Redirect URI 是否与 ToolJet 界面展示的完全一致;检查 Client ID/Secret 是否正确 |
发送消息返回not_in_channel | 将 App(Bot)邀请进目标频道后再试 |
发送消息返回missing_scope/ 权限错误 | 授权时勾选chat:write开关,或重新授权以补充 scope |
| List Messages 只拿到部分消息 | 使用响应中的response_metadata.next_cursor配合 Next Cursor 参数分页拉取 |
| 查询报 “Query could not be completed” | 检查 token 是否有效、网络是否可达slack.com,参见 index.ts 的异常包装逻辑 |
【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考