Download packages/slack/src/index.ts from SaylorTwift/opencode: direct link, hf CLI and curl.
- Browser
- Download file 4.6 kB
-
https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/slack/src/index.ts
- Command line
-
hf download hf://SaylorTwift/opencode/packages/slack/src/index.ts
-
curl -L -o index.ts https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/slack/src/index.ts
4.6 kB
| import { App } from "@slack/bolt" | |
| import { createOpencode, type ToolPart } from "@opencode-ai/sdk" | |
| const app = new App({ | |
| token: process.env.SLACK_BOT_TOKEN, | |
| signingSecret: process.env.SLACK_SIGNING_SECRET, | |
| socketMode: true, | |
| appToken: process.env.SLACK_APP_TOKEN, | |
| }) | |
| console.log("π§ Bot configuration:") | |
| console.log("- Bot token present:", !!process.env.SLACK_BOT_TOKEN) | |
| console.log("- Signing secret present:", !!process.env.SLACK_SIGNING_SECRET) | |
| console.log("- App token present:", !!process.env.SLACK_APP_TOKEN) | |
| console.log("π Starting opencode server...") | |
| const opencode = await createOpencode({ | |
| port: 0, | |
| }) | |
| console.log("β Opencode server ready") | |
| const sessions = new Map<string, { client: any; server: any; sessionId: string; channel: string; thread: string }>() | |
| void (async () => { | |
| const events = await opencode.client.event.subscribe() | |
| for await (const event of events.stream) { | |
| if (event.type === "message.part.updated") { | |
| const part = event.properties.part | |
| if (part.type === "tool") { | |
| // Find the session for this tool update | |
| for (const [_sessionKey, session] of sessions.entries()) { | |
| if (session.sessionId === part.sessionID) { | |
| void handleToolUpdate(part, session.channel, session.thread) | |
| break | |
| } | |
| } | |
| } | |
| } | |
| } | |
| })() | |
| async function handleToolUpdate(part: ToolPart, channel: string, thread: string) { | |
| if (part.state.status !== "completed") return | |
| const toolMessage = `*${part.tool}* - ${part.state.title}` | |
| await app.client.chat | |
| .postMessage({ | |
| channel, | |
| thread_ts: thread, | |
| text: toolMessage, | |
| }) | |
| .catch(() => {}) | |
| } | |
| app.use(async ({ next, context }) => { | |
| console.log("π‘ Raw Slack event:", JSON.stringify(context, null, 2)) | |
| await next() | |
| }) | |
| app.message(async ({ message, say }) => { | |
| console.log("π¨ Received message event:", JSON.stringify(message, null, 2)) | |
| if (message.subtype || !("text" in message) || !message.text) { | |
| console.log("βοΈ Skipping message - no text or has subtype") | |
| return | |
| } | |
| console.log("β Processing message:", message.text) | |
| const channel = message.channel | |
| const thread = (message as any).thread_ts || message.ts | |
| const sessionKey = `${channel}-${thread}` | |
| let session = sessions.get(sessionKey) | |
| if (!session) { | |
| console.log("π Creating new opencode session...") | |
| const { client, server } = opencode | |
| const createResult = await client.session.create({ | |
| body: { title: `Slack thread ${thread}` }, | |
| }) | |
| if (createResult.error) { | |
| console.error("β Failed to create session:", createResult.error) | |
| await say({ | |
| text: "Sorry, I had trouble creating a session. Please try again.", | |
| thread_ts: thread, | |
| }) | |
| return | |
| } | |
| console.log("β Created opencode session:", createResult.data.id) | |
| session = { client, server, sessionId: createResult.data.id, channel, thread } | |
| sessions.set(sessionKey, session) | |
| const shareResult = await client.session.share({ path: { id: createResult.data.id } }) | |
| if (!shareResult.error && shareResult.data) { | |
| const sessionUrl = shareResult.data.share?.url | |
| console.log("π Session shared:", sessionUrl) | |
| await app.client.chat.postMessage({ channel, thread_ts: thread, text: sessionUrl }) | |
| } | |
| } | |
| console.log("π Sending to opencode:", message.text) | |
| const result = await session.client.session.prompt({ | |
| path: { id: session.sessionId }, | |
| body: { parts: [{ type: "text", text: message.text }] }, | |
| }) | |
| console.log("π€ Opencode response:", JSON.stringify(result, null, 2)) | |
| if (result.error) { | |
| console.error("β Failed to send message:", result.error) | |
| await say({ | |
| text: "Sorry, I had trouble processing your message. Please try again.", | |
| thread_ts: thread, | |
| }) | |
| return | |
| } | |
| const response = result.data | |
| // Build response text | |
| const responseText = | |
| response.info?.content || | |
| response.parts | |
| ?.filter((p: any) => p.type === "text") | |
| .map((p: any) => p.text) | |
| .join("\n") || | |
| "I received your message but didn't have a response." | |
| console.log("π¬ Sending response:", responseText) | |
| // Send main response (tool updates will come via live events) | |
| await say({ text: responseText, thread_ts: thread }) | |
| }) | |
| app.command("/test", async ({ command, ack, say }) => { | |
| await ack() | |
| console.log("π§ͺ Test command received:", JSON.stringify(command, null, 2)) | |
| await say("π€ Bot is working! I can hear you loud and clear.") | |
| }) | |
| await app.start() | |
| console.log("β‘οΈ Slack bot is running!") | |