Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5bc8c31a7f | ||
|
|
d1ce0f0bef | ||
|
|
ff9c1f1dcf |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -31,6 +31,7 @@ App_info/
|
||||
# IDE and editor files
|
||||
.idea/
|
||||
.vscode/
|
||||
.history/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
getViewTicketKeyboard,
|
||||
getMainMenuKeyboard,
|
||||
getCancelKeyboard,
|
||||
getReplyMarkupForChat,
|
||||
} from '../utils/keyboards';
|
||||
import { formatSats, formatDate, formatTimeUntil } from '../utils/format';
|
||||
import { PendingPurchaseData, AwaitingPaymentData } from '../types';
|
||||
@@ -71,7 +72,9 @@ export async function handleBuyCommand(
|
||||
if (!user.lightningAddress) {
|
||||
await bot.sendMessage(chatId, messages.address.needAddressFirst, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: getCancelKeyboard(),
|
||||
reply_markup: isGroupChat(chatId)
|
||||
? getReplyMarkupForChat(chatId)
|
||||
: getCancelKeyboard(),
|
||||
});
|
||||
await stateManager.updateUserState(userId, 'awaiting_lightning_address');
|
||||
return;
|
||||
@@ -81,7 +84,10 @@ export async function handleBuyCommand(
|
||||
const jackpot = await apiClient.getNextJackpot();
|
||||
|
||||
if (!jackpot) {
|
||||
await bot.sendMessage(chatId, messages.buy.noActiveJackpot, { parse_mode: 'Markdown' });
|
||||
await bot.sendMessage(chatId, messages.buy.noActiveJackpot, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: getReplyMarkupForChat(chatId),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -96,7 +102,9 @@ export async function handleBuyCommand(
|
||||
|
||||
await bot.sendMessage(chatId, message, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: getTicketAmountKeyboard(),
|
||||
reply_markup: isGroupChat(chatId)
|
||||
? getReplyMarkupForChat(chatId)
|
||||
: getTicketAmountKeyboard(),
|
||||
});
|
||||
|
||||
// Store jackpot info in state for later use
|
||||
@@ -195,7 +203,11 @@ export async function handleCustomTicketAmount(
|
||||
await bot.sendMessage(
|
||||
chatId,
|
||||
messages.buy.invalidNumber(config.bot.maxTicketsPerPurchase),
|
||||
{ reply_markup: getCancelKeyboard() }
|
||||
{
|
||||
reply_markup: isGroupChat(chatId)
|
||||
? getReplyMarkupForChat(chatId)
|
||||
: getCancelKeyboard(),
|
||||
}
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@@ -204,7 +216,11 @@ export async function handleCustomTicketAmount(
|
||||
await bot.sendMessage(
|
||||
chatId,
|
||||
messages.buy.tooManyTickets(config.bot.maxTicketsPerPurchase),
|
||||
{ reply_markup: getCancelKeyboard() }
|
||||
{
|
||||
reply_markup: isGroupChat(chatId)
|
||||
? getReplyMarkupForChat(chatId)
|
||||
: getCancelKeyboard(),
|
||||
}
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@@ -359,8 +375,7 @@ export async function handlePurchaseConfirmation(
|
||||
} catch (error) {
|
||||
logger.error('Error in handlePurchaseConfirmation', { error, userId });
|
||||
await bot.sendMessage(chatId, messages.errors.invoiceCreationFailed, {
|
||||
// Only show reply keyboard in private chats, not in groups
|
||||
...(isGroupChat(chatId) ? {} : { reply_markup: getMainMenuKeyboard() }),
|
||||
reply_markup: getReplyMarkupForChat(chatId, getMainMenuKeyboard()),
|
||||
});
|
||||
await stateManager.clearUserStateData(userId);
|
||||
}
|
||||
@@ -421,8 +436,7 @@ async function pollPaymentStatus(
|
||||
|
||||
await bot.sendMessage(chatId, messages.buy.invoiceExpired, {
|
||||
parse_mode: 'Markdown',
|
||||
// Only show reply keyboard in private chats, not in groups
|
||||
...(isGroupChat(chatId) ? {} : { reply_markup: getMainMenuKeyboard() }),
|
||||
reply_markup: getReplyMarkupForChat(chatId, getMainMenuKeyboard()),
|
||||
});
|
||||
await stateManager.clearUserStateData(userId);
|
||||
return;
|
||||
@@ -476,8 +490,7 @@ async function pollPaymentStatus(
|
||||
|
||||
await bot.sendMessage(chatId, messages.buy.invoiceExpiredShort, {
|
||||
parse_mode: 'Markdown',
|
||||
// Only show reply keyboard in private chats, not in groups
|
||||
...(isGroupChat(chatId) ? {} : { reply_markup: getMainMenuKeyboard() }),
|
||||
reply_markup: getReplyMarkupForChat(chatId, getMainMenuKeyboard()),
|
||||
});
|
||||
await stateManager.clearUserStateData(userId);
|
||||
return;
|
||||
|
||||
@@ -2,6 +2,7 @@ import TelegramBot from 'node-telegram-bot-api';
|
||||
import { groupStateManager } from '../services/groupState';
|
||||
import { logger, logUserAction } from '../services/logger';
|
||||
import { messages } from '../messages';
|
||||
import { getReplyMarkupForChat } from '../utils/keyboards';
|
||||
import {
|
||||
GroupSettings,
|
||||
REMINDER_PRESETS,
|
||||
@@ -52,6 +53,7 @@ export async function handleBotAddedToGroup(
|
||||
|
||||
await bot.sendMessage(chatId, messages.groups.welcome(chatTitle), {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: getReplyMarkupForChat(chatId),
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to register group', { error, chatId });
|
||||
@@ -97,7 +99,9 @@ export async function handleGroupSettings(
|
||||
// Check if user is admin
|
||||
const isAdmin = await isGroupAdmin(bot, chatId, userId);
|
||||
if (!isAdmin) {
|
||||
await bot.sendMessage(chatId, messages.groups.adminOnly);
|
||||
await bot.sendMessage(chatId, messages.groups.adminOnly, {
|
||||
reply_markup: getReplyMarkupForChat(chatId),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,7 +121,9 @@ export async function handleGroupSettings(
|
||||
|
||||
const currentSettings = settings || await groupStateManager.getGroup(chatId);
|
||||
if (!currentSettings) {
|
||||
await bot.sendMessage(chatId, messages.errors.generic);
|
||||
await bot.sendMessage(chatId, messages.errors.generic, {
|
||||
reply_markup: getReplyMarkupForChat(chatId),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -138,19 +144,27 @@ export async function handleGroupSettings(
|
||||
// Notify in the group that settings were sent to DM
|
||||
await bot.sendMessage(
|
||||
chatId,
|
||||
`⚙️ @${msg.from?.username || 'Admin'}, I've sent the group settings to your DMs!`
|
||||
`⚙️ @${msg.from?.username || 'Admin'}, I've sent the group settings to your DMs!`,
|
||||
{
|
||||
reply_markup: getReplyMarkupForChat(chatId),
|
||||
}
|
||||
);
|
||||
} catch (dmError) {
|
||||
// If DM fails (user hasn't started the bot), prompt them to start it
|
||||
logger.warn('Failed to send settings DM', { error: dmError, userId });
|
||||
await bot.sendMessage(
|
||||
chatId,
|
||||
`⚙️ @${msg.from?.username || 'Admin'}, please start a private chat with me first (@LightningLottoBot) so I can send you the settings.`
|
||||
`⚙️ @${msg.from?.username || 'Admin'}, please start a private chat with me first (@LightningLottoBot) so I can send you the settings.`,
|
||||
{
|
||||
reply_markup: getReplyMarkupForChat(chatId),
|
||||
}
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error in handleGroupSettings', { error, chatId });
|
||||
await bot.sendMessage(chatId, messages.errors.generic);
|
||||
await bot.sendMessage(chatId, messages.errors.generic, {
|
||||
reply_markup: getReplyMarkupForChat(chatId),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,7 +597,10 @@ export async function broadcastDrawAnnouncement(
|
||||
|
||||
for (const group of groups) {
|
||||
try {
|
||||
await bot.sendMessage(group.groupId, announcement, { parse_mode: 'Markdown' });
|
||||
await bot.sendMessage(group.groupId, announcement, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: getReplyMarkupForChat(group.groupId),
|
||||
});
|
||||
sent++;
|
||||
} catch (error) {
|
||||
logger.error('Failed to send announcement to group', {
|
||||
@@ -613,7 +630,10 @@ export async function broadcastDrawReminder(
|
||||
|
||||
for (const group of groups) {
|
||||
try {
|
||||
await bot.sendMessage(group.groupId, reminder, { parse_mode: 'Markdown' });
|
||||
await bot.sendMessage(group.groupId, reminder, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: getReplyMarkupForChat(group.groupId),
|
||||
});
|
||||
sent++;
|
||||
} catch (error) {
|
||||
logger.error('Failed to send reminder to group', {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import TelegramBot from 'node-telegram-bot-api';
|
||||
import { logUserAction } from '../services/logger';
|
||||
import { getMainMenuKeyboard } from '../utils/keyboards';
|
||||
import { getMainMenuKeyboard, getReplyMarkupForChat } from '../utils/keyboards';
|
||||
import { messages } from '../messages';
|
||||
|
||||
/**
|
||||
@@ -22,6 +22,7 @@ export async function handleHelpCommand(
|
||||
// Show group-specific help with admin commands
|
||||
await bot.sendMessage(chatId, messages.help.groupMessage, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: getReplyMarkupForChat(chatId),
|
||||
});
|
||||
} else {
|
||||
// Show user help in DM
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
handleGroupSettingsCallback,
|
||||
handleGroupRefresh,
|
||||
} from './handlers';
|
||||
import { getMainMenuKeyboard } from './utils/keyboards';
|
||||
import { getMainMenuKeyboard, getReplyMarkupForChat } from './utils/keyboards';
|
||||
import { messages } from './messages';
|
||||
import { formatSats, formatDate, formatTimeUntil } from './utils/format';
|
||||
|
||||
@@ -93,12 +93,15 @@ bot.on('message', async (msg) => {
|
||||
bot.onText(/\/start/, async (msg) => {
|
||||
if (!shouldProcessMessage(msg.message_id)) return;
|
||||
|
||||
// In groups, just show a welcome message
|
||||
// In groups, just show a welcome message (explicitly remove keyboard)
|
||||
if (isGroupChat(msg)) {
|
||||
await bot.sendMessage(
|
||||
msg.chat.id,
|
||||
`⚡ *Lightning Jackpot Bot*\n\nTo buy tickets and manage your account, message me directly!\n\nUse /jackpot to see current jackpot info.\nUse /lottohelp for commands.\nAdmins: Use /lottosettings to configure the bot.`,
|
||||
{ parse_mode: 'Markdown' }
|
||||
{
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: getReplyMarkupForChat(msg.chat.id),
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -116,6 +119,7 @@ bot.onText(/\/buyticket/, async (msg) => {
|
||||
if (settings && !settings.ticketPurchaseAllowed) {
|
||||
await bot.sendMessage(msg.chat.id, messages.groups.purchasesDisabled, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: getReplyMarkupForChat(msg.chat.id),
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -130,7 +134,9 @@ bot.onText(/\/tickets/, async (msg) => {
|
||||
|
||||
// Only in private chat
|
||||
if (isGroupChat(msg)) {
|
||||
await bot.sendMessage(msg.chat.id, '🧾 To view your tickets, message me directly!');
|
||||
await bot.sendMessage(msg.chat.id, '🧾 To view your tickets, message me directly!', {
|
||||
reply_markup: getReplyMarkupForChat(msg.chat.id),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -143,7 +149,9 @@ bot.onText(/\/wins/, async (msg) => {
|
||||
|
||||
// Only in private chat
|
||||
if (isGroupChat(msg)) {
|
||||
await bot.sendMessage(msg.chat.id, '🏆 To view your wins, message me directly!');
|
||||
await bot.sendMessage(msg.chat.id, '🏆 To view your wins, message me directly!', {
|
||||
reply_markup: getReplyMarkupForChat(msg.chat.id),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -156,7 +164,9 @@ bot.onText(/\/lottoaddress/, async (msg) => {
|
||||
|
||||
// Only in private chat
|
||||
if (isGroupChat(msg)) {
|
||||
await bot.sendMessage(msg.chat.id, '⚡ To update your Lightning Address, message me directly!');
|
||||
await bot.sendMessage(msg.chat.id, '⚡ To update your Lightning Address, message me directly!', {
|
||||
reply_markup: getReplyMarkupForChat(msg.chat.id),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -169,7 +179,9 @@ bot.onText(/\/lottomenu/, async (msg) => {
|
||||
|
||||
// Only in private chat
|
||||
if (isGroupChat(msg)) {
|
||||
await bot.sendMessage(msg.chat.id, '📱 To access the full menu, message me directly!');
|
||||
await bot.sendMessage(msg.chat.id, '📱 To access the full menu, message me directly!', {
|
||||
reply_markup: getReplyMarkupForChat(msg.chat.id),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -206,7 +218,10 @@ bot.onText(/\/jackpot/, async (msg) => {
|
||||
|
||||
Use /buyticket to get your tickets! 🍀`;
|
||||
|
||||
await bot.sendMessage(msg.chat.id, message, { parse_mode: 'Markdown' });
|
||||
await bot.sendMessage(msg.chat.id, message, {
|
||||
parse_mode: 'Markdown',
|
||||
reply_markup: getReplyMarkupForChat(msg.chat.id),
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Error in /jackpot command', { error });
|
||||
await bot.sendMessage(msg.chat.id, messages.errors.systemUnavailable);
|
||||
@@ -229,6 +244,7 @@ bot.on('message', async (msg) => {
|
||||
if (!shouldProcessMessage(msg.message_id)) return;
|
||||
|
||||
// Ignore group messages for button handling
|
||||
// Keyboards are explicitly removed in all group chat messages
|
||||
if (isGroupChat(msg)) return;
|
||||
|
||||
const text = msg.text.trim();
|
||||
|
||||
@@ -1,9 +1,33 @@
|
||||
import TelegramBot, {
|
||||
InlineKeyboardMarkup,
|
||||
ReplyKeyboardMarkup,
|
||||
ReplyKeyboardRemove,
|
||||
} from 'node-telegram-bot-api';
|
||||
import { NotificationPreferences } from '../types';
|
||||
|
||||
/**
|
||||
* Check if chat ID is a group chat (negative IDs are groups/supergroups)
|
||||
*/
|
||||
function isGroupChat(chatId: number): boolean {
|
||||
return chatId < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reply markup for a chat - removes keyboard in group chats, shows keyboard in private chats
|
||||
* This ensures keyboards are completely disabled in group chats
|
||||
*/
|
||||
export function getReplyMarkupForChat(
|
||||
chatId: number,
|
||||
keyboard?: ReplyKeyboardMarkup
|
||||
): ReplyKeyboardMarkup | ReplyKeyboardRemove | undefined {
|
||||
if (isGroupChat(chatId)) {
|
||||
// Explicitly remove keyboard in group chats
|
||||
return { remove_keyboard: true };
|
||||
}
|
||||
// Return the provided keyboard for private chats, or undefined if none provided
|
||||
return keyboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main menu reply keyboard
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user