@grinev/opencode-telegram-bot 0.3.0 → 0.4.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/README.md +1 -0
- package/dist/bot/commands/definitions.js +1 -0
- package/dist/bot/commands/rename.js +91 -0
- package/dist/bot/index.js +10 -2
- package/dist/i18n/en.js +8 -0
- package/dist/i18n/ru.js +8 -0
- package/dist/rename/manager.js +50 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,6 +95,7 @@ opencode-telegram config
|
|
|
95
95
|
| `/projects` | Switch between OpenCode projects |
|
|
96
96
|
| `/model` | Choose a model from your favorites |
|
|
97
97
|
| `/agent` | Switch agent mode (Plan / Build) |
|
|
98
|
+
| `/rename` | Rename the current session |
|
|
98
99
|
| `/opencode_start` | Start the OpenCode server remotely |
|
|
99
100
|
| `/opencode_stop` | Stop the OpenCode server remotely |
|
|
100
101
|
| `/help` | Show available commands |
|
|
@@ -11,6 +11,7 @@ const COMMAND_DEFINITIONS = [
|
|
|
11
11
|
{ command: "projects", descriptionKey: "cmd.description.projects" },
|
|
12
12
|
{ command: "model", descriptionKey: "cmd.description.model" },
|
|
13
13
|
{ command: "agent", descriptionKey: "cmd.description.agent" },
|
|
14
|
+
{ command: "rename", descriptionKey: "cmd.description.rename" },
|
|
14
15
|
{ command: "opencode_start", descriptionKey: "cmd.description.opencode_start" },
|
|
15
16
|
{ command: "opencode_stop", descriptionKey: "cmd.description.opencode_stop" },
|
|
16
17
|
{ command: "help", descriptionKey: "cmd.description.help" },
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { InlineKeyboard } from "grammy";
|
|
2
|
+
import { opencodeClient } from "../../opencode/client.js";
|
|
3
|
+
import { getCurrentSession, setCurrentSession } from "../../session/manager.js";
|
|
4
|
+
import { renameManager } from "../../rename/manager.js";
|
|
5
|
+
import { pinnedMessageManager } from "../../pinned/manager.js";
|
|
6
|
+
import { logger } from "../../utils/logger.js";
|
|
7
|
+
import { t } from "../../i18n/index.js";
|
|
8
|
+
export async function renameCommand(ctx) {
|
|
9
|
+
try {
|
|
10
|
+
const currentSession = getCurrentSession();
|
|
11
|
+
if (!currentSession) {
|
|
12
|
+
await ctx.reply(t("rename.no_session"));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const keyboard = new InlineKeyboard().text(t("rename.button.cancel"), "rename:cancel");
|
|
16
|
+
const message = await ctx.reply(t("rename.prompt", { title: currentSession.title }), {
|
|
17
|
+
reply_markup: keyboard,
|
|
18
|
+
});
|
|
19
|
+
renameManager.startWaiting(currentSession.id, currentSession.directory, currentSession.title);
|
|
20
|
+
renameManager.setMessageId(message.message_id);
|
|
21
|
+
logger.info(`[RenameCommand] Waiting for new title for session: ${currentSession.id}`);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
logger.error("[RenameCommand] Error starting rename flow:", error);
|
|
25
|
+
await ctx.reply(t("rename.error"));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function handleRenameCancel(ctx) {
|
|
29
|
+
const data = ctx.callbackQuery?.data;
|
|
30
|
+
if (!data || data !== "rename:cancel") {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
logger.debug("[RenameHandler] Cancel callback received");
|
|
34
|
+
renameManager.clear();
|
|
35
|
+
await ctx.answerCallbackQuery();
|
|
36
|
+
await ctx.editMessageText(t("rename.cancelled"));
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
export async function handleRenameTextAnswer(ctx) {
|
|
40
|
+
if (!renameManager.isWaitingForName()) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const text = ctx.message?.text;
|
|
44
|
+
if (!text) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (text.startsWith("/")) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const sessionInfo = renameManager.getSessionInfo();
|
|
51
|
+
if (!sessionInfo) {
|
|
52
|
+
renameManager.clear();
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const newTitle = text.trim();
|
|
56
|
+
if (!newTitle) {
|
|
57
|
+
await ctx.reply(t("rename.empty_title"));
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
logger.info(`[RenameHandler] Renaming session ${sessionInfo.sessionId} to: ${newTitle}`);
|
|
61
|
+
try {
|
|
62
|
+
const { data: updatedSession, error } = await opencodeClient.session.update({
|
|
63
|
+
sessionID: sessionInfo.sessionId,
|
|
64
|
+
directory: sessionInfo.directory,
|
|
65
|
+
title: newTitle,
|
|
66
|
+
});
|
|
67
|
+
if (error || !updatedSession) {
|
|
68
|
+
throw error || new Error("Failed to update session");
|
|
69
|
+
}
|
|
70
|
+
setCurrentSession({
|
|
71
|
+
id: sessionInfo.sessionId,
|
|
72
|
+
title: newTitle,
|
|
73
|
+
directory: sessionInfo.directory,
|
|
74
|
+
});
|
|
75
|
+
if (pinnedMessageManager.isInitialized()) {
|
|
76
|
+
await pinnedMessageManager.onSessionChange(sessionInfo.sessionId, newTitle);
|
|
77
|
+
}
|
|
78
|
+
const messageId = renameManager.getMessageId();
|
|
79
|
+
if (messageId && ctx.chat) {
|
|
80
|
+
await ctx.api.deleteMessage(ctx.chat.id, messageId).catch(() => { });
|
|
81
|
+
}
|
|
82
|
+
await ctx.reply(t("rename.success", { title: newTitle }));
|
|
83
|
+
logger.info(`[RenameHandler] Session renamed successfully: ${newTitle}`);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
logger.error("[RenameHandler] Error renaming session:", error);
|
|
87
|
+
await ctx.reply(t("rename.error"));
|
|
88
|
+
}
|
|
89
|
+
renameManager.clear();
|
|
90
|
+
return true;
|
|
91
|
+
}
|
package/dist/bot/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import { opencodeStartCommand } from "./commands/opencode-start.js";
|
|
|
19
19
|
import { opencodeStopCommand } from "./commands/opencode-stop.js";
|
|
20
20
|
import { handleAgentCommand } from "./commands/agent.js";
|
|
21
21
|
import { handleModelCommand } from "./commands/model.js";
|
|
22
|
+
import { renameCommand, handleRenameCancel, handleRenameTextAnswer } from "./commands/rename.js";
|
|
22
23
|
import { handleQuestionCallback, showCurrentQuestion, handleQuestionTextAnswer, } from "./handlers/question.js";
|
|
23
24
|
import { handlePermissionCallback, showPermissionRequest } from "./handlers/permission.js";
|
|
24
25
|
import { handleAgentSelect, showAgentSelectionMenu } from "./handlers/agent.js";
|
|
@@ -375,6 +376,7 @@ export function createBot() {
|
|
|
375
376
|
bot.command("agent", handleAgentCommand);
|
|
376
377
|
bot.command("model", handleModelCommand);
|
|
377
378
|
bot.command("stop", stopCommand);
|
|
379
|
+
bot.command("rename", renameCommand);
|
|
378
380
|
bot.on("callback_query:data", async (ctx) => {
|
|
379
381
|
logger.debug(`[Bot] Received callback_query:data: ${ctx.callbackQuery?.data}`);
|
|
380
382
|
logger.debug(`[Bot] Callback context: from=${ctx.from?.id}, chat=${ctx.chat?.id}`);
|
|
@@ -388,7 +390,8 @@ export function createBot() {
|
|
|
388
390
|
const handledVariant = await handleVariantSelect(ctx);
|
|
389
391
|
const handledCompactConfirm = await handleCompactConfirm(ctx);
|
|
390
392
|
const handledCompactCancel = await handleCompactCancel(ctx);
|
|
391
|
-
|
|
393
|
+
const handledRenameCancel = await handleRenameCancel(ctx);
|
|
394
|
+
logger.debug(`[Bot] Callback handled: session=${handledSession}, project=${handledProject}, question=${handledQuestion}, permission=${handledPermission}, agent=${handledAgent}, model=${handledModel}, variant=${handledVariant}, compact=${handledCompactConfirm || handledCompactCancel}, rename=${handledRenameCancel}`);
|
|
392
395
|
if (!handledSession &&
|
|
393
396
|
!handledProject &&
|
|
394
397
|
!handledQuestion &&
|
|
@@ -397,7 +400,8 @@ export function createBot() {
|
|
|
397
400
|
!handledModel &&
|
|
398
401
|
!handledVariant &&
|
|
399
402
|
!handledCompactConfirm &&
|
|
400
|
-
!handledCompactCancel
|
|
403
|
+
!handledCompactCancel &&
|
|
404
|
+
!handledRenameCancel) {
|
|
401
405
|
logger.debug("Unknown callback query:", ctx.callbackQuery?.data);
|
|
402
406
|
await ctx.answerCallbackQuery({ text: t("callback.unknown_command") });
|
|
403
407
|
}
|
|
@@ -496,6 +500,10 @@ export function createBot() {
|
|
|
496
500
|
await handleQuestionTextAnswer(ctx);
|
|
497
501
|
return;
|
|
498
502
|
}
|
|
503
|
+
const handledRename = await handleRenameTextAnswer(ctx);
|
|
504
|
+
if (handledRename) {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
499
507
|
const currentProject = getCurrentProject();
|
|
500
508
|
if (!currentProject) {
|
|
501
509
|
await ctx.reply(t("bot.project_not_selected"));
|
package/dist/i18n/en.js
CHANGED
|
@@ -187,6 +187,14 @@ export const en = {
|
|
|
187
187
|
"runtime.wizard.saved": "Configuration saved:\n- {envPath}\n- {settingsPath}\n",
|
|
188
188
|
"runtime.wizard.not_configured_starting": "Application is not configured yet. Starting wizard...\n",
|
|
189
189
|
"runtime.wizard.tty_required": "Interactive wizard requires a TTY terminal. Run `opencode-telegram config` in an interactive shell.",
|
|
190
|
+
"rename.no_session": "⚠️ No active session. Create or select a session first.",
|
|
191
|
+
"rename.prompt": "📝 Enter new title for session:\n\nCurrent: {title}",
|
|
192
|
+
"rename.empty_title": "⚠️ Title cannot be empty.",
|
|
193
|
+
"rename.success": "✅ Session renamed to: {title}",
|
|
194
|
+
"rename.error": "🔴 Failed to rename session.",
|
|
195
|
+
"rename.cancelled": "❌ Rename cancelled.",
|
|
196
|
+
"rename.button.cancel": "❌ Cancel",
|
|
197
|
+
"cmd.description.rename": "Rename current session",
|
|
190
198
|
"cli.usage": "Usage:\n opencode-telegram [start] [--mode sources|installed]\n opencode-telegram status\n opencode-telegram stop\n opencode-telegram config\n\nNotes:\n - No command defaults to `start`\n - `--mode` is currently supported for `start` only",
|
|
191
199
|
"cli.placeholder.status": "Command `status` is currently a placeholder. Real status checks will be added in service layer (Phase 5).",
|
|
192
200
|
"cli.placeholder.stop": "Command `stop` is currently a placeholder. Real background process stop will be added in service layer (Phase 5).",
|
package/dist/i18n/ru.js
CHANGED
|
@@ -187,6 +187,14 @@ export const ru = {
|
|
|
187
187
|
"runtime.wizard.saved": "Конфигурация сохранена:\n- {envPath}\n- {settingsPath}\n",
|
|
188
188
|
"runtime.wizard.not_configured_starting": "Приложение еще не сконфигурировано. Запускаю wizard...\n",
|
|
189
189
|
"runtime.wizard.tty_required": "Интерактивный wizard требует TTY-терминал. Запустите `opencode-telegram config` в интерактивной оболочке.",
|
|
190
|
+
"rename.no_session": "⚠️ Нет активной сессии. Сначала создайте или выберите сессию.",
|
|
191
|
+
"rename.prompt": "📝 Введите новое название сессии:\n\nТекущее: {title}",
|
|
192
|
+
"rename.empty_title": "⚠️ Название не может быть пустым.",
|
|
193
|
+
"rename.success": "✅ Сессия переименована в: {title}",
|
|
194
|
+
"rename.error": "🔴 Не удалось переименовать сессию.",
|
|
195
|
+
"rename.cancelled": "❌ Переименование отменено.",
|
|
196
|
+
"rename.button.cancel": "❌ Отмена",
|
|
197
|
+
"cmd.description.rename": "Переименовать текущую сессию",
|
|
190
198
|
"cli.usage": "Использование:\n opencode-telegram [start] [--mode sources|installed]\n opencode-telegram status\n opencode-telegram stop\n opencode-telegram config\n\nЗаметки:\n - Без команды по умолчанию используется `start`\n - `--mode` сейчас поддерживается только для `start`",
|
|
191
199
|
"cli.placeholder.status": "Команда `status` пока работает как заглушка. Реальная проверка статуса появится на этапе service-слоя (Этап 5).",
|
|
192
200
|
"cli.placeholder.stop": "Команда `stop` пока работает как заглушка. Реальная остановка фонового процесса появится на этапе service-слоя (Этап 5).",
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { logger } from "../utils/logger.js";
|
|
2
|
+
class RenameManager {
|
|
3
|
+
state = {
|
|
4
|
+
isWaiting: false,
|
|
5
|
+
sessionId: null,
|
|
6
|
+
sessionDirectory: null,
|
|
7
|
+
currentTitle: null,
|
|
8
|
+
messageId: null,
|
|
9
|
+
};
|
|
10
|
+
startWaiting(sessionId, directory, currentTitle) {
|
|
11
|
+
logger.info(`[RenameManager] Starting rename flow for session: ${sessionId}`);
|
|
12
|
+
this.state = {
|
|
13
|
+
isWaiting: true,
|
|
14
|
+
sessionId,
|
|
15
|
+
sessionDirectory: directory,
|
|
16
|
+
currentTitle,
|
|
17
|
+
messageId: null,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
setMessageId(messageId) {
|
|
21
|
+
this.state.messageId = messageId;
|
|
22
|
+
}
|
|
23
|
+
getMessageId() {
|
|
24
|
+
return this.state.messageId;
|
|
25
|
+
}
|
|
26
|
+
isWaitingForName() {
|
|
27
|
+
return this.state.isWaiting;
|
|
28
|
+
}
|
|
29
|
+
getSessionInfo() {
|
|
30
|
+
if (!this.state.isWaiting || !this.state.sessionId) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
sessionId: this.state.sessionId,
|
|
35
|
+
directory: this.state.sessionDirectory,
|
|
36
|
+
currentTitle: this.state.currentTitle,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
clear() {
|
|
40
|
+
logger.debug("[RenameManager] Clearing rename state");
|
|
41
|
+
this.state = {
|
|
42
|
+
isWaiting: false,
|
|
43
|
+
sessionId: null,
|
|
44
|
+
sessionDirectory: null,
|
|
45
|
+
currentTitle: null,
|
|
46
|
+
messageId: null,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export const renameManager = new RenameManager();
|