appback-remoteagent 0.13.0 → 0.13.2
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/bot.js +25 -7
- package/dist/config.js +1 -0
- package/dist/index.js +48 -4
- package/dist/services/bot-management-service.js +22 -5
- package/package.json +1 -1
package/dist/bot.js
CHANGED
|
@@ -1030,12 +1030,11 @@ function sanitizeLoggedTelegramText(text) {
|
|
|
1030
1030
|
async function runWithPendingAnimation(botToken, chatId, task) {
|
|
1031
1031
|
let typingStopped = false;
|
|
1032
1032
|
let typingTimer;
|
|
1033
|
-
const
|
|
1034
|
-
const pulseTyping = () => {
|
|
1033
|
+
const pulseTyping = async () => {
|
|
1035
1034
|
if (typingStopped) {
|
|
1036
1035
|
return;
|
|
1037
1036
|
}
|
|
1038
|
-
|
|
1037
|
+
await sendTelegramChatAction(botToken, chatId, "typing").catch((error) => {
|
|
1039
1038
|
if (isTelegramForbiddenError(error)) {
|
|
1040
1039
|
console.warn(`[telegram-delivery] chat=${chatId} skipped typing action: ${error instanceof Error ? error.message : String(error)}`);
|
|
1041
1040
|
typingStopped = true;
|
|
@@ -1043,12 +1042,14 @@ async function runWithPendingAnimation(botToken, chatId, task) {
|
|
|
1043
1042
|
}
|
|
1044
1043
|
console.warn(`[telegram-chat-action] chat=${chatId} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1045
1044
|
});
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1045
|
+
typingTimer = setTimeout(() => {
|
|
1046
|
+
void pulseTyping();
|
|
1047
|
+
}, config.telegramTypingIntervalMs);
|
|
1049
1048
|
typingTimer.unref?.();
|
|
1050
1049
|
};
|
|
1051
|
-
pulseTyping()
|
|
1050
|
+
await withTimeout(pulseTyping(), 1500).catch((error) => {
|
|
1051
|
+
console.warn(`[telegram-chat-action] chat=${chatId} initial typing action was not confirmed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1052
|
+
});
|
|
1052
1053
|
try {
|
|
1053
1054
|
const helpers = {
|
|
1054
1055
|
reportProgress: async (chunks, parseMode) => {
|
|
@@ -1470,6 +1471,23 @@ function formatProviderTimeoutFinalMessage(message) {
|
|
|
1470
1471
|
async function sleep(ms) {
|
|
1471
1472
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
1472
1473
|
}
|
|
1474
|
+
async function withTimeout(promise, timeoutMs) {
|
|
1475
|
+
let timer;
|
|
1476
|
+
try {
|
|
1477
|
+
return await Promise.race([
|
|
1478
|
+
promise,
|
|
1479
|
+
new Promise((_, reject) => {
|
|
1480
|
+
timer = setTimeout(() => reject(new Error(`Timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
1481
|
+
timer.unref?.();
|
|
1482
|
+
}),
|
|
1483
|
+
]);
|
|
1484
|
+
}
|
|
1485
|
+
finally {
|
|
1486
|
+
if (timer) {
|
|
1487
|
+
clearTimeout(timer);
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1473
1491
|
async function ensureOwnerControlAccess(ctx) {
|
|
1474
1492
|
if (ctx.chat?.type !== "private") {
|
|
1475
1493
|
throw new Error("This command is available only in private 1:1 chats.");
|
package/dist/config.js
CHANGED
|
@@ -136,6 +136,7 @@ export const config = {
|
|
|
136
136
|
telegramPollingMaxConcurrency: readTimeout("TELEGRAM_POLLING_MAX_CONCURRENCY", 3),
|
|
137
137
|
telegramOwnerId: readOptional("TELEGRAM_OWNER_ID"),
|
|
138
138
|
telegramMessageBatchMs: readNonNegativeTimeout("TELEGRAM_MESSAGE_BATCH_MS", 1500),
|
|
139
|
+
telegramTypingIntervalMs: readTimeout("TELEGRAM_TYPING_INTERVAL_MS", 4_000),
|
|
139
140
|
telegramAutoProgressMaxTurns: readOptionalNonNegativeInteger("TELEGRAM_AUTO_PROGRESS_MAX_TURNS") ?? 6,
|
|
140
141
|
telegramEmptyResponseRetries: readOptionalNonNegativeInteger("TELEGRAM_EMPTY_RESPONSE_RETRIES") ?? 1,
|
|
141
142
|
telegramRetryableErrorRetries: readOptionalNonNegativeInteger("TELEGRAM_RETRYABLE_ERROR_RETRIES") ?? 2,
|
package/dist/index.js
CHANGED
|
@@ -60,7 +60,7 @@ async function main() {
|
|
|
60
60
|
console.error("Local UI failed to start:", error);
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
-
const botInfos = config.telegramBotTokens.map((token, index) =>
|
|
63
|
+
const botInfos = await Promise.all(config.telegramBotTokens.map((token, index) => resolveBotInfo(token, index)));
|
|
64
64
|
const bots = config.telegramBotTokens.map((token, index) => createBot(token, bridge, botManagement, botInfos[index]));
|
|
65
65
|
if (config.telegramCommandMenuEnabled) {
|
|
66
66
|
for (const bot of bots) {
|
|
@@ -416,11 +416,41 @@ class AsyncSemaphore {
|
|
|
416
416
|
}
|
|
417
417
|
}
|
|
418
418
|
}
|
|
419
|
-
function
|
|
419
|
+
async function resolveBotInfo(token, index) {
|
|
420
|
+
try {
|
|
421
|
+
const { stdout } = await execFileAsync("curl", [
|
|
422
|
+
"-sS",
|
|
423
|
+
"-4",
|
|
424
|
+
"--max-time",
|
|
425
|
+
"10",
|
|
426
|
+
`https://api.telegram.org/bot${token}/getMe`,
|
|
427
|
+
]);
|
|
428
|
+
const payload = JSON.parse(stdout);
|
|
429
|
+
if (payload.ok && payload.result?.id && payload.result.username) {
|
|
430
|
+
return buildBotInfoFromIdentity(payload.result.id, payload.result.username, payload.result.first_name);
|
|
431
|
+
}
|
|
432
|
+
console.warn(`Telegram getMe failed for bot ${tokenIdLabel(token)}: ${payload.description || "missing bot identity"}`);
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
console.warn(`Telegram getMe failed for bot ${tokenIdLabel(token)}: ${summarizeTelegramIdentityError(error)}`);
|
|
436
|
+
}
|
|
437
|
+
return buildFallbackBotInfo(token, index);
|
|
438
|
+
}
|
|
439
|
+
function buildBotInfoFromIdentity(id, username, firstName) {
|
|
440
|
+
return {
|
|
441
|
+
id,
|
|
442
|
+
is_bot: true,
|
|
443
|
+
first_name: firstName || username,
|
|
444
|
+
username,
|
|
445
|
+
can_join_groups: false,
|
|
446
|
+
can_read_all_group_messages: false,
|
|
447
|
+
supports_inline_queries: false,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function buildFallbackBotInfo(token, index) {
|
|
420
451
|
const id = Number.parseInt(token.split(":", 1)[0] ?? "", 10);
|
|
421
|
-
const configuredUsername = config.telegramBotUsernames[index];
|
|
422
452
|
const fallbackUsername = knownBotUsername(id);
|
|
423
|
-
const username =
|
|
453
|
+
const username = fallbackUsername || `bot_${Number.isFinite(id) ? id : index + 1}`;
|
|
424
454
|
return {
|
|
425
455
|
id: Number.isFinite(id) ? id : index + 1,
|
|
426
456
|
is_bot: true,
|
|
@@ -431,6 +461,20 @@ function buildBotInfo(token, index) {
|
|
|
431
461
|
supports_inline_queries: false,
|
|
432
462
|
};
|
|
433
463
|
}
|
|
464
|
+
function tokenIdLabel(token) {
|
|
465
|
+
return token.split(":", 1)[0] || "unknown";
|
|
466
|
+
}
|
|
467
|
+
function summarizeTelegramIdentityError(error) {
|
|
468
|
+
if (!(error instanceof Error)) {
|
|
469
|
+
return String(error);
|
|
470
|
+
}
|
|
471
|
+
const code = typeof error === "object" && error !== null && "code" in error
|
|
472
|
+
? String(error.code ?? "")
|
|
473
|
+
: "";
|
|
474
|
+
return [code, error.message.replace(/bot\d+:[A-Za-z0-9_-]+/g, "bot[redacted]")]
|
|
475
|
+
.filter(Boolean)
|
|
476
|
+
.join(" ");
|
|
477
|
+
}
|
|
434
478
|
function knownBotUsername(id) {
|
|
435
479
|
if (id === 8369496408) {
|
|
436
480
|
return "codex_remoteagent_bot";
|
|
@@ -206,8 +206,8 @@ export class BotManagementService {
|
|
|
206
206
|
const transient = [];
|
|
207
207
|
for (const bot of bots) {
|
|
208
208
|
try {
|
|
209
|
-
await this.fetchBotIdentity(bot.token);
|
|
210
|
-
alive.push(bot);
|
|
209
|
+
const identity = await this.fetchBotIdentity(bot.token);
|
|
210
|
+
alive.push({ ...identity, index: bot.index });
|
|
211
211
|
}
|
|
212
212
|
catch (error) {
|
|
213
213
|
const reason = error instanceof Error ? error.message : String(error);
|
|
@@ -453,7 +453,8 @@ export class BotManagementService {
|
|
|
453
453
|
: singleTokenLine
|
|
454
454
|
? [singleTokenLine.slice("TELEGRAM_BOT_TOKEN=".length).trim()].filter(Boolean)
|
|
455
455
|
: [];
|
|
456
|
-
const
|
|
456
|
+
const configuredUsernames = usernameLine ? this.parseCsv(usernameLine.slice("TELEGRAM_BOT_USERNAMES=".length)) : [];
|
|
457
|
+
const usernames = await this.normalizeUsernamesFromTelegram(tokens, configuredUsernames);
|
|
457
458
|
return {
|
|
458
459
|
lines,
|
|
459
460
|
tokens,
|
|
@@ -462,7 +463,7 @@ export class BotManagementService {
|
|
|
462
463
|
};
|
|
463
464
|
}
|
|
464
465
|
async writeEnvConfig(originalLines, tokens, usernames, mainBotId) {
|
|
465
|
-
const normalizedUsernames = this.
|
|
466
|
+
const normalizedUsernames = await this.normalizeUsernamesFromTelegram(tokens, usernames);
|
|
466
467
|
const normalizedMainBotId = this.resolveMainBotId(this.zipBots(tokens, normalizedUsernames), mainBotId);
|
|
467
468
|
const nextLines = [];
|
|
468
469
|
let hasMulti = false;
|
|
@@ -510,7 +511,23 @@ export class BotManagementService {
|
|
|
510
511
|
await fs.writeFile(this.envPath, output, "utf8");
|
|
511
512
|
}
|
|
512
513
|
normalizeUsernames(tokens, usernames) {
|
|
513
|
-
return tokens.map((token, index) => usernames[index]?.trim() ||
|
|
514
|
+
return tokens.map((token, index) => usernames[index]?.trim() || this.fallbackUsername(token));
|
|
515
|
+
}
|
|
516
|
+
async normalizeUsernamesFromTelegram(tokens, usernames) {
|
|
517
|
+
const normalized = [];
|
|
518
|
+
for (const token of tokens) {
|
|
519
|
+
try {
|
|
520
|
+
const identity = await this.fetchBotIdentity(token);
|
|
521
|
+
normalized.push(identity.username);
|
|
522
|
+
}
|
|
523
|
+
catch {
|
|
524
|
+
normalized.push(this.fallbackUsername(token));
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return normalized;
|
|
528
|
+
}
|
|
529
|
+
fallbackUsername(token) {
|
|
530
|
+
return `bot_${this.tokenId(token)}`;
|
|
514
531
|
}
|
|
515
532
|
zipBots(tokens, usernames) {
|
|
516
533
|
const normalizedUsernames = this.normalizeUsernames(tokens, usernames);
|