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,251 @@
1
+ import {
2
+ textToSpeech
3
+ } from "./chunk-F6QUZQGI.js";
4
+ import {
5
+ transcribeAudio
6
+ } from "./chunk-GVJVEWHI.js";
7
+ import {
8
+ scheduleReminder
9
+ } from "./chunk-4LVWXUNC.js";
10
+ import {
11
+ chatWithTools
12
+ } from "./chunk-CI6Q63MM.js";
13
+ import {
14
+ env
15
+ } from "./chunk-4TG2IG5K.js";
16
+
17
+ // src/inputs/telegram/bot.ts
18
+ import { Bot, session } from "grammy";
19
+
20
+ // src/inputs/telegram/handlers.ts
21
+ import { InputFile } from "grammy";
22
+ var MAX_HISTORY = 20;
23
+ async function handleMessage(ctx) {
24
+ const text = ctx.message?.text;
25
+ if (!text) return;
26
+ ctx.session.messages.push({ role: "user", content: text });
27
+ if (ctx.session.messages.length > MAX_HISTORY) {
28
+ ctx.session.messages = ctx.session.messages.slice(-MAX_HISTORY);
29
+ }
30
+ await ctx.replyWithChatAction("typing");
31
+ try {
32
+ const response = await chatWithTools(
33
+ ctx.session.messages,
34
+ ctx.chat?.id?.toString(),
35
+ async () => {
36
+ await ctx.replyWithChatAction("typing");
37
+ }
38
+ );
39
+ ctx.session.messages.push({ role: "assistant", content: response.content });
40
+ let finalResponse = response.content;
41
+ if (response.toolsUsed && response.toolsUsed.length > 0) {
42
+ const toolList = [...new Set(response.toolsUsed)].join(", ");
43
+ finalResponse = `\u{1F527} _Used: ${toolList}_
44
+
45
+ ${response.content}`;
46
+ }
47
+ await sendResponse(ctx, finalResponse);
48
+ console.log(
49
+ `[Telegram] Processed message. Tokens: ${response.inputTokens}/${response.outputTokens}` + (response.toolsUsed ? ` Tools: ${response.toolsUsed.join(", ")}` : "")
50
+ );
51
+ } catch (error) {
52
+ console.error("Error processing message:", error);
53
+ await ctx.reply(
54
+ "Sorry, I encountered an error processing your message. Please try again."
55
+ );
56
+ }
57
+ }
58
+ async function handleVoice(ctx) {
59
+ const voice = ctx.message?.voice;
60
+ if (!voice) return;
61
+ await ctx.replyWithChatAction("typing");
62
+ try {
63
+ const file = await ctx.getFile();
64
+ const fileUrl = `https://api.telegram.org/file/bot${process.env.TELEGRAM_BOT_TOKEN}/${file.file_path}`;
65
+ const response = await fetch(fileUrl);
66
+ const audioBuffer = await response.arrayBuffer();
67
+ const transcription = await transcribeAudio(Buffer.from(audioBuffer));
68
+ if (!transcription) {
69
+ await ctx.reply("Sorry, I couldn't transcribe that voice message.");
70
+ return;
71
+ }
72
+ await ctx.reply(`\u{1F3A4} _"${transcription}"_`, { parse_mode: "Markdown" });
73
+ ctx.session.messages.push({ role: "user", content: transcription });
74
+ if (ctx.session.messages.length > MAX_HISTORY) {
75
+ ctx.session.messages = ctx.session.messages.slice(-MAX_HISTORY);
76
+ }
77
+ await ctx.replyWithChatAction("typing");
78
+ const aiResponse = await chatWithTools(
79
+ ctx.session.messages,
80
+ ctx.chat?.id?.toString()
81
+ );
82
+ ctx.session.messages.push({
83
+ role: "assistant",
84
+ content: aiResponse.content
85
+ });
86
+ await sendResponse(ctx, aiResponse.content);
87
+ if (aiResponse.content.length < 1e3 && aiResponse.content.length > 10) {
88
+ try {
89
+ await ctx.replyWithChatAction("record_voice");
90
+ const audioBuffer2 = await textToSpeech(aiResponse.content);
91
+ if (audioBuffer2) {
92
+ await ctx.replyWithVoice(new InputFile(audioBuffer2, "response.ogg"));
93
+ }
94
+ } catch (ttsError) {
95
+ console.error("TTS error:", ttsError);
96
+ }
97
+ }
98
+ console.log(
99
+ `[Telegram] Processed voice message. Tokens: ${aiResponse.inputTokens}/${aiResponse.outputTokens}`
100
+ );
101
+ } catch (error) {
102
+ console.error("Error processing voice message:", error);
103
+ await ctx.reply(
104
+ "Sorry, I encountered an error processing your voice message. Please try again."
105
+ );
106
+ }
107
+ }
108
+ async function sendResponse(ctx, text) {
109
+ const maxLength = 4096;
110
+ try {
111
+ if (text.length <= maxLength) {
112
+ await ctx.reply(text, { parse_mode: "Markdown" });
113
+ } else {
114
+ const chunks = splitMessage(text, maxLength);
115
+ for (const chunk of chunks) {
116
+ await ctx.reply(chunk, { parse_mode: "Markdown" });
117
+ }
118
+ }
119
+ } catch {
120
+ if (text.length <= maxLength) {
121
+ await ctx.reply(text);
122
+ } else {
123
+ const chunks = splitMessage(text, maxLength);
124
+ for (const chunk of chunks) {
125
+ await ctx.reply(chunk);
126
+ }
127
+ }
128
+ }
129
+ }
130
+ function splitMessage(text, maxLength) {
131
+ const chunks = [];
132
+ let remaining = text;
133
+ while (remaining.length > 0) {
134
+ if (remaining.length <= maxLength) {
135
+ chunks.push(remaining);
136
+ break;
137
+ }
138
+ let breakPoint = remaining.lastIndexOf("\n", maxLength);
139
+ if (breakPoint === -1 || breakPoint < maxLength / 2) {
140
+ breakPoint = remaining.lastIndexOf(" ", maxLength);
141
+ }
142
+ if (breakPoint === -1 || breakPoint < maxLength / 2) {
143
+ breakPoint = maxLength;
144
+ }
145
+ chunks.push(remaining.slice(0, breakPoint));
146
+ remaining = remaining.slice(breakPoint).trim();
147
+ }
148
+ return chunks;
149
+ }
150
+
151
+ // src/inputs/telegram/bot.ts
152
+ function createBot() {
153
+ const bot = new Bot(env.TELEGRAM_BOT_TOKEN);
154
+ bot.use(
155
+ session({
156
+ initial: () => ({
157
+ messages: []
158
+ })
159
+ })
160
+ );
161
+ bot.use(async (ctx, next) => {
162
+ const chatId = ctx.chat?.id?.toString();
163
+ if (chatId !== env.TELEGRAM_CHAT_ID) {
164
+ console.log(`Unauthorized access attempt from chat ID: ${chatId}`);
165
+ return;
166
+ }
167
+ await next();
168
+ });
169
+ bot.command("start", async (ctx) => {
170
+ await ctx.reply(
171
+ `Hello! I'm OpenSentinel, your personal AI assistant with JARVIS-like capabilities.
172
+
173
+ I can:
174
+ \u2022 Chat and answer questions using Claude AI
175
+ \u2022 Execute shell commands on your system
176
+ \u2022 Read and write files
177
+ \u2022 Search the web
178
+ \u2022 Remember important information
179
+ \u2022 Set reminders
180
+
181
+ Send me a message or voice note to get started!`
182
+ );
183
+ });
184
+ bot.command("clear", async (ctx) => {
185
+ ctx.session.messages = [];
186
+ await ctx.reply("\u2713 Conversation history cleared.");
187
+ });
188
+ bot.command("remind", async (ctx) => {
189
+ const text = ctx.message?.text?.replace("/remind", "").trim();
190
+ if (!text) {
191
+ await ctx.reply(
192
+ "Usage: /remind <time> <message>\n\nExamples:\n\u2022 /remind 5m Check the oven\n\u2022 /remind 1h Call mom\n\u2022 /remind 30s Test reminder"
193
+ );
194
+ return;
195
+ }
196
+ const match = text.match(/^(\d+)(s|m|h)\s+(.+)$/i);
197
+ if (!match) {
198
+ await ctx.reply(
199
+ "Invalid format. Use: /remind <number><s/m/h> <message>\n\nExample: /remind 5m Check the oven"
200
+ );
201
+ return;
202
+ }
203
+ const [, amount, unit, message] = match;
204
+ const multipliers = {
205
+ s: 1e3,
206
+ m: 60 * 1e3,
207
+ h: 60 * 60 * 1e3
208
+ };
209
+ const delayMs = parseInt(amount) * multipliers[unit.toLowerCase()];
210
+ try {
211
+ await scheduleReminder(message, delayMs, ctx.chat?.id?.toString());
212
+ const timeStr = unit === "s" ? "seconds" : unit === "m" ? "minutes" : "hours";
213
+ await ctx.reply(`\u2713 Reminder set for ${amount} ${timeStr}: "${message}"`);
214
+ } catch (error) {
215
+ await ctx.reply("Sorry, I couldn't set the reminder. Please try again.");
216
+ }
217
+ });
218
+ bot.command("help", async (ctx) => {
219
+ await ctx.reply(
220
+ `*OpenSentinel Commands*
221
+
222
+ /start - Welcome message
223
+ /clear - Clear conversation history
224
+ /remind <time> <message> - Set a reminder
225
+ /help - Show this help
226
+
227
+ *Features*
228
+ \u2022 Send text messages for AI chat
229
+ \u2022 Send voice messages for voice interaction
230
+ \u2022 Ask me to run commands, search the web, or manage files
231
+
232
+ *Examples*
233
+ \u2022 "What's the weather like?"
234
+ \u2022 "List files in my Downloads folder"
235
+ \u2022 "Search for the latest news about AI"
236
+ \u2022 /remind 5m Take a break`,
237
+ { parse_mode: "Markdown" }
238
+ );
239
+ });
240
+ bot.on("message:text", handleMessage);
241
+ bot.on("message:voice", handleVoice);
242
+ bot.catch((err) => {
243
+ console.error("Bot error:", err);
244
+ });
245
+ return bot;
246
+ }
247
+
248
+ export {
249
+ createBot
250
+ };
251
+ //# sourceMappingURL=chunk-6DRDKB45.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/inputs/telegram/bot.ts","../src/inputs/telegram/handlers.ts"],"sourcesContent":["import { Bot, Context, session } from \"grammy\";\nimport { env } from \"../../config/env\";\nimport { handleMessage, handleVoice } from \"./handlers\";\nimport { scheduleReminder } from \"../../core/scheduler\";\n\nexport interface SessionData {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n}\n\nexport type OpenSentinelContext = Context & { session: SessionData };\n\nexport function createBot() {\n const bot = new Bot<OpenSentinelContext>(env.TELEGRAM_BOT_TOKEN);\n\n // Session middleware for conversation history\n bot.use(\n session({\n initial: (): SessionData => ({\n messages: [],\n }),\n })\n );\n\n // Only allow configured chat ID\n bot.use(async (ctx, next) => {\n const chatId = ctx.chat?.id?.toString();\n if (chatId !== env.TELEGRAM_CHAT_ID) {\n console.log(`Unauthorized access attempt from chat ID: ${chatId}`);\n return;\n }\n await next();\n });\n\n // Command handlers\n bot.command(\"start\", async (ctx) => {\n await ctx.reply(\n `Hello! I'm OpenSentinel, your personal AI assistant with JARVIS-like capabilities.\n\nI can:\n• Chat and answer questions using Claude AI\n• Execute shell commands on your system\n• Read and write files\n• Search the web\n• Remember important information\n• Set reminders\n\nSend me a message or voice note to get started!`\n );\n });\n\n bot.command(\"clear\", async (ctx) => {\n ctx.session.messages = [];\n await ctx.reply(\"✓ Conversation history cleared.\");\n });\n\n bot.command(\"remind\", async (ctx) => {\n const text = ctx.message?.text?.replace(\"/remind\", \"\").trim();\n\n if (!text) {\n await ctx.reply(\n \"Usage: /remind <time> <message>\\n\\nExamples:\\n• /remind 5m Check the oven\\n• /remind 1h Call mom\\n• /remind 30s Test reminder\"\n );\n return;\n }\n\n // Parse time and message\n const match = text.match(/^(\\d+)(s|m|h)\\s+(.+)$/i);\n if (!match) {\n await ctx.reply(\n \"Invalid format. Use: /remind <number><s/m/h> <message>\\n\\nExample: /remind 5m Check the oven\"\n );\n return;\n }\n\n const [, amount, unit, message] = match;\n const multipliers: Record<string, number> = {\n s: 1000,\n m: 60 * 1000,\n h: 60 * 60 * 1000,\n };\n const delayMs = parseInt(amount) * multipliers[unit.toLowerCase()];\n\n try {\n await scheduleReminder(message, delayMs, ctx.chat?.id?.toString());\n const timeStr = unit === \"s\" ? \"seconds\" : unit === \"m\" ? \"minutes\" : \"hours\";\n await ctx.reply(`✓ Reminder set for ${amount} ${timeStr}: \"${message}\"`);\n } catch (error) {\n await ctx.reply(\"Sorry, I couldn't set the reminder. Please try again.\");\n }\n });\n\n bot.command(\"help\", async (ctx) => {\n await ctx.reply(\n `*OpenSentinel Commands*\n\n/start - Welcome message\n/clear - Clear conversation history\n/remind <time> <message> - Set a reminder\n/help - Show this help\n\n*Features*\n• Send text messages for AI chat\n• Send voice messages for voice interaction\n• Ask me to run commands, search the web, or manage files\n\n*Examples*\n• \"What's the weather like?\"\n• \"List files in my Downloads folder\"\n• \"Search for the latest news about AI\"\n• /remind 5m Take a break`,\n { parse_mode: \"Markdown\" }\n );\n });\n\n // Message handlers\n bot.on(\"message:text\", handleMessage);\n bot.on(\"message:voice\", handleVoice);\n\n // Error handling\n bot.catch((err) => {\n console.error(\"Bot error:\", err);\n });\n\n return bot;\n}\n","import { InputFile } from \"grammy\";\nimport type { SentinelContext } from \"./bot\";\nimport { chatWithTools, type Message } from \"../../core/brain\";\nimport { transcribeAudio } from \"../../outputs/stt\";\nimport { textToSpeech } from \"../../outputs/tts\";\n\nconst MAX_HISTORY = 20; // Keep last 20 messages for context\n\nexport async function handleMessage(ctx: SentinelContext) {\n const text = ctx.message?.text;\n if (!text) return;\n\n // Add user message to history\n ctx.session.messages.push({ role: \"user\", content: text });\n\n // Trim history if too long\n if (ctx.session.messages.length > MAX_HISTORY) {\n ctx.session.messages = ctx.session.messages.slice(-MAX_HISTORY);\n }\n\n // Show typing indicator\n await ctx.replyWithChatAction(\"typing\");\n\n try {\n // Use chatWithTools for full capability\n const response = await chatWithTools(\n ctx.session.messages as Message[],\n ctx.chat?.id?.toString(),\n async () => {\n // Keep typing indicator alive during tool use\n await ctx.replyWithChatAction(\"typing\");\n }\n );\n\n // Add assistant response to history\n ctx.session.messages.push({ role: \"assistant\", content: response.content });\n\n // Build response with tool usage info\n let finalResponse = response.content;\n if (response.toolsUsed && response.toolsUsed.length > 0) {\n const toolList = [...new Set(response.toolsUsed)].join(\", \");\n finalResponse = `🔧 _Used: ${toolList}_\\n\\n${response.content}`;\n }\n\n // Send response (split if too long for Telegram)\n await sendResponse(ctx, finalResponse);\n\n console.log(\n `[Telegram] Processed message. Tokens: ${response.inputTokens}/${response.outputTokens}` +\n (response.toolsUsed ? ` Tools: ${response.toolsUsed.join(\", \")}` : \"\")\n );\n } catch (error) {\n console.error(\"Error processing message:\", error);\n await ctx.reply(\n \"Sorry, I encountered an error processing your message. Please try again.\"\n );\n }\n}\n\nexport async function handleVoice(ctx: SentinelContext) {\n const voice = ctx.message?.voice;\n if (!voice) return;\n\n await ctx.replyWithChatAction(\"typing\");\n\n try {\n // Get voice file\n const file = await ctx.getFile();\n const fileUrl = `https://api.telegram.org/file/bot${process.env.TELEGRAM_BOT_TOKEN}/${file.file_path}`;\n\n // Download and transcribe\n const response = await fetch(fileUrl);\n const audioBuffer = await response.arrayBuffer();\n\n const transcription = await transcribeAudio(Buffer.from(audioBuffer));\n\n if (!transcription) {\n await ctx.reply(\"Sorry, I couldn't transcribe that voice message.\");\n return;\n }\n\n // Show what was transcribed\n await ctx.reply(`🎤 _\"${transcription}\"_`, { parse_mode: \"Markdown\" });\n\n // Process as text message\n ctx.session.messages.push({ role: \"user\", content: transcription });\n\n if (ctx.session.messages.length > MAX_HISTORY) {\n ctx.session.messages = ctx.session.messages.slice(-MAX_HISTORY);\n }\n\n await ctx.replyWithChatAction(\"typing\");\n\n const aiResponse = await chatWithTools(\n ctx.session.messages as Message[],\n ctx.chat?.id?.toString()\n );\n ctx.session.messages.push({\n role: \"assistant\",\n content: aiResponse.content,\n });\n\n // Send text response first\n await sendResponse(ctx, aiResponse.content);\n\n // Also send voice response if text is short enough\n if (aiResponse.content.length < 1000 && aiResponse.content.length > 10) {\n try {\n await ctx.replyWithChatAction(\"record_voice\");\n const audioBuffer = await textToSpeech(aiResponse.content);\n if (audioBuffer) {\n await ctx.replyWithVoice(new InputFile(audioBuffer, \"response.ogg\"));\n }\n } catch (ttsError) {\n console.error(\"TTS error:\", ttsError);\n // Don't fail if TTS fails, text was already sent\n }\n }\n\n console.log(\n `[Telegram] Processed voice message. Tokens: ${aiResponse.inputTokens}/${aiResponse.outputTokens}`\n );\n } catch (error) {\n console.error(\"Error processing voice message:\", error);\n await ctx.reply(\n \"Sorry, I encountered an error processing your voice message. Please try again.\"\n );\n }\n}\n\n// Helper to send response, handling Markdown errors\nasync function sendResponse(ctx: SentinelContext, text: string) {\n const maxLength = 4096;\n\n // Try with Markdown first\n try {\n if (text.length <= maxLength) {\n await ctx.reply(text, { parse_mode: \"Markdown\" });\n } else {\n const chunks = splitMessage(text, maxLength);\n for (const chunk of chunks) {\n await ctx.reply(chunk, { parse_mode: \"Markdown\" });\n }\n }\n } catch {\n // If Markdown fails, send as plain text\n if (text.length <= maxLength) {\n await ctx.reply(text);\n } else {\n const chunks = splitMessage(text, maxLength);\n for (const chunk of chunks) {\n await ctx.reply(chunk);\n }\n }\n }\n}\n\nfunction splitMessage(text: string, maxLength: number): string[] {\n const chunks: string[] = [];\n let remaining = text;\n\n while (remaining.length > 0) {\n if (remaining.length <= maxLength) {\n chunks.push(remaining);\n break;\n }\n\n // Find a good break point (newline or space)\n let breakPoint = remaining.lastIndexOf(\"\\n\", maxLength);\n if (breakPoint === -1 || breakPoint < maxLength / 2) {\n breakPoint = remaining.lastIndexOf(\" \", maxLength);\n }\n if (breakPoint === -1 || breakPoint < maxLength / 2) {\n breakPoint = maxLength;\n }\n\n chunks.push(remaining.slice(0, breakPoint));\n remaining = remaining.slice(breakPoint).trim();\n }\n\n return chunks;\n}\n"],"mappings":"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","names":["audioBuffer"]}
@@ -0,0 +1,123 @@
1
+ import {
2
+ chatWithTools
3
+ } from "./chunk-CI6Q63MM.js";
4
+
5
+ // src/inputs/whatsapp/index.ts
6
+ import makeWASocket, {
7
+ DisconnectReason,
8
+ useMultiFileAuthState
9
+ } from "@whiskeysockets/baileys";
10
+ import * as qrcode from "qrcode-terminal";
11
+ var WhatsAppBot = class {
12
+ sock = null;
13
+ config;
14
+ conversations = /* @__PURE__ */ new Map();
15
+ isConnected = false;
16
+ constructor(config = {}) {
17
+ this.config = {
18
+ authDir: config.authDir || "./whatsapp-auth",
19
+ allowedNumbers: config.allowedNumbers || [],
20
+ printQR: config.printQR ?? true
21
+ };
22
+ }
23
+ async start() {
24
+ const { state, saveCreds } = await useMultiFileAuthState(
25
+ this.config.authDir
26
+ );
27
+ this.sock = makeWASocket({
28
+ auth: state,
29
+ printQRInTerminal: this.config.printQR
30
+ });
31
+ this.sock.ev.on("connection.update", async (update) => {
32
+ const { connection, lastDisconnect, qr } = update;
33
+ if (qr && this.config.printQR) {
34
+ console.log("[WhatsApp] Scan the QR code below to connect:");
35
+ qrcode.generate(qr, { small: true });
36
+ }
37
+ if (connection === "close") {
38
+ const shouldReconnect = lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut;
39
+ console.log(
40
+ "[WhatsApp] Connection closed. Reconnecting:",
41
+ shouldReconnect
42
+ );
43
+ if (shouldReconnect) {
44
+ await this.start();
45
+ } else {
46
+ this.isConnected = false;
47
+ console.log("[WhatsApp] Logged out. Please scan QR code again.");
48
+ }
49
+ } else if (connection === "open") {
50
+ this.isConnected = true;
51
+ console.log("[WhatsApp] Connected successfully!");
52
+ }
53
+ });
54
+ this.sock.ev.on("creds.update", saveCreds);
55
+ this.sock.ev.on("messages.upsert", async ({ messages }) => {
56
+ for (const msg of messages) {
57
+ await this.handleMessage(msg);
58
+ }
59
+ });
60
+ }
61
+ async handleMessage(msg) {
62
+ if (!this.sock) return;
63
+ if (!msg.message || !msg.key || msg.key.fromMe) return;
64
+ const remoteJid = msg.key.remoteJid;
65
+ if (!remoteJid) return;
66
+ const sender = remoteJid.replace("@s.whatsapp.net", "").replace("@g.us", "");
67
+ if (this.config.allowedNumbers && this.config.allowedNumbers.length > 0 && !this.config.allowedNumbers.includes(sender)) {
68
+ console.log(`[WhatsApp] Ignoring message from unauthorized number: ${sender}`);
69
+ return;
70
+ }
71
+ const messageText = msg.message.conversation || msg.message.extendedTextMessage?.text || "";
72
+ if (!messageText.trim()) return;
73
+ console.log(`[WhatsApp] Message from ${sender}: ${messageText.slice(0, 50)}...`);
74
+ let context = this.conversations.get(remoteJid);
75
+ if (!context) {
76
+ context = { messages: [], lastActivity: /* @__PURE__ */ new Date() };
77
+ this.conversations.set(remoteJid, context);
78
+ }
79
+ context.messages.push({ role: "user", content: messageText });
80
+ context.lastActivity = /* @__PURE__ */ new Date();
81
+ if (context.messages.length > 10) {
82
+ context.messages = context.messages.slice(-10);
83
+ }
84
+ try {
85
+ await this.sock.sendPresenceUpdate("composing", remoteJid);
86
+ const response = await chatWithTools(context.messages, sender);
87
+ context.messages.push({ role: "assistant", content: response.content });
88
+ await this.sock.sendMessage(remoteJid, { text: response.content });
89
+ await this.sock.sendPresenceUpdate("paused", remoteJid);
90
+ console.log(`[WhatsApp] Replied to ${sender}`);
91
+ } catch (error) {
92
+ console.error("[WhatsApp] Error processing message:", error);
93
+ await this.sock.sendMessage(remoteJid, {
94
+ text: "Sorry, I encountered an error processing your request."
95
+ });
96
+ }
97
+ }
98
+ async sendMessage(to, text) {
99
+ if (!this.sock || !this.isConnected) {
100
+ throw new Error("WhatsApp not connected");
101
+ }
102
+ const jid = to.includes("@") ? to : `${to}@s.whatsapp.net`;
103
+ await this.sock.sendMessage(jid, { text });
104
+ }
105
+ async stop() {
106
+ if (this.sock) {
107
+ this.sock.end(void 0);
108
+ this.sock = null;
109
+ this.isConnected = false;
110
+ console.log("[WhatsApp] Disconnected");
111
+ }
112
+ }
113
+ get connected() {
114
+ return this.isConnected;
115
+ }
116
+ };
117
+ var whatsapp_default = WhatsAppBot;
118
+
119
+ export {
120
+ WhatsAppBot,
121
+ whatsapp_default
122
+ };
123
+ //# sourceMappingURL=chunk-6SNHU3CY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/inputs/whatsapp/index.ts"],"sourcesContent":["/**\n * WhatsApp Integration using Baileys\n * Connects OpenSentinel to WhatsApp Web\n */\n\nimport makeWASocket, {\n DisconnectReason,\n useMultiFileAuthState,\n WASocket,\n proto,\n downloadMediaMessage,\n} from \"@whiskeysockets/baileys\";\nimport { Boom } from \"@hapi/boom\";\n// @ts-ignore - no type declarations available\nimport * as qrcode from \"qrcode-terminal\";\nimport { chatWithTools } from \"../../core/brain\";\nimport type { Message } from \"../../core/brain\";\n\ninterface WhatsAppConfig {\n authDir?: string;\n allowedNumbers?: string[];\n printQR?: boolean;\n}\n\ninterface ConversationContext {\n messages: Message[];\n lastActivity: Date;\n}\n\nexport class WhatsAppBot {\n private sock: WASocket | null = null;\n private config: WhatsAppConfig;\n private conversations: Map<string, ConversationContext> = new Map();\n private isConnected = false;\n\n constructor(config: WhatsAppConfig = {}) {\n this.config = {\n authDir: config.authDir || \"./whatsapp-auth\",\n allowedNumbers: config.allowedNumbers || [],\n printQR: config.printQR ?? true,\n };\n }\n\n async start(): Promise<void> {\n const { state, saveCreds } = await useMultiFileAuthState(\n this.config.authDir!\n );\n\n this.sock = makeWASocket({\n auth: state,\n printQRInTerminal: this.config.printQR,\n });\n\n // Handle connection updates\n this.sock.ev.on(\"connection.update\", async (update) => {\n const { connection, lastDisconnect, qr } = update;\n\n if (qr && this.config.printQR) {\n console.log(\"[WhatsApp] Scan the QR code below to connect:\");\n qrcode.generate(qr, { small: true });\n }\n\n if (connection === \"close\") {\n const shouldReconnect =\n (lastDisconnect?.error as Boom)?.output?.statusCode !==\n DisconnectReason.loggedOut;\n\n console.log(\n \"[WhatsApp] Connection closed. Reconnecting:\",\n shouldReconnect\n );\n\n if (shouldReconnect) {\n await this.start();\n } else {\n this.isConnected = false;\n console.log(\"[WhatsApp] Logged out. Please scan QR code again.\");\n }\n } else if (connection === \"open\") {\n this.isConnected = true;\n console.log(\"[WhatsApp] Connected successfully!\");\n }\n });\n\n // Save credentials on update\n this.sock.ev.on(\"creds.update\", saveCreds);\n\n // Handle incoming messages\n this.sock.ev.on(\"messages.upsert\", async ({ messages }) => {\n for (const msg of messages) {\n await this.handleMessage(msg);\n }\n });\n }\n\n private async handleMessage(msg: proto.IWebMessageInfo): Promise<void> {\n if (!this.sock) return;\n\n // Skip if no message content or if sent by us\n if (!msg.message || !msg.key || msg.key.fromMe) return;\n\n const remoteJid = msg.key!.remoteJid;\n if (!remoteJid) return;\n\n // Extract sender number\n const sender = remoteJid.replace(\"@s.whatsapp.net\", \"\").replace(\"@g.us\", \"\");\n\n // Check if sender is allowed\n if (\n this.config.allowedNumbers &&\n this.config.allowedNumbers.length > 0 &&\n !this.config.allowedNumbers.includes(sender)\n ) {\n console.log(`[WhatsApp] Ignoring message from unauthorized number: ${sender}`);\n return;\n }\n\n // Extract message text\n const messageText =\n msg.message.conversation ||\n msg.message.extendedTextMessage?.text ||\n \"\";\n\n if (!messageText.trim()) return;\n\n console.log(`[WhatsApp] Message from ${sender}: ${messageText.slice(0, 50)}...`);\n\n // Get or create conversation context\n let context = this.conversations.get(remoteJid);\n if (!context) {\n context = { messages: [], lastActivity: new Date() };\n this.conversations.set(remoteJid, context);\n }\n\n // Add user message to context\n context.messages.push({ role: \"user\", content: messageText });\n context.lastActivity = new Date();\n\n // Keep only last 10 messages for context\n if (context.messages.length > 10) {\n context.messages = context.messages.slice(-10);\n }\n\n try {\n // Show typing indicator\n await this.sock.sendPresenceUpdate(\"composing\", remoteJid);\n\n // Get AI response\n const response = await chatWithTools(context.messages, sender);\n\n // Add assistant response to context\n context.messages.push({ role: \"assistant\", content: response.content });\n\n // Send response\n await this.sock.sendMessage(remoteJid, { text: response.content });\n\n // Clear typing indicator\n await this.sock.sendPresenceUpdate(\"paused\", remoteJid);\n\n console.log(`[WhatsApp] Replied to ${sender}`);\n } catch (error) {\n console.error(\"[WhatsApp] Error processing message:\", error);\n\n await this.sock.sendMessage(remoteJid, {\n text: \"Sorry, I encountered an error processing your request.\",\n });\n }\n }\n\n async sendMessage(to: string, text: string): Promise<void> {\n if (!this.sock || !this.isConnected) {\n throw new Error(\"WhatsApp not connected\");\n }\n\n const jid = to.includes(\"@\") ? to : `${to}@s.whatsapp.net`;\n await this.sock.sendMessage(jid, { text });\n }\n\n async stop(): Promise<void> {\n if (this.sock) {\n this.sock.end(undefined);\n this.sock = null;\n this.isConnected = false;\n console.log(\"[WhatsApp] Disconnected\");\n }\n }\n\n get connected(): boolean {\n return this.isConnected;\n }\n}\n\nexport default WhatsAppBot;\n"],"mappings":"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","names":[]}