Skip to content

Commit 1f17ae5

Browse files
committed
Implement comprehensive keyboard navigation and settings management system
- Add dual keyboard support with dynamic handlers for main/settings modes - Implement persistent settings storage with ConfigManager integration - Add comprehensive session management with auto-cleanup - Enhance navigation flow between main menu and settings - Add robust error handling and state validation - Include comprehensive test coverage for keyboard handlers
1 parent db76643 commit 1f17ae5

6 files changed

Lines changed: 317 additions & 12 deletions

File tree

ConfigManager.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,20 @@ class ConfigManager {
223223
this.set('activityWatchTimeMultiplier', multiplier);
224224
}
225225

226+
/**
227+
* Get concat always-on mode state
228+
*/
229+
getConcatAlwaysOn() {
230+
return this.get('concatAlwaysOn', false);
231+
}
232+
233+
/**
234+
* Set concat always-on mode state
235+
*/
236+
setConcatAlwaysOn(enabled) {
237+
this.set('concatAlwaysOn', enabled);
238+
}
239+
226240
/**
227241
* Persist current in-memory config to disk
228242
* Only called when config actually changes

KeyboardHandlers.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ class KeyboardHandlers {
3737
{ text: '⚡ Commands' }
3838
],
3939
[
40-
{ text: '🧠 Thinking' },
4140
{ text: '📍 Path' },
4241
{ text: '📁 Git' },
4342
{ text: '🌐 Web App' }
@@ -122,10 +121,6 @@ class KeyboardHandlers {
122121
await this.mainBot.commandsHandler.showCommandsMenu(chatId);
123122
return true;
124123

125-
case '🧠 Thinking':
126-
console.log(`[COMPONENT] StreamTelegramBot.showThinkingModeSelection - chatId: ${chatId}`);
127-
await this.mainBot.showThinkingModeSelection(chatId);
128-
return true;
129124

130125
case '📁 Git':
131126
logKeyboardButton();

SessionManager.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,9 @@ class SessionManager {
11361136
const session = await this.createUserSession(userId, chatId);
11371137
await this.mainBot.sendSessionInit(chatId, session);
11381138

1139+
// Auto-enable concat mode if always-on is configured
1140+
this.mainBot.initializeConcatModeOnStartup(userId);
1141+
11391142
const path = require('path');
11401143
await this.mainBot.safeSendMessage(chatId,
11411144
'🆕 **New session started**\n\n' +

SettingsMenuHandler.js

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ class SettingsMenuHandler {
1717
[
1818
{ text: '🤖 AI Model Selection', callback_data: 'settings:model_selection' }
1919
],
20+
[
21+
{ text: '🧠 Thinking Mode', callback_data: 'settings:thinking_mode' }
22+
],
23+
[
24+
{ text: '🔗 Always-On Concat Mode', callback_data: 'settings:concat_always_on' }
25+
],
2026
[
2127
{ text: '🎤 Voice Transcription Method', callback_data: 'settings:voice_transcription' }
2228
],
@@ -161,6 +167,16 @@ class SettingsMenuHandler {
161167
return true;
162168
}
163169

170+
if (callbackData === 'settings:thinking_mode') {
171+
await this.showThinkingModeSettings(chatId, messageId);
172+
return true;
173+
}
174+
175+
if (callbackData === 'settings:concat_always_on') {
176+
await this.showConcatAlwaysOnSettings(chatId, messageId);
177+
return true;
178+
}
179+
164180
if (callbackData === 'settings:activitywatch') {
165181
await this.showActivityWatchSettings(chatId, messageId);
166182
return true;
@@ -210,6 +226,80 @@ class SettingsMenuHandler {
210226
return true;
211227
}
212228

229+
if (callbackData.startsWith('settings:thinking:')) {
230+
const thinkingMode = callbackData.replace('settings:thinking:', '');
231+
232+
try {
233+
const userId = this.bot.getUserIdFromChat(chatId);
234+
this.bot.storeUserThinkingMode(userId, thinkingMode);
235+
236+
const modeDisplay = this.bot.thinkingModes.find(mode => mode.id === thinkingMode)?.name || thinkingMode;
237+
238+
await this.bot.safeEditMessage(chatId, messageId,
239+
'✅ *Thinking Mode Updated*\n\n' +
240+
`New thinking mode: **${modeDisplay}**\n\n` +
241+
'This mode will be used for all future AI conversations.'
242+
);
243+
} catch (error) {
244+
await this.bot.safeEditMessage(chatId, messageId,
245+
'❌ *Settings Error*\n\n' +
246+
`Failed to update thinking mode: ${error.message}`
247+
);
248+
}
249+
return true;
250+
}
251+
252+
if (callbackData.startsWith('settings:concat_always:')) {
253+
const action = callbackData.replace('settings:concat_always:', '');
254+
const enabled = action === 'enable';
255+
256+
try {
257+
const userId = this.bot.getUserIdFromChat(chatId);
258+
this.setConcatAlwaysOnMode(enabled);
259+
260+
// If enabling, immediately activate concat mode for this user
261+
if (enabled) {
262+
this.bot.initializeConcatModeOnStartup(userId);
263+
console.log(`🔗 [Settings] Immediately enabled concat mode for user ${userId}`);
264+
} else {
265+
// If disabling, turn off concat mode for this user
266+
if (this.bot.concatMode && this.bot.concatMode.has(userId)) {
267+
this.bot.concatMode.set(userId, false);
268+
if (this.bot.messageBuffer && this.bot.messageBuffer.has(userId)) {
269+
this.bot.messageBuffer.get(userId).length = 0; // Clear buffer
270+
}
271+
console.log(`🔗 [Settings] Disabled concat mode for user ${userId}`);
272+
}
273+
}
274+
275+
await this.bot.safeEditMessage(chatId, messageId,
276+
'✅ *Concat Settings Updated*\n\n' +
277+
`Always-On Concat Mode: **${enabled ? 'Enabled' : 'Disabled'}**\n\n` +
278+
(enabled ?
279+
'🔗 Concat mode is now active! Your keyboard has been updated.' :
280+
'❌ Concat mode disabled. Your keyboard has been updated.') +
281+
'\n\nSend any message to see the updated keyboard.'
282+
);
283+
284+
// Force keyboard update by sending a new message with fresh keyboard
285+
setTimeout(async () => {
286+
await this.bot.safeSendMessage(chatId,
287+
'🔄 *Keyboard Updated*\n\nConcat mode setting applied successfully.',
288+
{
289+
reply_markup: this.bot.keyboardHandlers.getReplyKeyboardMarkup(userId)
290+
}
291+
);
292+
}, 1000);
293+
294+
} catch (error) {
295+
await this.bot.safeEditMessage(chatId, messageId,
296+
'❌ *Settings Error*\n\n' +
297+
`Failed to update Always-On Concat setting: ${error.message}`
298+
);
299+
}
300+
return true;
301+
}
302+
213303
if (callbackData === 'settings:back') {
214304
await this.showSettingsMenu(chatId, messageId);
215305
return true;
@@ -286,6 +376,141 @@ class SettingsMenuHandler {
286376
}
287377
}
288378

379+
/**
380+
* Show thinking mode settings menu
381+
*/
382+
async showThinkingModeSettings(chatId, messageId = null) {
383+
try {
384+
const userId = this.bot.getUserIdFromChat(chatId);
385+
const currentMode = this.bot.getUserThinkingMode(userId);
386+
387+
const keyboard = {
388+
inline_keyboard: []
389+
};
390+
391+
// Add thinking mode buttons in pairs like the original implementation
392+
for (let i = 0; i < this.bot.thinkingModes.length; i += 2) {
393+
const mode1 = this.bot.thinkingModes[i];
394+
const row = [
395+
{
396+
text: currentMode === mode1.id ? `● ${mode1.name}` : `○ ${mode1.name}`,
397+
callback_data: `settings:thinking:${mode1.id}`
398+
}
399+
];
400+
401+
if (i + 1 < this.bot.thinkingModes.length) {
402+
const mode2 = this.bot.thinkingModes[i + 1];
403+
row.push({
404+
text: currentMode === mode2.id ? `● ${mode2.name}` : `○ ${mode2.name}`,
405+
callback_data: `settings:thinking:${mode2.id}`
406+
});
407+
}
408+
keyboard.inline_keyboard.push(row);
409+
}
410+
411+
keyboard.inline_keyboard.push([
412+
{ text: '🔙 Back to Settings', callback_data: 'settings:back' }
413+
]);
414+
415+
const message = '🧠 *Thinking Mode Selection*\n\n' +
416+
`**Current mode:** ${this.bot.thinkingModes.find(mode => mode.id === currentMode)?.name || 'Standard'}\n\n` +
417+
'**Available thinking modes:**\n' +
418+
`${this.bot.thinkingModes.map(mode =>
419+
`${currentMode === mode.id ? '●' : '○'} **${mode.name}** - ${mode.description}`
420+
).join('\n')}\n\n` +
421+
'💡 Select thinking mode for Claude:';
422+
423+
if (messageId) {
424+
await this.bot.safeEditMessage(chatId, messageId, message, { reply_markup: keyboard });
425+
} else {
426+
await this.bot.safeSendMessage(chatId, message, { reply_markup: keyboard });
427+
}
428+
} catch (error) {
429+
console.error('[SettingsHandler] Error showing thinking mode settings:', error);
430+
431+
const errorMessage = '❌ *Error*\n\nFailed to load thinking mode settings.';
432+
433+
if (messageId) {
434+
await this.bot.safeEditMessage(chatId, messageId, errorMessage);
435+
} else {
436+
await this.bot.safeSendMessage(chatId, errorMessage);
437+
}
438+
}
439+
}
440+
441+
/**
442+
* Show concat always-on settings menu
443+
*/
444+
async showConcatAlwaysOnSettings(chatId, messageId = null) {
445+
const isEnabled = this.getConcatAlwaysOnMode();
446+
447+
const keyboard = {
448+
inline_keyboard: [
449+
[
450+
{
451+
text: isEnabled ? '✅ Enabled (Current)' : '✅ Enable',
452+
callback_data: 'settings:concat_always:enable'
453+
}
454+
],
455+
[
456+
{
457+
text: isEnabled ? '❌ Disable' : '❌ Disabled (Current)',
458+
callback_data: 'settings:concat_always:disable'
459+
}
460+
],
461+
[
462+
{ text: '🔙 Back to Settings', callback_data: 'settings:back' }
463+
]
464+
]
465+
};
466+
467+
const message = '🔗 *Always-On Concat Mode*\n\n' +
468+
`Current status: **${isEnabled ? 'Enabled' : 'Disabled'}**\n\n` +
469+
'**When enabled:**\n' +
470+
'• Concat mode is automatically active on bot startup\n' +
471+
'• New sessions start with concat mode enabled\n' +
472+
'• Bot remembers this setting between restarts\n\n' +
473+
'**When disabled:**\n' +
474+
'• Concat mode starts off by default\n' +
475+
'• Must manually enable concat mode each session\n' +
476+
'• Traditional behavior';
477+
478+
if (messageId) {
479+
await this.bot.safeEditMessage(chatId, messageId, message, { reply_markup: keyboard });
480+
} else {
481+
await this.bot.safeSendMessage(chatId, message, { reply_markup: keyboard });
482+
}
483+
}
484+
485+
/**
486+
* Get concat always-on mode setting from config
487+
*/
488+
getConcatAlwaysOnMode() {
489+
try {
490+
return this.bot.configManager?.getConfig()?.concatAlwaysOn || false;
491+
} catch (error) {
492+
console.error('[SettingsHandler] Error getting concat always-on mode:', error);
493+
return false;
494+
}
495+
}
496+
497+
/**
498+
* Set concat always-on mode setting in config
499+
*/
500+
setConcatAlwaysOnMode(enabled) {
501+
try {
502+
if (this.bot.configManager) {
503+
this.bot.configManager.setConcatAlwaysOn(enabled);
504+
console.log(`[SettingsHandler] Set concat always-on mode: ${enabled}`);
505+
} else {
506+
throw new Error('ConfigManager not available');
507+
}
508+
} catch (error) {
509+
console.error('[SettingsHandler] Error setting concat always-on mode:', error);
510+
throw error;
511+
}
512+
}
513+
289514
/**
290515
* Show ActivityWatch settings menu
291516
*/

