- /lottosettings now opens settings in private DM instead of group - Bot only reacts to / commands in groups (keyboard buttons ignored) - Reply keyboard buttons removed from group messages - Default display name now uses @username instead of 'Anon' - Users can still manually update display name in settings - Updated all display name usages to use centralized getDisplayName()
174 lines
5.0 KiB
TypeScript
174 lines
5.0 KiB
TypeScript
import TelegramBot from 'node-telegram-bot-api';
|
|
import { stateManager } from '../services/state';
|
|
import { logger, logUserAction } from '../services/logger';
|
|
import { messages } from '../messages';
|
|
import { getMainMenuKeyboard, getSettingsKeyboard } from '../utils/keyboards';
|
|
import { NotificationPreferences, DEFAULT_NOTIFICATIONS } from '../types';
|
|
|
|
/**
|
|
* Handle /settings command (private chat only)
|
|
*/
|
|
export async function handleSettingsCommand(
|
|
bot: TelegramBot,
|
|
msg: TelegramBot.Message
|
|
): Promise<void> {
|
|
const chatId = msg.chat.id;
|
|
const userId = msg.from?.id;
|
|
|
|
if (!userId) return;
|
|
|
|
// Only works in private chats
|
|
if (msg.chat.type !== 'private') {
|
|
await bot.sendMessage(chatId, '❌ This command only works in private chat. Message me directly!');
|
|
return;
|
|
}
|
|
|
|
logUserAction(userId, 'Viewed settings');
|
|
|
|
const user = await stateManager.getUser(userId);
|
|
if (!user) {
|
|
await bot.sendMessage(chatId, messages.errors.startFirst, {
|
|
reply_markup: getMainMenuKeyboard(),
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Ensure notifications object exists
|
|
const notifications = user.notifications || { ...DEFAULT_NOTIFICATIONS };
|
|
const displayName = stateManager.getDisplayName(user);
|
|
|
|
await bot.sendMessage(
|
|
chatId,
|
|
messages.settings.overview(displayName, notifications),
|
|
{
|
|
parse_mode: 'Markdown',
|
|
reply_markup: getSettingsKeyboard(displayName, notifications),
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Handle settings callback
|
|
*/
|
|
export async function handleSettingsCallback(
|
|
bot: TelegramBot,
|
|
query: TelegramBot.CallbackQuery,
|
|
action: string
|
|
): Promise<void> {
|
|
const chatId = query.message?.chat.id;
|
|
const userId = query.from.id;
|
|
const messageId = query.message?.message_id;
|
|
|
|
if (!chatId || !messageId) return;
|
|
|
|
const user = await stateManager.getUser(userId);
|
|
if (!user) {
|
|
await bot.answerCallbackQuery(query.id, { text: 'Please /start first' });
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Handle notification toggles
|
|
if (action.startsWith('toggle_notif_')) {
|
|
const setting = action.replace('toggle_notif_', '') as keyof NotificationPreferences;
|
|
const currentNotifications = user.notifications || { ...DEFAULT_NOTIFICATIONS };
|
|
const newValue = !currentNotifications[setting];
|
|
|
|
const updatedUser = await stateManager.updateNotifications(userId, { [setting]: newValue });
|
|
|
|
if (updatedUser) {
|
|
logUserAction(userId, 'Updated notification setting', { setting, newValue });
|
|
await bot.answerCallbackQuery(query.id, {
|
|
text: `${setting} ${newValue ? 'enabled' : 'disabled'}`,
|
|
});
|
|
|
|
// Update message
|
|
const displayName = stateManager.getDisplayName(updatedUser);
|
|
await bot.editMessageText(
|
|
messages.settings.overview(displayName, updatedUser.notifications),
|
|
{
|
|
chat_id: chatId,
|
|
message_id: messageId,
|
|
parse_mode: 'Markdown',
|
|
reply_markup: getSettingsKeyboard(displayName, updatedUser.notifications),
|
|
}
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Handle display name change
|
|
if (action === 'change_name') {
|
|
await bot.answerCallbackQuery(query.id);
|
|
await stateManager.updateUserState(userId, 'awaiting_display_name');
|
|
await bot.sendMessage(
|
|
chatId,
|
|
messages.settings.enterDisplayName,
|
|
{ parse_mode: 'Markdown' }
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Handle back to menu
|
|
if (action === 'back_menu') {
|
|
await bot.answerCallbackQuery(query.id);
|
|
await bot.deleteMessage(chatId, messageId);
|
|
await bot.sendMessage(chatId, messages.menu.header, {
|
|
parse_mode: 'Markdown',
|
|
reply_markup: getMainMenuKeyboard(),
|
|
});
|
|
return;
|
|
}
|
|
|
|
await bot.answerCallbackQuery(query.id);
|
|
} catch (error) {
|
|
logger.error('Error in handleSettingsCallback', { error, userId, action });
|
|
await bot.answerCallbackQuery(query.id, { text: 'Error updating settings' });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle display name input
|
|
*/
|
|
export async function handleDisplayNameInput(
|
|
bot: TelegramBot,
|
|
msg: TelegramBot.Message
|
|
): Promise<void> {
|
|
const chatId = msg.chat.id;
|
|
const userId = msg.from?.id;
|
|
const text = msg.text?.trim();
|
|
|
|
if (!userId || !text) return;
|
|
|
|
// Validate display name (max 20 chars, alphanumeric + spaces + some symbols)
|
|
if (text.length > 20) {
|
|
await bot.sendMessage(chatId, messages.settings.nameTooLong);
|
|
return;
|
|
}
|
|
|
|
// Clean the display name (allow @ for usernames)
|
|
const cleanName = text.replace(/[^\w\s\-_.@]/g, '').trim() || 'Anon';
|
|
|
|
await stateManager.updateDisplayName(userId, cleanName);
|
|
logUserAction(userId, 'Set display name', { displayName: cleanName });
|
|
|
|
const user = await stateManager.getUser(userId);
|
|
const notifications = user?.notifications || { ...DEFAULT_NOTIFICATIONS };
|
|
|
|
await bot.sendMessage(
|
|
chatId,
|
|
messages.settings.nameUpdated(cleanName),
|
|
{
|
|
parse_mode: 'Markdown',
|
|
reply_markup: getSettingsKeyboard(cleanName, notifications),
|
|
}
|
|
);
|
|
}
|
|
|
|
export default {
|
|
handleSettingsCommand,
|
|
handleSettingsCallback,
|
|
handleDisplayNameInput,
|
|
};
|
|
|