opencode-telegram-bot 1.0.6 → 1.0.7

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 CHANGED
@@ -111,6 +111,8 @@ These are handled directly by the Telegram bot:
111
111
  | `/usage` | Show token and cost usage for this session |
112
112
  | `/help` | Show available commands |
113
113
 
114
+ The bot also registers these commands in Telegram's command menu (the `/` button), plus any OpenCode server commands discovered at startup (excluding hidden ones like `/init` and `/review`).
115
+
114
116
  ### Verbose Mode
115
117
 
116
118
  By default, the bot only shows the assistant's final text response. Use `/verbose` to toggle verbose mode (or `/verbose on|off` to set it explicitly), which also displays:
@@ -217,17 +219,14 @@ Bot: Your sessions:
217
219
  Current session: Fix the broken tests
218
220
  Tap a session to switch or delete.
219
221
 
220
- You: /switch def6
221
- Bot: Switched to session: Auth refactoring (def67890)
222
-
223
222
  You: What were we working on?
224
223
  Bot: [agent responds with context from the auth refactoring session]
225
224
 
226
- You: /delete abc1
227
- Bot: Deleted session: Fix the broken tests (abc12345)
225
+ You: [tap "Auth refactoring"]
226
+ Bot: Switched to session: Auth refactoring
228
227
  ```
229
228
 
230
- The `/sessions` list only shows sessions created by the Telegram bot, not sessions from other OpenCode clients (like the TUI). You cannot delete the currently active session -- use `/new` or `/switch` first.
229
+ The `/sessions` list only shows sessions created by the Telegram bot, not sessions from other OpenCode clients (like the TUI). You cannot delete the currently active session -- use `/new` first.
231
230
 
232
231
  ## Session Persistence
233
232
 
package/dist/app.d.ts CHANGED
@@ -49,6 +49,10 @@ interface TelegramBot {
49
49
  getFileLink: (fileId: string) => Promise<{
50
50
  toString(): string;
51
51
  }>;
52
+ setMyCommands: (commands: Array<{
53
+ command: string;
54
+ description: string;
55
+ }>) => Promise<unknown>;
52
56
  };
53
57
  }
54
58
  export declare function startTelegram(options: StartOptions): Promise<TelegramBot>;
package/dist/index.js CHANGED
@@ -18589,6 +18589,9 @@ async function startTelegram(options) {
18589
18589
  const client = options.client || client_createOpencodeClient({ baseUrl: url });
18590
18590
  // Verify connection to the OpenCode server and fetch available commands
18591
18591
  const opencodeCommands = new Set();
18592
+ const opencodeCommandMenu = [];
18593
+ const hiddenOpenCodeCommands = new Set(["init", "review", "reviews"]);
18594
+ const isHiddenOpenCodeCommand = (name) => hiddenOpenCodeCommands.has(name.toLowerCase());
18592
18595
  let projectDirectory = "";
18593
18596
  try {
18594
18597
  const sessions = await client.session.list();
@@ -18606,7 +18609,16 @@ async function startTelegram(options) {
18606
18609
  const cmds = await client.command.list();
18607
18610
  if (cmds.data) {
18608
18611
  for (const cmd of cmds.data) {
18609
- opencodeCommands.add(cmd.name);
18612
+ const name = cmd.name;
18613
+ if (!name)
18614
+ continue;
18615
+ opencodeCommands.add(name);
18616
+ if (!isHiddenOpenCodeCommand(name)) {
18617
+ opencodeCommandMenu.push({
18618
+ command: name,
18619
+ description: cmd.description || "OpenCode command",
18620
+ });
18621
+ }
18610
18622
  }
18611
18623
  console.log(`[Telegram] Available OpenCode commands: ${[...opencodeCommands].join(", ")}`);
18612
18624
  }
@@ -18618,6 +18630,17 @@ async function startTelegram(options) {
18618
18630
  const telegramCommands = new Set([
18619
18631
  "start", "help", "new", "sessions", "switch", "title", "delete", "export", "verbose", "model", "usage",
18620
18632
  ]);
18633
+ const telegramCommandMenu = [
18634
+ { command: "new", description: "Start a new conversation" },
18635
+ { command: "sessions", description: "List your sessions" },
18636
+ { command: "title", description: "Rename a session (/title <text>)" },
18637
+ { command: "export", description: "Export session (/export full for details)" },
18638
+ { command: "verbose", description: "Toggle verbose mode" },
18639
+ { command: "model", description: "Search models (/model <keyword>)" },
18640
+ { command: "usage", description: "Show token and cost usage" },
18641
+ { command: "help", description: "Show available commands" },
18642
+ ];
18643
+ const getVisibleOpenCodeCommands = () => [...opencodeCommands].filter((command) => !isHiddenOpenCodeCommand(command));
18621
18644
  // Map of chatId -> sessionId for the active session per chat
18622
18645
  const chatSessions = new Map();
18623
18646
  // Set of all session IDs ever created/used by this bot (for filtering)
@@ -19119,6 +19142,25 @@ async function startTelegram(options) {
19119
19142
  const bot = options.botFactory
19120
19143
  ? options.botFactory(token)
19121
19144
  : new lib.Telegraf(token);
19145
+ async function registerCommandMenu() {
19146
+ const combined = [...telegramCommandMenu, ...opencodeCommandMenu];
19147
+ const seen = new Set();
19148
+ const commands = [];
19149
+ for (const entry of combined) {
19150
+ if (!entry.command || seen.has(entry.command))
19151
+ continue;
19152
+ seen.add(entry.command);
19153
+ commands.push(entry);
19154
+ }
19155
+ if (commands.length === 0)
19156
+ return;
19157
+ try {
19158
+ await bot.telegram.setMyCommands(commands);
19159
+ }
19160
+ catch (err) {
19161
+ console.warn("[Telegram] Failed to register command menu:", err);
19162
+ }
19163
+ }
19122
19164
  // Middleware to check if the user is authorized
19123
19165
  bot.use((ctx, next) => {
19124
19166
  if (!authorizedUserId) {
@@ -19134,24 +19176,26 @@ async function startTelegram(options) {
19134
19176
  return;
19135
19177
  }
19136
19178
  });
19179
+ await registerCommandMenu();
19137
19180
  // Handle /start command
19138
19181
  bot.start((ctx) => {
19139
19182
  let msg = "Hello! I'm your OpenCode bot. Send me a message and I'll forward it to the OpenCode agent.\n\n" +
19140
19183
  "Bot commands:\n" +
19141
19184
  "/new - Start a new conversation\n" +
19142
- "/sessions - List your sessions\n" +
19185
+ "/sessions - List your sessions (buttons)\n" +
19143
19186
  "/title <text> - Rename the current session\n" +
19144
19187
  "/export - Export the current session as a markdown file\n" +
19145
19188
  "/export full - Export with all details (thinking, costs, steps)\n" +
19146
19189
  "/verbose - Toggle verbose mode (show thinking and tool calls)\n" +
19147
19190
  "/verbose on|off - Set verbose mode explicitly\n" +
19148
- "/model - Show or search available models\n" +
19191
+ "/model <keyword> - Search available models\n" +
19149
19192
  "/usage - Show token and cost usage for this session\n" +
19150
19193
  "/help - Show this help message\n";
19151
- if (opencodeCommands.size > 0) {
19194
+ const visibleOpenCodeCommands = getVisibleOpenCodeCommands();
19195
+ if (visibleOpenCodeCommands.length > 0) {
19152
19196
  msg +=
19153
19197
  "\nOpenCode commands are also available:\n" +
19154
- [...opencodeCommands].map((c) => `/${c}`).join(", ");
19198
+ visibleOpenCodeCommands.map((c) => `/${c}`).join(", ");
19155
19199
  }
19156
19200
  ctx.reply(msg);
19157
19201
  });
@@ -19160,19 +19204,20 @@ async function startTelegram(options) {
19160
19204
  let msg = "Send me any message and I'll process it using OpenCode.\n\n" +
19161
19205
  "Bot commands:\n" +
19162
19206
  "/new - Start a new conversation\n" +
19163
- "/sessions - List your sessions\n" +
19207
+ "/sessions - List your sessions (buttons)\n" +
19164
19208
  "/title <text> - Rename the current session\n" +
19165
19209
  "/export - Export the current session as a markdown file\n" +
19166
19210
  "/export full - Export with all details (thinking, costs, steps)\n" +
19167
19211
  "/verbose - Toggle verbose mode (show thinking and tool calls)\n" +
19168
19212
  "/verbose on|off - Set verbose mode explicitly\n" +
19169
- "/model - Show or search available models\n" +
19213
+ "/model <keyword> - Search available models\n" +
19170
19214
  "/usage - Show token and cost usage for this session\n" +
19171
19215
  "/help - Show this help message\n";
19172
- if (opencodeCommands.size > 0) {
19216
+ const visibleOpenCodeCommands = getVisibleOpenCodeCommands();
19217
+ if (visibleOpenCodeCommands.length > 0) {
19173
19218
  msg +=
19174
19219
  "\nOpenCode commands:\n" +
19175
- [...opencodeCommands].map((c) => `/${c}`).join(", ");
19220
+ visibleOpenCodeCommands.map((c) => `/${c}`).join(", ");
19176
19221
  }
19177
19222
  ctx.reply(msg);
19178
19223
  });
@@ -19842,7 +19887,9 @@ async function startTelegram(options) {
19842
19887
  }
19843
19888
  // Check if it's a known OpenCode command
19844
19889
  if (!opencodeCommands.has(commandName)) {
19845
- const available = [...opencodeCommands].map((c) => `/${c}`).join(", ");
19890
+ const available = getVisibleOpenCodeCommands()
19891
+ .map((c) => `/${c}`)
19892
+ .join(", ");
19846
19893
  await ctx.reply(`Unknown command: /${commandName}\n\n` +
19847
19894
  `Available OpenCode commands: ${available || "none"}\n` +
19848
19895
  `Bot commands: /new, /help`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-telegram-bot",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "type": "module",
5
5
  "description": "Telegram bot that forwards messages to an OpenCode agent",
6
6
  "main": "./dist/index.js",