Skip to content

Commit 2d382b8

Browse files
committed
bot: replace reply keyboard with /settings inline menu
1 parent 440fcc8 commit 2d382b8

48 files changed

Lines changed: 181 additions & 1203 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/attach/service.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { opencodeClient } from "../opencode/client.js";
33
import { stopEventListening } from "../opencode/events.js";
44
import { summaryAggregator } from "../summary/aggregator.js";
55
import { pinnedMessageManager } from "../pinned/manager.js";
6-
import { keyboardManager } from "../keyboard/manager.js";
76
import { questionManager } from "../question/manager.js";
87
import { permissionManager } from "../permission/manager.js";
98
import { showCurrentQuestion } from "../bot/handlers/question.js";
@@ -58,8 +57,6 @@ async function ensureAttachPinnedSession({
5857
pinnedMessageManager.initialize(api, chatId);
5958
}
6059

61-
keyboardManager.initialize(api, chatId);
62-
6360
const pinnedState = pinnedMessageManager.getState();
6461
if (pinnedState.sessionId === session.id && pinnedState.messageId) {
6562
return;
@@ -72,11 +69,6 @@ async function ensureAttachPinnedSession({
7269
}
7370

7471
await pinnedMessageManager.loadContextFromHistory(session.id, session.directory);
75-
76-
const contextInfo = pinnedMessageManager.getContextInfo();
77-
if (contextInfo) {
78-
keyboardManager.updateContext(contextInfo.tokensUsed, contextInfo.tokensLimit);
79-
}
8072
}
8173

8274
async function syncPinnedAttachState(): Promise<void> {

src/bot/commands/definitions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const COMMAND_DEFINITIONS: BotCommandI18nDefinition[] = [
5353
{ command: "skill_update", descriptionKey: "cmd.description.skill_update" },
5454
{ command: "skill_verify", descriptionKey: "cmd.description.skill_verify" },
5555
{ command: "skill_remove", descriptionKey: "cmd.description.skill_remove" },
56+
{ command: "settings", descriptionKey: "cmd.description.settings" },
5657
{ command: "help", descriptionKey: "cmd.description.help" },
5758
];
5859

src/bot/commands/new.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@ import { setCurrentSession, SessionInfo } from "../../session/manager.js";
55
import { ingestSessionInfoForCache } from "../../session/cache-manager.js";
66
import { getCurrentProject } from "../../settings/manager.js";
77
import { clearAllInteractionState } from "../../interaction/cleanup.js";
8-
import { keyboardManager } from "../../keyboard/manager.js";
9-
import { getStoredAgent, resolveProjectAgent } from "../../agent/manager.js";
10-
import { getStoredModel } from "../../model/manager.js";
11-
import { formatVariantForButton } from "../../variant/manager.js";
12-
import { createMainKeyboard } from "../utils/keyboard.js";
138
import { isForegroundBusy, replyBusyBlocked } from "../utils/busy-guard.js";
149
import { logger } from "../../utils/logger.js";
1510
import { t } from "../../i18n/index.js";
@@ -64,22 +59,7 @@ export async function newCommand(ctx: CommandContext<Context>, deps: NewCommandD
6459
ensureEventSubscription: deps.ensureEventSubscription,
6560
});
6661

67-
// Get current state for keyboard
68-
const currentAgent = await resolveProjectAgent(getStoredAgent());
69-
const currentModel = getStoredModel();
70-
keyboardManager.updateAgent(currentAgent);
71-
const contextInfo = keyboardManager.getContextInfo();
72-
const variantName = formatVariantForButton(currentModel.variant || "default");
73-
const keyboard = createMainKeyboard(
74-
currentAgent,
75-
currentModel,
76-
contextInfo ?? undefined,
77-
variantName,
78-
);
79-
80-
await ctx.reply(t("new.created", { title: session.title }), {
81-
reply_markup: keyboard,
82-
});
62+
await ctx.reply(t("new.created", { title: session.title }));
8363
} catch (error) {
8464
logger.error("[Bot] Error creating session:", error);
8565
await ctx.reply(t("new.create_error"));

src/bot/commands/open.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -359,16 +359,14 @@ async function selectDirectory(ctx: Context, directory: string) {
359359
await upsertSessionDirectory(directory, Date.now());
360360

361361
const projectInfo = await getProjectByWorktree(directory);
362-
const replyKeyboard = await switchToProject(
362+
await switchToProject(
363363
ctx,
364364
{ ...projectInfo, name: displayPath },
365365
"open_project_selected",
366366
);
367367

368368
await ctx.answerCallbackQuery();
369-
await ctx.reply(t("open.selected", { project: displayPath }), {
370-
reply_markup: replyKeyboard,
371-
});
369+
await ctx.reply(t("open.selected", { project: displayPath }));
372370

373371
// Clean up the inline menu message
374372
await ctx.deleteMessage();

src/bot/commands/projects.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,12 +287,10 @@ export async function handleProjectSelect(ctx: Context): Promise<boolean> {
287287

288288
logger.info(`[Bot] Project selected: ${projectName} (id: ${projectId})`);
289289

290-
const keyboard = await switchToProject(ctx, selectedProject, "project_switched");
290+
await switchToProject(ctx, selectedProject, "project_switched");
291291

292292
await ctx.answerCallbackQuery();
293-
await ctx.reply(t("projects.selected", { project: projectName }), {
294-
reply_markup: keyboard,
295-
});
293+
await ctx.reply(t("projects.selected", { project: projectName }));
296294

297295
await ctx.deleteMessage();
298296
} catch (error) {

src/bot/commands/sessions.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { resolveProjectAgent } from "../../agent/manager.js";
66
import { setCurrentSession, SessionInfo } from "../../session/manager.js";
77
import { getCurrentProject } from "../../settings/manager.js";
88
import { clearAllInteractionState } from "../../interaction/cleanup.js";
9-
import { keyboardManager } from "../../keyboard/manager.js";
109
import {
1110
appendInlineMenuCancelButton,
1211
ensureActiveInlineMenu,
@@ -296,14 +295,6 @@ export async function handleSessionSelect(ctx: Context, deps: SessionSelectDeps)
296295

297296
if (ctx.chat) {
298297
const chatId = ctx.chat.id;
299-
const currentAgent = await resolveProjectAgent();
300-
301-
keyboardManager.updateAgent(currentAgent);
302-
303-
const contextInfo = keyboardManager.getContextInfo();
304-
if (contextInfo) {
305-
keyboardManager.updateContext(contextInfo.tokensUsed, contextInfo.tokensLimit);
306-
}
307298

308299
// Delete loading message
309300
if (loadingMessageId) {
@@ -314,12 +305,9 @@ export async function handleSessionSelect(ctx: Context, deps: SessionSelectDeps)
314305
}
315306
}
316307

317-
// Send session selection confirmation with updated keyboard
318-
const keyboard = keyboardManager.getKeyboard();
308+
// Send session selection confirmation
319309
try {
320-
await ctx.api.sendMessage(chatId, t("sessions.selected", { title: session.title }), {
321-
reply_markup: keyboard,
322-
});
310+
await ctx.api.sendMessage(chatId, t("sessions.selected", { title: session.title }));
323311
} catch (err) {
324312
logger.error("[Sessions] Failed to send selection message:", err);
325313
}

src/bot/commands/settings.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { Context, InlineKeyboard } from "grammy";
2+
import {
3+
clearActiveInlineMenu,
4+
ensureActiveInlineMenu,
5+
replyWithInlineMenu,
6+
} from "../handlers/inline-menu.js";
7+
import { showAgentSelectionMenu } from "../handlers/agent.js";
8+
import { showModelSelectionMenu } from "../handlers/model.js";
9+
import { showVariantSelectionMenu } from "../handlers/variant.js";
10+
import { handleContextButtonPress } from "../handlers/context.js";
11+
import { logger } from "../../utils/logger.js";
12+
import { t } from "../../i18n/index.js";
13+
14+
export async function settingsCommand(ctx: Context): Promise<void> {
15+
const keyboard = new InlineKeyboard()
16+
.text(t("settings.button.agent"), "settings:agent")
17+
.row()
18+
.text(t("settings.button.model"), "settings:model")
19+
.row()
20+
.text(t("settings.button.variant"), "settings:variant")
21+
.row()
22+
.text(t("settings.button.context"), "settings:context");
23+
24+
await replyWithInlineMenu(ctx, {
25+
menuKind: "settings",
26+
text: t("settings.select"),
27+
keyboard,
28+
});
29+
}
30+
31+
export async function handleSettingsSelect(ctx: Context): Promise<boolean> {
32+
const callbackQuery = ctx.callbackQuery;
33+
if (!callbackQuery?.data || !callbackQuery.data.startsWith("settings:")) {
34+
return false;
35+
}
36+
37+
const isActiveMenu = await ensureActiveInlineMenu(ctx, "settings");
38+
if (!isActiveMenu) {
39+
return true;
40+
}
41+
42+
const action = callbackQuery.data.slice("settings:".length);
43+
44+
try {
45+
switch (action) {
46+
case "agent":
47+
clearActiveInlineMenu("settings_selected");
48+
await ctx.deleteMessage().catch(() => {});
49+
await ctx.answerCallbackQuery().catch(() => {});
50+
await showAgentSelectionMenu(ctx);
51+
break;
52+
case "model":
53+
clearActiveInlineMenu("settings_selected");
54+
await ctx.deleteMessage().catch(() => {});
55+
await ctx.answerCallbackQuery().catch(() => {});
56+
await showModelSelectionMenu(ctx);
57+
break;
58+
case "variant":
59+
clearActiveInlineMenu("settings_selected");
60+
await ctx.deleteMessage().catch(() => {});
61+
await ctx.answerCallbackQuery().catch(() => {});
62+
await showVariantSelectionMenu(ctx);
63+
break;
64+
case "context":
65+
clearActiveInlineMenu("settings_selected");
66+
await ctx.deleteMessage().catch(() => {});
67+
await ctx.answerCallbackQuery().catch(() => {});
68+
await handleContextButtonPress(ctx);
69+
break;
70+
default:
71+
await ctx.answerCallbackQuery({ text: t("callback.unknown_command") });
72+
return true;
73+
}
74+
75+
return true;
76+
} catch (err) {
77+
clearActiveInlineMenu("settings_error");
78+
logger.error("[Settings] Error handling settings select:", err);
79+
await ctx.answerCallbackQuery({ text: t("callback.processing_error") }).catch(() => {});
80+
return false;
81+
}
82+
}

src/bot/commands/start.ts

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import { Context } from "grammy";
2-
import { createMainKeyboard } from "../utils/keyboard.js";
3-
import { getStoredAgent } from "../../agent/manager.js";
4-
import { getStoredModel } from "../../model/manager.js";
5-
import { formatVariantForButton } from "../../variant/manager.js";
62
import { pinnedMessageManager } from "../../pinned/manager.js";
7-
import { keyboardManager } from "../../keyboard/manager.js";
83
import { clearSession } from "../../session/manager.js";
94
import { clearProject } from "../../settings/manager.js";
105
import { foregroundSessionState } from "../../scheduled-task/foreground-state.js";
@@ -18,7 +13,6 @@ export async function startCommand(ctx: Context): Promise<void> {
1813
if (!pinnedMessageManager.isInitialized()) {
1914
pinnedMessageManager.initialize(ctx.api, ctx.chat.id);
2015
}
21-
keyboardManager.initialize(ctx.api, ctx.chat.id);
2216
}
2317

2418
await abortCurrentOperation(ctx, { notifyUser: false });
@@ -28,35 +22,11 @@ export async function startCommand(ctx: Context): Promise<void> {
2822

2923
clearSession();
3024
clearProject();
31-
keyboardManager.clearContext();
3225
await pinnedMessageManager.clear();
3326

3427
if (pinnedMessageManager.getContextLimit() === 0) {
3528
await pinnedMessageManager.refreshContextLimit();
3629
}
3730

38-
// Get current agent, model, and context
39-
const currentAgent = getStoredAgent();
40-
const currentModel = getStoredModel();
41-
const variantName = formatVariantForButton(currentModel.variant || "default");
42-
const contextInfo =
43-
pinnedMessageManager.getContextInfo() ??
44-
(pinnedMessageManager.getContextLimit() > 0
45-
? { tokensUsed: 0, tokensLimit: pinnedMessageManager.getContextLimit() }
46-
: null);
47-
48-
keyboardManager.updateAgent(currentAgent);
49-
keyboardManager.updateModel(currentModel);
50-
if (contextInfo) {
51-
keyboardManager.updateContext(contextInfo.tokensUsed, contextInfo.tokensLimit);
52-
}
53-
54-
const keyboard = createMainKeyboard(
55-
currentAgent,
56-
currentModel,
57-
contextInfo ?? undefined,
58-
variantName,
59-
);
60-
61-
await ctx.reply(t("start.welcome"), { reply_markup: keyboard });
31+
await ctx.reply(t("start.welcome"));
6232
}

src/bot/commands/status.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { getCurrentProject, isTtsEnabled } from "../../settings/manager.js";
66
import { fetchCurrentAgent } from "../../agent/manager.js";
77
import { getAgentDisplayName } from "../../agent/types.js";
88
import { fetchCurrentModel } from "../../model/manager.js";
9-
import { keyboardManager } from "../../keyboard/manager.js";
109
import { pinnedMessageManager } from "../../pinned/manager.js";
1110
import { logger } from "../../utils/logger.js";
1211
import { t } from "../../i18n/index.js";
@@ -86,23 +85,14 @@ export async function statusCommand(ctx: CommandContext<Context>) {
8685
if (pinnedMessageManager.getContextLimit() === 0) {
8786
await pinnedMessageManager.refreshContextLimit();
8887
}
89-
keyboardManager.initialize(ctx.api, ctx.chat.id);
90-
}
91-
// Sync current context (tokens used + limit) into keyboard state
92-
const contextInfo = pinnedMessageManager.getContextInfo();
93-
if (contextInfo) {
94-
keyboardManager.updateContext(contextInfo.tokensUsed, contextInfo.tokensLimit);
95-
}
96-
const keyboard = keyboardManager.getKeyboard();
97-
if (ctx.chat) {
88+
9889
await sendBotText({
9990
api: ctx.api,
10091
chatId: ctx.chat.id,
10192
text: message,
102-
options: { reply_markup: keyboard },
10393
});
10494
} else {
105-
await ctx.reply(message, { reply_markup: keyboard });
95+
await ctx.reply(message);
10696
}
10797
} catch (error) {
10898
logger.error("[Bot] Error checking server status:", error);

0 commit comments

Comments
 (0)