botinabox 2.7.10 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ import {
4
4
  extractVoiceTranscript,
5
5
  parseSlackEvent,
6
6
  transcribeAudio
7
- } from "../../chunk-NNPCKR6G.js";
7
+ } from "../../chunk-OEMM2LEA.js";
8
8
  import {
9
9
  chunkText
10
10
  } from "../../chunk-ZTZFPTOQ.js";
@@ -71,8 +71,8 @@ var SlackAdapter = class {
71
71
  /** Simulate receiving an inbound message (for testing/webhooks). */
72
72
  async receive(event) {
73
73
  if (this.onMessage) {
74
- const { parseSlackEvent: parseSlackEvent2 } = await import("../../inbound-IBKXBFZF.js");
75
- const { enrichVoiceMessage: enrichVoiceMessage2 } = await import("../../inbound-IBKXBFZF.js");
74
+ const { parseSlackEvent: parseSlackEvent2 } = await import("../../inbound-5FKJBWPL.js");
75
+ const { enrichVoiceMessage: enrichVoiceMessage2 } = await import("../../inbound-5FKJBWPL.js");
76
76
  let msg = parseSlackEvent2(event);
77
77
  if (msg.body.includes("[Voice message") && this.config?.botToken) {
78
78
  msg = await enrichVoiceMessage2(msg, this.config.botToken);
@@ -85,6 +85,33 @@ function createSlackAdapter(client) {
85
85
  return new SlackAdapter(client);
86
86
  }
87
87
 
88
+ // src/channels/slack/enrichers/enrich.ts
89
+ async function enrichAttachments(msg, botToken, enrichers) {
90
+ if (!msg.attachments?.length) return msg;
91
+ const parts = msg.body ? [msg.body] : [];
92
+ for (const att of msg.attachments) {
93
+ const enricher = enrichers[att.type];
94
+ const label = att.filename ?? att.url ?? att.type;
95
+ if (!enricher) {
96
+ parts.push(`[Attached: ${label}]`);
97
+ continue;
98
+ }
99
+ let extracted = null;
100
+ try {
101
+ extracted = await enricher(att, botToken);
102
+ } catch {
103
+ extracted = null;
104
+ }
105
+ if (extracted) {
106
+ parts.push(`[Attached: ${label}]
107
+ ${extracted}`);
108
+ } else {
109
+ parts.push(`[Attached: ${label}]`);
110
+ }
111
+ }
112
+ return { ...msg, body: parts.join("\n\n") };
113
+ }
114
+
88
115
  // src/channels/slack/bolt-adapter.ts
89
116
  var SlackBoltAdapter = class {
90
117
  app = null;
@@ -95,7 +122,7 @@ var SlackBoltAdapter = class {
95
122
  async start() {
96
123
  const boltModule = "@slack/bolt";
97
124
  const bolt = await import(boltModule);
98
- const { enrichVoiceMessage: enrichVoiceMessage2 } = await import("../../inbound-IBKXBFZF.js");
125
+ const { enrichVoiceMessage: enrichVoiceMessage2 } = await import("../../inbound-5FKJBWPL.js");
99
126
  const boltApp = new bolt.App({
100
127
  token: this.config.botToken,
101
128
  appToken: this.config.appToken,
@@ -113,6 +140,9 @@ var SlackBoltAdapter = class {
113
140
  if (inbound.body.includes("[Voice message \u2014 no transcript available]")) {
114
141
  inbound = await enrichVoiceMessage2(inbound, botToken);
115
142
  }
143
+ if (inbound.attachments?.length && this.config.attachmentEnrichers) {
144
+ inbound = await enrichAttachments(inbound, botToken, this.config.attachmentEnrichers);
145
+ }
116
146
  await hooks.emit("message.inbound", inbound);
117
147
  });
118
148
  hooks.register("response.ready", async (ctx) => {
@@ -128,7 +158,8 @@ var SlackBoltAdapter = class {
128
158
  await boltApp.client.chat.postMessage({
129
159
  token: botToken,
130
160
  channel: channelId,
131
- text: chunk
161
+ text: chunk,
162
+ ...threadId ? { thread_ts: threadId } : {}
132
163
  });
133
164
  }
134
165
  }, { priority: 90 });
@@ -148,7 +179,8 @@ var SlackBoltAdapter = class {
148
179
  channel_id: channelId,
149
180
  file: createReadStream(filePath),
150
181
  filename: basename(filePath),
151
- title: fileName ?? basename(filePath)
182
+ title: fileName ?? basename(filePath),
183
+ ...threadId ? { thread_ts: threadId } : {}
152
184
  });
153
185
  }
154
186
  } catch {
@@ -162,11 +194,113 @@ var SlackBoltAdapter = class {
162
194
  }
163
195
  }
164
196
  };
197
+
198
+ // src/channels/slack/enrichers/image-enricher.ts
199
+ var DEFAULT_PROMPT = "Describe this image in detail. Include any visible text (OCR), objects, people, layout, and context. Be thorough \u2014 the description will be used as input to another agent that cannot see the image.";
200
+ async function downloadAsBase64(urlPrivate, botToken) {
201
+ try {
202
+ const response = await fetch(urlPrivate, {
203
+ headers: { Authorization: `Bearer ${botToken}` },
204
+ signal: AbortSignal.timeout(3e4)
205
+ });
206
+ if (!response.ok) return null;
207
+ const buffer = Buffer.from(await response.arrayBuffer());
208
+ return buffer.toString("base64");
209
+ } catch {
210
+ return null;
211
+ }
212
+ }
213
+ function createImageEnricher(config) {
214
+ const { apiKey, model = "claude-sonnet-4-6", maxTokens = 1024, prompt = DEFAULT_PROMPT } = config;
215
+ return async (att, botToken) => {
216
+ if (!att.url) return null;
217
+ const mediaType = att.mimeType ?? "image/jpeg";
218
+ const base64 = await downloadAsBase64(att.url, botToken);
219
+ if (!base64) return null;
220
+ const anthropicModule = "@anthropic-ai/sdk";
221
+ const sdk = await import(anthropicModule);
222
+ const client = new sdk.default({ apiKey });
223
+ try {
224
+ const message = await client.messages.create({
225
+ model,
226
+ max_tokens: maxTokens,
227
+ messages: [
228
+ {
229
+ role: "user",
230
+ content: [
231
+ {
232
+ type: "image",
233
+ source: { type: "base64", media_type: mediaType, data: base64 }
234
+ },
235
+ { type: "text", text: prompt }
236
+ ]
237
+ }
238
+ ]
239
+ });
240
+ const textBlock = message.content.find((c) => c.type === "text");
241
+ return textBlock?.text ?? null;
242
+ } catch {
243
+ return null;
244
+ }
245
+ };
246
+ }
247
+
248
+ // src/channels/slack/enrichers/pdf-enricher.ts
249
+ var DEFAULT_PROMPT2 = "Summarize this PDF in detail. Include all key facts, numbers, dates, names, and any structured content (tables, lists). The summary will be used as input to another agent that cannot see the PDF.";
250
+ async function downloadAsBase642(urlPrivate, botToken) {
251
+ try {
252
+ const response = await fetch(urlPrivate, {
253
+ headers: { Authorization: `Bearer ${botToken}` },
254
+ signal: AbortSignal.timeout(6e4)
255
+ });
256
+ if (!response.ok) return null;
257
+ const buffer = Buffer.from(await response.arrayBuffer());
258
+ return buffer.toString("base64");
259
+ } catch {
260
+ return null;
261
+ }
262
+ }
263
+ function createPdfEnricher(config) {
264
+ const { apiKey, model = "claude-sonnet-4-6", maxTokens = 4096, prompt = DEFAULT_PROMPT2 } = config;
265
+ return async (att, botToken) => {
266
+ if (!att.url) return null;
267
+ const base64 = await downloadAsBase642(att.url, botToken);
268
+ if (!base64) return null;
269
+ const anthropicModule = "@anthropic-ai/sdk";
270
+ const sdk = await import(anthropicModule);
271
+ const client = new sdk.default({ apiKey });
272
+ try {
273
+ const message = await client.messages.create({
274
+ model,
275
+ max_tokens: maxTokens,
276
+ messages: [
277
+ {
278
+ role: "user",
279
+ content: [
280
+ {
281
+ type: "document",
282
+ source: { type: "base64", media_type: "application/pdf", data: base64 }
283
+ },
284
+ { type: "text", text: prompt }
285
+ ]
286
+ }
287
+ ]
288
+ });
289
+ const textBlock = message.content.find((c) => c.type === "text");
290
+ return textBlock?.text ?? null;
291
+ } catch {
292
+ return null;
293
+ }
294
+ };
295
+ }
165
296
  export {
166
297
  SlackAdapter,
167
298
  SlackBoltAdapter,
299
+ createImageEnricher,
300
+ createPdfEnricher,
168
301
  createSlackAdapter as default,
169
302
  downloadAudio,
303
+ enrichAttachments,
170
304
  enrichVoiceMessage,
171
305
  extractVoiceTranscript,
172
306
  formatForSlack,
@@ -1,4 +1,4 @@
1
- import { C as ChannelAdapter, c as ChannelMeta, a as ChannelCapabilities, I as InboundMessage, b as ChannelConfig, H as HealthStatus, O as OutboundPayload, S as SendResult } from '../../channel-06G0vbIn.js';
1
+ import { C as ChannelAdapter, d as ChannelMeta, b as ChannelCapabilities, I as InboundMessage, c as ChannelConfig, H as HealthStatus, O as OutboundPayload, S as SendResult } from '../../channel-DziSPayj.js';
2
2
 
3
3
  /**
4
4
  * WebhookAdapter — ChannelAdapter implementation for webhook-based channels.