opensentinel 2.1.1

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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +283 -0
  3. package/dist/bot-KJ26BG56.js +15 -0
  4. package/dist/bot-KJ26BG56.js.map +1 -0
  5. package/dist/charts-MMXM6BWW.js +241 -0
  6. package/dist/charts-MMXM6BWW.js.map +1 -0
  7. package/dist/chunk-4LVWXUNC.js +1079 -0
  8. package/dist/chunk-4LVWXUNC.js.map +1 -0
  9. package/dist/chunk-4TG2IG5K.js +5249 -0
  10. package/dist/chunk-4TG2IG5K.js.map +1 -0
  11. package/dist/chunk-6DRDKB45.js +251 -0
  12. package/dist/chunk-6DRDKB45.js.map +1 -0
  13. package/dist/chunk-6SNHU3CY.js +123 -0
  14. package/dist/chunk-6SNHU3CY.js.map +1 -0
  15. package/dist/chunk-CI6Q63MM.js +1613 -0
  16. package/dist/chunk-CI6Q63MM.js.map +1 -0
  17. package/dist/chunk-CQ4JURG7.js +57 -0
  18. package/dist/chunk-CQ4JURG7.js.map +1 -0
  19. package/dist/chunk-F6QUZQGI.js +51 -0
  20. package/dist/chunk-F6QUZQGI.js.map +1 -0
  21. package/dist/chunk-GK3E2I7A.js +216 -0
  22. package/dist/chunk-GK3E2I7A.js.map +1 -0
  23. package/dist/chunk-GUBEEYDW.js +211 -0
  24. package/dist/chunk-GUBEEYDW.js.map +1 -0
  25. package/dist/chunk-GVJVEWHI.js +29 -0
  26. package/dist/chunk-GVJVEWHI.js.map +1 -0
  27. package/dist/chunk-HH2HBTQM.js +806 -0
  28. package/dist/chunk-HH2HBTQM.js.map +1 -0
  29. package/dist/chunk-JXUP2X7V.js +129 -0
  30. package/dist/chunk-JXUP2X7V.js.map +1 -0
  31. package/dist/chunk-KHNYJY2Z.js +178 -0
  32. package/dist/chunk-KHNYJY2Z.js.map +1 -0
  33. package/dist/chunk-L3F43VPB.js +652 -0
  34. package/dist/chunk-L3F43VPB.js.map +1 -0
  35. package/dist/chunk-L3PDU3XN.js +803 -0
  36. package/dist/chunk-L3PDU3XN.js.map +1 -0
  37. package/dist/chunk-NSBPE2FW.js +17 -0
  38. package/dist/chunk-NSBPE2FW.js.map +1 -0
  39. package/dist/cli.d.ts +1 -0
  40. package/dist/cli.js +52 -0
  41. package/dist/cli.js.map +1 -0
  42. package/dist/commands/setup.d.ts +9 -0
  43. package/dist/commands/setup.js +374 -0
  44. package/dist/commands/setup.js.map +1 -0
  45. package/dist/commands/start.d.ts +8 -0
  46. package/dist/commands/start.js +27 -0
  47. package/dist/commands/start.js.map +1 -0
  48. package/dist/commands/status.d.ts +8 -0
  49. package/dist/commands/status.js +57 -0
  50. package/dist/commands/status.js.map +1 -0
  51. package/dist/commands/stop.d.ts +8 -0
  52. package/dist/commands/stop.js +37 -0
  53. package/dist/commands/stop.js.map +1 -0
  54. package/dist/commands/utils.d.ts +50 -0
  55. package/dist/commands/utils.js +36 -0
  56. package/dist/commands/utils.js.map +1 -0
  57. package/dist/discord-ZOJFTVTB.js +49 -0
  58. package/dist/discord-ZOJFTVTB.js.map +1 -0
  59. package/dist/imessage-JFRB6EJ7.js +14 -0
  60. package/dist/imessage-JFRB6EJ7.js.map +1 -0
  61. package/dist/lib.d.ts +855 -0
  62. package/dist/lib.js +274 -0
  63. package/dist/lib.js.map +1 -0
  64. package/dist/mcp-LS7Q3Z5W.js +30 -0
  65. package/dist/mcp-LS7Q3Z5W.js.map +1 -0
  66. package/dist/scheduler-EZ7CZMCS.js +42 -0
  67. package/dist/scheduler-EZ7CZMCS.js.map +1 -0
  68. package/dist/signal-T3MCSULM.js +14 -0
  69. package/dist/signal-T3MCSULM.js.map +1 -0
  70. package/dist/slack-N2M4FHAJ.js +54 -0
  71. package/dist/slack-N2M4FHAJ.js.map +1 -0
  72. package/dist/src-K7GASHRH.js +430 -0
  73. package/dist/src-K7GASHRH.js.map +1 -0
  74. package/dist/tools-24GZHYRF.js +16 -0
  75. package/dist/tools-24GZHYRF.js.map +1 -0
  76. package/dist/whatsapp-VCRUPAO5.js +14 -0
  77. package/dist/whatsapp-VCRUPAO5.js.map +1 -0
  78. package/drizzle/0000_chilly_shinobi_shaw.sql +75 -0
  79. package/drizzle/0001_freezing_shape.sql +274 -0
  80. package/drizzle/meta/0000_snapshot.json +529 -0
  81. package/drizzle/meta/0001_snapshot.json +2576 -0
  82. package/drizzle/meta/_journal.json +20 -0
  83. package/package.json +98 -0
