|
| 1 | +import { App } from "@slack/bolt" |
| 2 | +import { createOpencode } from "@opencode-ai/sdk" |
| 3 | + |
| 4 | +const app = new App({ |
| 5 | + token: process.env.SLACK_BOT_TOKEN, |
| 6 | + signingSecret: process.env.SLACK_SIGNING_SECRET, |
| 7 | + socketMode: true, |
| 8 | + appToken: process.env.SLACK_APP_TOKEN, |
| 9 | +}) |
| 10 | + |
| 11 | +console.log("🔧 Bot configuration:") |
| 12 | +console.log("- Bot token present:", !!process.env.SLACK_BOT_TOKEN) |
| 13 | +console.log("- Signing secret present:", !!process.env.SLACK_SIGNING_SECRET) |
| 14 | +console.log("- App token present:", !!process.env.SLACK_APP_TOKEN) |
| 15 | + |
| 16 | +console.log("🚀 Starting opencode server...") |
| 17 | +const opencode = await createOpencode({ |
| 18 | + port: 0, |
| 19 | +}) |
| 20 | +console.log("✅ Opencode server ready") |
| 21 | + |
| 22 | +const sessions = new Map<string, { client: any; server: any; sessionId: string; channel: string; thread: string }>() |
| 23 | + |
| 24 | +app.use(async ({ next, context }) => { |
| 25 | + console.log("📡 Raw Slack event:", JSON.stringify(context, null, 2)) |
| 26 | + await next() |
| 27 | +}) |
| 28 | + |
| 29 | +app.message(async ({ message, say }) => { |
| 30 | + console.log("📨 Received message event:", JSON.stringify(message, null, 2)) |
| 31 | + |
| 32 | + if (message.subtype || !("text" in message) || !message.text) { |
| 33 | + console.log("⏭️ Skipping message - no text or has subtype") |
| 34 | + return |
| 35 | + } |
| 36 | + |
| 37 | + console.log("✅ Processing message:", message.text) |
| 38 | + |
| 39 | + const channel = message.channel |
| 40 | + const thread = (message as any).thread_ts || message.ts |
| 41 | + const sessionKey = `${channel}-${thread}` |
| 42 | + |
| 43 | + let session = sessions.get(sessionKey) |
| 44 | + |
| 45 | + if (!session) { |
| 46 | + console.log("🆕 Creating new opencode session...") |
| 47 | + const { client, server } = opencode |
| 48 | + |
| 49 | + const createResult = await client.session.create({ |
| 50 | + body: { title: `Slack thread ${thread}` }, |
| 51 | + }) |
| 52 | + |
| 53 | + if (createResult.error) { |
| 54 | + console.error("❌ Failed to create session:", createResult.error) |
| 55 | + await say({ text: "Sorry, I had trouble creating a session. Please try again.", thread_ts: thread }) |
| 56 | + return |
| 57 | + } |
| 58 | + |
| 59 | + console.log("✅ Created opencode session:", createResult.data.id) |
| 60 | + session = { client, server, sessionId: createResult.data.id, channel, thread } |
| 61 | + sessions.set(sessionKey, session) |
| 62 | + |
| 63 | + const shareResult = await client.session.share({ path: { id: createResult.data.id } }) |
| 64 | + if (!shareResult.error && shareResult.data) { |
| 65 | + const sessionUrl = shareResult.data.share?.url! |
| 66 | + console.log("🔗 Session shared:", sessionUrl) |
| 67 | + await app.client.chat.postMessage({ channel, thread_ts: thread, text: sessionUrl }) |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + console.log("📝 Sending to opencode:", message.text) |
| 72 | + const result = await session.client.session.prompt({ |
| 73 | + path: { id: session.sessionId }, |
| 74 | + body: { parts: [{ type: "text", text: message.text }] }, |
| 75 | + }) |
| 76 | + |
| 77 | + console.log("📤 Opencode response:", JSON.stringify(result, null, 2)) |
| 78 | + |
| 79 | + if (result.error) { |
| 80 | + console.error("❌ Failed to send message:", result.error) |
| 81 | + await say({ text: "Sorry, I had trouble processing your message. Please try again.", thread_ts: thread }) |
| 82 | + return |
| 83 | + } |
| 84 | + |
| 85 | + const response = result.data |
| 86 | + const responseText = |
| 87 | + response.info?.content || |
| 88 | + response.parts |
| 89 | + ?.filter((p: any) => p.type === "text") |
| 90 | + .map((p: any) => p.text) |
| 91 | + .join("\n") || |
| 92 | + "I received your message but didn't have a response." |
| 93 | + |
| 94 | + console.log("💬 Sending response:", responseText) |
| 95 | + await say({ text: responseText, thread_ts: thread }) |
| 96 | +}) |
| 97 | + |
| 98 | +app.command("/test", async ({ command, ack, say }) => { |
| 99 | + await ack() |
| 100 | + console.log("🧪 Test command received:", JSON.stringify(command, null, 2)) |
| 101 | + await say("🤖 Bot is working! I can hear you loud and clear.") |
| 102 | +}) |
| 103 | + |
| 104 | +await app.start() |
| 105 | +console.log("⚡️ Slack bot is running!") |
0 commit comments