Buckets:
| 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!") | |
Xet Storage Details
- Size:
- 4.6 kB
- Xet hash:
- 405c745509e70cc8d5596f0fdfa40615286c27100a18026b68d91cad100710ce
ยท
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.