从 Slack bot 迁移到 Hashee
如果你已经有 Slack bot,本教程帮你完整迁移到 Hashee:API 映射、UI 元素 对照、消息事件改造、配置项变更。
一句话差异
| 维度 | Slack | Hashee |
|---|---|---|
| 消息内容平台可见 | ✓ | ✗(盲管道 E2EE) |
| Bot 身份 | App + Bot User Token | Agent (agent_id + agent_token) |
| 连接 | Socket Mode / Events API | WebSocket / Webhook |
| 富 UI | Block Kit | Artifact(subtype 列表) |
| 命令 | Slash commands | 任意文本 + 业务侧解析(V2 会有 commands API) |
| @mention | <@U12345> | @<U:user_id> + mentions 数组 |
| 互动 | Block actions (button_click) | artifact_response(含 action 字段) |
| 工作区概念 | Workspace | 无(用户全局账号) |
| 信任模型 | App owner 信任 Slack | Agent 用户撤销关系即清理 |
API 字段映射
接收消息
Slack: app.message(async ({ message, say }) => { say(`hi <@${message.user}>`); });Hashee: agent.addMessageHandler(async (msg) => { if (msg.payload?.type !== "text") return; await agent.send(msg.conversation_id, { type: "text", text: `hi @<U:${msg.sender_id}>`, mentions: [msg.sender_id], }); });命令
Slack: app.command("/poll", async ({ command, ack, say }) => { ... });Hashee: agent.addMessageHandler(async (msg) => { if (msg.payload?.text?.startsWith("/poll")) { ... } });Block Kit → Artifact
Slack Block Kit:{ blocks: [ { type: "section", text: { type: "mrkdwn", text: "*Hello*" } }, { type: "actions", elements: [ { type: "button", text: { type: "plain_text", text: "Click" }, action_id: "click" } ]} ]}
Hashee Artifact:agent.sendArtifact(convId, { artifact: { subtype: "info", title: "Hello", payload: { body: "**Hello**", actions: [{ id: "click", label: "Click" }], }, },});Button → artifact_response
Slack: app.action("click", async ({ ack, body }) => { ... });Hashee: agent.addEventHandler(async (event) => { if (event.type === "artifact_response" && event.payload.action === "click") { // event.payload.payload 是用户的回流 data } });Slack 文件 → Hashee 文件
Slack: files.upload({ channels, file, filename });Hashee: agent.uploadFile(conversationId, { filename, mime, data: fileBuffer, });完整迁移示例
把 Slack 的 /echo + button 改成 Hashee:
原 Slack 版本(Bolt SDK):
import { App } from "@slack/bolt";const app = new App({ token, signingSecret, socketMode: true, appToken });
app.command("/echo", async ({ command, ack, say }) => { await ack(); await say({ blocks: [ { type: "section", text: { type: "mrkdwn", text: `Echo: ${command.text}` } }, { type: "actions", elements: [{ type: "button", text: { type: "plain_text", text: "再说一次" }, action_id: "again", value: command.text }] }, ], });});
app.action("again", async ({ ack, body, say }) => { await ack(); await say(`再次 Echo: ${(body as any).actions[0].value}`);});
await app.start();Hashee 版本:
import { HasheeAgent } from "@hasheeai/agent-sdk-ts";import { ulid } from "ulid";
const agent = await HasheeAgent.init({ agentId: process.env.HASHEE_AGENT_ID!, token: process.env.HASHEE_AGENT_TOKEN!, baseUrl: "https://api.hashee.ai", connectionMode: "websocket",});
// 业务侧记录每个 artifact 关联的"原始 echo 文本"const echoStore = new Map<string, string>();
agent.addMessageHandler(async (msg) => { if (msg.payload?.type !== "text") return; const m = msg.payload.text.match(/^\/echo\s+(.+)$/); if (!m) return; const text = m[1];
const artifactId = ulid(); echoStore.set(artifactId, text);
await agent.sendArtifact(msg.conversation_id, { artifact: { artifact_id: artifactId, subtype: "info", title: "Echo", payload: { body: `Echo: ${text}`, actions: [{ id: "again", label: "再说一次", action: "again" }], }, }, });});
agent.addEventHandler(async (event) => { if (event.type !== "artifact_response") return; if (event.payload.action !== "again") return; const original = echoStore.get(event.payload.ref_artifact); if (!original) return; await agent.send(event.payload.conversation_id, { type: "text", text: `再次 Echo: ${original}`, });});
agent.addStatusHandler((s) => console.log(`[status] ${s}`));console.log("[migrated-echo] up");配置项迁移
| Slack | Hashee |
|---|---|
SLACK_BOT_TOKEN (xoxb-…) | HASHEE_AGENT_TOKEN (hsk_…) |
SLACK_SIGNING_SECRET | HASHEE_WEBHOOK_SECRET(webhook 模式) |
SLACK_APP_TOKEN (Socket Mode) | n/a (WebSocket 自动处理) |
OAuth scopes (chat:write / channels:history) | Capability Manifest + Permission Scopes(notification:send 等) |
events_url(HTTP Events Mode) | webhook_url |
| Workspace 安装 | 用户主动添加 Agent |
部署对比
| Slack | Hashee |
|---|---|
| Slack App + OAuth flow | Agent 创建 + Token 一次性显示 |
| Render / Heroku 跑 Bolt + Socket Mode | VPS / 容器跑 WebSocket Agent |
| Vercel / Lambda + Events Mode | Cloudflare Workers / Vercel Edge + Webhook |
详见 部署到 Cloudflare Workers / 自托管 VPS。
你可能会怀念的(V1 暂无)
- OAuth distribute:V2 marketplace 会有”安装到我的 workspace”流程;V1 是每个用户自己加 Agent
- App Directory 上架:V2 marketplace
- Slash commands 注册中心:V1 让 Agent 自己解析消息文本;V2 commands API
- Modal:用 Artifact form 替代(功能更强)
- Home tab:V1 无对等概念;用主动推送的 artifact 模拟
你会得到的(Slack 没有)
- 端到端加密:消息平台看不到
- Tool Call with sensitivity:3 级权限同意机制
- 数据撤销 + cleanup 事件:
relation.revoked自动通知 cleanup - 多设备同步:用户切手机 / 桌面 / 平板无缝
- Grant Ledger:每次 Agent 读用户数据有 append-only 审计