@ducci/jarvis 1.0.77 → 1.0.79
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.
- package/package.json +1 -1
- package/src/channels/telegram/index.js +258 -54
- package/src/server/tools.js +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { Bot } from 'grammy';
|
|
3
|
+
import { Bot, InlineKeyboard } from 'grammy';
|
|
4
4
|
import { run } from '@grammyjs/runner';
|
|
5
5
|
import { handleChat, requestAbort } from '../../server/agent.js';
|
|
6
6
|
import { loadSession } from '../../server/sessions.js';
|
|
@@ -98,15 +98,72 @@ export async function startTelegramChannel(config) {
|
|
|
98
98
|
const bot = new Bot(token);
|
|
99
99
|
const sessions = load();
|
|
100
100
|
|
|
101
|
-
// Tracks chats with an active agent run
|
|
102
|
-
//
|
|
101
|
+
// Tracks chats with an active agent run per slot.
|
|
102
|
+
// Keys are "chatId:slot" strings.
|
|
103
103
|
const isRunning = new Set();
|
|
104
|
-
const pendingMessages = new Map(); // chatId -> [{text, attachments, ts}]
|
|
104
|
+
const pendingMessages = new Map(); // "chatId:slot" -> [{text, attachments, ts}]
|
|
105
|
+
const runStartTimes = new Map(); // "chatId:slot" -> Date
|
|
106
|
+
|
|
107
|
+
// --- Slot helpers ---
|
|
108
|
+
// sessions[chatId] is either:
|
|
109
|
+
// - undefined (no session yet)
|
|
110
|
+
// - string (legacy: single session ID, treated as slot 1)
|
|
111
|
+
// - { active: number, slots: { "1": sessionId, "2": sessionId, ... } }
|
|
112
|
+
|
|
113
|
+
function getActiveSlot(chatId) {
|
|
114
|
+
const d = sessions[chatId];
|
|
115
|
+
if (!d || typeof d === 'string') return 1;
|
|
116
|
+
return d.active ?? 1;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getSessionId(chatId, slot) {
|
|
120
|
+
const d = sessions[chatId];
|
|
121
|
+
if (!d) return null;
|
|
122
|
+
if (typeof d === 'string') return slot == 1 ? d : null;
|
|
123
|
+
return d.slots?.[String(slot)] ?? null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function setSessionId(chatId, slot, sessionId) {
|
|
127
|
+
const d = sessions[chatId];
|
|
128
|
+
if (!d || typeof d === 'string') {
|
|
129
|
+
const legacy = typeof d === 'string' ? d : null;
|
|
130
|
+
sessions[chatId] = { active: Number(slot), slots: {} };
|
|
131
|
+
if (legacy) sessions[chatId].slots['1'] = legacy;
|
|
132
|
+
}
|
|
133
|
+
if (sessionId === null) {
|
|
134
|
+
delete sessions[chatId].slots[String(slot)];
|
|
135
|
+
if (Object.keys(sessions[chatId].slots).length === 0) {
|
|
136
|
+
delete sessions[chatId];
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
sessions[chatId].slots[String(slot)] = sessionId;
|
|
140
|
+
}
|
|
141
|
+
save(sessions);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function setActiveSlot(chatId, slot) {
|
|
145
|
+
const d = sessions[chatId];
|
|
146
|
+
if (!d || typeof d === 'string') {
|
|
147
|
+
const legacy = typeof d === 'string' ? d : null;
|
|
148
|
+
sessions[chatId] = { active: Number(slot), slots: {} };
|
|
149
|
+
if (legacy) sessions[chatId].slots['1'] = legacy;
|
|
150
|
+
} else {
|
|
151
|
+
sessions[chatId].active = Number(slot);
|
|
152
|
+
}
|
|
153
|
+
save(sessions);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function slotKey(chatId, slot) {
|
|
157
|
+
return `${chatId}:${slot}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// --- Commands ---
|
|
105
161
|
|
|
106
162
|
await bot.api.setMyCommands([
|
|
107
|
-
{ command: 'new',
|
|
108
|
-
{ command: 'usage', description: '
|
|
109
|
-
{ command: 'stop',
|
|
163
|
+
{ command: 'new', description: 'Reset the active slot (fresh session)' },
|
|
164
|
+
{ command: 'usage', description: 'Token usage for the active slot' },
|
|
165
|
+
{ command: 'stop', description: 'Stop the running agent on the active slot' },
|
|
166
|
+
{ command: 'slots', description: 'Show all slots and their status' },
|
|
110
167
|
]);
|
|
111
168
|
|
|
112
169
|
bot.command('usage', async (ctx) => {
|
|
@@ -114,7 +171,8 @@ export async function startTelegramChannel(config) {
|
|
|
114
171
|
if (!allowedUserIds.includes(userId)) return;
|
|
115
172
|
|
|
116
173
|
const chatId = ctx.chat.id;
|
|
117
|
-
const
|
|
174
|
+
const slot = getActiveSlot(chatId);
|
|
175
|
+
const sessionId = getSessionId(chatId, slot);
|
|
118
176
|
if (!sessionId) {
|
|
119
177
|
await ctx.reply('No active session. Send a message to start one.');
|
|
120
178
|
return;
|
|
@@ -134,7 +192,7 @@ export async function startTelegramChannel(config) {
|
|
|
134
192
|
? `\nCache read: ${cacheRead.toLocaleString()}\nCache written: ${cacheCreation.toLocaleString()}`
|
|
135
193
|
: '';
|
|
136
194
|
await ctx.reply(
|
|
137
|
-
`Token usage for
|
|
195
|
+
`Token usage for slot ${slot}:\nIn: ${u.prompt.toLocaleString()}\nOut: ${u.completion.toLocaleString()}\nTotal: ${total.toLocaleString()}${cacheLines}`
|
|
138
196
|
);
|
|
139
197
|
});
|
|
140
198
|
|
|
@@ -143,9 +201,11 @@ export async function startTelegramChannel(config) {
|
|
|
143
201
|
if (!allowedUserIds.includes(userId)) return;
|
|
144
202
|
|
|
145
203
|
const chatId = ctx.chat.id;
|
|
146
|
-
const
|
|
204
|
+
const slot = getActiveSlot(chatId);
|
|
205
|
+
const sessionId = getSessionId(chatId, slot);
|
|
206
|
+
const key = slotKey(chatId, slot);
|
|
147
207
|
|
|
148
|
-
if (!isRunning.has(
|
|
208
|
+
if (!isRunning.has(key) || !sessionId) {
|
|
149
209
|
await ctx.reply('Nothing is currently running.');
|
|
150
210
|
return;
|
|
151
211
|
}
|
|
@@ -160,24 +220,155 @@ export async function startTelegramChannel(config) {
|
|
|
160
220
|
if (!allowedUserIds.includes(userId)) return;
|
|
161
221
|
|
|
162
222
|
const chatId = ctx.chat.id;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
223
|
+
const slot = getActiveSlot(chatId);
|
|
224
|
+
const key = slotKey(chatId, slot);
|
|
225
|
+
pendingMessages.delete(key);
|
|
226
|
+
const oldSessionId = getSessionId(chatId, slot);
|
|
227
|
+
if (oldSessionId) {
|
|
228
|
+
await appendTelegramChatLog(chatId, oldSessionId, 'SYSTEM', '--- /new: session reset ---');
|
|
229
|
+
setSessionId(chatId, slot, null);
|
|
230
|
+
console.log(`[telegram] session unlinked chat_id=${chatId} slot=${slot}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
await ctx.reply(`New session started on slot ${slot}.`);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
function buildSlotsDisplay(chatId) {
|
|
237
|
+
const d = sessions[chatId];
|
|
238
|
+
const activeSlot = getActiveSlot(chatId);
|
|
239
|
+
|
|
240
|
+
const slotsMap = {};
|
|
241
|
+
if (typeof d === 'string') {
|
|
242
|
+
slotsMap['1'] = d;
|
|
243
|
+
} else if (d && d.slots) {
|
|
244
|
+
Object.assign(slotsMap, d.slots);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const slotNums = [...new Set(['1', ...Object.keys(slotsMap)])].sort((a, b) => Number(a) - Number(b));
|
|
248
|
+
const maxSlot = Math.max(...slotNums.map(Number));
|
|
249
|
+
const nextSlot = maxSlot + 1;
|
|
250
|
+
|
|
251
|
+
// Status text
|
|
252
|
+
const lines = ['<b>Slots:</b>'];
|
|
253
|
+
for (const sn of slotNums) {
|
|
254
|
+
const n = Number(sn);
|
|
255
|
+
const sid = slotsMap[sn] ?? null;
|
|
256
|
+
const key = slotKey(chatId, n);
|
|
257
|
+
const activeMarker = n === activeSlot ? ' ← aktiv' : '';
|
|
258
|
+
let statusIcon;
|
|
259
|
+
if (isRunning.has(key)) {
|
|
260
|
+
const startTime = runStartTimes.get(key);
|
|
261
|
+
let elapsed = '';
|
|
262
|
+
if (startTime) {
|
|
263
|
+
const secs = Math.floor((Date.now() - startTime.getTime()) / 1000);
|
|
264
|
+
const m = Math.floor(secs / 60);
|
|
265
|
+
const s = secs % 60;
|
|
266
|
+
elapsed = m > 0 ? ` (seit ${m}m ${s}s)` : ` (seit ${s}s)`;
|
|
267
|
+
}
|
|
268
|
+
statusIcon = `🟢 läuft${elapsed}`;
|
|
269
|
+
} else if (sid) {
|
|
270
|
+
statusIcon = '💬 bereit';
|
|
271
|
+
} else {
|
|
272
|
+
statusIcon = '➕ leer';
|
|
273
|
+
}
|
|
274
|
+
lines.push(`Slot ${n}: ${statusIcon}${activeMarker}`);
|
|
275
|
+
}
|
|
276
|
+
if (!isRunning.has(slotKey(chatId, nextSlot)) && !slotsMap[String(nextSlot)]) {
|
|
277
|
+
lines.push(`Slot ${nextSlot}: ➕ leer`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Inline keyboard
|
|
281
|
+
const kb = new InlineKeyboard();
|
|
282
|
+
for (const sn of slotNums) {
|
|
283
|
+
const n = Number(sn);
|
|
284
|
+
const sid = slotsMap[sn] ?? null;
|
|
285
|
+
const key = slotKey(chatId, n);
|
|
286
|
+
const running = isRunning.has(key);
|
|
287
|
+
if (n === activeSlot) {
|
|
288
|
+
kb.text(`✓ Slot ${n} (aktiv)`, `slots_noop`);
|
|
289
|
+
} else {
|
|
290
|
+
kb.text(`↩️ Slot ${n}`, `slots_switch_${n}`);
|
|
291
|
+
}
|
|
292
|
+
if (sid && !running) {
|
|
293
|
+
kb.text(`🗑️`, `slots_del_${n}`);
|
|
294
|
+
}
|
|
295
|
+
kb.row();
|
|
296
|
+
}
|
|
297
|
+
// Button for the next empty slot
|
|
298
|
+
kb.text(`➕ Slot ${nextSlot} (neu)`, `slots_switch_${nextSlot}`);
|
|
299
|
+
|
|
300
|
+
return { text: lines.join('\n'), keyboard: kb };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
bot.command('slots', async (ctx) => {
|
|
304
|
+
const userId = ctx.from?.id;
|
|
305
|
+
if (!allowedUserIds.includes(userId)) return;
|
|
306
|
+
|
|
307
|
+
const chatId = ctx.chat.id;
|
|
308
|
+
const { text, keyboard } = buildSlotsDisplay(chatId);
|
|
309
|
+
await ctx.reply(text, { parse_mode: 'HTML', reply_markup: keyboard });
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
bot.callbackQuery(/^slots_switch_(\d+)$/, async (ctx) => {
|
|
313
|
+
const userId = ctx.from?.id;
|
|
314
|
+
if (!allowedUserIds.includes(userId)) { await ctx.answerCallbackQuery(); return; }
|
|
315
|
+
|
|
316
|
+
const chatId = ctx.chat.id;
|
|
317
|
+
const n = parseInt(ctx.match[1], 10);
|
|
318
|
+
setActiveSlot(chatId, n);
|
|
319
|
+
const key = slotKey(chatId, n);
|
|
320
|
+
const sid = getSessionId(chatId, n);
|
|
321
|
+
let status;
|
|
322
|
+
if (isRunning.has(key)) status = '🟢 läuft';
|
|
323
|
+
else if (sid) status = '💬 bereit';
|
|
324
|
+
else status = '➕ leer (neue Session beim nächsten Message)';
|
|
325
|
+
|
|
326
|
+
const { text, keyboard } = buildSlotsDisplay(chatId);
|
|
327
|
+
await ctx.editMessageText(text, { parse_mode: 'HTML', reply_markup: keyboard });
|
|
328
|
+
await ctx.answerCallbackQuery(`Slot ${n} aktiv — ${status}`);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
bot.callbackQuery(/^slots_del_(\d+)$/, async (ctx) => {
|
|
332
|
+
const userId = ctx.from?.id;
|
|
333
|
+
if (!allowedUserIds.includes(userId)) { await ctx.answerCallbackQuery(); return; }
|
|
334
|
+
|
|
335
|
+
const chatId = ctx.chat.id;
|
|
336
|
+
const n = parseInt(ctx.match[1], 10);
|
|
337
|
+
const key = slotKey(chatId, n);
|
|
338
|
+
|
|
339
|
+
if (isRunning.has(key) || pendingMessages.has(key)) {
|
|
340
|
+
await ctx.answerCallbackQuery(`Slot ${n} läuft gerade — erst /stop`);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const oldSid = getSessionId(chatId, n);
|
|
345
|
+
if (oldSid) {
|
|
346
|
+
await appendTelegramChatLog(chatId, oldSid, 'SYSTEM', `--- slot del ${n} (via keyboard) ---`);
|
|
169
347
|
}
|
|
348
|
+
setSessionId(chatId, n, null);
|
|
349
|
+
pendingMessages.delete(key);
|
|
350
|
+
runStartTimes.delete(key);
|
|
351
|
+
|
|
352
|
+
if (getActiveSlot(chatId) === n) {
|
|
353
|
+
setActiveSlot(chatId, 1);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const { text, keyboard } = buildSlotsDisplay(chatId);
|
|
357
|
+
await ctx.editMessageText(text, { parse_mode: 'HTML', reply_markup: keyboard });
|
|
358
|
+
await ctx.answerCallbackQuery(`Slot ${n} gelöscht`);
|
|
359
|
+
});
|
|
170
360
|
|
|
171
|
-
|
|
361
|
+
bot.callbackQuery('slots_noop', async (ctx) => {
|
|
362
|
+
await ctx.answerCallbackQuery();
|
|
172
363
|
});
|
|
173
364
|
|
|
174
365
|
// Runs one or more batches until the pending queue is drained.
|
|
175
366
|
// Each iteration takes all currently pending messages, merges them into a
|
|
176
367
|
// single user turn, calls handleChat once, and sends one response.
|
|
177
|
-
async function processQueue(api, chatId, firstBatch) {
|
|
368
|
+
async function processQueue(api, chatId, slot, firstBatch) {
|
|
178
369
|
let batch = firstBatch;
|
|
179
370
|
while (batch.length > 0) {
|
|
180
|
-
const sessionId =
|
|
371
|
+
const sessionId = getSessionId(chatId, slot) || null;
|
|
181
372
|
const combinedText = batch.length === 1
|
|
182
373
|
? batch[0].text
|
|
183
374
|
: batch.map(m => m.text).join('\n\n');
|
|
@@ -213,28 +404,30 @@ export async function startTelegramChannel(config) {
|
|
|
213
404
|
|
|
214
405
|
let lastCheckpointSent = null;
|
|
215
406
|
let result;
|
|
407
|
+
const key = slotKey(chatId, slot);
|
|
216
408
|
try {
|
|
217
409
|
result = await handleChat(config, sessionId, userText, allAttachments, async (checkpointResponse) => {
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
410
|
+
const rawText = typeof checkpointResponse === 'string' ? checkpointResponse : JSON.stringify(checkpointResponse);
|
|
411
|
+
const currentActive = getActiveSlot(chatId);
|
|
412
|
+
const prefixed = slot !== currentActive ? `[Slot ${slot}] ${rawText}` : rawText;
|
|
413
|
+
lastCheckpointSent = prefixed;
|
|
414
|
+
await appendTelegramChatLog(chatId, getSessionId(chatId, slot) || null, 'JARVIS', prefixed);
|
|
415
|
+
await sendMessage(api, chatId, prefixed, getSessionId(chatId, slot) || null);
|
|
222
416
|
});
|
|
223
417
|
} catch (e) {
|
|
224
|
-
console.error(`[telegram] agent error chat_id=${chatId}: ${e.message}`);
|
|
418
|
+
console.error(`[telegram] agent error chat_id=${chatId} slot=${slot}: ${e.message}`);
|
|
225
419
|
const errText = e.message
|
|
226
420
|
? `Sorry, something went wrong: ${e.message}`
|
|
227
421
|
: 'Sorry, something went wrong. Please try again.';
|
|
228
422
|
await api.sendMessage(chatId, errText).catch(() => {});
|
|
229
|
-
batch = pendingMessages.get(
|
|
230
|
-
pendingMessages.delete(
|
|
423
|
+
batch = pendingMessages.get(key) || [];
|
|
424
|
+
pendingMessages.delete(key);
|
|
231
425
|
continue;
|
|
232
426
|
}
|
|
233
427
|
|
|
234
|
-
if (!
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
console.log(`[telegram] session created sessionId=${result.sessionId.slice(0, 8)}`);
|
|
428
|
+
if (!getSessionId(chatId, slot)) {
|
|
429
|
+
setSessionId(chatId, slot, result.sessionId);
|
|
430
|
+
console.log(`[telegram] session created slot=${slot} sessionId=${result.sessionId.slice(0, 8)}`);
|
|
238
431
|
}
|
|
239
432
|
|
|
240
433
|
// Log each original message individually with its own timestamp
|
|
@@ -248,24 +441,27 @@ export async function startTelegramChannel(config) {
|
|
|
248
441
|
: result.response != null ? JSON.stringify(result.response, null, 2) : '';
|
|
249
442
|
const text = rawResponse.trim()
|
|
250
443
|
|| 'The agent encountered an error and could not produce a response. Please try again.';
|
|
444
|
+
// Prefix response with slot number if the user has switched away from this slot
|
|
445
|
+
const currentActive = getActiveSlot(chatId);
|
|
446
|
+
const displayText = slot !== currentActive ? `[Slot ${slot}] ${text}` : text;
|
|
251
447
|
// Skip sending if this response was already sent as a checkpoint update —
|
|
252
448
|
// intervention_required and zero-progress reuse the last checkpoint response
|
|
253
449
|
// as their finalResponse, which would otherwise cause a duplicate message.
|
|
254
|
-
if (
|
|
255
|
-
await appendTelegramChatLog(chatId, result.sessionId, 'JARVIS',
|
|
256
|
-
await sendMessage(api, chatId,
|
|
257
|
-
console.log(`[telegram] response sent chat_id=${chatId} length=${
|
|
450
|
+
if (displayText !== lastCheckpointSent) {
|
|
451
|
+
await appendTelegramChatLog(chatId, result.sessionId, 'JARVIS', displayText);
|
|
452
|
+
await sendMessage(api, chatId, displayText, result.sessionId);
|
|
453
|
+
console.log(`[telegram] response sent chat_id=${chatId} slot=${slot} length=${displayText.length}`);
|
|
258
454
|
} else {
|
|
259
|
-
console.log(`[telegram] skipped duplicate final response chat_id=${chatId}`);
|
|
455
|
+
console.log(`[telegram] skipped duplicate final response chat_id=${chatId} slot=${slot}`);
|
|
260
456
|
}
|
|
261
457
|
} catch (e) {
|
|
262
|
-
console.error(`[telegram] delivery error chat_id=${chatId}: ${e.message}`);
|
|
458
|
+
console.error(`[telegram] delivery error chat_id=${chatId} slot=${slot}: ${e.message}`);
|
|
263
459
|
await api.sendMessage(chatId, 'Sorry, something went wrong sending the response. Please try again.').catch(() => {});
|
|
264
460
|
}
|
|
265
461
|
|
|
266
462
|
// Drain any messages that arrived while we were running
|
|
267
|
-
batch = pendingMessages.get(
|
|
268
|
-
pendingMessages.delete(
|
|
463
|
+
batch = pendingMessages.get(key) || [];
|
|
464
|
+
pendingMessages.delete(key);
|
|
269
465
|
}
|
|
270
466
|
}
|
|
271
467
|
|
|
@@ -296,25 +492,29 @@ export async function startTelegramChannel(config) {
|
|
|
296
492
|
}
|
|
297
493
|
|
|
298
494
|
const entry = { text: ctx.message.caption || '', attachments: [attachment], ts };
|
|
495
|
+
const slot = getActiveSlot(chatId);
|
|
496
|
+
const key = slotKey(chatId, slot);
|
|
299
497
|
|
|
300
|
-
if (isRunning.has(
|
|
301
|
-
if (!pendingMessages.has(
|
|
302
|
-
pendingMessages.get(
|
|
303
|
-
console.log(`[telegram] buffered photo chat_id=${chatId} pending=${pendingMessages.get(
|
|
498
|
+
if (isRunning.has(key)) {
|
|
499
|
+
if (!pendingMessages.has(key)) pendingMessages.set(key, []);
|
|
500
|
+
pendingMessages.get(key).push(entry);
|
|
501
|
+
console.log(`[telegram] buffered photo chat_id=${chatId} slot=${slot} pending=${pendingMessages.get(key).length}`);
|
|
304
502
|
return;
|
|
305
503
|
}
|
|
306
504
|
|
|
307
|
-
isRunning.add(
|
|
505
|
+
isRunning.add(key);
|
|
506
|
+
runStartTimes.set(key, new Date());
|
|
308
507
|
await ctx.api.sendChatAction(chatId, 'typing');
|
|
309
508
|
const typingInterval = setInterval(() => {
|
|
310
509
|
ctx.api.sendChatAction(chatId, 'typing').catch(() => {});
|
|
311
510
|
}, 4000);
|
|
312
511
|
|
|
313
512
|
try {
|
|
314
|
-
await processQueue(ctx.api, chatId, [entry]);
|
|
513
|
+
await processQueue(ctx.api, chatId, slot, [entry]);
|
|
315
514
|
} finally {
|
|
316
515
|
clearInterval(typingInterval);
|
|
317
|
-
isRunning.delete(
|
|
516
|
+
isRunning.delete(key);
|
|
517
|
+
runStartTimes.delete(key);
|
|
318
518
|
}
|
|
319
519
|
});
|
|
320
520
|
|
|
@@ -327,26 +527,30 @@ export async function startTelegramChannel(config) {
|
|
|
327
527
|
const chatId = ctx.chat.id;
|
|
328
528
|
const ts = new Date().toISOString();
|
|
329
529
|
const entry = { text: ctx.message.text, attachments: [], ts };
|
|
530
|
+
const slot = getActiveSlot(chatId);
|
|
531
|
+
const key = slotKey(chatId, slot);
|
|
330
532
|
|
|
331
|
-
if (isRunning.has(
|
|
332
|
-
if (!pendingMessages.has(
|
|
333
|
-
pendingMessages.get(
|
|
334
|
-
console.log(`[telegram] buffered message chat_id=${chatId} pending=${pendingMessages.get(
|
|
533
|
+
if (isRunning.has(key)) {
|
|
534
|
+
if (!pendingMessages.has(key)) pendingMessages.set(key, []);
|
|
535
|
+
pendingMessages.get(key).push(entry);
|
|
536
|
+
console.log(`[telegram] buffered message chat_id=${chatId} slot=${slot} pending=${pendingMessages.get(key).length}`);
|
|
335
537
|
return;
|
|
336
538
|
}
|
|
337
539
|
|
|
338
|
-
isRunning.add(
|
|
339
|
-
|
|
540
|
+
isRunning.add(key);
|
|
541
|
+
runStartTimes.set(key, new Date());
|
|
542
|
+
console.log(`[telegram] incoming chat_id=${chatId} slot=${slot}`);
|
|
340
543
|
await ctx.api.sendChatAction(chatId, 'typing');
|
|
341
544
|
const typingInterval = setInterval(() => {
|
|
342
545
|
ctx.api.sendChatAction(chatId, 'typing').catch(() => {});
|
|
343
546
|
}, 4000);
|
|
344
547
|
|
|
345
548
|
try {
|
|
346
|
-
await processQueue(ctx.api, chatId, [entry]);
|
|
549
|
+
await processQueue(ctx.api, chatId, slot, [entry]);
|
|
347
550
|
} finally {
|
|
348
551
|
clearInterval(typingInterval);
|
|
349
|
-
isRunning.delete(
|
|
552
|
+
isRunning.delete(key);
|
|
553
|
+
runStartTimes.delete(key);
|
|
350
554
|
}
|
|
351
555
|
});
|
|
352
556
|
|
package/src/server/tools.js
CHANGED
|
@@ -543,7 +543,10 @@ const SEED_TOOLS = {
|
|
|
543
543
|
try {
|
|
544
544
|
const tgSessionsFile = path.join(process.env.HOME, '.jarvis/data/channels/telegram/sessions.json');
|
|
545
545
|
const tgSessions = JSON.parse(await fs.promises.readFile(tgSessionsFile, 'utf8').catch(() => '{}'));
|
|
546
|
-
const
|
|
546
|
+
const chatData = tgSessions[String(chatId)];
|
|
547
|
+
const sessionId = typeof chatData === 'string'
|
|
548
|
+
? chatData
|
|
549
|
+
: chatData?.slots?.[String(chatData?.active ?? 1)] ?? null;
|
|
547
550
|
const prefix = sessionId ? String(sessionId).slice(0, 8) : 'unknown';
|
|
548
551
|
const logDir = path.join(process.env.HOME, '.jarvis/telegram-chats');
|
|
549
552
|
const logFile = path.join(logDir, String(chatId) + '-' + prefix + '.log');
|