@deadragdoll/tellymcp 0.0.10 → 0.0.12
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/.env.example.client +42 -52
- package/.env.example.gateway +48 -62
- package/CHANGELOG.md +3 -3
- package/README-ru.md +226 -376
- package/README.md +213 -1184
- package/TOOLS.md +320 -377
- package/VERSION.md +13 -13
- package/config/codex/plugins/telly-workflows/.codex-plugin/plugin.json +18 -0
- package/config/codex/plugins/telly-workflows/references/invariants.md +10 -0
- package/config/codex/plugins/telly-workflows/skills/telly-browser-screenshot/SKILL.md +27 -0
- package/config/codex/plugins/telly-workflows/skills/telly-collab-artifact/SKILL.md +22 -0
- package/config/codex/plugins/telly-workflows/skills/telly-human-telegram/SKILL.md +31 -0
- package/config/codex/plugins/telly-workflows/skills/telly-partner-note/SKILL.md +36 -0
- package/config/templates/env.both.template +39 -20
- package/config/templates/env.client.template +34 -19
- package/config/templates/env.gateway.template +37 -22
- package/dist/cli.js +322 -75
- package/dist/codexPluginInstaller.js +215 -0
- package/dist/lib/mixins/session.errors.js +34 -1
- package/dist/lib/pinoTargets.js +2 -2
- package/dist/moleculer.config.js +7 -5
- package/dist/services/features/telegram-mcp/browser.service.js +94 -1
- package/dist/services/features/telegram-mcp/collaboration.service.js +40 -3
- package/dist/services/features/telegram-mcp/ensuredb.service.js +145 -26
- package/dist/services/features/telegram-mcp/gateway-delivery.service.js +285 -103
- package/dist/services/features/telegram-mcp/gateway-socket.service.js +907 -85
- package/dist/services/features/telegram-mcp/gateway.service.js +876 -81
- package/dist/services/features/telegram-mcp/mcp-http.service.js +8 -0
- package/dist/services/features/telegram-mcp/mcp-server.service.js +17 -26
- package/dist/services/features/telegram-mcp/notify.service.js +127 -1
- package/dist/services/features/telegram-mcp/session-context.service.js +28 -1
- package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +132 -10
- package/dist/services/features/telegram-mcp/src/app/config/env.js +158 -52
- package/dist/services/features/telegram-mcp/src/app/http.js +375 -42
- package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +386 -58
- package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +3 -0
- package/dist/services/features/telegram-mcp/src/app/webapp/terminal.js +12 -0
- package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +120 -104
- package/dist/services/features/telegram-mcp/src/entities/xchange/model/types.js +2 -0
- package/dist/services/features/telegram-mcp/src/features/ask-user/model/askUserTelegram.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserOpenTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserScreenshotTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +306 -26
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/gatewaySessionsService.js +71 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/listGatewaySessionsTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/localCollaborationBackend.js +93 -82
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +47 -5
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerNoteTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +82 -0
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayCollaborationBackend.js +22 -14
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +697 -75
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/remoteConsoleActionClient.js +76 -0
- package/dist/services/features/telegram-mcp/src/features/embedded-runtime/model/embeddedRuntimeBroker.js +92 -0
- package/dist/services/features/telegram-mcp/src/features/foreground-terminal/model/foregroundTerminalRuntime.js +192 -0
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +382 -3
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyTelegramTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/notify/model/sendFileToTelegramTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/session-context/model/clearSessionContextTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/session-context/model/getSessionContextTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/session-context/model/renameSessionTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/session-context/model/sessionContextService.js +42 -200
- package/dist/services/features/telegram-mcp/src/features/session-context/model/setSessionContextTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/terminal-buffer/model/terminalBufferService.js +96 -0
- package/dist/services/features/telegram-mcp/src/features/terminal-input/model/terminalInputService.js +97 -0
- package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownService.js +107 -58
- package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/xchange/model/getXchangeRecordTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/xchange/model/listXchangeRecordsTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/xchange/model/markXchangeRecordReadTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/xchange/model/xchangeService.js +169 -0
- package/dist/services/features/telegram-mcp/src/processes/human-approval/model/orchestrator.js +5 -4
- package/dist/services/features/telegram-mcp/src/shared/i18n/resources/en.js +143 -42
- package/dist/services/features/telegram-mcp/src/shared/i18n/resources/ru.js +139 -38
- package/dist/services/features/telegram-mcp/src/shared/integrations/memory/processLocalSessionStore.js +27 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/object-storage/minioExchangeStore.js +11 -8
- package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +69 -70
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/proxyFetch.js +21 -22
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +444 -7093
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportAttachmentStore.js +93 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportBroadcastActions.js +385 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConsoleRegistry.js +149 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConstructorWiring.js +642 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportContent.js +84 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportContext.js +78 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportDocumentActions.js +36 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportEventActions.js +292 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFileHandoffActions.js +352 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFormatting.js +75 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportGatewayActions.js +44 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLifecycleActions.js +161 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLive.js +56 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLiveActions.js +77 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuCallbacks.js +254 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFactories.js +538 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFingerprints.js +93 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFlow.js +344 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuShell.js +65 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuState.js +408 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuText.js +216 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMessageFlow.js +452 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportOutputActions.js +189 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportPartnerActions.js +286 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportPayloadState.js +108 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectActions.js +463 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectEntryActions.js +202 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectEvents.js +99 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectMenus.js +138 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectState.js +308 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectView.js +426 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportRequestFlow.js +278 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportSessionActions.js +143 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalActions.js +665 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalRuntime.js +226 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTypes.js +2 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportUtils.js +330 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportXchangeState.js +107 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/client.js +255 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +543 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/xchange/sqliteRecordStore.js +223 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/gatewayScope.js +24 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/logger/logger.js +6 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js +147 -76
- package/dist/services/features/telegram-mcp/src/shared/lib/telegramXchangeRecords.js +72 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/terminalPromptDetection.js +409 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/version/versionHandshake.js +129 -1
- package/dist/services/features/telegram-mcp/src/shared/lib/xchangeRecordHints.js +98 -0
- package/dist/services/features/telegram-mcp/standalone-http.service.js +12 -0
- package/dist/services/features/telegram-mcp/terminal-buffer.service.js +42 -0
- package/dist/services/features/telegram-mcp/terminal-input.service.js +41 -0
- package/dist/services/features/telegram-mcp/tools-sync.service.js +15 -1
- package/dist/services/features/telegram-mcp/xchange.service.js +123 -0
- package/docs/STANDALONE-ru.md +172 -0
- package/docs/STANDALONE.md +172 -0
- package/package.json +10 -5
- package/scripts/build-package-artifact.sh +27 -0
- package/scripts/deploy-gateway.sh +64 -0
- package/scripts/deploy-onebot-branch.sh +27 -0
- package/scripts/postinstall.js +11 -36
- package/STANDALONE-ru.md +0 -274
- package/STANDALONE.md +0 -274
- package/dist/services/features/telegram-mcp/inbox.service.js +0 -33
- package/dist/services/features/telegram-mcp/pair.service.js +0 -33
- package/dist/services/features/telegram-mcp/src/app/webapp/tmux.js +0 -10
- package/dist/services/features/telegram-mcp/src/features/inbox/model/deleteTelegramInboxMessageTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxCountTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/inbox/model/inboxService.js +0 -77
- package/dist/services/features/telegram-mcp/src/features/pair-session/model/clearSessionPairingTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/pair-session/model/createSessionPairCodeTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/pair-session/model/generatePairCode.js +0 -202
- package/dist/services/features/telegram-mcp/src/features/session-context/model/getTmuxTargetTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/session-context/model/setTmuxTargetTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/shared/integrations/tmux/client.js +0 -363
package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMessageFlow.js
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TransportMessageFlow = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const redactSecrets_1 = require("../../lib/redact-secrets/redactSecrets");
|
|
9
|
+
const telegramXchangeRecords_1 = require("../../lib/telegramXchangeRecords");
|
|
10
|
+
const relay_1 = require("../../../app/webapp/relay");
|
|
11
|
+
const transportUtils_1 = require("./transportUtils");
|
|
12
|
+
class TransportMessageFlow {
|
|
13
|
+
host;
|
|
14
|
+
constructor(host) {
|
|
15
|
+
this.host = host;
|
|
16
|
+
}
|
|
17
|
+
async handleMessage(ctx) {
|
|
18
|
+
const text = this.host.extractIncomingText(ctx.message);
|
|
19
|
+
const attachments = this.host.collectIncomingAttachments(ctx.message);
|
|
20
|
+
if (!text && attachments.length === 0) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
this.host.logger.info("Telegram message received", {
|
|
24
|
+
chatId: ctx.chat?.id,
|
|
25
|
+
userId: ctx.from?.id,
|
|
26
|
+
messageId: ctx.message?.message_id,
|
|
27
|
+
replyToMessageId: ctx.message?.reply_to_message?.message_id,
|
|
28
|
+
...(text ? { text: (0, redactSecrets_1.redactSecrets)(text) } : {}),
|
|
29
|
+
attachmentCount: attachments.length,
|
|
30
|
+
activeWaiters: this.host.waiters.size,
|
|
31
|
+
});
|
|
32
|
+
if (this.host.isAdminBotProfile()) {
|
|
33
|
+
const handled = await this.handleGatewayTopLevelMessage(ctx, text);
|
|
34
|
+
if (handled) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (text && (await this.host.handlePendingRename(ctx, text)))
|
|
39
|
+
return;
|
|
40
|
+
if (text && (await this.host.handlePendingBroadcast(ctx, text)))
|
|
41
|
+
return;
|
|
42
|
+
if (text && (await this.host.handlePendingPartnerNote(ctx, text)))
|
|
43
|
+
return;
|
|
44
|
+
if (text && (await this.host.handlePendingFileHandoff(ctx, text)))
|
|
45
|
+
return;
|
|
46
|
+
if (text && (await this.host.handlePendingProject(ctx, text)))
|
|
47
|
+
return;
|
|
48
|
+
if (text && (0, transportUtils_1.isMenuEntryCommand)(text)) {
|
|
49
|
+
this.host.clearPendingInteractionsForContext(ctx);
|
|
50
|
+
await this.host.showSessionsMenu(ctx);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (text && (0, transportUtils_1.isHelpCommand)(text)) {
|
|
54
|
+
this.host.clearPendingInteractionsForContext(ctx);
|
|
55
|
+
await this.host.showHelp(ctx);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const replyMatched = text ? await this.handleReply(ctx) : false;
|
|
59
|
+
if (replyMatched) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (attachments.length > 0) {
|
|
63
|
+
await this.handleAttachmentUpload(ctx, attachments);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
await this.handleInboxCapture(ctx);
|
|
67
|
+
}
|
|
68
|
+
async handleGatewayTopLevelMessage(ctx, text) {
|
|
69
|
+
const principal = this.host.getPrincipalFromContext(ctx);
|
|
70
|
+
const authToken = text ? (0, transportUtils_1.parseAdminAuthCommand)(text) : null;
|
|
71
|
+
if (authToken !== null) {
|
|
72
|
+
if (!this.host.isAdminAuthEnabled()) {
|
|
73
|
+
await this.host.replyText(ctx, await this.host.tForContext(ctx, "menu:admin.auth.disabled"), { sessionId: "gateway-auth", kind: "transport" });
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
if (authToken !== this.host.config.distributed.gatewayToken) {
|
|
77
|
+
await this.host.replyText(ctx, await this.host.tForContext(ctx, "menu:admin.auth.invalid"), { sessionId: "gateway-auth", kind: "transport" });
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
if (!principal) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
const user = await this.host.ensureGatewayUserForPrincipal({
|
|
84
|
+
principal,
|
|
85
|
+
ctx,
|
|
86
|
+
});
|
|
87
|
+
await this.host.setPrincipalAdminAuthorized(principal);
|
|
88
|
+
await this.host.replyText(ctx, [
|
|
89
|
+
await this.host.tForContext(ctx, "menu:admin.auth.success"),
|
|
90
|
+
"",
|
|
91
|
+
`<code>GATEWAY_USER_UUID=${user.gateway_user_uuid}</code>`,
|
|
92
|
+
"Set this in your agent .env.",
|
|
93
|
+
].join("\n"), { sessionId: "gateway-auth", kind: "transport" }, { parse_mode: "HTML" });
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
if (this.host.isAdminAuthEnabled() &&
|
|
97
|
+
!(await this.host.isPrincipalAdminAuthorized(principal))) {
|
|
98
|
+
await this.host.replyText(ctx, await this.host.tForContext(ctx, "menu:admin.auth.prompt"), { sessionId: "gateway-auth", kind: "transport" });
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
if (text && (0, transportUtils_1.isMenuEntryCommand)(text)) {
|
|
102
|
+
if (principal) {
|
|
103
|
+
await this.host.ensureGatewayScopeConsolesBound({ principal, ctx });
|
|
104
|
+
}
|
|
105
|
+
const activeSessionId = principal
|
|
106
|
+
? await this.host.bindingStore.getActiveSessionIdForPrincipal(principal)
|
|
107
|
+
: null;
|
|
108
|
+
const boundSessionIds = principal
|
|
109
|
+
? await this.host.bindingStore.listBoundSessionIdsForPrincipal(principal)
|
|
110
|
+
: [];
|
|
111
|
+
const hasLinkedSessions = Boolean(activeSessionId) || boundSessionIds.length > 0;
|
|
112
|
+
this.host.logger.info("Gateway /menu routing evaluated", {
|
|
113
|
+
chatId: ctx.chat?.id,
|
|
114
|
+
userId: ctx.from?.id,
|
|
115
|
+
activeSessionId,
|
|
116
|
+
boundSessionCount: boundSessionIds.length,
|
|
117
|
+
boundSessionIds,
|
|
118
|
+
});
|
|
119
|
+
void hasLinkedSessions;
|
|
120
|
+
this.host.clearPendingInteractionsForContext(ctx);
|
|
121
|
+
await this.host.showSessionsMenu(ctx);
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
if (text && (0, transportUtils_1.isHelpCommand)(text)) {
|
|
125
|
+
const principal = this.host.getPrincipalFromContext(ctx);
|
|
126
|
+
if (principal) {
|
|
127
|
+
await this.host.ensureGatewayScopeConsolesBound({ principal, ctx });
|
|
128
|
+
}
|
|
129
|
+
this.host.clearPendingInteractionsForContext(ctx);
|
|
130
|
+
await this.host.showHelp(ctx);
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
resolveGatewayTelegramSourceLabel(ctx) {
|
|
136
|
+
const firstName = ctx.from?.first_name?.trim();
|
|
137
|
+
const lastName = ctx.from?.last_name?.trim();
|
|
138
|
+
const displayName = [firstName, lastName].filter(Boolean).join(" ").trim();
|
|
139
|
+
const username = ctx.from?.username?.trim();
|
|
140
|
+
if (displayName)
|
|
141
|
+
return displayName;
|
|
142
|
+
if (username)
|
|
143
|
+
return `@${username.replace(/^@/u, "")}`;
|
|
144
|
+
return `Telegram user ${ctx.from?.id ?? "unknown"}`;
|
|
145
|
+
}
|
|
146
|
+
async routeTelegramInboxToRelaySession(input) {
|
|
147
|
+
const sourceActorLabel = this.resolveGatewayTelegramSourceLabel(input.ctx);
|
|
148
|
+
const output = await this.host.callGatewayJson("/relay/console-message", {
|
|
149
|
+
source_actor_label: sourceActorLabel,
|
|
150
|
+
target_client_uuid: input.relayTarget.clientUuid,
|
|
151
|
+
target_local_session_id: input.relayTarget.localSessionId,
|
|
152
|
+
message: input.messageText,
|
|
153
|
+
...(input.attachments.length > 0
|
|
154
|
+
? {
|
|
155
|
+
attachments: input.attachments.map((attachment) => attachment.filePath),
|
|
156
|
+
}
|
|
157
|
+
: {}),
|
|
158
|
+
});
|
|
159
|
+
this.host.logger.info("Telegram message routed to gateway relay session", {
|
|
160
|
+
sessionId: input.sourceSessionId,
|
|
161
|
+
targetClientUuid: input.relayTarget.clientUuid,
|
|
162
|
+
targetLocalSessionId: input.relayTarget.localSessionId,
|
|
163
|
+
submittedTextLength: output.submitted_text.length,
|
|
164
|
+
chatId: input.principal.telegramChatId,
|
|
165
|
+
userId: input.principal.telegramUserId,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
async handleReply(ctx) {
|
|
169
|
+
const message = ctx.message;
|
|
170
|
+
const fromUserId = ctx.from?.id;
|
|
171
|
+
const chatId = ctx.chat?.id;
|
|
172
|
+
if (!message?.text || !fromUserId || !chatId) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
const waiters = Array.from(this.host.waiters.values());
|
|
176
|
+
if (waiters.length === 0) {
|
|
177
|
+
this.host.logger.debug("Telegram message ignored because there are no active waiters", {
|
|
178
|
+
chatId,
|
|
179
|
+
userId: fromUserId,
|
|
180
|
+
messageId: message.message_id,
|
|
181
|
+
text: (0, redactSecrets_1.redactSecrets)(message.text.trim()),
|
|
182
|
+
});
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
const replyToMessageId = message.reply_to_message?.message_id;
|
|
186
|
+
const messageTimestampMs = message.date * 1000;
|
|
187
|
+
const matched = waiters.find((waiter) => waiter.telegramChatId === chatId &&
|
|
188
|
+
waiter.telegramUserId === fromUserId &&
|
|
189
|
+
replyToMessageId === waiter.telegramMessageId) ??
|
|
190
|
+
(waiters.length === 1
|
|
191
|
+
? waiters.find((waiter) => waiter.telegramChatId === chatId &&
|
|
192
|
+
waiter.telegramUserId === fromUserId &&
|
|
193
|
+
messageTimestampMs >= waiter.sentAtMs)
|
|
194
|
+
: undefined);
|
|
195
|
+
if (!matched) {
|
|
196
|
+
this.host.logger.debug("Telegram message did not match any active waiter", {
|
|
197
|
+
chatId,
|
|
198
|
+
userId: fromUserId,
|
|
199
|
+
messageId: message.message_id,
|
|
200
|
+
replyToMessageId,
|
|
201
|
+
activeWaiterIds: waiters.map((waiter) => waiter.requestId),
|
|
202
|
+
text: (0, redactSecrets_1.redactSecrets)(message.text.trim()),
|
|
203
|
+
});
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
this.host.logger.info("Telegram message matched active waiter", {
|
|
207
|
+
requestId: matched.requestId,
|
|
208
|
+
chatId,
|
|
209
|
+
userId: fromUserId,
|
|
210
|
+
messageId: message.message_id,
|
|
211
|
+
replyToMessageId,
|
|
212
|
+
text: (0, redactSecrets_1.redactSecrets)(message.text.trim()),
|
|
213
|
+
});
|
|
214
|
+
const reply = {
|
|
215
|
+
requestId: matched.requestId,
|
|
216
|
+
answer: message.text.trim(),
|
|
217
|
+
receivedAt: new Date(message.date * 1000).toISOString(),
|
|
218
|
+
};
|
|
219
|
+
if (matched.sourceClientUuid && this.host.config.distributed.gatewayPublicUrl) {
|
|
220
|
+
try {
|
|
221
|
+
await this.host.callGatewayJson("/transport/reply", {
|
|
222
|
+
client_uuid: matched.sourceClientUuid,
|
|
223
|
+
request_id: matched.requestId,
|
|
224
|
+
answer: reply.answer,
|
|
225
|
+
received_at: reply.receivedAt,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
this.host.logger.error("Failed to forward gateway transport reply to client", {
|
|
230
|
+
requestId: matched.requestId,
|
|
231
|
+
sourceClientUuid: matched.sourceClientUuid,
|
|
232
|
+
chatId,
|
|
233
|
+
userId: fromUserId,
|
|
234
|
+
error: error instanceof Error ? (error.stack ?? error.message) : String(error),
|
|
235
|
+
});
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
this.host.clearWaiter(matched.requestId);
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
if (matched.resolve) {
|
|
242
|
+
matched.resolve(reply);
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
matched.reply = reply;
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
async handleInboxCapture(ctx) {
|
|
249
|
+
const message = ctx.message;
|
|
250
|
+
const fromUserId = ctx.from?.id;
|
|
251
|
+
const chatId = ctx.chat?.id;
|
|
252
|
+
const text = this.host.extractIncomingText(message);
|
|
253
|
+
const attachmentDescriptors = this.host.collectIncomingAttachments(message);
|
|
254
|
+
if (!message || (!text && attachmentDescriptors.length === 0) || !fromUserId || !chatId) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const principal = { telegramChatId: chatId, telegramUserId: fromUserId };
|
|
258
|
+
const sessionId = await this.host.bindingStore.getActiveSessionIdForPrincipal(principal);
|
|
259
|
+
if (!sessionId) {
|
|
260
|
+
this.host.logger.debug("Telegram message ignored because no active session is linked for principal", { chatId, userId: fromUserId, messageId: message.message_id });
|
|
261
|
+
await this.host.replyText(ctx, "No active console selected yet. Open /menu and choose a console first.", { kind: "transport" });
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const session = await this.host.sessionStore.getSession(sessionId);
|
|
265
|
+
const relayTarget = (0, relay_1.parseLiveRelaySessionId)(sessionId);
|
|
266
|
+
let attachments = [];
|
|
267
|
+
try {
|
|
268
|
+
attachments = await this.host.downloadIncomingAttachments(session, sessionId, message.message_id, attachmentDescriptors);
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
this.host.logger.error("Telegram attachment upload failed", {
|
|
272
|
+
sessionId,
|
|
273
|
+
chatId,
|
|
274
|
+
userId: fromUserId,
|
|
275
|
+
messageId: message.message_id,
|
|
276
|
+
error: error instanceof Error ? (error.stack ?? error.message) : String(error),
|
|
277
|
+
});
|
|
278
|
+
await this.host.replyText(ctx, error instanceof Error
|
|
279
|
+
? `Could not save uploaded file: ${error.message}`
|
|
280
|
+
: "Could not save uploaded file.", { kind: "transport", sessionId }, {
|
|
281
|
+
reply_markup: this.host.getMainMenu(),
|
|
282
|
+
});
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const normalizedText = this.host.buildInboxText(text, attachments.map((attachment) => attachment.filePath));
|
|
286
|
+
if (relayTarget) {
|
|
287
|
+
try {
|
|
288
|
+
await this.routeTelegramInboxToRelaySession({
|
|
289
|
+
ctx,
|
|
290
|
+
principal,
|
|
291
|
+
relayTarget,
|
|
292
|
+
sourceSessionId: sessionId,
|
|
293
|
+
messageText: normalizedText,
|
|
294
|
+
attachments,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
this.host.logger.error("Failed to route Telegram message to gateway relay session", {
|
|
299
|
+
sessionId,
|
|
300
|
+
targetClientUuid: relayTarget.clientUuid,
|
|
301
|
+
targetLocalSessionId: relayTarget.localSessionId,
|
|
302
|
+
chatId,
|
|
303
|
+
userId: fromUserId,
|
|
304
|
+
messageId: message.message_id,
|
|
305
|
+
error: error instanceof Error ? (error.stack ?? error.message) : String(error),
|
|
306
|
+
});
|
|
307
|
+
await this.host.replyText(ctx, await this.host.tForContext(ctx, "menu:system.gateway_relay_inbox_failed"), { kind: "transport", sessionId });
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
await this.host.replyText(ctx, await this.host.tForContext(ctx, "menu:system.gateway_relay_inbox_sent", {
|
|
311
|
+
sessionName: session?.label ?? relayTarget.localSessionId,
|
|
312
|
+
}), { kind: "transport", sessionId });
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
await this.host.storeTelegramUploadMetas({
|
|
316
|
+
sessionId,
|
|
317
|
+
sourceTelegramMessageId: message.message_id,
|
|
318
|
+
uploadedAt: new Date(message.date * 1000).toISOString(),
|
|
319
|
+
attachments,
|
|
320
|
+
descriptors: attachmentDescriptors,
|
|
321
|
+
});
|
|
322
|
+
const recordId = await (0, telegramXchangeRecords_1.writeTelegramMessageXchangeRecord)({
|
|
323
|
+
config: this.host.config,
|
|
324
|
+
session,
|
|
325
|
+
sessionId,
|
|
326
|
+
text: normalizedText,
|
|
327
|
+
createdAt: new Date(message.date * 1000).toISOString(),
|
|
328
|
+
attachments: attachments.map((attachment) => ({
|
|
329
|
+
file_path: attachment.filePath,
|
|
330
|
+
})),
|
|
331
|
+
tags: [
|
|
332
|
+
"telegram",
|
|
333
|
+
"human",
|
|
334
|
+
...(attachments.length > 0 ? ["attachments"] : []),
|
|
335
|
+
],
|
|
336
|
+
});
|
|
337
|
+
this.host.logger.info("Telegram message stored in xchange", {
|
|
338
|
+
sessionId,
|
|
339
|
+
chatId,
|
|
340
|
+
userId: fromUserId,
|
|
341
|
+
messageId: message.message_id,
|
|
342
|
+
recordId,
|
|
343
|
+
text: (0, redactSecrets_1.redactSecrets)(normalizedText),
|
|
344
|
+
attachmentCount: attachments.length,
|
|
345
|
+
attachments: attachments.map((attachment) => attachment.filePath),
|
|
346
|
+
});
|
|
347
|
+
try {
|
|
348
|
+
this.host.scheduleTerminalNudgeForInboxMessage(sessionId, session);
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
this.host.logger.error("terminal nudge failed after xchange capture", {
|
|
352
|
+
sessionId,
|
|
353
|
+
error: error instanceof Error ? (error.stack ?? error.message) : String(error),
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
await this.host.replyText(ctx, session?.label
|
|
357
|
+
? attachments.length > 0
|
|
358
|
+
? `Saved to xchange for session: ${session.label}. Files downloaded: ${attachments.length}`
|
|
359
|
+
: `Saved to xchange for session: ${session.label}`
|
|
360
|
+
: attachments.length > 0
|
|
361
|
+
? `Saved to xchange for session: ${sessionId}. Files downloaded: ${attachments.length}`
|
|
362
|
+
: `Saved to xchange for session: ${sessionId}`, { kind: "transport", sessionId }, {
|
|
363
|
+
reply_markup: this.host.getMainMenu(),
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
async handleAttachmentUpload(ctx, attachmentDescriptors) {
|
|
367
|
+
const message = ctx.message;
|
|
368
|
+
const fromUserId = ctx.from?.id;
|
|
369
|
+
const chatId = ctx.chat?.id;
|
|
370
|
+
if (!message || !fromUserId || !chatId || attachmentDescriptors.length === 0) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const principal = { telegramChatId: chatId, telegramUserId: fromUserId };
|
|
374
|
+
const principalKey = (0, transportUtils_1.buildPrincipalKey)(principal);
|
|
375
|
+
const sessionId = await this.host.bindingStore.getActiveSessionIdForPrincipal(principal);
|
|
376
|
+
if (!sessionId) {
|
|
377
|
+
await this.host.replyText(ctx, "No active console selected yet. Open /menu and choose a console first.", { kind: "transport" });
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const session = await this.host.sessionStore.getSession(sessionId);
|
|
381
|
+
const caption = this.host.extractIncomingText(message);
|
|
382
|
+
const attachments = await this.host.downloadIncomingAttachments(session, sessionId, message.message_id, attachmentDescriptors);
|
|
383
|
+
const currentTarget = this.host.currentAttachmentTargets.get(principalKey);
|
|
384
|
+
if (currentTarget && currentTarget.sessionId === sessionId) {
|
|
385
|
+
await this.host.storeTelegramUploadMetas({
|
|
386
|
+
sessionId,
|
|
387
|
+
sourceTelegramMessageId: message.message_id,
|
|
388
|
+
uploadedAt: new Date(message.date * 1000).toISOString(),
|
|
389
|
+
attachments,
|
|
390
|
+
descriptors: attachmentDescriptors,
|
|
391
|
+
caption: caption || undefined,
|
|
392
|
+
});
|
|
393
|
+
for (const attachment of attachments) {
|
|
394
|
+
await this.host.deliverAttachmentToPartner({
|
|
395
|
+
sessionId,
|
|
396
|
+
filePath: attachment.filePath,
|
|
397
|
+
description: (caption || "").trim() || node_path_1.default.basename(attachment.filePath),
|
|
398
|
+
targetSessionId: currentTarget.targetSessionId,
|
|
399
|
+
...(currentTarget.projectUuid
|
|
400
|
+
? { projectUuid: currentTarget.projectUuid }
|
|
401
|
+
: {}),
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
await this.host.replyText(ctx, currentTarget.projectUuid
|
|
405
|
+
? await this.host.tForContext(ctx, "menu:handoff.uploaded_to_session", {
|
|
406
|
+
label: currentTarget.targetSessionLabel,
|
|
407
|
+
})
|
|
408
|
+
: await this.host.tForContext(ctx, "menu:handoff.uploaded_to_partner", {
|
|
409
|
+
label: currentTarget.targetSessionLabel,
|
|
410
|
+
}), { kind: "inbox", sessionId }, {
|
|
411
|
+
reply_markup: this.host.getMainMenu(),
|
|
412
|
+
});
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
await this.host.storeTelegramUploadMetas({
|
|
416
|
+
sessionId,
|
|
417
|
+
sourceTelegramMessageId: message.message_id,
|
|
418
|
+
uploadedAt: new Date(message.date * 1000).toISOString(),
|
|
419
|
+
attachments,
|
|
420
|
+
descriptors: attachmentDescriptors,
|
|
421
|
+
caption: caption || undefined,
|
|
422
|
+
});
|
|
423
|
+
this.host.logger.info("Telegram files uploaded for session", {
|
|
424
|
+
sessionId,
|
|
425
|
+
chatId,
|
|
426
|
+
userId: fromUserId,
|
|
427
|
+
messageId: message.message_id,
|
|
428
|
+
attachmentCount: attachments.length,
|
|
429
|
+
attachments: attachments.map((attachment) => attachment.filePath),
|
|
430
|
+
});
|
|
431
|
+
await this.host.replyText(ctx, session?.label
|
|
432
|
+
? attachments.length === 1
|
|
433
|
+
? await this.host.tForContext(ctx, "menu:handoff.delivered_one", {
|
|
434
|
+
label: session.label,
|
|
435
|
+
})
|
|
436
|
+
: await this.host.tForContext(ctx, "menu:handoff.delivered_many", {
|
|
437
|
+
label: session.label,
|
|
438
|
+
count: attachments.length,
|
|
439
|
+
})
|
|
440
|
+
: attachments.length === 1
|
|
441
|
+
? await this.host.tForContext(ctx, "menu:handoff.delivered_one", {
|
|
442
|
+
label: sessionId,
|
|
443
|
+
})
|
|
444
|
+
: await this.host.tForContext(ctx, "menu:handoff.delivered_many", {
|
|
445
|
+
label: sessionId,
|
|
446
|
+
count: attachments.length,
|
|
447
|
+
}), { kind: "inbox", sessionId }, {
|
|
448
|
+
reply_markup: this.host.getMainMenu(),
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
exports.TransportMessageFlow = TransportMessageFlow;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TransportOutputActions = void 0;
|
|
4
|
+
const grammy_1 = require("grammy");
|
|
5
|
+
const transportUtils_1 = require("./transportUtils");
|
|
6
|
+
class TransportOutputActions {
|
|
7
|
+
host;
|
|
8
|
+
constructor(host) {
|
|
9
|
+
this.host = host;
|
|
10
|
+
}
|
|
11
|
+
async sendTextChunks(telegramChatId, text, meta) {
|
|
12
|
+
const safeLimit = Math.min(this.host.config.telegram.maxMessageChars, 3900);
|
|
13
|
+
const { title, body } = (0, transportUtils_1.splitTitleAndBody)(text);
|
|
14
|
+
const rawChunkLimit = Math.max(256, safeLimit - title.length - 96);
|
|
15
|
+
const rawChunks = (0, transportUtils_1.splitLongTelegramText)(body, rawChunkLimit);
|
|
16
|
+
const bodyChunks = rawChunks.flatMap((chunk) => this.buildSizedBodyChunks(title, chunk, safeLimit));
|
|
17
|
+
const chunkCount = bodyChunks.length;
|
|
18
|
+
const chunks = bodyChunks.map((chunkBody, index) => (0, transportUtils_1.renderMarkdownChunk)(chunkCount > 1 ? `${title} (${index + 1}/${chunkCount})` : title, chunkBody));
|
|
19
|
+
const sent = [];
|
|
20
|
+
this.host.logger.debug("Telegram message chunking prepared", {
|
|
21
|
+
kind: meta.kind,
|
|
22
|
+
sessionId: meta.sessionId,
|
|
23
|
+
requestId: meta.requestId,
|
|
24
|
+
chunkCount: chunks.length,
|
|
25
|
+
totalLength: text.length,
|
|
26
|
+
safeLimit,
|
|
27
|
+
});
|
|
28
|
+
for (let index = 0; index < chunks.length; index += 1) {
|
|
29
|
+
const chunk = chunks[index];
|
|
30
|
+
if (!chunk) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const response = await this.sendTelegramMessageWithRetry(telegramChatId, chunk, { parse_mode: "MarkdownV2" }, {
|
|
35
|
+
kind: meta.kind,
|
|
36
|
+
sessionId: meta.sessionId,
|
|
37
|
+
chunkIndex: index + 1,
|
|
38
|
+
chunkCount: chunks.length,
|
|
39
|
+
...(meta.requestId ? { requestId: meta.requestId } : {}),
|
|
40
|
+
});
|
|
41
|
+
sent.push({
|
|
42
|
+
messageId: response.message_id,
|
|
43
|
+
textLength: chunk.length,
|
|
44
|
+
});
|
|
45
|
+
this.host.logger.debug("Telegram message chunk sent", {
|
|
46
|
+
kind: meta.kind,
|
|
47
|
+
sessionId: meta.sessionId,
|
|
48
|
+
requestId: meta.requestId,
|
|
49
|
+
chunkIndex: index + 1,
|
|
50
|
+
chunkCount: chunks.length,
|
|
51
|
+
messageId: response.message_id,
|
|
52
|
+
textLength: chunk.length,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
this.host.logger.error("Telegram message chunk send failed", {
|
|
57
|
+
kind: meta.kind,
|
|
58
|
+
sessionId: meta.sessionId,
|
|
59
|
+
requestId: meta.requestId,
|
|
60
|
+
chunkIndex: index + 1,
|
|
61
|
+
chunkCount: chunks.length,
|
|
62
|
+
textLength: chunk.length,
|
|
63
|
+
error: error instanceof Error
|
|
64
|
+
? (error.stack ?? error.message)
|
|
65
|
+
: String(error),
|
|
66
|
+
});
|
|
67
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return sent;
|
|
71
|
+
}
|
|
72
|
+
async sendChatMessage(telegramChatId, text, options, meta) {
|
|
73
|
+
return this.sendTelegramMessageWithRetry(telegramChatId, text, options, meta);
|
|
74
|
+
}
|
|
75
|
+
async replyText(ctx, text, meta, options = {}) {
|
|
76
|
+
let attempt = 0;
|
|
77
|
+
while (true) {
|
|
78
|
+
attempt += 1;
|
|
79
|
+
try {
|
|
80
|
+
return await ctx.reply(text, options);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
if (!(error instanceof grammy_1.GrammyError) || error.error_code !== 429) {
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
const retryAfterSeconds = Math.max(1, error.parameters.retry_after ?? 1);
|
|
87
|
+
this.host.logger.warn("Telegram rate limit hit while replying, cooling down", {
|
|
88
|
+
kind: meta.kind,
|
|
89
|
+
sessionId: meta.sessionId,
|
|
90
|
+
requestId: meta.requestId,
|
|
91
|
+
attempt,
|
|
92
|
+
retryAfterSeconds,
|
|
93
|
+
description: error.description,
|
|
94
|
+
});
|
|
95
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfterSeconds * 1000));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async editText(ctx, text, meta, options = {}) {
|
|
100
|
+
let attempt = 0;
|
|
101
|
+
while (true) {
|
|
102
|
+
attempt += 1;
|
|
103
|
+
try {
|
|
104
|
+
await ctx.editMessageText(text, options);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error instanceof grammy_1.GrammyError) {
|
|
109
|
+
if (error.description.includes("message is not modified")) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (error.error_code === 429) {
|
|
113
|
+
const retryAfterSeconds = Math.max(1, error.parameters.retry_after ?? 1);
|
|
114
|
+
this.host.logger.warn("Telegram rate limit hit while editing message, cooling down", {
|
|
115
|
+
kind: meta.kind,
|
|
116
|
+
sessionId: meta.sessionId,
|
|
117
|
+
requestId: meta.requestId,
|
|
118
|
+
attempt,
|
|
119
|
+
retryAfterSeconds,
|
|
120
|
+
description: error.description,
|
|
121
|
+
});
|
|
122
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfterSeconds * 1000));
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
buildSizedBodyChunks(title, rawBody, safeLimit) {
|
|
131
|
+
const queue = [rawBody];
|
|
132
|
+
const bodyChunks = [];
|
|
133
|
+
while (queue.length > 0) {
|
|
134
|
+
const current = queue.shift();
|
|
135
|
+
if (!current) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const candidate = (0, transportUtils_1.renderMarkdownChunk)(`${title} (88/88)`, current);
|
|
139
|
+
if (candidate.length <= safeLimit) {
|
|
140
|
+
bodyChunks.push(current);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const midpoint = Math.floor(current.length / 2);
|
|
144
|
+
const splitAtNewline = current.lastIndexOf("\n", midpoint);
|
|
145
|
+
const splitIndex = splitAtNewline > 64 ? splitAtNewline : midpoint;
|
|
146
|
+
const head = current.slice(0, splitIndex).trim();
|
|
147
|
+
const tail = current.slice(splitIndex).trim();
|
|
148
|
+
if (!head || !tail) {
|
|
149
|
+
const hardLimit = Math.max(64, safeLimit - title.length - 96);
|
|
150
|
+
for (let index = 0; index < current.length; index += hardLimit) {
|
|
151
|
+
const slice = current.slice(index, index + hardLimit).trim();
|
|
152
|
+
if (slice) {
|
|
153
|
+
bodyChunks.push(slice);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
queue.unshift(tail, head);
|
|
159
|
+
}
|
|
160
|
+
return bodyChunks;
|
|
161
|
+
}
|
|
162
|
+
async sendTelegramMessageWithRetry(telegramChatId, text, options = {}, meta) {
|
|
163
|
+
let attempt = 0;
|
|
164
|
+
while (true) {
|
|
165
|
+
attempt += 1;
|
|
166
|
+
try {
|
|
167
|
+
return await this.host.bot.api.sendMessage(telegramChatId, text, options);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
if (!(error instanceof grammy_1.GrammyError) || error.error_code !== 429) {
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
const retryAfterSeconds = Math.max(1, error.parameters.retry_after ?? 1);
|
|
174
|
+
this.host.logger.warn("Telegram rate limit hit, cooling down before retry", {
|
|
175
|
+
kind: meta.kind,
|
|
176
|
+
sessionId: meta.sessionId,
|
|
177
|
+
requestId: meta.requestId,
|
|
178
|
+
chunkIndex: meta.chunkIndex,
|
|
179
|
+
chunkCount: meta.chunkCount,
|
|
180
|
+
attempt,
|
|
181
|
+
retryAfterSeconds,
|
|
182
|
+
description: error.description,
|
|
183
|
+
});
|
|
184
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfterSeconds * 1000));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
exports.TransportOutputActions = TransportOutputActions;
|