@moxxy/plugin-telegram 0.27.0
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/LICENSE +21 -0
- package/dist/approval.d.ts +37 -0
- package/dist/approval.d.ts.map +1 -0
- package/dist/approval.js +75 -0
- package/dist/approval.js.map +1 -0
- package/dist/channel/approval-prompt.d.ts +20 -0
- package/dist/channel/approval-prompt.d.ts.map +1 -0
- package/dist/channel/approval-prompt.js +34 -0
- package/dist/channel/approval-prompt.js.map +1 -0
- package/dist/channel/callback-handler.d.ts +32 -0
- package/dist/channel/callback-handler.d.ts.map +1 -0
- package/dist/channel/callback-handler.js +212 -0
- package/dist/channel/callback-handler.js.map +1 -0
- package/dist/channel/frame-pump.d.ts +58 -0
- package/dist/channel/frame-pump.d.ts.map +1 -0
- package/dist/channel/frame-pump.js +159 -0
- package/dist/channel/frame-pump.js.map +1 -0
- package/dist/channel/html.d.ts +24 -0
- package/dist/channel/html.d.ts.map +1 -0
- package/dist/channel/html.js +62 -0
- package/dist/channel/html.js.map +1 -0
- package/dist/channel/pairing-handler.d.ts +66 -0
- package/dist/channel/pairing-handler.d.ts.map +1 -0
- package/dist/channel/pairing-handler.js +147 -0
- package/dist/channel/pairing-handler.js.map +1 -0
- package/dist/channel/permission-prompt.d.ts +21 -0
- package/dist/channel/permission-prompt.d.ts.map +1 -0
- package/dist/channel/permission-prompt.js +36 -0
- package/dist/channel/permission-prompt.js.map +1 -0
- package/dist/channel/slash-handler.d.ts +47 -0
- package/dist/channel/slash-handler.d.ts.map +1 -0
- package/dist/channel/slash-handler.js +214 -0
- package/dist/channel/slash-handler.js.map +1 -0
- package/dist/channel/text-handler.d.ts +43 -0
- package/dist/channel/text-handler.d.ts.map +1 -0
- package/dist/channel/text-handler.js +116 -0
- package/dist/channel/text-handler.js.map +1 -0
- package/dist/channel/turn-runner.d.ts +40 -0
- package/dist/channel/turn-runner.d.ts.map +1 -0
- package/dist/channel/turn-runner.js +67 -0
- package/dist/channel/turn-runner.js.map +1 -0
- package/dist/channel/typing-indicator.d.ts +14 -0
- package/dist/channel/typing-indicator.d.ts.map +1 -0
- package/dist/channel/typing-indicator.js +29 -0
- package/dist/channel/typing-indicator.js.map +1 -0
- package/dist/channel/voice-handler.d.ts +42 -0
- package/dist/channel/voice-handler.d.ts.map +1 -0
- package/dist/channel/voice-handler.js +138 -0
- package/dist/channel/voice-handler.js.map +1 -0
- package/dist/channel.d.ts +118 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/channel.js +478 -0
- package/dist/channel.js.map +1 -0
- package/dist/channel.test-d.d.ts +2 -0
- package/dist/channel.test-d.d.ts.map +1 -0
- package/dist/channel.test-d.js +5 -0
- package/dist/channel.test-d.js.map +1 -0
- package/dist/format.d.ts +38 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +237 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +304 -0
- package/dist/index.js.map +1 -0
- package/dist/keys.d.ts +37 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +42 -0
- package/dist/keys.js.map +1 -0
- package/dist/pair-flow.d.ts +18 -0
- package/dist/pair-flow.d.ts.map +1 -0
- package/dist/pair-flow.js +119 -0
- package/dist/pair-flow.js.map +1 -0
- package/dist/pairing.d.ts +90 -0
- package/dist/pairing.d.ts.map +1 -0
- package/dist/pairing.js +97 -0
- package/dist/pairing.js.map +1 -0
- package/dist/permission.d.ts +25 -0
- package/dist/permission.d.ts.map +1 -0
- package/dist/permission.js +49 -0
- package/dist/permission.js.map +1 -0
- package/dist/render.d.ts +68 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +439 -0
- package/dist/render.js.map +1 -0
- package/dist/setup-wizard.d.ts +21 -0
- package/dist/setup-wizard.d.ts.map +1 -0
- package/dist/setup-wizard.js +140 -0
- package/dist/setup-wizard.js.map +1 -0
- package/package.json +91 -0
- package/src/approval.test.ts +103 -0
- package/src/approval.ts +86 -0
- package/src/channel/approval-prompt.ts +46 -0
- package/src/channel/callback-handler.test.ts +188 -0
- package/src/channel/callback-handler.ts +256 -0
- package/src/channel/frame-pump.ts +176 -0
- package/src/channel/html.test.ts +40 -0
- package/src/channel/html.ts +60 -0
- package/src/channel/pairing-handler.ts +181 -0
- package/src/channel/permission-prompt.ts +51 -0
- package/src/channel/slash-handler.ts +259 -0
- package/src/channel/text-handler.test.ts +222 -0
- package/src/channel/text-handler.ts +174 -0
- package/src/channel/turn-runner.ts +102 -0
- package/src/channel/typing-indicator.ts +31 -0
- package/src/channel/voice-handler.test.ts +328 -0
- package/src/channel/voice-handler.ts +198 -0
- package/src/channel/voice-reply.test.ts +223 -0
- package/src/channel.test-d.ts +7 -0
- package/src/channel.ts +564 -0
- package/src/format.test.ts +159 -0
- package/src/format.ts +257 -0
- package/src/index.ts +347 -0
- package/src/keys.ts +52 -0
- package/src/pair-flow.ts +131 -0
- package/src/pairing.test.ts +113 -0
- package/src/pairing.ts +184 -0
- package/src/permission.test.ts +56 -0
- package/src/permission.ts +61 -0
- package/src/render.test.ts +350 -0
- package/src/render.ts +522 -0
- package/src/setup-wizard.ts +178 -0
- package/src/subcommands.test.ts +153 -0
package/dist/channel.js
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import { Bot, GrammyError, HttpError, InputFile } from 'grammy';
|
|
2
|
+
import { newTurnId } from '@moxxy/core';
|
|
3
|
+
import { TurnCoordinator, deliverVoiceReply, resolveSecret } from '@moxxy/channel-kit';
|
|
4
|
+
import { TelegramPermissionResolver } from './permission.js';
|
|
5
|
+
import { TelegramApprovalResolver } from './approval.js';
|
|
6
|
+
import { FramePump } from './channel/frame-pump.js';
|
|
7
|
+
import { TypingIndicator } from './channel/typing-indicator.js';
|
|
8
|
+
import { PairingHandler } from './channel/pairing-handler.js';
|
|
9
|
+
import { askForPermission } from './channel/permission-prompt.js';
|
|
10
|
+
import { askForApproval } from './channel/approval-prompt.js';
|
|
11
|
+
import { publishBotCommands } from './channel/slash-handler.js';
|
|
12
|
+
import { handleCallback, } from './channel/callback-handler.js';
|
|
13
|
+
import { runUserTurn } from './channel/turn-runner.js';
|
|
14
|
+
import { handleTextMessage } from './channel/text-handler.js';
|
|
15
|
+
import { handleVoiceMessage } from './channel/voice-handler.js';
|
|
16
|
+
import { loadVoiceReplies, saveVoiceReplies } from './keys.js';
|
|
17
|
+
const TOKEN_KEY = 'telegram_bot_token';
|
|
18
|
+
/** Cap on the up-front `getMe` (bot identity → `t.me` connect link). It must
|
|
19
|
+
* never gate channel startup: if Telegram is slow/unreachable, we proceed
|
|
20
|
+
* without the link rather than wedge `start()` (and the readiness it unblocks). */
|
|
21
|
+
const BOT_IDENTITY_TIMEOUT_MS = 8_000;
|
|
22
|
+
export class TelegramChannel {
|
|
23
|
+
name = 'telegram';
|
|
24
|
+
permissionResolver;
|
|
25
|
+
approvalResolver;
|
|
26
|
+
opts;
|
|
27
|
+
bot = null;
|
|
28
|
+
// The resolved bot's `t.me/<botname>` link (from getMe at start), published as
|
|
29
|
+
// this channel's `requestUrl` connect value so control surfaces can render a
|
|
30
|
+
// QR / "open the bot" step. While a host-issued pairing window is open it
|
|
31
|
+
// carries the `?start=<code>` deep-link payload. Null until resolved (or if
|
|
32
|
+
// getMe failed).
|
|
33
|
+
botLink = null;
|
|
34
|
+
// The host-issued pairing code embedded in `botLink`'s `?start=` payload, set
|
|
35
|
+
// when `start()` opens a host pairing window (dedicated + unpaired). Null
|
|
36
|
+
// otherwise (terminal pairing, or already paired).
|
|
37
|
+
hostPairingCode = null;
|
|
38
|
+
// Listeners notified when this channel's connect-state changes (i.e. a chat
|
|
39
|
+
// just paired). The dedicated-runner host subscribes via the handle to
|
|
40
|
+
// re-publish the channel's status file. See `onConnectChange` on the handle.
|
|
41
|
+
connectListeners = new Set();
|
|
42
|
+
currentChatId = null;
|
|
43
|
+
// Last chat we ran a turn for — the target for mirroring turns this channel
|
|
44
|
+
// did NOT initiate (e.g. a web-surface action on a shared session).
|
|
45
|
+
lastChatId = null;
|
|
46
|
+
logUnsub = null;
|
|
47
|
+
session = null;
|
|
48
|
+
model;
|
|
49
|
+
activeModelOverride = null;
|
|
50
|
+
yolo = false;
|
|
51
|
+
// When true, the final assistant reply of each turn is also synthesized (via
|
|
52
|
+
// the session's active Synthesizer) and sent as a voice note. Persisted per
|
|
53
|
+
// paired chat in the vault (`telegram_voice_replies`), toggled with `/voice`.
|
|
54
|
+
voiceReplies = false;
|
|
55
|
+
// Single-flight turn state: `busy` guard, per-turn AbortController (so
|
|
56
|
+
// /cancel aborts only the current turn without poisoning the session-level
|
|
57
|
+
// signal other channels share), and the bounded own-turn-id set that
|
|
58
|
+
// mirrorForeignTurn filters on (invariant #8: filter event-log subscribers
|
|
59
|
+
// by turnId when multiplexing turns on one Session) rather than the coarse
|
|
60
|
+
// `busy` flag alone — so an assistant_message dispatched for our own turn
|
|
61
|
+
// AFTER `busy` flips false (async event ordering / RemoteSession replay)
|
|
62
|
+
// isn't re-mirrored as foreign.
|
|
63
|
+
turns = new TurnCoordinator();
|
|
64
|
+
// When a user clicks an approval option that needs text follow-up
|
|
65
|
+
// (e.g. plan-execute "Redraft with feedback"), we stash the
|
|
66
|
+
// approval+option pair and capture the user's NEXT message as the
|
|
67
|
+
// follow-up text — same mechanism the TUI uses, just over chat.
|
|
68
|
+
awaitingApprovalText = null;
|
|
69
|
+
handle = null;
|
|
70
|
+
framePump;
|
|
71
|
+
typing = new TypingIndicator();
|
|
72
|
+
pairing;
|
|
73
|
+
constructor(opts) {
|
|
74
|
+
this.opts = opts;
|
|
75
|
+
this.permissionResolver = new TelegramPermissionResolver();
|
|
76
|
+
this.approvalResolver = new TelegramApprovalResolver();
|
|
77
|
+
this.framePump = new FramePump({
|
|
78
|
+
editFrameMs: opts.editFrameMs ?? 1000,
|
|
79
|
+
...(opts.logger ? { logger: opts.logger } : {}),
|
|
80
|
+
});
|
|
81
|
+
this.pairing = new PairingHandler({
|
|
82
|
+
vault: opts.vault,
|
|
83
|
+
...(opts.logger ? { logger: opts.logger } : {}),
|
|
84
|
+
});
|
|
85
|
+
// A completed pairing flips this channel's connect-state to "connected";
|
|
86
|
+
// notify the host so it can swap the QR for a "✓ Connected" affordance.
|
|
87
|
+
this.pairing.onPaired(() => this.notifyConnectChange());
|
|
88
|
+
}
|
|
89
|
+
/** This channel's connect value (see {@link Channel.requestUrl}): the resolved
|
|
90
|
+
* bot's `t.me` link, surfaced by control surfaces as a QR / open-bot step.
|
|
91
|
+
* While a host pairing window is open it includes the `?start=<code>` payload. */
|
|
92
|
+
get requestUrl() {
|
|
93
|
+
return this.botLink;
|
|
94
|
+
}
|
|
95
|
+
/** Whether the "connect the other side" step is satisfied — i.e. a chat is
|
|
96
|
+
* paired (see {@link Channel.connected}). A host shows "✓ Connected" instead
|
|
97
|
+
* of the pairing QR once this is true. */
|
|
98
|
+
get connected() {
|
|
99
|
+
return this.pairing.phase() === 'paired';
|
|
100
|
+
}
|
|
101
|
+
notifyConnectChange() {
|
|
102
|
+
for (const listener of this.connectListeners) {
|
|
103
|
+
try {
|
|
104
|
+
listener();
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
this.opts.logger?.warn?.('telegram connect-change listener threw', { err: String(err) });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async start(startOpts) {
|
|
112
|
+
if (this.handle)
|
|
113
|
+
return this.handle;
|
|
114
|
+
this.session = startOpts.session;
|
|
115
|
+
this.model = startOpts.model;
|
|
116
|
+
// Precedence mirrors Slack (and this channel's own `isAvailable` gate +
|
|
117
|
+
// error message): an explicit option wins, then the `MOXXY_TELEGRAM_TOKEN`
|
|
118
|
+
// env override, then the vault (the shared env→vault resolution in
|
|
119
|
+
// @moxxy/channel-kit). Without the env fallback a headless
|
|
120
|
+
// `moxxy channels start telegram` that set the env var would pass the
|
|
121
|
+
// availability check but then fail to boot — the gate and the channel must
|
|
122
|
+
// agree on where the token comes from.
|
|
123
|
+
const token = this.opts.token ??
|
|
124
|
+
(await resolveSecret(this.opts.vault, {
|
|
125
|
+
envVar: 'MOXXY_TELEGRAM_TOKEN',
|
|
126
|
+
vaultKey: TOKEN_KEY,
|
|
127
|
+
}));
|
|
128
|
+
if (!token) {
|
|
129
|
+
throw new Error(`Telegram bot token not found. Store one via vault_set('${TOKEN_KEY}', ...) or set MOXXY_TELEGRAM_TOKEN.`);
|
|
130
|
+
}
|
|
131
|
+
await this.pairing.loadAuthorized();
|
|
132
|
+
this.voiceReplies = await loadVoiceReplies(this.opts.vault);
|
|
133
|
+
// Open the single host-issued QR pairing window when a pairing surface asked
|
|
134
|
+
// for it (`pair`, the terminal `pair` command) OR when running GUI-supervised
|
|
135
|
+
// on a dedicated runner (the desktop) and no chat is paired yet. The code is
|
|
136
|
+
// minted now so we can embed it in the `t.me/<bot>?start=<code>` deep link
|
|
137
|
+
// below; the surface renders that as a QR, and pairing completes when the
|
|
138
|
+
// user taps START (or sends the digits) — no terminal round-trip. Only a
|
|
139
|
+
// headless start with neither signal still errors with a pairing hint.
|
|
140
|
+
const dedicated = startOpts.dedicated === true || process.env.MOXXY_DEDICATED_RUNNER === '1';
|
|
141
|
+
if (this.pairing.phase() !== 'paired') {
|
|
142
|
+
if (startOpts.pair || dedicated) {
|
|
143
|
+
this.hostPairingCode = this.pairing.beginHostWindow();
|
|
144
|
+
this.opts.logger?.info?.('telegram pairing window open');
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
throw new Error('No Telegram chat is paired yet. Run `moxxy channels telegram pair` to pair, or start it from the desktop Channels panel.');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
this.bot = new Bot(token);
|
|
151
|
+
this.framePump.attachBot(this.bot);
|
|
152
|
+
this.pairing.attachBot(this.bot);
|
|
153
|
+
this.permissionResolver.setDecider((call, ctx) => this.askForPermission(call, ctx));
|
|
154
|
+
this.approvalResolver.setDecider((id, request) => this.askForApproval(id, request));
|
|
155
|
+
// Register the approval resolver on the session so loop strategies
|
|
156
|
+
// (plan-execute) actually surface their plan-validation dialog on
|
|
157
|
+
// this channel. setApprovalResolver(null) on stop tears it down so
|
|
158
|
+
// headless code paths after channel close don't see a stale handler.
|
|
159
|
+
this.session.setApprovalResolver(this.approvalResolver);
|
|
160
|
+
// Mirror-to-both: when the session runs a turn this channel did NOT
|
|
161
|
+
// initiate (e.g. a user submitted a form on the co-attached web surface),
|
|
162
|
+
// post the assistant's prose into the last chat we served. Our OWN turns
|
|
163
|
+
// are rendered by the FramePump (gated by `busy`), so skip those.
|
|
164
|
+
this.logUnsub = this.session.log.subscribe((event) => this.mirrorForeignTurn(event));
|
|
165
|
+
this.bot.command('start', (ctx) => this.pairing.handleStartCommand(ctx));
|
|
166
|
+
this.bot.on('callback_query:data', (ctx) => this.dispatchCallback(ctx));
|
|
167
|
+
// grammy's built-in long-poller (Bot.handleUpdates) awaits each update
|
|
168
|
+
// handler before fetching the next getUpdates batch — so awaiting a
|
|
169
|
+
// whole user turn here would PARK the poll loop for the turn's lifetime.
|
|
170
|
+
// A mid-turn permission prompt waits for a `callback_query` click that
|
|
171
|
+
// grammy could never deliver while parked (deadlock), and `/cancel`
|
|
172
|
+
// would sit undelivered until the turn ends. We dispatch text/voice
|
|
173
|
+
// turns in the background (fire-and-track) so the poll loop stays free
|
|
174
|
+
// to deliver callback_query / permission clicks / `/cancel` mid-turn.
|
|
175
|
+
// Single-flight is preserved because runUserTurn sets `busy` true
|
|
176
|
+
// synchronously and the text/voice handlers reject overlapping turns.
|
|
177
|
+
this.bot.on('message:text', (ctx) => this.dispatchInBackground(this.handleText(ctx), 'text'));
|
|
178
|
+
// Voice notes (press-and-hold) and uploaded audio files. Both go
|
|
179
|
+
// through the same transcribe-then-runUserTurn pipeline; the
|
|
180
|
+
// handler picks the right one off `ctx.message`.
|
|
181
|
+
this.bot.on(['message:voice', 'message:audio'], (ctx) => this.dispatchInBackground(this.handleVoice(ctx, token), 'voice'));
|
|
182
|
+
// Surface the shared registry commands in Telegram's bot-command
|
|
183
|
+
// menu so users see /info, /clear, /new, /exit, /help next to the
|
|
184
|
+
// Telegram-local /model, /loop, /yolo, /tools, /skills, /cancel.
|
|
185
|
+
void publishBotCommands(this.bot, this.session, this.opts.logger);
|
|
186
|
+
this.bot.catch((err) => {
|
|
187
|
+
const e = err.error;
|
|
188
|
+
if (e instanceof GrammyError)
|
|
189
|
+
this.opts.logger?.warn('grammy error', { description: e.description });
|
|
190
|
+
else if (e instanceof HttpError)
|
|
191
|
+
this.opts.logger?.warn('http error', { message: e.message });
|
|
192
|
+
else
|
|
193
|
+
this.opts.logger?.warn('telegram error', { err: String(e) });
|
|
194
|
+
});
|
|
195
|
+
this.opts.logger?.info?.('telegram channel starting', {
|
|
196
|
+
paired: this.pairing.phase() === 'paired',
|
|
197
|
+
});
|
|
198
|
+
// Resolve the bot's own identity up front (getMe) so a control surface can
|
|
199
|
+
// show a `t.me/<botname>` connect step. grammy caches this on `bot.botInfo`,
|
|
200
|
+
// and `bot.start()` below reuses it instead of calling getMe again. Non-fatal
|
|
201
|
+
// AND non-blocking: a transient getMe failure must not block the channel from
|
|
202
|
+
// starting — and neither must a STALLED one. We bound it with a timeout so a
|
|
203
|
+
// slow/unreachable Telegram can't wedge `start()` (which gates bot polling,
|
|
204
|
+
// the web-surface co-attach, and the dedicated runner's status file). On
|
|
205
|
+
// timeout/error we proceed without the link; the poll loop surfaces a
|
|
206
|
+
// genuinely invalid token on its own. (We race a timer rather than pass
|
|
207
|
+
// grammy an AbortSignal — its `init(signal)` types the param against the
|
|
208
|
+
// `abort-controller` polyfill, not the global signal.)
|
|
209
|
+
const bot = this.bot;
|
|
210
|
+
const init = bot.init();
|
|
211
|
+
init.catch(() => undefined); // a late rejection (post-timeout) isn't unhandled
|
|
212
|
+
try {
|
|
213
|
+
await Promise.race([
|
|
214
|
+
init,
|
|
215
|
+
new Promise((_, reject) => {
|
|
216
|
+
const t = setTimeout(() => reject(new Error(`getMe timed out after ${BOT_IDENTITY_TIMEOUT_MS}ms`)), BOT_IDENTITY_TIMEOUT_MS);
|
|
217
|
+
t.unref?.();
|
|
218
|
+
}),
|
|
219
|
+
]);
|
|
220
|
+
const username = bot.botInfo.username;
|
|
221
|
+
if (username) {
|
|
222
|
+
// Embed the host-issued code as the `?start=` deep-link payload so a scan
|
|
223
|
+
// → tap-START round-trips `/start <code>` back to us, pairing with zero
|
|
224
|
+
// typing. Plain `t.me/<bot>` once paired (or in terminal-pair mode).
|
|
225
|
+
this.botLink = this.hostPairingCode
|
|
226
|
+
? `https://t.me/${username}?start=${this.hostPairingCode}`
|
|
227
|
+
: `https://t.me/${username}`;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
this.opts.logger?.warn?.('telegram: could not resolve bot identity (getMe)', {
|
|
232
|
+
error: err instanceof Error ? err.message : String(err),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
const running = bot.start({ drop_pending_updates: false });
|
|
236
|
+
this.handle = {
|
|
237
|
+
running,
|
|
238
|
+
// Let the dedicated-runner host re-publish our status file when a chat
|
|
239
|
+
// pairs (connect-state → connected), so its panel swaps QR for "Connected".
|
|
240
|
+
onConnectChange: (listener) => {
|
|
241
|
+
this.connectListeners.add(listener);
|
|
242
|
+
return () => this.connectListeners.delete(listener);
|
|
243
|
+
},
|
|
244
|
+
stop: async (reason = 'shutdown') => {
|
|
245
|
+
// Abort the in-flight turn FIRST so the model loop stops generating
|
|
246
|
+
// tokens / executing side-effecting tools the moment the operator
|
|
247
|
+
// asks to shut down — otherwise (shared/remote Session) spend and
|
|
248
|
+
// tool calls continue to completion and only their output is discarded.
|
|
249
|
+
this.turns.abort(reason);
|
|
250
|
+
this.permissionResolver.abortAll(reason);
|
|
251
|
+
this.approvalResolver.abortAll(reason);
|
|
252
|
+
this.logUnsub?.();
|
|
253
|
+
this.logUnsub = null;
|
|
254
|
+
if (this.session)
|
|
255
|
+
this.session.setApprovalResolver(null);
|
|
256
|
+
this.framePump.endTurn();
|
|
257
|
+
this.typing.stop();
|
|
258
|
+
if (this.bot)
|
|
259
|
+
await this.bot.stop();
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
return this.handle;
|
|
263
|
+
}
|
|
264
|
+
pairingPhase() {
|
|
265
|
+
return this.pairing.phase();
|
|
266
|
+
}
|
|
267
|
+
unpair() {
|
|
268
|
+
this.pairing.unpair();
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Subscribe to "a chat just paired" — fires once when the host-issued QR
|
|
272
|
+
* pairing completes. The `moxxy channels telegram pair` command uses this to
|
|
273
|
+
* print success and stop waiting; returns an unsubscribe function.
|
|
274
|
+
*/
|
|
275
|
+
onPaired(listener) {
|
|
276
|
+
return this.pairing.onPaired(listener);
|
|
277
|
+
}
|
|
278
|
+
handleVoice(ctx, token) {
|
|
279
|
+
return handleVoiceMessage(ctx, {
|
|
280
|
+
session: this.session,
|
|
281
|
+
busy: this.turns.busy,
|
|
282
|
+
}, {
|
|
283
|
+
pairing: this.pairing,
|
|
284
|
+
token,
|
|
285
|
+
...(this.opts.logger ? { logger: this.opts.logger } : {}),
|
|
286
|
+
}, {
|
|
287
|
+
runUserTurn: (c, chatId, text) => this.runUserTurn(c, chatId, text),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
handleText(ctx) {
|
|
291
|
+
return handleTextMessage(ctx, {
|
|
292
|
+
session: this.session,
|
|
293
|
+
model: this.model,
|
|
294
|
+
activeModelOverride: this.activeModelOverride,
|
|
295
|
+
yolo: this.yolo,
|
|
296
|
+
voiceReplies: this.voiceReplies,
|
|
297
|
+
busy: this.turns.busy,
|
|
298
|
+
turnController: this.turns.controller,
|
|
299
|
+
awaitingApprovalText: this.awaitingApprovalText,
|
|
300
|
+
handle: this.handle,
|
|
301
|
+
}, {
|
|
302
|
+
pairing: this.pairing,
|
|
303
|
+
approvalResolver: this.approvalResolver,
|
|
304
|
+
permissionResolver: this.permissionResolver,
|
|
305
|
+
framePump: this.framePump,
|
|
306
|
+
}, {
|
|
307
|
+
setAwaitingApprovalText: (state) => {
|
|
308
|
+
this.awaitingApprovalText = state;
|
|
309
|
+
},
|
|
310
|
+
toggleYolo: () => {
|
|
311
|
+
this.yolo = !this.yolo;
|
|
312
|
+
return this.yolo;
|
|
313
|
+
},
|
|
314
|
+
setYolo: (value) => {
|
|
315
|
+
this.yolo = value;
|
|
316
|
+
},
|
|
317
|
+
setVoiceReplies: (on) => this.setVoiceReplies(on),
|
|
318
|
+
runUserTurn: (c, chatId, text) => this.runUserTurn(c, chatId, text),
|
|
319
|
+
tryHostPair: (chatId, text) => this.tryHostPair(ctx, chatId, text),
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
/** Persist + apply the voice-replies preference (the `/voice` toggle). */
|
|
323
|
+
async setVoiceReplies(on) {
|
|
324
|
+
this.voiceReplies = on;
|
|
325
|
+
try {
|
|
326
|
+
await saveVoiceReplies(this.opts.vault, on);
|
|
327
|
+
}
|
|
328
|
+
catch (err) {
|
|
329
|
+
this.opts.logger?.warn('telegram voice-replies persist failed', { err: String(err) });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Speak the final assistant reply as a voice note, when enabled. Best-effort
|
|
334
|
+
* and fully isolated (never throws): synthesize via the session's active
|
|
335
|
+
* Synthesizer, transcode to OGG/Opus (or send plain audio when ffmpeg is
|
|
336
|
+
* unavailable), and deliver via grammy. The text reply already went out.
|
|
337
|
+
*/
|
|
338
|
+
async sendVoiceReply(chatId, text) {
|
|
339
|
+
if (!this.voiceReplies || !this.bot || !this.session)
|
|
340
|
+
return;
|
|
341
|
+
const bot = this.bot;
|
|
342
|
+
const outcome = await deliverVoiceReply(this.session, text, {
|
|
343
|
+
send: async (audio, meta) => {
|
|
344
|
+
const file = new InputFile(audio, meta.filename);
|
|
345
|
+
if (meta.isVoiceNote)
|
|
346
|
+
await bot.api.sendVoice(chatId, file);
|
|
347
|
+
else
|
|
348
|
+
await bot.api.sendAudio(chatId, file);
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
if (outcome.status === 'failed') {
|
|
352
|
+
this.opts.logger?.warn('telegram voice reply failed', {
|
|
353
|
+
reason: outcome.reason,
|
|
354
|
+
...(outcome.error ? { err: outcome.error } : {}),
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Plain-message fallback for host-issued pairing: if a host window is open and
|
|
360
|
+
* an unauthorized chat sends exactly the 6-digit code, pair it (covers clients
|
|
361
|
+
* that don't auto-deliver the `?start=` deep-link payload). Returns true when
|
|
362
|
+
* the message was a pairing attempt we handled (paired, or a wrong-code reply)
|
|
363
|
+
* so the caller doesn't also emit the generic "not paired" rejection.
|
|
364
|
+
*/
|
|
365
|
+
async tryHostPair(ctx, chatId, text) {
|
|
366
|
+
if (this.pairing.phase() !== 'awaiting-host-code')
|
|
367
|
+
return false;
|
|
368
|
+
const normalized = text.replace(/\s+/g, '');
|
|
369
|
+
if (!/^\d{6}$/.test(normalized))
|
|
370
|
+
return false;
|
|
371
|
+
const result = await this.pairing.confirmChatCode(chatId, normalized);
|
|
372
|
+
if (result.ok)
|
|
373
|
+
return true; // confirmChatCode greeted + fired onPaired
|
|
374
|
+
await ctx.reply(result.message);
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Run a handler promise detached from the grammy poll loop. grammy awaits the
|
|
379
|
+
* value a middleware returns before fetching the next update batch; returning
|
|
380
|
+
* void here (instead of the handler promise) lets the loop keep delivering
|
|
381
|
+
* callback_query / `/cancel` while a turn runs. Errors are logged here because
|
|
382
|
+
* `bot.catch` only sees rejections of the AWAITED middleware chain.
|
|
383
|
+
*/
|
|
384
|
+
dispatchInBackground(work, kind) {
|
|
385
|
+
void work.catch((err) => {
|
|
386
|
+
this.opts.logger?.warn('telegram handler failed', { kind, err: String(err) });
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
async runUserTurn(ctx, chatId, text) {
|
|
390
|
+
if (!this.session)
|
|
391
|
+
throw new Error('TelegramChannel.start() must be called first');
|
|
392
|
+
// Atomic single-flight guard: `begin` claims the slot synchronously BEFORE
|
|
393
|
+
// any await so a second turn dispatched concurrently (the poll loop is no
|
|
394
|
+
// longer parked on us) can't slip past the busy check in the text/voice
|
|
395
|
+
// handlers. If we are already busy, refuse rather than corrupt the
|
|
396
|
+
// single-instance per-turn state (framePump / currentChatId / controller).
|
|
397
|
+
// The turnId is minted here so the coordinator records it as an own-turn
|
|
398
|
+
// id — that's what mirrorForeignTurn filters on.
|
|
399
|
+
const lease = this.turns.begin(newTurnId());
|
|
400
|
+
if (!lease) {
|
|
401
|
+
await ctx.reply('I am still working on the previous prompt. Send /cancel to abort it.');
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
this.currentChatId = chatId;
|
|
405
|
+
this.lastChatId = chatId;
|
|
406
|
+
const effectiveModel = this.activeModelOverride ?? this.model;
|
|
407
|
+
try {
|
|
408
|
+
await runUserTurn(ctx, {
|
|
409
|
+
session: this.session,
|
|
410
|
+
bot: this.bot,
|
|
411
|
+
framePump: this.framePump,
|
|
412
|
+
typing: this.typing,
|
|
413
|
+
...(this.opts.logger ? { logger: this.opts.logger } : {}),
|
|
414
|
+
onFinalReply: (finalText) => this.sendVoiceReply(chatId, finalText),
|
|
415
|
+
}, { chatId, text, model: effectiveModel, controller: lease.controller, turnId: lease.turnId });
|
|
416
|
+
}
|
|
417
|
+
finally {
|
|
418
|
+
lease.end();
|
|
419
|
+
this.currentChatId = null;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Post the assistant's prose for a turn this channel did not initiate. Gated
|
|
424
|
+
* by `!busy` (our own turns are rendered by the FramePump from the runUserTurn
|
|
425
|
+
* iterator) and by having served a chat at least once. Sent as plain text to
|
|
426
|
+
* avoid parse-mode pitfalls; the view itself lives on the web surface.
|
|
427
|
+
*/
|
|
428
|
+
mirrorForeignTurn(event) {
|
|
429
|
+
// The coordinator skips turns THIS channel initiated, by turnId — robust to
|
|
430
|
+
// events that arrive after `busy` flips false (async ordering /
|
|
431
|
+
// RemoteSession replay), which the `busy` flag alone could mis-mirror as
|
|
432
|
+
// foreign (invariant #8) — and yields the trimmed assistant prose.
|
|
433
|
+
const text = this.turns.mirrorText(event);
|
|
434
|
+
if (text == null)
|
|
435
|
+
return;
|
|
436
|
+
if (!this.bot || this.lastChatId == null)
|
|
437
|
+
return;
|
|
438
|
+
void this.bot.api.sendMessage(this.lastChatId, text).catch((err) => {
|
|
439
|
+
this.opts.logger?.warn('telegram mirror failed', { err: String(err) });
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
askForPermission(call, ctx) {
|
|
443
|
+
return askForPermission(call, ctx, {
|
|
444
|
+
bot: this.bot,
|
|
445
|
+
chatId: this.currentChatId,
|
|
446
|
+
session: this.session,
|
|
447
|
+
resolver: this.permissionResolver,
|
|
448
|
+
yolo: this.yolo,
|
|
449
|
+
...(this.opts.logger ? { logger: this.opts.logger } : {}),
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
askForApproval(id, request) {
|
|
453
|
+
return askForApproval(id, request, {
|
|
454
|
+
bot: this.bot,
|
|
455
|
+
chatId: this.currentChatId,
|
|
456
|
+
resolver: this.approvalResolver,
|
|
457
|
+
...(this.opts.logger ? { logger: this.opts.logger } : {}),
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
dispatchCallback(ctx) {
|
|
461
|
+
return handleCallback(ctx, {
|
|
462
|
+
bot: this.bot,
|
|
463
|
+
session: this.session,
|
|
464
|
+
chatId: this.currentChatId,
|
|
465
|
+
permissionResolver: this.permissionResolver,
|
|
466
|
+
approvalResolver: this.approvalResolver,
|
|
467
|
+
pairing: this.pairing,
|
|
468
|
+
}, {
|
|
469
|
+
setAwaitingApprovalText: (state) => {
|
|
470
|
+
this.awaitingApprovalText = state;
|
|
471
|
+
},
|
|
472
|
+
setActiveModelOverride: (modelId) => {
|
|
473
|
+
this.activeModelOverride = modelId;
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
//# sourceMappingURL=channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel.js","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel.test-d.d.ts","sourceRoot":"","sources":["../src/channel.test-d.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel.test-d.js","sourceRoot":"","sources":["../src/channel.test-d.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,EAA0B,MAAM,cAAc,CAAC;AAEtD,6FAA6F;AAC7F,YAAY,EAAmB,CAAC,aAAa,EAA8B,CAAC"}
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert the model's Markdown output into Telegram-flavoured HTML so
|
|
3
|
+
* the bot renders **bold**, `code`, ```code blocks```, [links](…),
|
|
4
|
+
* lists, and headings instead of dumping raw `**` and `#` characters
|
|
5
|
+
* into the chat.
|
|
6
|
+
*
|
|
7
|
+
* Why HTML and not MarkdownV2: MarkdownV2 requires escaping
|
|
8
|
+
* `_ * [ ] ( ) ~ \` > # + - = | { } . !` literally everywhere they
|
|
9
|
+
* AREN'T part of formatting — one stray `.` in a sentence breaks the
|
|
10
|
+
* whole message. HTML only needs three escapes (`< > &`) in text
|
|
11
|
+
* segments, and Telegram's HTML mode supports every tag we need
|
|
12
|
+
* (`<b>`, `<i>`, `<s>`, `<tg-spoiler>`, `<code>`, `<pre>`, `<a>`,
|
|
13
|
+
* `<blockquote>` / `<blockquote expandable>`).
|
|
14
|
+
*
|
|
15
|
+
* Telegram has no native headings or list elements, so:
|
|
16
|
+
* - `#`, `##`, `###` headings → bold text on their own line.
|
|
17
|
+
* - `- item` / `* item` bullets → `• item` (bullet glyph).
|
|
18
|
+
* - `1. item` numbered → kept as-is (the digit is fine).
|
|
19
|
+
*
|
|
20
|
+
* Beyond CommonMark we also map a few extensions so the model can lean
|
|
21
|
+
* on Telegram's richer surface without learning a new syntax:
|
|
22
|
+
* - `~~strike~~` → `<s>` strikethrough.
|
|
23
|
+
* - `||spoiler||` → `<tg-spoiler>` (tap-to-reveal hidden text).
|
|
24
|
+
* - `> [!note] Heading` → a titled, emoji-tagged callout blockquote.
|
|
25
|
+
* - `> [!details]- Title` → a COLLAPSED (expandable) callout — the
|
|
26
|
+
* load-bearing "hide the details" box. The
|
|
27
|
+
* trailing `-` forces collapsed, `+` forces
|
|
28
|
+
* open; some types (details/example/faq)
|
|
29
|
+
* collapse by default.
|
|
30
|
+
* - a plain `>` quote that runs long auto-collapses into an
|
|
31
|
+
* `<blockquote expandable>` so a wall of quoted text stays tidy.
|
|
32
|
+
*
|
|
33
|
+
* Code blocks are emitted as `<pre><code class="language-xxx">…</code></pre>`,
|
|
34
|
+
* inline code as `<code>…</code>`. Everything inside code is escaped
|
|
35
|
+
* but no formatting markers run.
|
|
36
|
+
*/
|
|
37
|
+
export declare function markdownToTelegramHtml(md: string): string;
|
|
38
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAwFzD"}
|