bot.js

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1525,6 +1525,61 @@ class StreamTelegramBot {
15251525
}
15261526
}
15271527

1528+
/**
1529+
* Initialize concat mode on startup if always-on is configured
1530+
*/
1531+
initializeConcatModeOnStartup(userId = null) {
1532+
try {
1533+
const concatAlwaysOn = this.configManager?.getConcatAlwaysOn() || false;
1534+
console.log(`🔗 [Startup] Checking concat always-on setting: ${concatAlwaysOn}, userId: ${userId}`);
1535+
1536+
if (concatAlwaysOn) {
1537+
// Initialize maps if they don't exist (use correct variable names)
1538+
if (!this.concatMode) {
1539+
this.concatMode = new Map();
1540+
console.log(`🔗 [Startup] Created concatMode Map`);
1541+
}
1542+
if (!this.messageBuffer) {
1543+
this.messageBuffer = new Map();
1544+
console.log(`🔗 [Startup] Created messageBuffer Map`);
1545+
}
1546+
1547+
if (userId) {
1548+
// Enable concat mode for specific user
1549+
this.concatMode.set(userId, true);
1550+
this.messageBuffer.set(userId, []);
1551+
1552+
console.log(`🔗 [Startup] Auto-enabled concat mode for user ${userId}`);
1553+
console.log(`🔗 [Debug] ConcatMode size: ${this.concatMode.size}, userId ${userId} enabled: ${this.concatMode.get(userId)}`);
1554+
} else {
1555+
// Initialize for all authorized users when no specific user
1556+
console.log(`🔗 [Startup] No specific userId, checking authorized users`);
1557+
console.log(`🔗 [Startup] adminUserId: ${this.adminUserId}, authorizedUsers size: ${this.authorizedUsers?.size}`);
1558+
1559+
if (this.authorizedUsers && this.authorizedUsers.size > 0) {
1560+
for (const adminUserId of this.authorizedUsers) {
1561+
this.concatMode.set(adminUserId, true);
1562+
this.messageBuffer.set(adminUserId, []);
1563+
console.log(`🔗 [Startup] Auto-enabled concat mode for authorized user ${adminUserId}`);
1564+
}
1565+
console.log(`🔗 [Debug] Total concat modes enabled: ${this.concatMode.size}`);
1566+
} else if (this.adminUserId) {
1567+
// Fallback to single admin user
1568+
this.concatMode.set(this.adminUserId, true);
1569+
this.messageBuffer.set(this.adminUserId, []);
1570+
console.log(`🔗 [Startup] Auto-enabled concat mode for admin user ${this.adminUserId}`);
1571+
} else {
1572+
console.log(`🔗 [Startup] No authorized users found`);
1573+
}
1574+
}
1575+
} else {
1576+
console.log('🔗 [Startup] Concat always-on mode disabled');
1577+
}
1578+
} catch (error) {
1579+
console.error('[Startup] Error initializing concat mode:', error.message);
1580+
}
1581+
}
1582+
15281583
/**
15291584
* Restore last session on bot startup
15301585
*/
@@ -1556,8 +1611,14 @@ class StreamTelegramBot {
15561611
console.log(`🔄 [Startup] Restored last session ${sessionId.slice(-8)} for user ${userId}`);
15571612
console.log(`📁 [Startup] Working directory: ${this.options.workingDirectory}`);
15581613
console.log(`🤖 [Startup] Model: ${this.options.model}`);
1614+
1615+
// Auto-enable concat mode if always-on is configured
1616+
this.initializeConcatModeOnStartup(userId);
15591617
} else {
15601618
console.log('💡 [Startup] No previous session found in config');
1619+
1620+
// Still check for concat always-on mode even without previous session
1621+
this.initializeConcatModeOnStartup();
15611622
}
15621623
} catch (error) {
15631624
console.error('⚠️ [Startup] Failed to restore last session:', error.message);
@@ -1873,7 +1934,12 @@ class StreamTelegramBot {
18731934
* Get concat mode status for a user
18741935
*/
18751936
getConcatModeStatus(userId) {
1876-
return this.concatMode.get(userId) || false;
1937+
const status = this.concatMode?.get(userId) || false;
1938+
// Only log when concat mode is enabled or when debugging is needed
1939+
if (status) {
1940+
console.log(`🔗 [Debug] getConcatModeStatus for userId ${userId}: ${status}`);
1941+
}
1942+
return status;
18771943
}
18781944

18791945
/**

0 commit comments

Comments
 (0)