@delt/claude-alarm 0.5.1 → 0.5.3
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/dist/channel/server.js +3 -1
- package/dist/channel/server.js.map +1 -1
- package/dist/cli.js +52 -3
- package/dist/cli.js.map +1 -1
- package/dist/dashboard/index.html +141 -26
- package/dist/hub/server.js +49 -2
- package/dist/hub/server.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +49 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/dashboard/index.html +141 -26
|
@@ -650,24 +650,64 @@
|
|
|
650
650
|
</div>
|
|
651
651
|
</div>
|
|
652
652
|
<div id="settingsTelegram" style="display:none">
|
|
653
|
-
|
|
654
|
-
<div
|
|
655
|
-
<
|
|
656
|
-
|
|
653
|
+
<!-- Step 1: Bot Token -->
|
|
654
|
+
<div id="tgStep1">
|
|
655
|
+
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
|
656
|
+
<span style="background:var(--accent);color:#fff;border-radius:50%;width:22px;height:22px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;flex-shrink:0">1</span>
|
|
657
|
+
<span style="font-size:13px;font-weight:600;color:var(--text)">Create a bot & paste the token</span>
|
|
658
|
+
</div>
|
|
659
|
+
<p style="margin:0 0 10px 30px;font-size:12px;color:var(--text-dim)">Open <strong>@BotFather</strong> on Telegram → <code>/newbot</code> → copy the token</p>
|
|
660
|
+
<div style="margin-left:30px;margin-bottom:12px">
|
|
661
|
+
<input type="text" id="tgBotToken" placeholder="123456:ABC-DEF1234ghIkl-zyx57W2v..." autocomplete="off" style="width:100%;box-sizing:border-box;padding:10px;background:var(--input-bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px;font-family:monospace">
|
|
662
|
+
</div>
|
|
663
|
+
<div style="margin-left:30px">
|
|
664
|
+
<button id="tgNextStep2" style="padding:8px 20px;font-size:13px">Next →</button>
|
|
665
|
+
</div>
|
|
657
666
|
</div>
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
667
|
+
|
|
668
|
+
<!-- Step 2: Detect Chat ID -->
|
|
669
|
+
<div id="tgStep2" style="display:none">
|
|
670
|
+
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
|
671
|
+
<span style="background:var(--accent);color:#fff;border-radius:50%;width:22px;height:22px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;flex-shrink:0">2</span>
|
|
672
|
+
<span style="font-size:13px;font-weight:600;color:var(--text)">Find your Chat ID</span>
|
|
673
|
+
</div>
|
|
674
|
+
<p style="margin:0 0 10px 30px;font-size:12px;color:var(--text-dim)">Send any message to your bot on Telegram, then click Detect.</p>
|
|
675
|
+
<div style="margin-left:30px;margin-bottom:10px;display:flex;gap:8px">
|
|
676
|
+
<button id="tgDetect" style="padding:8px 20px;font-size:13px;background:none;color:var(--accent);border:1px solid var(--accent);border-radius:6px;cursor:pointer;font-weight:500">Detect Chat ID</button>
|
|
677
|
+
<span id="tgDetectStatus" style="font-size:12px;color:var(--text-dim);display:flex;align-items:center"></span>
|
|
678
|
+
</div>
|
|
679
|
+
<div id="tgChatList" style="margin-left:30px;margin-bottom:10px"></div>
|
|
680
|
+
<div style="margin-left:30px;margin-bottom:12px">
|
|
681
|
+
<label style="display:block;font-size:11px;color:var(--text-dim);margin-bottom:4px">Or enter manually:</label>
|
|
682
|
+
<input type="text" id="tgChatId" placeholder="-1001234567890" autocomplete="off" style="width:100%;box-sizing:border-box;padding:10px;background:var(--input-bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px;font-family:monospace">
|
|
683
|
+
</div>
|
|
684
|
+
<div style="margin-left:30px;display:flex;gap:8px">
|
|
685
|
+
<button id="tgBackStep1" style="padding:8px 20px;font-size:13px;background:none;color:var(--text-dim);border:1px solid var(--border);border-radius:6px;cursor:pointer">← Back</button>
|
|
686
|
+
<button id="tgNextStep3" style="padding:8px 20px;font-size:13px">Next →</button>
|
|
687
|
+
</div>
|
|
665
688
|
</div>
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
<
|
|
670
|
-
|
|
689
|
+
|
|
690
|
+
<!-- Step 3: Test & Save -->
|
|
691
|
+
<div id="tgStep3" style="display:none">
|
|
692
|
+
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
|
693
|
+
<span style="background:var(--accent);color:#fff;border-radius:50%;width:22px;height:22px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;flex-shrink:0">3</span>
|
|
694
|
+
<span style="font-size:13px;font-weight:600;color:var(--text)">Test & Enable</span>
|
|
695
|
+
</div>
|
|
696
|
+
<div style="margin-left:30px;margin-bottom:12px;padding:10px;background:var(--input-bg);border-radius:6px;font-size:12px;color:var(--text-dim)">
|
|
697
|
+
<div>Bot Token: <span id="tgSummaryToken" style="color:var(--text);font-family:monospace"></span></div>
|
|
698
|
+
<div style="margin-top:4px">Chat ID: <span id="tgSummaryChatId" style="color:var(--text);font-family:monospace"></span></div>
|
|
699
|
+
</div>
|
|
700
|
+
<div style="margin-left:30px;margin-bottom:12px;display:flex;align-items:center;gap:8px">
|
|
701
|
+
<input type="checkbox" id="tgEnabled" checked style="width:16px;height:16px;accent-color:var(--accent)">
|
|
702
|
+
<label for="tgEnabled" style="font-size:13px;color:var(--text)">Enable Telegram notifications</label>
|
|
703
|
+
</div>
|
|
704
|
+
<div id="tgStatus" style="display:none;margin:0 0 12px 30px;padding:8px 12px;border-radius:6px;font-size:12px"></div>
|
|
705
|
+
<div style="margin-left:30px;display:flex;gap:8px">
|
|
706
|
+
<button id="tgBackStep2" style="padding:8px 20px;font-size:13px;background:none;color:var(--text-dim);border:1px solid var(--border);border-radius:6px;cursor:pointer">← Back</button>
|
|
707
|
+
<button id="tgTest" style="flex:1;padding:10px;background:none;color:var(--accent);border:1px solid var(--accent);border-radius:6px;cursor:pointer;font-size:13px;font-weight:500">Send Test</button>
|
|
708
|
+
<button id="tgSave" style="flex:1;padding:10px">Save</button>
|
|
709
|
+
<button id="settingsCancel2" style="padding:10px 16px;background:none;color:var(--text);border:1px solid var(--border);border-radius:6px;cursor:pointer;font-size:13px">Cancel</button>
|
|
710
|
+
</div>
|
|
671
711
|
</div>
|
|
672
712
|
</div>
|
|
673
713
|
</div>
|
|
@@ -1051,20 +1091,19 @@
|
|
|
1051
1091
|
if (!content && !hasImage) return;
|
|
1052
1092
|
if (!state.selectedSession || !state.ws) return;
|
|
1053
1093
|
|
|
1054
|
-
// Send text
|
|
1055
|
-
if (content) {
|
|
1056
|
-
state.ws.send(JSON.stringify({ type: 'message_to_session', sessionId: state.selectedSession, content }));
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
// Send image
|
|
1060
1094
|
if (hasImage) {
|
|
1095
|
+
// Send image + text together
|
|
1061
1096
|
state.ws.send(JSON.stringify({
|
|
1062
1097
|
type: 'image_upload',
|
|
1063
1098
|
sessionId: state.selectedSession,
|
|
1064
1099
|
imageData: state.pendingImage.data,
|
|
1065
1100
|
mimeType: state.pendingImage.mimeType,
|
|
1066
1101
|
originalName: state.pendingImage.name,
|
|
1102
|
+
content: content || '',
|
|
1067
1103
|
}));
|
|
1104
|
+
} else if (content) {
|
|
1105
|
+
// Text only
|
|
1106
|
+
state.ws.send(JSON.stringify({ type: 'message_to_session', sessionId: state.selectedSession, content }));
|
|
1068
1107
|
}
|
|
1069
1108
|
|
|
1070
1109
|
// Add to local messages
|
|
@@ -1218,7 +1257,17 @@
|
|
|
1218
1257
|
}
|
|
1219
1258
|
$('#tgChatId').value = tg.chatId || '';
|
|
1220
1259
|
$('#tgEnabled').checked = !!tg.enabled;
|
|
1221
|
-
|
|
1260
|
+
// Jump to appropriate step
|
|
1261
|
+
if (tg.chatId && tg.botToken) {
|
|
1262
|
+
$('#tgSummaryToken').textContent = (tg.botToken || '').slice(0, 8) + '...';
|
|
1263
|
+
$('#tgSummaryChatId').textContent = tg.chatId;
|
|
1264
|
+
showTgStep(3);
|
|
1265
|
+
} else if (tg.botToken) {
|
|
1266
|
+
showTgStep(2);
|
|
1267
|
+
} else {
|
|
1268
|
+
showTgStep(1);
|
|
1269
|
+
}
|
|
1270
|
+
} catch { showTgStep(1); }
|
|
1222
1271
|
$('#tgStatus').style.display = 'none';
|
|
1223
1272
|
$('#settingsOverlay').classList.remove('hidden');
|
|
1224
1273
|
});
|
|
@@ -1269,7 +1318,13 @@
|
|
|
1269
1318
|
});
|
|
1270
1319
|
}
|
|
1271
1320
|
|
|
1272
|
-
// Telegram
|
|
1321
|
+
// Telegram wizard
|
|
1322
|
+
function showTgStep(step) {
|
|
1323
|
+
$('#tgStep1').style.display = step === 1 ? 'block' : 'none';
|
|
1324
|
+
$('#tgStep2').style.display = step === 2 ? 'block' : 'none';
|
|
1325
|
+
$('#tgStep3').style.display = step === 3 ? 'block' : 'none';
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1273
1328
|
function showTgStatus(msg, ok) {
|
|
1274
1329
|
const el = $('#tgStatus');
|
|
1275
1330
|
el.textContent = msg;
|
|
@@ -1278,11 +1333,70 @@
|
|
|
1278
1333
|
el.style.color = ok ? 'var(--green)' : 'var(--red)';
|
|
1279
1334
|
}
|
|
1280
1335
|
|
|
1336
|
+
// Step navigation
|
|
1337
|
+
$('#tgNextStep2').addEventListener('click', () => {
|
|
1338
|
+
const token = $('#tgBotToken').value.trim();
|
|
1339
|
+
if (!token) { $('#tgBotToken').style.borderColor = 'var(--red)'; return; }
|
|
1340
|
+
$('#tgBotToken').style.borderColor = 'var(--border)';
|
|
1341
|
+
showTgStep(2);
|
|
1342
|
+
});
|
|
1343
|
+
$('#tgBackStep1').addEventListener('click', () => showTgStep(1));
|
|
1344
|
+
$('#tgNextStep3').addEventListener('click', () => {
|
|
1345
|
+
const chatId = $('#tgChatId').value.trim();
|
|
1346
|
+
if (!chatId) { $('#tgChatId').style.borderColor = 'var(--red)'; return; }
|
|
1347
|
+
$('#tgChatId').style.borderColor = 'var(--border)';
|
|
1348
|
+
const token = $('#tgBotToken').value.trim();
|
|
1349
|
+
$('#tgSummaryToken').textContent = token.slice(0, 8) + '...';
|
|
1350
|
+
$('#tgSummaryChatId').textContent = chatId;
|
|
1351
|
+
$('#tgStatus').style.display = 'none';
|
|
1352
|
+
showTgStep(3);
|
|
1353
|
+
});
|
|
1354
|
+
$('#tgBackStep2').addEventListener('click', () => showTgStep(2));
|
|
1355
|
+
|
|
1356
|
+
// Detect Chat ID
|
|
1357
|
+
$('#tgDetect').addEventListener('click', async () => {
|
|
1358
|
+
const botToken = $('#tgBotToken').value.trim();
|
|
1359
|
+
if (!botToken || botToken.includes('...')) { $('#tgDetectStatus').textContent = 'Enter a valid Bot Token first.'; return; }
|
|
1360
|
+
$('#tgDetectStatus').textContent = 'Detecting...';
|
|
1361
|
+
$('#tgChatList').innerHTML = '';
|
|
1362
|
+
try {
|
|
1363
|
+
const tokenQuery = state.token ? `?token=${encodeURIComponent(state.token)}` : '';
|
|
1364
|
+
const res = await fetch(`/api/telegram/detect${tokenQuery}`, {
|
|
1365
|
+
method: 'POST',
|
|
1366
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1367
|
+
body: JSON.stringify({ botToken }),
|
|
1368
|
+
});
|
|
1369
|
+
const data = await res.json();
|
|
1370
|
+
if (data.error) { $('#tgDetectStatus').textContent = data.error; return; }
|
|
1371
|
+
if (!data.ok || !data.chats.length) {
|
|
1372
|
+
$('#tgDetectStatus').textContent = 'No messages found. Send a message to your bot first!';
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
$('#tgDetectStatus').textContent = 'Found ' + data.chats.length + ' chat(s):';
|
|
1376
|
+
$('#tgChatList').innerHTML = data.chats.map(c =>
|
|
1377
|
+
`<button class="tg-chat-pick" data-id="${esc(c.id)}" style="display:block;width:100%;text-align:left;padding:8px 12px;margin-bottom:4px;background:var(--input-bg);border:1px solid var(--border);border-radius:6px;color:var(--text);cursor:pointer;font-size:12px;transition:border-color 0.15s">
|
|
1378
|
+
<strong>${esc(c.name)}</strong> <span style="color:var(--text-dim)">(${esc(c.type)}, ID: ${esc(c.id)})</span>
|
|
1379
|
+
</button>`
|
|
1380
|
+
).join('');
|
|
1381
|
+
$('#tgChatList').querySelectorAll('.tg-chat-pick').forEach(btn => {
|
|
1382
|
+
btn.addEventListener('click', () => {
|
|
1383
|
+
$('#tgChatId').value = btn.dataset.id;
|
|
1384
|
+
$('#tgChatList').querySelectorAll('.tg-chat-pick').forEach(b => b.style.borderColor = 'var(--border)');
|
|
1385
|
+
btn.style.borderColor = 'var(--accent)';
|
|
1386
|
+
});
|
|
1387
|
+
btn.addEventListener('mouseenter', () => { btn.style.borderColor = 'var(--accent)'; });
|
|
1388
|
+
btn.addEventListener('mouseleave', () => {
|
|
1389
|
+
if ($('#tgChatId').value !== btn.dataset.id) btn.style.borderColor = 'var(--border)';
|
|
1390
|
+
});
|
|
1391
|
+
});
|
|
1392
|
+
} catch { $('#tgDetectStatus').textContent = 'Connection error.'; }
|
|
1393
|
+
});
|
|
1394
|
+
|
|
1395
|
+
// Test
|
|
1281
1396
|
$('#tgTest').addEventListener('click', async () => {
|
|
1282
1397
|
const botToken = $('#tgBotToken').value.trim();
|
|
1283
1398
|
const chatId = $('#tgChatId').value.trim();
|
|
1284
1399
|
if (!botToken || !chatId) { showTgStatus('Bot Token and Chat ID are required.', false); return; }
|
|
1285
|
-
if (botToken.includes('...')) { showTgStatus('Please enter full Bot Token (not masked).', false); return; }
|
|
1286
1400
|
try {
|
|
1287
1401
|
const tokenQuery = state.token ? `?token=${encodeURIComponent(state.token)}` : '';
|
|
1288
1402
|
const res = await fetch(`/api/telegram/test${tokenQuery}`, {
|
|
@@ -1296,11 +1410,12 @@
|
|
|
1296
1410
|
} catch (e) { showTgStatus('Connection error.', false); }
|
|
1297
1411
|
});
|
|
1298
1412
|
|
|
1413
|
+
// Save
|
|
1299
1414
|
$('#tgSave').addEventListener('click', async () => {
|
|
1300
1415
|
const botToken = $('#tgBotToken').value.trim();
|
|
1301
1416
|
const chatId = $('#tgChatId').value.trim();
|
|
1302
1417
|
const enabled = $('#tgEnabled').checked;
|
|
1303
|
-
if (
|
|
1418
|
+
if (!botToken || !chatId) { showTgStatus('Bot Token and Chat ID are required.', false); return; }
|
|
1304
1419
|
try {
|
|
1305
1420
|
const tokenQuery = state.token ? `?token=${encodeURIComponent(state.token)}` : '';
|
|
1306
1421
|
await fetch(`/api/telegram${tokenQuery}`, {
|
package/dist/hub/server.js
CHANGED
|
@@ -551,6 +551,8 @@ var HubServer = class {
|
|
|
551
551
|
this.handleTelegramSave(req, res);
|
|
552
552
|
} else if (url.pathname === "/api/telegram/test" && req.method === "POST") {
|
|
553
553
|
this.handleTelegramTest(req, res);
|
|
554
|
+
} else if (url.pathname === "/api/telegram/detect" && req.method === "POST") {
|
|
555
|
+
this.handleTelegramDetect(req, res);
|
|
554
556
|
} else {
|
|
555
557
|
this.jsonResponse(res, 404, { error: "Not found" });
|
|
556
558
|
}
|
|
@@ -736,7 +738,7 @@ var HubServer = class {
|
|
|
736
738
|
}
|
|
737
739
|
}
|
|
738
740
|
handleImageUpload(msg) {
|
|
739
|
-
const { sessionId, imageData, mimeType, originalName } = msg;
|
|
741
|
+
const { sessionId, imageData, mimeType, originalName, content } = msg;
|
|
740
742
|
if (!this.localChannels.has(sessionId)) {
|
|
741
743
|
logger.warn(`Image upload rejected: session ${sessionId} is not local`);
|
|
742
744
|
return;
|
|
@@ -761,7 +763,8 @@ var HubServer = class {
|
|
|
761
763
|
sessionId,
|
|
762
764
|
imagePath: filePath,
|
|
763
765
|
mimeType,
|
|
764
|
-
originalName
|
|
766
|
+
originalName,
|
|
767
|
+
content
|
|
765
768
|
};
|
|
766
769
|
channelWs.send(JSON.stringify(forwardMsg));
|
|
767
770
|
logger.info(`Image saved and forwarded: ${filename} (${buffer.length} bytes)`);
|
|
@@ -858,6 +861,50 @@ var HubServer = class {
|
|
|
858
861
|
this.jsonResponse(res, 500, { error: err.message });
|
|
859
862
|
}
|
|
860
863
|
}
|
|
864
|
+
async handleTelegramDetect(req, res) {
|
|
865
|
+
const body = await this.readBody(req);
|
|
866
|
+
if (!body) {
|
|
867
|
+
this.jsonResponse(res, 400, { error: "Invalid JSON" });
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
const { botToken } = body;
|
|
871
|
+
if (!botToken) {
|
|
872
|
+
this.jsonResponse(res, 400, { error: "botToken required" });
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
try {
|
|
876
|
+
const detectRes = await fetch(`https://api.telegram.org/bot${botToken}/getUpdates?timeout=0&limit=10`, {
|
|
877
|
+
signal: AbortSignal.timeout(1e4)
|
|
878
|
+
});
|
|
879
|
+
if (!detectRes.ok) {
|
|
880
|
+
const err = await detectRes.json();
|
|
881
|
+
this.jsonResponse(res, 400, { error: err.description || "Invalid bot token" });
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
const data = await detectRes.json();
|
|
885
|
+
if (!data.ok || !data.result.length) {
|
|
886
|
+
this.jsonResponse(res, 200, { ok: false, chats: [] });
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
const chatMap = /* @__PURE__ */ new Map();
|
|
890
|
+
for (const update of data.result) {
|
|
891
|
+
if (update.message?.chat) {
|
|
892
|
+
const chat = update.message.chat;
|
|
893
|
+
const id = String(chat.id);
|
|
894
|
+
if (!chatMap.has(id)) {
|
|
895
|
+
chatMap.set(id, {
|
|
896
|
+
id,
|
|
897
|
+
name: chat.title || chat.first_name || id,
|
|
898
|
+
type: chat.type
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
this.jsonResponse(res, 200, { ok: true, chats: [...chatMap.values()] });
|
|
904
|
+
} catch (err) {
|
|
905
|
+
this.jsonResponse(res, 500, { error: err.message });
|
|
906
|
+
}
|
|
907
|
+
}
|
|
861
908
|
cleanupUploads() {
|
|
862
909
|
try {
|
|
863
910
|
if (!fs2.existsSync(UPLOADS_DIR)) return;
|