@@ -0,0 +1,803 @@
1
+ import {
2
+ transcribeAudio
3
+ } from "./chunk-GVJVEWHI.js";
4
+ import {
5
+ scheduleReminder
6
+ } from "./chunk-4LVWXUNC.js";
7
+ import {
8
+ chatWithTools
9
+ } from "./chunk-CI6Q63MM.js";
10
+
11
+ // src/inputs/slack/index.ts
12
+ import { createRequire } from "module";
13
+
14
+ // src/inputs/slack/commands.ts
15
+ var sessions = /* @__PURE__ */ new Map();
16
+ var MAX_HISTORY = 20;
17
+ function getSession(userId) {
18
+ if (!sessions.has(userId)) {
19
+ sessions.set(userId, []);
20
+ }
21
+ return sessions.get(userId);
22
+ }
23
+ function addToSession(userId, message) {
24
+ const session = getSession(userId);
25
+ session.push(message);
26
+ if (session.length > MAX_HISTORY) {
27
+ sessions.set(userId, session.slice(-MAX_HISTORY));
28
+ }
29
+ }
30
+ function clearSession(userId) {
31
+ sessions.set(userId, []);
32
+ }
33
+ var askCommand = {
34
+ command: "/opensentinel-ask",
35
+ description: "Ask OpenSentinel a question",
36
+ usage: "/opensentinel-ask <question>",
37
+ async handler({ command, ack, respond, client }) {
38
+ await ack();
39
+ const question = command.text.trim();
40
+ const userId = command.user_id;
41
+ if (!question) {
42
+ await respond({
43
+ response_type: "ephemeral",
44
+ text: "Please provide a question. Usage: `/opensentinel-ask <question>`"
45
+ });
46
+ return;
47
+ }
48
+ try {
49
+ addToSession(userId, { role: "user", content: question });
50
+ const response = await chatWithTools(
51
+ getSession(userId),
52
+ `slack:${userId}`
53
+ );
54
+ addToSession(userId, { role: "assistant", content: response.content });
55
+ let finalResponse = response.content;
56
+ if (response.toolsUsed && response.toolsUsed.length > 0) {
57
+ const toolList = [...new Set(response.toolsUsed)].join(", ");
58
+ finalResponse = `_Used: ${toolList}_
59
+
60
+ ${response.content}`;
61
+ }
62
+ await respond({
63
+ response_type: "in_channel",
64
+ text: finalResponse,
65
+ mrkdwn: true
66
+ });
67
+ console.log(
68
+ `[Slack] Processed /opensentinel-ask from ${userId}. Tokens: ${response.inputTokens}/${response.outputTokens}`
69
+ );
70
+ } catch (error) {
71
+ console.error("[Slack] Error processing /opensentinel-ask:", error);
72
+ await respond({
73
+ response_type: "ephemeral",
74
+ text: "Sorry, I encountered an error processing your question. Please try again."
75
+ });
76
+ }
77
+ }
78
+ };
79
+ var chatCommand = {
80
+ command: "/opensentinel-chat",
81
+ description: "Continue a conversation with OpenSentinel",
82
+ usage: "/opensentinel-chat <message>",
83
+ async handler({ command, ack, respond }) {
84
+ await ack();
85
+ const message = command.text.trim();
86
+ const userId = command.user_id;
87
+ if (!message) {
88
+ await respond({
89
+ response_type: "ephemeral",
90
+ text: "Please provide a message. Usage: `/opensentinel-chat <message>`"
91
+ });
92
+ return;
93
+ }
94
+ try {
95
+ addToSession(userId, { role: "user", content: message });
96
+ const response = await chatWithTools(
97
+ getSession(userId),
98
+ `slack:${userId}`
99
+ );
100
+ addToSession(userId, { role: "assistant", content: response.content });
101
+ let finalResponse = response.content;
102
+ if (response.toolsUsed && response.toolsUsed.length > 0) {
103
+ const toolList = [...new Set(response.toolsUsed)].join(", ");
104
+ finalResponse = `_Used: ${toolList}_
105
+
106
+ ${response.content}`;
107
+ }
108
+ await respond({
109
+ response_type: "in_channel",
110
+ text: finalResponse,
111
+ mrkdwn: true
112
+ });
113
+ console.log(
114
+ `[Slack] Processed /opensentinel-chat from ${userId}. Tokens: ${response.inputTokens}/${response.outputTokens}`
115
+ );
116
+ } catch (error) {
117
+ console.error("[Slack] Error processing /opensentinel-chat:", error);
118
+ await respond({
119
+ response_type: "ephemeral",
120
+ text: "Sorry, I encountered an error processing your message. Please try again."
121
+ });
122
+ }
123
+ }
124
+ };
125
+ var clearCommand = {
126
+ command: "/opensentinel-clear",
127
+ description: "Clear your conversation history with OpenSentinel",
128
+ usage: "/opensentinel-clear",
129
+ async handler({ command, ack, respond }) {
130
+ await ack();
131
+ clearSession(command.user_id);
132
+ await respond({
133
+ response_type: "ephemeral",
134
+ text: "Conversation history cleared."
135
+ });
136
+ console.log(`[Slack] Cleared history for ${command.user_id}`);
137
+ }
138
+ };
139
+ var remindCommand = {
140
+ command: "/opensentinel-remind",
141
+ description: "Set a reminder",
142
+ usage: "/opensentinel-remind <time><s/m/h> <message>",
143
+ async handler({ command, ack, respond }) {
144
+ await ack();
145
+ const text = command.text.trim();
146
+ const match = text.match(/^(\d+)(s|m|h)\s+(.+)$/i);
147
+ if (!match) {
148
+ await respond({
149
+ response_type: "ephemeral",
150
+ text: "Invalid format. Use: `/opensentinel-remind <number><s/m/h> <message>`\n\nExamples:\n\u2022 `/opensentinel-remind 5m Check the oven`\n\u2022 `/opensentinel-remind 1h Call mom`\n\u2022 `/opensentinel-remind 30s Test reminder`"
151
+ });
152
+ return;
153
+ }
154
+ const [, amount, unit, message] = match;
155
+ const multipliers = {
156
+ s: 1e3,
157
+ m: 60 * 1e3,
158
+ h: 60 * 60 * 1e3
159
+ };
160
+ const delayMs = parseInt(amount) * multipliers[unit.toLowerCase()];
161
+ try {
162
+ await scheduleReminder(
163
+ message,
164
+ delayMs,
165
+ `slack:${command.channel_id}:${command.user_id}`
166
+ );
167
+ const timeStr = unit === "s" ? "seconds" : unit === "m" ? "minutes" : "hours";
168
+ await respond({
169
+ response_type: "ephemeral",
170
+ text: `Reminder set for ${amount} ${timeStr}: "${message}"`
171
+ });
172
+ console.log(
173
+ `[Slack] Reminder set by ${command.user_id}: ${amount}${unit} - "${message}"`
174
+ );
175
+ } catch (error) {
176
+ console.error("[Slack] Error setting reminder:", error);
177
+ await respond({
178
+ response_type: "ephemeral",
179
+ text: "Sorry, I couldn't set the reminder. Please try again."
180
+ });
181
+ }
182
+ }
183
+ };
184
+ var statusCommand = {
185
+ command: "/opensentinel-status",
186
+ description: "Check OpenSentinel status and capabilities",
187
+ usage: "/opensentinel-status",
188
+ async handler({ command, ack, respond }) {
189
+ await ack();
190
+ const session = getSession(command.user_id);
191
+ const historyCount = session.length;
192
+ await respond({
193
+ response_type: "ephemeral",
194
+ text: `*OpenSentinel Status*
195
+
196
+ Bot: Online
197
+ Your conversation history: ${historyCount} messages
198
+
199
+ *Capabilities:*
200
+ \u2022 Chat and answer questions using Claude AI
201
+ \u2022 Execute shell commands
202
+ \u2022 Read and write files
203
+ \u2022 Search the web
204
+ \u2022 Remember important information
205
+ \u2022 Set reminders
206
+
207
+ Use \`/opensentinel-help\` for available commands.`
208
+ });
209
+ }
210
+ };
211
+ var helpCommand = {
212
+ command: "/opensentinel-help",
213
+ description: "Show available OpenSentinel commands",
214
+ usage: "/opensentinel-help",
215
+ async handler({ ack, respond }) {
216
+ await ack();
217
+ await respond({
218
+ response_type: "ephemeral",
219
+ text: "*OpenSentinel Commands*\n\n`/opensentinel-ask <question>` - Ask a single question\n`/opensentinel-chat <message>` - Continue a conversation\n`/opensentinel-clear` - Clear your conversation history\n`/opensentinel-remind <time> <message>` - Set a reminder\n`/opensentinel-status` - Check bot status\n`/opensentinel-help` - Show this help message\n\n*Tips:*\n\u2022 Use `/opensentinel-chat` for multi-turn conversations with context\n\u2022 Use `/opensentinel-ask` for quick one-off questions\n\u2022 Mention @OpenSentinel in any channel to chat directly\n\u2022 DM the bot for private conversations"
220
+ });
221
+ }
222
+ };
223
+ var mainCommand = {
224
+ command: "/opensentinel",
225
+ description: "OpenSentinel AI assistant",
226
+ usage: "/opensentinel <ask|chat|clear|remind|status|help> [args]",
227
+ async handler({ command, ack, respond }) {
228
+ await ack();
229
+ const [subcommand, ...args] = command.text.trim().split(/\s+/);
230
+ const argsText = args.join(" ");
231
+ switch (subcommand?.toLowerCase()) {
232
+ case "ask":
233
+ if (!argsText) {
234
+ await respond({
235
+ response_type: "ephemeral",
236
+ text: "Please provide a question. Usage: `/opensentinel ask <question>`"
237
+ });
238
+ return;
239
+ }
240
+ command.text = argsText;
241
+ await askCommand.handler({ command, ack, respond });
242
+ break;
243
+ case "chat":
244
+ if (!argsText) {
245
+ await respond({
246
+ response_type: "ephemeral",
247
+ text: "Please provide a message. Usage: `/opensentinel chat <message>`"
248
+ });
249
+ return;
250
+ }
251
+ command.text = argsText;
252
+ await chatCommand.handler({ command, ack, respond });
253
+ break;
254
+ case "clear":
255
+ await clearCommand.handler({ command, ack, respond });
256
+ break;
257
+ case "remind":
258
+ command.text = argsText;
259
+ await remindCommand.handler({ command, ack, respond });
260
+ break;
261
+ case "status":
262
+ await statusCommand.handler({ command, ack, respond });
263
+ break;
264
+ case "help":
265
+ default:
266
+ await respond({
267
+ response_type: "ephemeral",
268
+ text: "*OpenSentinel Commands*\n\n`/opensentinel ask <question>` - Ask a single question\n`/opensentinel chat <message>` - Continue a conversation\n`/opensentinel clear` - Clear your conversation history\n`/opensentinel remind <time> <message>` - Set a reminder\n`/opensentinel status` - Check bot status\n`/opensentinel help` - Show this help message\n\n*Or use individual commands:*\n`/opensentinel-ask`, `/opensentinel-chat`, `/opensentinel-clear`, `/opensentinel-remind`, `/opensentinel-status`, `/opensentinel-help`"
269
+ });
270
+ break;
271
+ }
272
+ }
273
+ };
274
+ var slashCommands = [
275
+ mainCommand,
276
+ askCommand,
277
+ chatCommand,
278
+ clearCommand,
279
+ remindCommand,
280
+ statusCommand,
281
+ helpCommand
282
+ ];
283
+ function getCommand(name) {
284
+ return slashCommands.find((cmd) => cmd.command === name);
285
+ }
286
+ function getCommandNames() {
287
+ return slashCommands.map((cmd) => cmd.command);
288
+ }
289
+ function splitMessage(text, maxLength = 3e3) {
290
+ const chunks = [];
291
+ let remaining = text;
292
+ while (remaining.length > 0) {
293
+ if (remaining.length <= maxLength) {
294
+ chunks.push(remaining);
295
+ break;
296
+ }
297
+ let breakPoint = remaining.lastIndexOf("\n", maxLength);
298
+ if (breakPoint === -1 || breakPoint < maxLength / 2) {
299
+ breakPoint = remaining.lastIndexOf(" ", maxLength);
300
+ }
301
+ if (breakPoint === -1 || breakPoint < maxLength / 2) {
302
+ breakPoint = maxLength;
303
+ }
304
+ chunks.push(remaining.slice(0, breakPoint));
305
+ remaining = remaining.slice(breakPoint).trim();
306
+ }
307
+ return chunks;
308
+ }
309
+ function formatAsBlocks(text) {
310
+ return [
311
+ {
312
+ type: "section",
313
+ text: {
314
+ type: "mrkdwn",
315
+ text
316
+ }
317
+ }
318
+ ];
319
+ }
320
+ function createErrorBlocks(message) {
321
+ return [
322
+ {
323
+ type: "section",
324
+ text: {
325
+ type: "mrkdwn",
326
+ text: `:warning: *Error*
327
+ ${message}`
328
+ }
329
+ }
330
+ ];
331
+ }
332
+ function createSuccessBlocks(message) {
333
+ return [
334
+ {
335
+ type: "section",
336
+ text: {
337
+ type: "mrkdwn",
338
+ text: `:white_check_mark: ${message}`
339
+ }
340
+ }
341
+ ];
342
+ }
343
+
344
+ // src/inputs/slack/index.ts
345
+ var require2 = createRequire(import.meta.url);
346
+ var { App, ExpressReceiver } = require2("@slack/bolt");
347
+ var { WebClient } = require2("@slack/web-api");
348
+ var SlackBot = class {
349
+ app;
350
+ config;
351
+ client;
352
+ receiver;
353
+ isRunning = false;
354
+ constructor(config) {
355
+ this.config = {
356
+ allowDMs: true,
357
+ allowMentions: true,
358
+ allowThreadReplies: true,
359
+ ...config
360
+ };
361
+ if (!config.socketMode) {
362
+ this.receiver = new ExpressReceiver({
363
+ signingSecret: config.signingSecret
364
+ });
365
+ }
366
+ this.app = new App({
367
+ token: config.token,
368
+ signingSecret: config.signingSecret,
369
+ socketMode: config.socketMode,
370
+ appToken: config.appToken,
371
+ receiver: this.receiver
372
+ });
373
+ this.client = new WebClient(config.token);
374
+ this.setupEventHandlers();
375
+ this.setupSlashCommands();
376
+ }
377
+ /**
378
+ * Set up Slack event handlers
379
+ */
380
+ setupEventHandlers() {
381
+ if (this.config.allowMentions) {
382
+ this.app.event("app_mention", async (args) => {
383
+ await this.handleAppMention(args);
384
+ });
385
+ }
386
+ if (this.config.allowDMs) {
387
+ this.app.message(async (args) => {
388
+ await this.handleMessage(args);
389
+ });
390
+ }
391
+ this.app.event("file_shared", async (args) => {
392
+ await this.handleFileShared(args);
393
+ });
394
+ this.app.error(async (error) => {
395
+ console.error("[Slack] App error:", error);
396
+ });
397
+ }
398
+ /**
399
+ * Set up slash command handlers
400
+ */
401
+ setupSlashCommands() {
402
+ for (const cmd of slashCommands) {
403
+ this.app.command(cmd.command, async (args) => {
404
+ if (!this.isUserAuthorized(args.command.user_id, args.command.channel_id)) {
405
+ await args.ack();
406
+ await args.respond({
407
+ response_type: "ephemeral",
408
+ text: "You are not authorized to use this bot."
409
+ });
410
+ return;
411
+ }
412
+ await cmd.handler(args);
413
+ });
414
+ }
415
+ }
416
+ /**
417
+ * Handle app mentions
418
+ */
419
+ async handleAppMention(args) {
420
+ const { event, say, client } = args;
421
+ if (!this.isUserAuthorized(event.user, event.channel)) {
422
+ return;
423
+ }
424
+ const text = event.text.replace(/<@[A-Z0-9]+>/gi, "").trim();
425
+ if (!text) {
426
+ await say({
427
+ text: "Hi! How can I help you? Mention me with a question or use `/sentinel help` for available commands.",
428
+ thread_ts: event.thread_ts || event.ts
429
+ });
430
+ return;
431
+ }
432
+ const userId = event.user;
433
+ const sessionKey = event.thread_ts ? `${userId}:thread:${event.thread_ts}` : userId;
434
+ try {
435
+ await client.reactions.add({
436
+ channel: event.channel,
437
+ timestamp: event.ts,
438
+ name: "hourglass_flowing_sand"
439
+ });
440
+ addToSession(sessionKey, { role: "user", content: text });
441
+ const response = await chatWithTools(
442
+ getSession(sessionKey),
443
+ `slack:${userId}`
444
+ );
445
+ addToSession(sessionKey, { role: "assistant", content: response.content });
446
+ let finalResponse = response.content;
447
+ if (response.toolsUsed && response.toolsUsed.length > 0) {
448
+ const toolList = [...new Set(response.toolsUsed)].join(", ");
449
+ finalResponse = `_Used: ${toolList}_
450
+
451
+ ${response.content}`;
452
+ }
453
+ try {
454
+ await client.reactions.remove({
455
+ channel: event.channel,
456
+ timestamp: event.ts,
457
+ name: "hourglass_flowing_sand"
458
+ });
459
+ } catch {
460
+ }
461
+ if (finalResponse.length > 3e3) {
462
+ const chunks = splitMessage(finalResponse, 3e3);
463
+ for (const chunk of chunks) {
464
+ await say({
465
+ text: chunk,
466
+ thread_ts: event.thread_ts || event.ts,
467
+ mrkdwn: true
468
+ });
469
+ }
470
+ } else {
471
+ await say({
472
+ text: finalResponse,
473
+ thread_ts: event.thread_ts || event.ts,
474
+ mrkdwn: true
475
+ });
476
+ }
477
+ console.log(
478
+ `[Slack] Processed mention from ${userId}. Tokens: ${response.inputTokens}/${response.outputTokens}` + (response.toolsUsed ? ` Tools: ${response.toolsUsed.join(", ")}` : "")
479
+ );
480
+ } catch (error) {
481
+ console.error("[Slack] Error processing mention:", error);
482
+ try {
483
+ await client.reactions.remove({
484
+ channel: event.channel,
485
+ timestamp: event.ts,
486
+ name: "hourglass_flowing_sand"
487
+ });
488
+ await client.reactions.add({
489
+ channel: event.channel,
490
+ timestamp: event.ts,
491
+ name: "x"
492
+ });
493
+ } catch {
494
+ }
495
+ await say({
496
+ text: "Sorry, I encountered an error processing your message. Please try again.",
497
+ thread_ts: event.thread_ts || event.ts
498
+ });
499
+ }
500
+ }
501
+ /**
502
+ * Handle direct messages
503
+ */
504
+ async handleMessage(args) {
505
+ const { event, say, client, message } = args;
506
+ const genericMessage = message;
507
+ if (genericMessage.subtype) return;
508
+ if ("bot_id" in event) return;
509
+ const channelType = event.channel_type;
510
+ if (channelType !== "im" && channelType !== "mpim") return;
511
+ const userId = message.user;
512
+ if (!userId) return;
513
+ if (!this.isUserAuthorized(userId, event.channel)) {
514
+ return;
515
+ }
516
+ const text = message.text || "";
517
+ if (!text.trim()) return;
518
+ const threadedMessage = message;
519
+ const sessionKey = threadedMessage.thread_ts ? `${userId}:thread:${threadedMessage.thread_ts}` : userId;
520
+ try {
521
+ await client.reactions.add({
522
+ channel: event.channel,
523
+ timestamp: message.ts,
524
+ name: "hourglass_flowing_sand"
525
+ });
526
+ addToSession(sessionKey, { role: "user", content: text });
527
+ const response = await chatWithTools(
528
+ getSession(sessionKey),
529
+ `slack:${userId}`
530
+ );
531
+ addToSession(sessionKey, { role: "assistant", content: response.content });
532
+ let finalResponse = response.content;
533
+ if (response.toolsUsed && response.toolsUsed.length > 0) {
534
+ const toolList = [...new Set(response.toolsUsed)].join(", ");
535
+ finalResponse = `_Used: ${toolList}_
536
+
537
+ ${response.content}`;
538
+ }
539
+ try {
540
+ await client.reactions.remove({
541
+ channel: event.channel,
542
+ timestamp: message.ts,
543
+ name: "hourglass_flowing_sand"
544
+ });
545
+ } catch {
546
+ }
547
+ const replyTs = this.config.allowThreadReplies && threadedMessage.thread_ts ? threadedMessage.thread_ts : void 0;
548
+ if (finalResponse.length > 3e3) {
549
+ const chunks = splitMessage(finalResponse, 3e3);
550
+ for (const chunk of chunks) {
551
+ await say({
552
+ text: chunk,
553
+ thread_ts: replyTs,
554
+ mrkdwn: true
555
+ });
556
+ }
557
+ } else {
558
+ await say({
559
+ text: finalResponse,
560
+ thread_ts: replyTs,
561
+ mrkdwn: true
562
+ });
563
+ }
564
+ console.log(
565
+ `[Slack] Processed DM from ${userId}. Tokens: ${response.inputTokens}/${response.outputTokens}` + (response.toolsUsed ? ` Tools: ${response.toolsUsed.join(", ")}` : "")
566
+ );
567
+ } catch (error) {
568
+ console.error("[Slack] Error processing message:", error);
569
+ try {
570
+ await client.reactions.remove({
571
+ channel: event.channel,
572
+ timestamp: message.ts,
573
+ name: "hourglass_flowing_sand"
574
+ });
575
+ await client.reactions.add({
576
+ channel: event.channel,
577
+ timestamp: message.ts,
578
+ name: "x"
579
+ });
580
+ } catch {
581
+ }
582
+ await say({
583
+ text: "Sorry, I encountered an error processing your message. Please try again."
584
+ });
585
+ }
586
+ }
587
+ /**
588
+ * Handle file shared events
589
+ */
590
+ async handleFileShared(args) {
591
+ const { event, client } = args;
592
+ try {
593
+ const fileInfo = await client.files.info({ file: event.file_id });
594
+ const file = fileInfo.file;
595
+ if (!file) return;
596
+ const mimetype = file.mimetype || "";
597
+ if (mimetype.startsWith("audio/")) {
598
+ if (file.url_private_download) {
599
+ const response = await fetch(file.url_private_download, {
600
+ headers: {
601
+ Authorization: `Bearer ${this.config.token}`
602
+ }
603
+ });
604
+ if (response.ok) {
605
+ const audioBuffer = await response.arrayBuffer();
606
+ const transcription = await transcribeAudio(Buffer.from(audioBuffer));
607
+ if (transcription) {
608
+ const channelId = file.channels && file.channels[0] || file.ims && file.ims[0];
609
+ if (channelId) {
610
+ await client.chat.postMessage({
611
+ channel: channelId,
612
+ text: `Audio transcription: "${transcription}"`,
613
+ mrkdwn: true
614
+ });
615
+ console.log(
616
+ `[Slack] Transcribed audio file: ${file.name}`
617
+ );
618
+ }
619
+ }
620
+ }
621
+ }
622
+ }
623
+ } catch (error) {
624
+ console.error("[Slack] Error processing file:", error);
625
+ }
626
+ }
627
+ /**
628
+ * Check if a user is authorized to use the bot
629
+ */
630
+ isUserAuthorized(userId, channelId) {
631
+ if (!this.config.allowedUserIds?.length && !this.config.allowedChannelIds?.length) {
632
+ return true;
633
+ }
634
+ if (this.config.allowedUserIds?.includes(userId)) {
635
+ return true;
636
+ }
637
+ if (this.config.allowedChannelIds?.includes(channelId)) {
638
+ return true;
639
+ }
640
+ return false;
641
+ }
642
+ /**
643
+ * Start the Slack bot
644
+ */
645
+ async start() {
646
+ const port = this.config.port || 3e3;
647
+ console.log("[Slack] Starting bot...");
648
+ await this.app.start(port);
649
+ this.isRunning = true;
650
+ if (this.config.socketMode) {
651
+ console.log("[Slack] Bot started in Socket Mode");
652
+ } else {
653
+ console.log(`[Slack] Bot started on port ${port}`);
654
+ }
655
+ }
656
+ /**
657
+ * Stop the Slack bot
658
+ */
659
+ async stop() {
660
+ console.log("[Slack] Stopping bot...");
661
+ await this.app.stop();
662
+ this.isRunning = false;
663
+ console.log("[Slack] Bot stopped");
664
+ }
665
+ /**
666
+ * Get the Slack app instance
667
+ */
668
+ getApp() {
669
+ return this.app;
670
+ }
671
+ /**
672
+ * Get the Slack Web API client
673
+ */
674
+ getClient() {
675
+ return this.client;
676
+ }
677
+ /**
678
+ * Check if bot is running
679
+ */
680
+ running() {
681
+ return this.isRunning;
682
+ }
683
+ /**
684
+ * Send a message to a channel
685
+ */
686
+ async sendMessage(channelId, text, options) {
687
+ return this.client.chat.postMessage({
688
+ channel: channelId,
689
+ text,
690
+ thread_ts: options?.threadTs,
691
+ mrkdwn: options?.mrkdwn ?? true,
692
+ blocks: options?.blocks
693
+ });
694
+ }
695
+ /**
696
+ * Send a message with blocks
697
+ */
698
+ async sendBlocks(channelId, blocks, text, threadTs) {
699
+ return this.client.chat.postMessage({
700
+ channel: channelId,
701
+ text: text || "Message",
702
+ blocks,
703
+ thread_ts: threadTs
704
+ });
705
+ }
706
+ /**
707
+ * Send a file to a channel
708
+ */
709
+ async sendFile(channelId, content, filename, options) {
710
+ await this.client.files.uploadV2({
711
+ channel_id: channelId,
712
+ file: content,
713
+ filename,
714
+ title: options?.title,
715
+ initial_comment: options?.initialComment,
716
+ thread_ts: options?.threadTs
717
+ });
718
+ }
719
+ /**
720
+ * Reply to a thread
721
+ */
722
+ async replyToThread(channelId, threadTs, text, mrkdwn) {
723
+ return this.client.chat.postMessage({
724
+ channel: channelId,
725
+ text,
726
+ thread_ts: threadTs,
727
+ mrkdwn: mrkdwn ?? true
728
+ });
729
+ }
730
+ /**
731
+ * Add a reaction to a message
732
+ */
733
+ async addReaction(channelId, timestamp, emoji) {
734
+ await this.client.reactions.add({
735
+ channel: channelId,
736
+ timestamp,
737
+ name: emoji
738
+ });
739
+ }
740
+ /**
741
+ * Remove a reaction from a message
742
+ */
743
+ async removeReaction(channelId, timestamp, emoji) {
744
+ await this.client.reactions.remove({
745
+ channel: channelId,
746
+ timestamp,
747
+ name: emoji
748
+ });
749
+ }
750
+ /**
751
+ * Get user info
752
+ */
753
+ async getUserInfo(userId) {
754
+ const result = await this.client.users.info({ user: userId });
755
+ return result.user;
756
+ }
757
+ /**
758
+ * Get channel info
759
+ */
760
+ async getChannelInfo(channelId) {
761
+ const result = await this.client.conversations.info({ channel: channelId });
762
+ return result.channel;
763
+ }
764
+ /**
765
+ * Get the Express receiver (for adding custom routes)
766
+ */
767
+ getReceiver() {
768
+ return this.receiver;
769
+ }
770
+ };
771
+ function createSlackBot(config) {
772
+ return new SlackBot(config);
773
+ }
774
+ var slack_default = {
775
+ createSlackBot,
776
+ SlackBot,
777
+ slashCommands
778
+ };
779
+
780
+ export {
781
+ sessions,
782
+ getSession,
783
+ addToSession,
784
+ clearSession,
785
+ askCommand,
786
+ chatCommand,
787
+ clearCommand,
788
+ remindCommand,
789
+ statusCommand,
790
+ helpCommand,
791
+ mainCommand,
792
+ slashCommands,
793
+ getCommand,
794
+ getCommandNames,
795
+ splitMessage,
796
+ formatAsBlocks,
797
+ createErrorBlocks,
798
+ createSuccessBlocks,
799
+ SlackBot,
800
+ createSlackBot,
801
+ slack_default
802
+ };
803
+ //# sourceMappingURL=chunk-L3PDU3XN.js.map