@quangnv13/nonstop 1.0.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/dist/bot.js ADDED
@@ -0,0 +1,605 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.loadLastChatId = loadLastChatId;
37
+ exports.createBotRuntime = createBotRuntime;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const logger_js_1 = require("./logger.js");
41
+ const session_controls_js_1 = require("./session-controls.js");
42
+ const store_js_1 = require("./store.js");
43
+ const grammyRequire = require;
44
+ const { Bot, InlineKeyboard } = grammyRequire('grammy');
45
+ const SUPPORTED_PRESETS = ['powershell', 'bash', 'codex', 'antigravity'];
46
+ const LAST_CHAT_ID_PATH = path.join(process.cwd(), 'data', 'last-chat-id.txt');
47
+ function saveLastChatId(chatId) {
48
+ try {
49
+ fs.mkdirSync(path.dirname(LAST_CHAT_ID_PATH), { recursive: true });
50
+ fs.writeFileSync(LAST_CHAT_ID_PATH, String(chatId), 'utf8');
51
+ }
52
+ catch {
53
+ // ignore
54
+ }
55
+ }
56
+ function loadLastChatId() {
57
+ try {
58
+ if (!fs.existsSync(LAST_CHAT_ID_PATH))
59
+ return null;
60
+ const val = parseInt(fs.readFileSync(LAST_CHAT_ID_PATH, 'utf8').trim(), 10);
61
+ return Number.isFinite(val) ? val : null;
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ }
67
+ function createBotRuntime(deps) {
68
+ const token = process.env.TELEGRAM_BOT_TOKEN;
69
+ if (!token) {
70
+ throw new Error('TELEGRAM_BOT_TOKEN là bắt buộc.');
71
+ }
72
+ const bot = new Bot(token);
73
+ const chatStates = new Map();
74
+ function getChatState(chatId) {
75
+ const existing = chatStates.get(chatId);
76
+ if (existing)
77
+ return existing;
78
+ const nextState = { workspaceDraft: null, configFieldDraft: null };
79
+ chatStates.set(chatId, nextState);
80
+ return nextState;
81
+ }
82
+ // Lưu chat ID mỗi lần có tương tác
83
+ function trackChatId(ctx) {
84
+ const chatId = ctx.chat?.id;
85
+ if (chatId)
86
+ saveLastChatId(chatId);
87
+ }
88
+ async function safeAnswerCallback(ctx, text) {
89
+ if (!ctx.callbackQuery)
90
+ return;
91
+ try {
92
+ await ctx.answerCallbackQuery(text ? { text } : undefined);
93
+ }
94
+ catch { /* ignore */ }
95
+ }
96
+ async function renderText(ctx, text, replyMarkup, useEdit = true) {
97
+ if (useEdit && ctx.callbackQuery) {
98
+ try {
99
+ await ctx.editMessageText(text, replyMarkup ? { reply_markup: replyMarkup } : undefined);
100
+ return;
101
+ }
102
+ catch { /* fallback to reply */ }
103
+ }
104
+ await ctx.reply(text, replyMarkup ? { reply_markup: replyMarkup } : undefined);
105
+ }
106
+ function createKeyboard() {
107
+ return new InlineKeyboard();
108
+ }
109
+ function getWorkspaceById(workspaceId) {
110
+ return deps.getWorkspaces().find((w) => w.id === workspaceId);
111
+ }
112
+ function buildMainMenuText() {
113
+ const activeSession = deps.getActiveSession();
114
+ return [
115
+ '🖥 nonstop client',
116
+ '',
117
+ `📁 Workspaces: ${deps.getWorkspaces().length}`,
118
+ activeSession
119
+ ? `⚡ Session: ${activeSession.preset} | ${activeSession.cwd}`
120
+ : '⚡ Session: không có'
121
+ ].join('\n');
122
+ }
123
+ function buildMainMenuKeyboard() {
124
+ return createKeyboard()
125
+ .text('📁 Workspaces', 'workspaces_list')
126
+ .text('⚡ Session', 'sessions_list')
127
+ .row()
128
+ .text('⚙️ Cấu hình', 'config_menu')
129
+ .text('ℹ️ Trợ giúp', 'help_view');
130
+ }
131
+ async function showMainMenu(ctx) {
132
+ await renderText(ctx, buildMainMenuText(), buildMainMenuKeyboard());
133
+ }
134
+ async function showHelp(ctx) {
135
+ await renderText(ctx, [
136
+ '📖 Lệnh có sẵn',
137
+ '',
138
+ '/start — Mở menu chính',
139
+ '/status — Trạng thái runtime',
140
+ '/help — Trợ giúp',
141
+ '/config — Cấu hình hệ thống',
142
+ '/send <lệnh> — Gửi lệnh thô tới session',
143
+ '',
144
+ 'Khi input mode BẬT, tin nhắn thường sẽ được gửi thẳng vào session.'
145
+ ].join('\n'), createKeyboard().text('⬅️ Quay lại', 'main_menu'));
146
+ }
147
+ async function showStatus(ctx) {
148
+ const activeSession = deps.getActiveSession();
149
+ const currentAllowedUsername = normalizeUsername(process.env.ADMIN_USERNAME || process.env.TELEGRAM_USERNAME || '');
150
+ await renderText(ctx, [
151
+ '📊 Trạng thái Runtime',
152
+ '',
153
+ `Người dùng: ${currentAllowedUsername || 'không giới hạn'}`,
154
+ `Workspaces: ${deps.getWorkspaces().length}`,
155
+ `Session: ${activeSession ? 'đang chạy' : 'không có'}`,
156
+ activeSession ? `Preset: ${activeSession.preset}` : '',
157
+ activeSession ? `Thư mục: ${activeSession.cwd}` : ''
158
+ ].filter(Boolean).join('\n'), createKeyboard().text('⬅️ Quay lại', 'main_menu'));
159
+ }
160
+ async function showConfigMenu(ctx) {
161
+ const config = deps.getConfig();
162
+ const lines = [
163
+ '⚙️ Cấu hình nonstop',
164
+ '',
165
+ `• Token: ${config.telegramBotToken ? '••••' + config.telegramBotToken.slice(-4) : 'Chưa cấu hình'}`,
166
+ `• Admin Username: ${config.adminUsername || 'Chưa cấu hình'}`,
167
+ `• Client Name: ${config.clientName || 'Chưa cấu hình'}`,
168
+ `• Telegram Username: ${config.telegramUsername || 'Chưa cấu hình'}`,
169
+ `• Ngôn ngữ: ${config.language} (vi/en)`,
170
+ `• Chế độ khởi động: ${config.startupMode} (disabled/background/open-ui)`,
171
+ `• Output Interval: ${config.outputInterval} ms`,
172
+ `• Max Output Lines: ${config.maxOutputLines}`,
173
+ `• Max Render Lines: ${config.maxRenderLines}`,
174
+ `• Codex Cmd: ${config.codexCmd}`,
175
+ `• Codex Args: ${config.codexArgs}`,
176
+ `• Antigravity Cmd: ${config.antigravityCmd}`,
177
+ `• Antigravity Args: ${config.antigravityArgs}`
178
+ ];
179
+ const keyboard = createKeyboard()
180
+ .text('Token', 'config_edit:telegramBotToken')
181
+ .text('Admin', 'config_edit:adminUsername')
182
+ .row()
183
+ .text('Client Name', 'config_edit:clientName')
184
+ .text('Telegram User', 'config_edit:telegramUsername')
185
+ .row()
186
+ .text(`Ngôn ngữ (${config.language === 'vi' ? '🇻🇳 vi' : '🇬🇧 en'})`, 'config_edit:language')
187
+ .text(`Khởi động (${config.startupMode})`, 'config_edit:startupMode')
188
+ .row()
189
+ .text('Interval', 'config_edit:outputInterval')
190
+ .text('Max Output Lines', 'config_edit:maxOutputLines')
191
+ .row()
192
+ .text('Max Render Lines', 'config_edit:maxRenderLines')
193
+ .text('Codex Cmd', 'config_edit:codexCmd')
194
+ .row()
195
+ .text('Codex Args', 'config_edit:codexArgs')
196
+ .text('Antigravity Cmd', 'config_edit:antigravityCmd')
197
+ .row()
198
+ .text('Antigravity Args', 'config_edit:antigravityArgs')
199
+ .row()
200
+ .text('⬅️ Quay lại', 'main_menu');
201
+ await renderText(ctx, lines.join('\n'), keyboard);
202
+ }
203
+ async function showWorkspacesMenu(ctx) {
204
+ const workspaces = deps.getWorkspaces();
205
+ const lines = ['📁 Danh sách Workspace', ''];
206
+ const keyboard = createKeyboard();
207
+ if (workspaces.length === 0) {
208
+ lines.push('Chưa có workspace nào.');
209
+ }
210
+ else {
211
+ for (const ws of workspaces) {
212
+ lines.push(`• ${ws.name}`);
213
+ lines.push(` ${ws.path}`);
214
+ keyboard.text(`📁 ${ws.name}`, `view_workspace:${ws.id}`).row();
215
+ }
216
+ }
217
+ keyboard.text('➕ Thêm workspace', 'workspace_action:add').row().text('⬅️ Quay lại', 'main_menu');
218
+ await renderText(ctx, lines.join('\n'), keyboard);
219
+ }
220
+ function buildWorkspaceDetailsKeyboard(workspace) {
221
+ return createKeyboard()
222
+ .text('✏️ Sửa tên', `workspace_action:edit_name:${workspace.id}`)
223
+ .text('🛠️ Sửa đường dẫn', `workspace_action:edit_path:${workspace.id}`)
224
+ .row()
225
+ .text('🗑️ Xóa', `workspace_action:delete:${workspace.id}`)
226
+ .row()
227
+ .text('Powershell', `start_session:${workspace.id}:powershell`)
228
+ .text('Bash', `start_session:${workspace.id}:bash`)
229
+ .row()
230
+ .text('Codex', `start_session:${workspace.id}:codex`)
231
+ .text('Antigravity', `start_session:${workspace.id}:antigravity`)
232
+ .row()
233
+ .text('⬅️ Quay lại', 'workspaces_list');
234
+ }
235
+ async function showWorkspaceDetails(ctx, workspaceId) {
236
+ const workspace = getWorkspaceById(workspaceId);
237
+ if (!workspace) {
238
+ await renderText(ctx, 'Workspace không tìm thấy.', createKeyboard().text('⬅️ Quay lại', 'workspaces_list'));
239
+ return;
240
+ }
241
+ await renderText(ctx, ['📁 Chi tiết Workspace', '', `Tên: ${workspace.name}`, `Đường dẫn: ${workspace.path}`].join('\n'), buildWorkspaceDetailsKeyboard(workspace));
242
+ }
243
+ async function showSessionsMenu(ctx) {
244
+ const activeSession = deps.getActiveSession();
245
+ const keyboard = createKeyboard();
246
+ const lines = ['⚡ Session', ''];
247
+ if (!activeSession || activeSession.status !== 'running') {
248
+ lines.push('Không có session đang chạy.');
249
+ }
250
+ else {
251
+ lines.push(`ID: ${activeSession.sessionId}`);
252
+ lines.push(`Preset: ${activeSession.preset}`);
253
+ lines.push(`Thư mục: ${activeSession.cwd}`);
254
+ keyboard.text('🎮 Điều khiển', `view_session:${activeSession.sessionId}`).row();
255
+ }
256
+ keyboard.text('⬅️ Quay lại', 'main_menu');
257
+ await renderText(ctx, lines.join('\n'), keyboard);
258
+ }
259
+ async function showSessionDetails(ctx, sessionId) {
260
+ const session = deps.getActiveSession();
261
+ if (!session || session.status !== 'running' || (sessionId && session.sessionId !== sessionId)) {
262
+ await renderText(ctx, 'Session không đang chạy.', createKeyboard().text('⬅️ Quay lại', 'main_menu'));
263
+ return;
264
+ }
265
+ const keyboard = (0, session_controls_js_1.buildSessionActionMarkup)({
266
+ sessionId: session.sessionId,
267
+ inputMode: session.inputMode,
268
+ autoEnter: session.autoEnter,
269
+ includeBackButton: true
270
+ });
271
+ await renderText(ctx, [
272
+ '🎮 Điều khiển Session',
273
+ '',
274
+ `ID: ${session.sessionId}`,
275
+ `Preset: ${session.preset}`,
276
+ `Trạng thái: ${session.status}`,
277
+ `Thư mục: ${session.cwd}`,
278
+ `Input mode: ${session.inputMode ? 'BẬT' : 'TẮT'}`,
279
+ `Auto enter: ${session.autoEnter ? 'BẬT' : 'TẮT'}`
280
+ ].join('\n'), keyboard);
281
+ }
282
+ async function beginWorkspaceDraft(ctx, workspaceDraft, prompt) {
283
+ const chatId = ctx.chat?.id;
284
+ if (!chatId)
285
+ return;
286
+ getChatState(chatId).workspaceDraft = workspaceDraft;
287
+ await ctx.reply(prompt);
288
+ }
289
+ async function handleWorkspaceDraft(ctx) {
290
+ const chatId = ctx.chat?.id;
291
+ const text = ctx.message?.text?.trim();
292
+ if (!chatId || !text)
293
+ return false;
294
+ const state = getChatState(chatId);
295
+ const draft = state.workspaceDraft;
296
+ if (!draft)
297
+ return false;
298
+ const workspaces = [...deps.getWorkspaces()];
299
+ if (draft.mode === 'add_name') {
300
+ state.workspaceDraft = { mode: 'add_path', name: text };
301
+ await ctx.reply('Nhập đường dẫn workspace:');
302
+ return true;
303
+ }
304
+ if (draft.mode === 'add_path') {
305
+ const workspace = {
306
+ id: (0, store_js_1.createWorkspaceId)(),
307
+ name: draft.name || 'Workspace',
308
+ path: text
309
+ };
310
+ workspaces.push(workspace);
311
+ deps.saveWorkspaces(workspaces);
312
+ state.workspaceDraft = null;
313
+ await ctx.reply(`✓ Đã thêm workspace "${workspace.name}".`);
314
+ await showWorkspaceDetails(ctx, workspace.id);
315
+ return true;
316
+ }
317
+ const targetIndex = workspaces.findIndex((w) => w.id === draft.workspaceId);
318
+ if (targetIndex === -1) {
319
+ state.workspaceDraft = null;
320
+ await ctx.reply('Workspace không còn tồn tại.');
321
+ return true;
322
+ }
323
+ if (draft.mode === 'edit_name') {
324
+ workspaces[targetIndex] = { ...workspaces[targetIndex], name: text };
325
+ deps.saveWorkspaces(workspaces);
326
+ state.workspaceDraft = null;
327
+ await ctx.reply('✓ Đã cập nhật tên workspace.');
328
+ await showWorkspaceDetails(ctx, workspaces[targetIndex].id);
329
+ return true;
330
+ }
331
+ if (draft.mode === 'edit_path') {
332
+ workspaces[targetIndex] = { ...workspaces[targetIndex], path: text };
333
+ deps.saveWorkspaces(workspaces);
334
+ state.workspaceDraft = null;
335
+ await ctx.reply('✓ Đã cập nhật đường dẫn workspace.');
336
+ await showWorkspaceDetails(ctx, workspaces[targetIndex].id);
337
+ return true;
338
+ }
339
+ return false;
340
+ }
341
+ async function handleConfigDraft(ctx) {
342
+ const chatId = ctx.chat?.id;
343
+ const text = ctx.message?.text?.trim();
344
+ if (!chatId || text === undefined)
345
+ return false;
346
+ const state = getChatState(chatId);
347
+ const draft = state.configFieldDraft;
348
+ if (!draft)
349
+ return false;
350
+ const field = draft.field;
351
+ const config = deps.getConfig();
352
+ const isNumeric = ['outputInterval', 'maxOutputLines', 'maxRenderLines'].includes(field);
353
+ if (isNumeric) {
354
+ const parsed = parseInt(text, 10);
355
+ if (isNaN(parsed) || !Number.isFinite(parsed)) {
356
+ await ctx.reply(`❌ Giá trị nhập vào không hợp lệ. Vui lòng nhập một số nguyên hợp lệ cho field "${field}".`);
357
+ return true;
358
+ }
359
+ const nextConfig = { ...config, [field]: parsed };
360
+ await deps.saveConfig(nextConfig);
361
+ }
362
+ else {
363
+ let value = text;
364
+ if (field === 'adminUsername' || field === 'telegramUsername') {
365
+ value = text.startsWith('@') ? text : `@${text}`;
366
+ }
367
+ const nextConfig = { ...config, [field]: value };
368
+ await deps.saveConfig(nextConfig);
369
+ }
370
+ state.configFieldDraft = null;
371
+ await ctx.reply(`✓ Đã cập nhật cấu hình cho "${field}".`);
372
+ await showConfigMenu(ctx);
373
+ return true;
374
+ }
375
+ // Middleware: kiểm tra quyền & lưu chat ID
376
+ bot.use(async (ctx, next) => {
377
+ trackChatId(ctx);
378
+ const username = normalizeUsername(ctx.from?.username);
379
+ const currentAllowedUsername = normalizeUsername(process.env.ADMIN_USERNAME || process.env.TELEGRAM_USERNAME || '');
380
+ if (currentAllowedUsername && username !== currentAllowedUsername) {
381
+ await ctx.reply('Bot này chỉ dành cho tài khoản Telegram đã cấu hình.');
382
+ return;
383
+ }
384
+ await next();
385
+ });
386
+ bot.command('start', async (ctx) => {
387
+ await showMainMenu(ctx);
388
+ });
389
+ bot.command('help', async (ctx) => {
390
+ await showHelp(ctx);
391
+ });
392
+ bot.command('status', async (ctx) => {
393
+ await showStatus(ctx);
394
+ });
395
+ bot.command('config', async (ctx) => {
396
+ await showConfigMenu(ctx);
397
+ });
398
+ // /send <lệnh> — gửi raw text tới session đang chạy
399
+ bot.command('send', async (ctx) => {
400
+ const rawText = ctx.message?.text ?? '';
401
+ // Bỏ phần "/send " ở đầu
402
+ const payload = rawText.replace(/^\/send\s*/i, '').trim();
403
+ if (!payload) {
404
+ await ctx.reply('Cách dùng: /send <lệnh cần gửi>');
405
+ return;
406
+ }
407
+ const session = deps.getActiveSession();
408
+ if (!session || session.status !== 'running') {
409
+ await ctx.reply('Không có session đang chạy.');
410
+ return;
411
+ }
412
+ deps.sendInput(session.autoEnter ? `${payload}\r` : payload);
413
+ await ctx.reply('✓ Đã gửi lệnh');
414
+ });
415
+ bot.on('message:text', async (ctx) => {
416
+ const text = ctx.message.text;
417
+ // Bỏ qua các lệnh bắt đầu bằng /
418
+ if (text.startsWith('/'))
419
+ return;
420
+ if (await handleWorkspaceDraft(ctx))
421
+ return;
422
+ if (await handleConfigDraft(ctx))
423
+ return;
424
+ const session = deps.getActiveSession();
425
+ if (session?.status === 'running' && session.inputMode) {
426
+ const payload = session.autoEnter ? `${text}\r` : text;
427
+ deps.sendInput(payload);
428
+ await ctx.reply('✓ Đã gửi lệnh');
429
+ return;
430
+ }
431
+ await ctx.reply('Dùng /start để mở menu.');
432
+ });
433
+ bot.callbackQuery('main_menu', async (ctx) => {
434
+ await safeAnswerCallback(ctx);
435
+ await showMainMenu(ctx);
436
+ });
437
+ bot.callbackQuery('config_menu', async (ctx) => {
438
+ await safeAnswerCallback(ctx);
439
+ await showConfigMenu(ctx);
440
+ });
441
+ bot.callbackQuery(/^config_edit:(.+)$/, async (ctx) => {
442
+ await safeAnswerCallback(ctx);
443
+ const field = ctx.match[1];
444
+ if (field === 'language') {
445
+ const config = deps.getConfig();
446
+ const nextLang = config.language === 'vi' ? 'en' : 'vi';
447
+ await deps.saveConfig({ ...config, language: nextLang });
448
+ await showConfigMenu(ctx);
449
+ return;
450
+ }
451
+ if (field === 'startupMode') {
452
+ const config = deps.getConfig();
453
+ let nextMode;
454
+ if (config.startupMode === 'disabled') {
455
+ nextMode = 'background';
456
+ }
457
+ else if (config.startupMode === 'background') {
458
+ nextMode = 'open-ui';
459
+ }
460
+ else {
461
+ nextMode = 'disabled';
462
+ }
463
+ await deps.saveConfig({ ...config, startupMode: nextMode });
464
+ await showConfigMenu(ctx);
465
+ return;
466
+ }
467
+ const chatId = ctx.chat?.id;
468
+ if (!chatId)
469
+ return;
470
+ getChatState(chatId).configFieldDraft = { field };
471
+ await ctx.reply(`Nhập giá trị mới cho field "${field}":`);
472
+ });
473
+ bot.callbackQuery('help_view', async (ctx) => {
474
+ await safeAnswerCallback(ctx);
475
+ await showHelp(ctx);
476
+ });
477
+ bot.callbackQuery('workspaces_list', async (ctx) => {
478
+ await safeAnswerCallback(ctx);
479
+ await showWorkspacesMenu(ctx);
480
+ });
481
+ bot.callbackQuery(/^view_workspace:(.+)$/, async (ctx) => {
482
+ await safeAnswerCallback(ctx);
483
+ await showWorkspaceDetails(ctx, ctx.match[1]);
484
+ });
485
+ bot.callbackQuery('workspace_action:add', async (ctx) => {
486
+ await safeAnswerCallback(ctx);
487
+ await beginWorkspaceDraft(ctx, { mode: 'add_name' }, 'Nhập tên workspace mới:');
488
+ });
489
+ bot.callbackQuery(/^workspace_action:edit_name:(.+)$/, async (ctx) => {
490
+ await safeAnswerCallback(ctx);
491
+ await beginWorkspaceDraft(ctx, { mode: 'edit_name', workspaceId: ctx.match[1] }, 'Nhập tên workspace mới:');
492
+ });
493
+ bot.callbackQuery(/^workspace_action:edit_path:(.+)$/, async (ctx) => {
494
+ await safeAnswerCallback(ctx);
495
+ await beginWorkspaceDraft(ctx, { mode: 'edit_path', workspaceId: ctx.match[1] }, 'Nhập đường dẫn workspace mới:');
496
+ });
497
+ bot.callbackQuery(/^workspace_action:delete:(.+)$/, async (ctx) => {
498
+ await safeAnswerCallback(ctx);
499
+ const workspaces = deps.getWorkspaces().filter((w) => w.id !== ctx.match[1]);
500
+ deps.saveWorkspaces(workspaces);
501
+ await showWorkspacesMenu(ctx);
502
+ });
503
+ bot.callbackQuery(/^start_session:(.+):(.+)$/, async (ctx) => {
504
+ await safeAnswerCallback(ctx);
505
+ const workspaceId = ctx.match[1];
506
+ const preset = ctx.match[2];
507
+ if (!SUPPORTED_PRESETS.includes(preset)) {
508
+ await ctx.reply(`Preset không hỗ trợ: ${preset}`);
509
+ return;
510
+ }
511
+ if (deps.getActiveSession()?.status === 'running') {
512
+ await ctx.reply('Đã có session đang chạy. Dừng session hiện tại trước.');
513
+ return;
514
+ }
515
+ const workspace = getWorkspaceById(workspaceId);
516
+ if (!workspace) {
517
+ await ctx.reply('Workspace không tìm thấy.');
518
+ return;
519
+ }
520
+ try {
521
+ await deps.startSession(ctx.chat.id, workspace.id, preset);
522
+ await showSessionDetails(ctx);
523
+ }
524
+ catch (error) {
525
+ logger_js_1.logger.error('Lỗi khi khởi chạy session', {
526
+ workspaceId,
527
+ preset,
528
+ error: error instanceof Error ? error.message : String(error)
529
+ });
530
+ await ctx.reply(`Lỗi khi khởi chạy session: ${error instanceof Error ? error.message : String(error)}`);
531
+ }
532
+ });
533
+ bot.callbackQuery('sessions_list', async (ctx) => {
534
+ await safeAnswerCallback(ctx);
535
+ await showSessionsMenu(ctx);
536
+ });
537
+ bot.callbackQuery(/^view_session:(.+)$/, async (ctx) => {
538
+ await safeAnswerCallback(ctx);
539
+ await showSessionDetails(ctx, ctx.match[1]);
540
+ });
541
+ bot.callbackQuery(/^session_cmd:(.+):(.+)$/, async (ctx) => {
542
+ await safeAnswerCallback(ctx);
543
+ const session = deps.getActiveSession();
544
+ const sessionId = ctx.match[1];
545
+ const action = ctx.match[2];
546
+ if (!session || session.sessionId !== sessionId || session.status !== 'running') {
547
+ await ctx.reply('Session không đang chạy.');
548
+ return;
549
+ }
550
+ switch (action) {
551
+ case 'toggle_input':
552
+ deps.setInputMode(!session.inputMode);
553
+ break;
554
+ case 'toggle_enter':
555
+ deps.setAutoEnter(!session.autoEnter);
556
+ break;
557
+ case 'send_enter':
558
+ case 'send_up':
559
+ case 'send_down':
560
+ case 'send_escape':
561
+ deps.sendKey(action);
562
+ break;
563
+ case 'stop':
564
+ await deps.stopSession();
565
+ await showSessionsMenu(ctx);
566
+ return;
567
+ case 'refresh':
568
+ break;
569
+ default:
570
+ await ctx.reply(`Hành động không hỗ trợ: ${action}`);
571
+ return;
572
+ }
573
+ await showSessionDetails(ctx, sessionId);
574
+ });
575
+ bot.catch((error) => {
576
+ logger_js_1.logger.error('Lỗi bot handler', {
577
+ error: error.error instanceof Error ? error.error.message : String(error.error)
578
+ });
579
+ });
580
+ return {
581
+ async start(options) {
582
+ const botInfo = await bot.api.getMe();
583
+ void bot.start();
584
+ // Đợi bot khởi động
585
+ await new Promise(r => setTimeout(r, 800));
586
+ options?.onStart?.(botInfo);
587
+ },
588
+ async stop() {
589
+ bot.stop();
590
+ },
591
+ async pushSessionOutput(chatId, text, options) {
592
+ await bot.api.sendMessage(chatId, text, options);
593
+ },
594
+ // Confirmation prompt đã bị xóa — không dùng nữa
595
+ async showConfirmationPrompt(_session, _text) {
596
+ // Intentionally disabled
597
+ }
598
+ };
599
+ }
600
+ function normalizeUsername(username) {
601
+ const value = (username || '').trim();
602
+ if (!value)
603
+ return '';
604
+ return value.startsWith('@') ? value.toLowerCase() : `@${value.toLowerCase()}`;
605
+ }