dsh-tg-bridge 1.2.4 → 1.2.6
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/bridge.mjs +22 -15
- package/control.mjs +11 -0
- package/package.json +1 -1
package/bridge.mjs
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from 'node:fs';
|
|
13
13
|
import { homedir } from 'node:os';
|
|
14
14
|
import { join, basename } from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
15
16
|
// Force IPv4 for every fetch. DNS returns an AAAA next to the A record, and
|
|
16
17
|
// Happy Eyeballs happily picks the IPv6 path, which here costs ~5 s or dies
|
|
17
18
|
// outright ("fetch failed" every 6 s, measured 22.10.2026: IPv4 0.34 s, IPv6
|
|
@@ -36,6 +37,10 @@ const CRED = join(DSH_HOME, '.credentials.yaml');
|
|
|
36
37
|
const PENDING_DIR = join(DIR, 'pending');
|
|
37
38
|
const DONE_FILE = join(DIR, 'done.json');
|
|
38
39
|
const RESTART_MARKER = join(DIR, 'restart.pending');
|
|
40
|
+
// `/restart confirm` leaves a note here for the process that replaces it: the
|
|
41
|
+
// old code exits ~300 ms after spawning the new one, and without this the chat
|
|
42
|
+
// saw "Перезапускаю мост…" and then silence — no proof the bot came back.
|
|
43
|
+
const RESTART_NOTICE = join(fileURLToPath(new URL('.', import.meta.url)), 'restart-notice.json');
|
|
39
44
|
const DONE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
40
45
|
// dsh.cmd runs the Electron binary as Node against this CLI entrypoint; the
|
|
41
46
|
// bridge mirrors that exactly so no shell quoting of task text is needed.
|
|
@@ -853,10 +858,6 @@ async function startProgress(chatId, threadId, lang = 'ru') {
|
|
|
853
858
|
chat_id: chatId,
|
|
854
859
|
text: body(),
|
|
855
860
|
disable_web_page_preview: true,
|
|
856
|
-
// Stop is available for as long as the turn runs; the keyboard is cleared
|
|
857
|
-
// when the status turns into the final line, so a stale button cannot be
|
|
858
|
-
// pressed after the work is over.
|
|
859
|
-
reply_markup: stopKeyboard(sessionKey(chatId, threadId), lang),
|
|
860
861
|
...(threadId > 0 ? { message_thread_id: threadId } : {}),
|
|
861
862
|
});
|
|
862
863
|
messageId = res.message_id;
|
|
@@ -1439,17 +1440,6 @@ const lastPrompt = new Map();
|
|
|
1439
1440
|
const approvals = new Map();
|
|
1440
1441
|
let approvalSeq = 0;
|
|
1441
1442
|
|
|
1442
|
-
function stopKeyboard(key, lang = 'ru') {
|
|
1443
|
-
return {
|
|
1444
|
-
inline_keyboard: [
|
|
1445
|
-
[
|
|
1446
|
-
{ text: `⏹ ${t(lang, 'btn.stop')}`, callback_data: 'stop' },
|
|
1447
|
-
{ text: `📄 ${t(lang, 'btn.logs')}`, callback_data: 'logs' },
|
|
1448
|
-
],
|
|
1449
|
-
],
|
|
1450
|
-
};
|
|
1451
|
-
}
|
|
1452
|
-
|
|
1453
1443
|
function errorKeyboard(key, lang = 'ru') {
|
|
1454
1444
|
return {
|
|
1455
1445
|
inline_keyboard: [
|
|
@@ -1670,6 +1660,23 @@ async function main() {
|
|
|
1670
1660
|
const me = await tg('getMe');
|
|
1671
1661
|
botUsername = me.username || '';
|
|
1672
1662
|
log(`bot @${me.username}`);
|
|
1663
|
+
// Confirm the restart in the topic it was asked from, not in the DM: the
|
|
1664
|
+
// person who typed `/restart confirm` was looking at that thread.
|
|
1665
|
+
try {
|
|
1666
|
+
if (existsSync(RESTART_NOTICE)) {
|
|
1667
|
+
const notice = JSON.parse(readFileSync(RESTART_NOTICE, 'utf8'));
|
|
1668
|
+
unlinkSync(RESTART_NOTICE);
|
|
1669
|
+
if (Date.now() - Number(notice.at || 0) < 10 * 60 * 1000) {
|
|
1670
|
+
await send(
|
|
1671
|
+
notice.chatId,
|
|
1672
|
+
`✅ Мост перезапущен — на связи (pid ${process.pid}).\nТема и сессия прежние, можно продолжать.`,
|
|
1673
|
+
notice.threadId || 0,
|
|
1674
|
+
);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
} catch (error) {
|
|
1678
|
+
log(`restart notice failed: ${error.message}`);
|
|
1679
|
+
}
|
|
1673
1680
|
for (const id of config.allowedUserIds) {
|
|
1674
1681
|
try { await send(id, `🤖 ${t(config.lang || 'ru', 'msg.started')}`); } catch {}
|
|
1675
1682
|
}
|
package/control.mjs
CHANGED
|
@@ -554,6 +554,17 @@ export async function handleControl(ctx) {
|
|
|
554
554
|
}
|
|
555
555
|
say('Перезапускаю мост…');
|
|
556
556
|
const bridge = fileURLToPath(new URL('./bridge.mjs', import.meta.url));
|
|
557
|
+
// Tell the replacement process where to confirm, so the chat does not go
|
|
558
|
+
// silent after "Перезапускаю мост…" (it did, twice on 2026-10-24).
|
|
559
|
+
try {
|
|
560
|
+
writeFileSync(
|
|
561
|
+
fileURLToPath(new URL('./restart-notice.json', import.meta.url)),
|
|
562
|
+
JSON.stringify({ chatId: ctx.chatId, threadId: ctx.threadId || 0, at: Date.now() }),
|
|
563
|
+
'utf8',
|
|
564
|
+
);
|
|
565
|
+
} catch (error) {
|
|
566
|
+
log(`restart notice not written: ${error.message}`);
|
|
567
|
+
}
|
|
557
568
|
spawn(process.execPath, [bridge], { detached: true, stdio: 'ignore', windowsHide: true }).unref();
|
|
558
569
|
setTimeout(() => process.exit(0), 300);
|
|
559
570
|
return true;
|
package/package.json
CHANGED