feat: Add Telegram bot with group support

- Full Telegram bot implementation for Lightning Jackpot
- Commands: /start, /buy, /tickets, /wins, /address, /jackpot, /help
- Lightning invoice generation with QR codes
- Payment polling and confirmation notifications
- User state management (Redis/in-memory fallback)
- Group support with admin settings panel
- Configurable draw announcements and reminders
- Centralized messages for easy i18n
- Docker configuration included
This commit is contained in:
Michilis
2025-11-27 23:10:25 +00:00
parent d3bf8080b6
commit f743a6749c
29 changed files with 7616 additions and 1 deletions

View File

@@ -0,0 +1,444 @@
/**
* All Telegram bot messages centralized for easy management and future i18n support
*/
export const messages = {
// ═══════════════════════════════════════════════════════════════════════════
// ERRORS
// ═══════════════════════════════════════════════════════════════════════════
errors: {
userNotIdentified: '❌ Could not identify user.',
startFirst: '❌ Please start the bot first with /start',
generic: '❌ An error occurred. Please try again.',
startAgain: '❌ An error occurred. Please try again with /start',
systemUnavailable: '❌ The lottery system is temporarily unavailable. Please try again soon.',
invoiceCreationFailed: '❌ Failed to create invoice. Please try again.',
fetchTicketsFailed: '❌ Failed to fetch tickets. Please try again.',
fetchWinsFailed: '❌ Failed to fetch wins. Please try again.',
ticketNotFound: '❌ Ticket not found.',
fetchTicketDetailsFailed: '❌ Failed to fetch ticket details.',
checkStatusFailed: '❌ Failed to check status',
noPendingPurchase: '❌ No pending purchase. Please start again with /buy',
setAddressFirst: '❌ Please set your Lightning Address first.',
},
// ═══════════════════════════════════════════════════════════════════════════
// START / ONBOARDING
// ═══════════════════════════════════════════════════════════════════════════
start: {
welcome: `⚡🎉 *Welcome to Lightning Jackpot!* 🎉⚡
You can buy Bitcoin Lightning lottery tickets, and if you win, your prize is paid instantly to your Lightning Address!
🎯 *How it works:*
1⃣ Set your Lightning Address (where you'll receive winnings)
2⃣ Buy tickets with Lightning
3⃣ Wait for the draw
4⃣ If you win, prize is sent instantly!`,
needAddress: `Before you can play, I need your Lightning Address to send any winnings.
*Example:* \`yourname@getalby.com\`
Please send your Lightning Address now:`,
addressSet: (address: string) =>
`✅ Your payout address is set to: \`${address}\`
Use the menu below to get started! Good luck! 🍀`,
welcomeUnknown: '👋 Welcome! Please use /start to begin.',
},
// ═══════════════════════════════════════════════════════════════════════════
// LIGHTNING ADDRESS
// ═══════════════════════════════════════════════════════════════════════════
address: {
currentAddress: (address: string) =>
`⚡ *Your Current Payout Address:*
\`${address}\`
Send me your new Lightning Address to update it:`,
noAddressSet: `⚡ You don't have a Lightning Address set yet.
Send me your Lightning Address now:
*Example:* \`yourname@getalby.com\``,
invalidFormat: `❌ That doesn't look like a valid Lightning Address.
*Format:* \`username@domain.com\`
*Example:* \`satoshi@getalby.com\`
Please try again:`,
firstTimeSuccess: (address: string) =>
`✅ *Perfect!* I'll use \`${address}\` to send any winnings.
You're all set! Use the menu below to buy tickets and check your results. Good luck! 🍀`,
updateSuccess: (address: string) =>
`✅ *Lightning Address updated!*
New address: \`${address}\`
⚠️ *Note:* Previous ticket purchases will still use their original addresses.`,
saveFailed: '❌ An error occurred saving your address. Please try again.',
needAddressFirst: `❌ I don't have your Lightning Address yet!
Please send your Lightning Address first:
*Example:* \`yourname@getalby.com\``,
},
// ═══════════════════════════════════════════════════════════════════════════
// BUY TICKETS
// ═══════════════════════════════════════════════════════════════════════════
buy: {
noActiveJackpot: `😔 *No Active Jackpot*
There's no lottery cycle available right now. Please check back soon!`,
jackpotInfo: (
potSats: string,
ticketPrice: string,
drawTime: string,
timeLeft: string
) =>
`🎰 *Next Jackpot Info*
💰 *Prize Pool:* ${potSats} sats
🎟 *Ticket Price:* ${ticketPrice} sats each
⏰ *Draw at:* ${drawTime}
⏳ *Time left:* ${timeLeft}
How many tickets do you want to buy?`,
customAmountPrompt: (maxTickets: number) =>
`🔢 *Custom Amount*
Enter the number of tickets you want to buy (1-${maxTickets}):`,
invalidNumber: (maxTickets: number) =>
`❌ Please enter a valid number (1-${maxTickets}):`,
tooManyTickets: (maxTickets: number) =>
`❌ Maximum ${maxTickets} tickets per purchase.
Please enter a smaller number:`,
confirmPurchase: (
ticketCount: number,
ticketPrice: string,
totalAmount: string,
drawTime: string
) =>
`📋 *Confirm Purchase*
🎟 *Tickets:* ${ticketCount}
💰 *Price:* ${ticketPrice} sats each
💵 *Total:* ${totalAmount} sats
⏰ *Draw:* ${drawTime}
Confirm this purchase?`,
creatingInvoice: 'Creating invoice...',
invoiceCreated: '💸 *Pay this Lightning invoice to complete your purchase:*',
invoiceCaption: (
ticketCount: number,
totalAmount: string,
paymentRequest: string,
expiryMinutes: number
) =>
`🎟 *${ticketCount} ticket${ticketCount > 1 ? 's' : ''}*
💰 *Amount:* ${totalAmount} sats
\`${paymentRequest}\`
⏳ This invoice expires in ${expiryMinutes} minutes.
I'll notify you when payment is received!`,
paymentReceived: (ticketNumbers: string, drawTime: string) =>
`🎉 *Payment Received!*
Your tickets have been issued!
*Your Ticket Numbers:*
${ticketNumbers}
*Draw Time:* ${drawTime}
Good luck! 🍀 I'll notify you after the draw!`,
invoiceExpired: `❌ *Invoice Expired*
No payment was received in time. No tickets were issued.
Use /buy to try again.`,
invoiceExpiredShort: `❌ *Invoice Expired*
This invoice has expired. No tickets were issued.
Use /buy to try again.`,
jackpotUnavailable: '❌ Jackpot is no longer available.',
},
// ═══════════════════════════════════════════════════════════════════════════
// TICKETS
// ═══════════════════════════════════════════════════════════════════════════
tickets: {
header: `🧾 *Your Recent Purchases*\n\n`,
empty: `🧾 *Your Tickets*
You haven't purchased any tickets yet!
Use /buy to get started! 🎟`,
notFound: `🧾 *Your Tickets*
No ticket purchases found. Purchase history may have expired.
Use /buy to get new tickets! 🎟`,
tapForDetails: `\nTap a ticket below for details:`,
statusPending: 'Pending',
statusExpired: 'Expired',
statusActive: 'Active',
statusWon: 'Won!',
statusLost: 'Lost',
// Ticket detail view
detailHeader: '🎫 *Ticket Details*',
detailAwaitingPayment: '⏳ *Status:* Awaiting Payment',
detailExpired: '❌ *Status:* Invoice Expired (No tickets issued)',
detailActive: '🎟 *Status:* Active - Draw Pending',
detailWinner: (prizeSats: string, payoutStatus: string) =>
`🏆 *Status:* WINNER!
💰 *Prize:* ${prizeSats} sats
📤 *Payout:* ${payoutStatus}`,
detailLost: (winningTicketId: string) =>
`😔 *Status:* Did not win this round
🎯 *Winning Ticket:* #${winningTicketId}`,
detailFormat: (
purchaseId: string,
ticketCount: number,
ticketNumbers: string,
amountSats: string,
drawDate: string,
statusSection: string
) =>
`🎫 *Ticket Details*
📋 *Purchase ID:* \`${purchaseId}...\`
🎟 *Tickets:* ${ticketCount}
🔢 *Numbers:* ${ticketNumbers}
💰 *Amount Paid:* ${amountSats} sats
📅 *Draw:* ${drawDate}
${statusSection}`,
// Status check responses
checkStatusPending: '⏳ Still waiting for payment...',
checkStatusConfirmed: '✅ Payment confirmed! Tickets issued. Waiting for draw...',
checkStatusWon: '🏆 YOU WON! Check your Lightning wallet!',
checkStatusLost: '😔 Draw completed. Better luck next time!',
checkStatusExpired: '❌ Invoice expired. No tickets were issued.',
checkStatusProcessing: '⏳ Processing...',
},
// ═══════════════════════════════════════════════════════════════════════════
// WINS
// ═══════════════════════════════════════════════════════════════════════════
wins: {
empty: `🏆 *Your Wins*
You haven't purchased any tickets yet, so no wins to show!
Use /buy to get started! 🎟`,
noWinsYet: `🏆 *Your Wins*
You haven't won any jackpots yet. Keep playing!
Use /buy to get more tickets! 🎟🍀`,
header: (totalWinnings: string, paidWinnings: string) =>
`🏆 *Your Wins*
💰 *Total Winnings:* ${totalWinnings} sats
✅ *Paid:* ${paidWinnings} sats
*Win History:*
`,
},
// ═══════════════════════════════════════════════════════════════════════════
// HELP
// ═══════════════════════════════════════════════════════════════════════════
help: {
message: `⚡🎰 *Lightning Jackpot Bot* 🎰⚡
This is the Lightning Jackpot lottery bot! Buy tickets with Bitcoin Lightning, and if you win, your prize is paid instantly!
*How It Works:*
1⃣ Set your Lightning Address (where winnings go)
2⃣ Buy tickets using the menu
3⃣ Pay the Lightning invoice
4⃣ Wait for the draw
5⃣ If you win, sats are sent to your address instantly!
*Commands:*
• /buy — Buy lottery tickets
• /tickets — View your tickets
• /wins — View your past wins
• /address — Update Lightning Address
• /menu — Show main menu
• /help — Show this help
*Tips:*
🎟 Each ticket is one chance to win
💰 Prize pool grows with each ticket sold
⚡ Winnings are paid instantly via Lightning
🔔 You'll be notified after every draw
Good luck! 🍀`,
},
// ═══════════════════════════════════════════════════════════════════════════
// MENU
// ═══════════════════════════════════════════════════════════════════════════
menu: {
header: `🎰 *Lightning Jackpot Menu*
What would you like to do?`,
cancelled: '❌ Cancelled.',
whatToDo: 'What would you like to do?',
didNotUnderstand:
"I didn't understand that. Use the menu below or type /help for available commands.",
},
// ═══════════════════════════════════════════════════════════════════════════
// DRAW NOTIFICATIONS
// ═══════════════════════════════════════════════════════════════════════════
notifications: {
winner: (
prizeSats: string,
winningTicket: string,
payoutStatus: string
) =>
`🎉 *YOU WON THE LIGHTNING JACKPOT!* 🎉
💰 *Prize:* ${prizeSats} sats
🎟 *Winning Ticket:* #${winningTicket}
📤 *Payout Status:* ${payoutStatus}
Congratulations! 🥳`,
loser: (winningTicket: string, prizeSats: string) =>
`The draw has finished!
Your tickets did not win this time.
🎟 *Winning Ticket:* #${winningTicket}
💰 *Prize:* ${prizeSats} sats
Good luck next round! 🍀`,
drawAnnouncement: (
winnerName: string,
winningTicket: string,
prizeSats: string,
totalTickets: number
) =>
`🎰 *JACKPOT DRAW COMPLETE!* 🎰
🏆 *Winner:* ${winnerName}
🎟 *Winning Ticket:* #${winningTicket}
💰 *Prize:* ${prizeSats} sats
📊 *Total Tickets:* ${totalTickets}
Congratulations to the winner! ⚡
Use /buy to enter the next draw! 🍀`,
drawReminder: (potSats: string, drawTime: string, timeLeft: string) =>
`⏰ *Draw Reminder!*
🎰 The next Lightning Jackpot draw is coming up!
💰 *Current Prize Pool:* ${potSats} sats
🕐 *Draw Time:* ${drawTime}
⏳ *Time Left:* ${timeLeft}
Don't miss your chance to win! Use /buy to get your tickets! 🎟`,
},
// ═══════════════════════════════════════════════════════════════════════════
// GROUPS
// ═══════════════════════════════════════════════════════════════════════════
groups: {
welcome: (groupName: string) =>
`⚡🎰 *Lightning Jackpot Bot Added!* 🎰⚡
Hello *${groupName}*! I'm the Lightning Jackpot lottery bot.
I can announce lottery draws and remind you when jackpots are coming up!
*Group Admin Commands:*
• /settings — Configure bot settings for this group
*User Commands:*
• /buy — Buy lottery tickets (in DM)
• /jackpot — View current jackpot info
• /help — Get help
To buy tickets, message me directly @LightningLottoBot! 🎟`,
privateChat: '❌ This command only works in groups. Use /menu to see available commands.',
adminOnly: '⚠️ Only group administrators can change these settings.',
settingsOverview: (settings: {
groupTitle: string;
enabled: boolean;
drawAnnouncements: boolean;
reminders: boolean;
ticketPurchaseAllowed: boolean;
}) =>
`⚙️ *Group Settings*
📍 *Group:* ${settings.groupTitle}
*Current Configuration:*
${settings.enabled ? '✅' : '❌'} Bot Enabled
${settings.drawAnnouncements ? '✅' : '❌'} Draw Announcements
${settings.reminders ? '✅' : '❌'} Draw Reminders
${settings.ticketPurchaseAllowed ? '✅' : '❌'} Ticket Purchases in Group
Tap a button below to toggle settings:`,
settingUpdated: (setting: string, enabled: boolean) =>
`✅ *${setting}* has been ${enabled ? 'enabled' : 'disabled'}.`,
botDisabled: 'The Lightning Jackpot bot is currently disabled for this group.',
purchasesDisabled: `🎟 Ticket purchases are disabled in this group for privacy.
Please message me directly to buy tickets: @LightningLottoBot`,
},
};
export default messages;