Add SQLite database for Telegram bot user/group settings
- Replace Redis/in-memory storage with SQLite for persistence - Add database.ts service with tables for users, groups, purchases, participants - Update state.ts and groupState.ts to use SQLite backend - Fix buyer_name to use display name instead of Telegram ID - Remove legacy reminder array handlers (now using 3-slot system) - Add better-sqlite3 dependency, remove ioredis - Update env.example with BOT_DATABASE_PATH option - Add data/ directory to .gitignore for database files
This commit is contained in:
@@ -18,7 +18,7 @@ export const messages = {
|
||||
ticketNotFound: '❌ Ticket not found.',
|
||||
fetchTicketDetailsFailed: '❌ Failed to fetch ticket details.',
|
||||
checkStatusFailed: '❌ Failed to check status',
|
||||
noPendingPurchase: '❌ No pending purchase. Please start again with /buy',
|
||||
noPendingPurchase: '❌ No pending purchase. Please start again with /buyticket',
|
||||
setAddressFirst: '❌ Please set your Lightning Address first.',
|
||||
},
|
||||
|
||||
@@ -42,6 +42,21 @@ You can buy Bitcoin Lightning lottery tickets, and if you win, your prize is pai
|
||||
|
||||
Please send your Lightning Address now:`,
|
||||
|
||||
needAddressWithOptions: (username?: string) => {
|
||||
let msg = `Before you can play, I need your Lightning Address to send any winnings.\n\n`;
|
||||
|
||||
if (username) {
|
||||
msg += `🤖 *Quick Setup* - Choose a tip bot below:\n\n`;
|
||||
msg += `Or send me your Lightning Address to set a custom one.\n`;
|
||||
msg += `*Example:* \`yourname@getalby.com\``;
|
||||
} else {
|
||||
msg += `*Example:* \`yourname@getalby.com\`\n\n`;
|
||||
msg += `Please send your Lightning Address now:`;
|
||||
}
|
||||
|
||||
return msg;
|
||||
},
|
||||
|
||||
addressSet: (address: string) =>
|
||||
`✅ Your payout address is set to: \`${address}\`
|
||||
|
||||
@@ -60,12 +75,40 @@ Use the menu below to get started! Good luck! 🍀`,
|
||||
|
||||
Send me your new Lightning Address to update it:`,
|
||||
|
||||
currentAddressWithOptions: (address: string, username?: string) => {
|
||||
let msg = `⚡ *Your Current Payout Address:*\n\`${address}\`\n\n`;
|
||||
|
||||
if (username) {
|
||||
msg += `🤖 *Quick Options* - Choose a tip bot below:\n\n`;
|
||||
msg += `Or send me your Lightning Address to set a custom one.`;
|
||||
} else {
|
||||
msg += `Send me your new Lightning Address to update it:`;
|
||||
}
|
||||
|
||||
return msg;
|
||||
},
|
||||
|
||||
noAddressSet: `⚡ You don't have a Lightning Address set yet.
|
||||
|
||||
Send me your Lightning Address now:
|
||||
|
||||
*Example:* \`yourname@getalby.com\``,
|
||||
|
||||
noAddressSetWithOptions: (username?: string) => {
|
||||
let msg = `⚡ You don't have a Lightning Address set yet.\n\n`;
|
||||
|
||||
if (username) {
|
||||
msg += `🤖 *Quick Setup* - Choose a tip bot below:\n\n`;
|
||||
msg += `Or send me your Lightning Address to set a custom one.\n`;
|
||||
msg += `*Example:* \`yourname@getalby.com\``;
|
||||
} else {
|
||||
msg += `Send me your Lightning Address now:\n\n`;
|
||||
msg += `*Example:* \`yourname@getalby.com\``;
|
||||
}
|
||||
|
||||
return msg;
|
||||
},
|
||||
|
||||
invalidFormat: `❌ That doesn't look like a valid Lightning Address.
|
||||
|
||||
*Format:* \`username@domain.com\`
|
||||
@@ -73,6 +116,31 @@ Send me your Lightning Address now:
|
||||
|
||||
Please try again:`,
|
||||
|
||||
verifying: '🔍 Verifying your Lightning Address...',
|
||||
|
||||
verificationFailed: (address: string, error?: string) =>
|
||||
`❌ *Could not verify Lightning Address*
|
||||
|
||||
Address: \`${address}\`
|
||||
${error ? `Error: ${error}\n` : ''}
|
||||
Please check the address and try again, or choose a different option:`,
|
||||
|
||||
verifyingService: (service: string, address: string) =>
|
||||
`🔍 Verifying your ${service} address: \`${address}\`...`,
|
||||
|
||||
serviceNotSetup: (service: string, error?: string) =>
|
||||
`❌ *${service} address not found*
|
||||
|
||||
It looks like you haven't set up ${service} yet, or your username doesn't match.
|
||||
|
||||
${error ? `Error: ${error}\n\n` : ''}Please set up ${service} first, or enter a different Lightning Address:`,
|
||||
|
||||
noUsername: `❌ You don't have a Telegram username set!
|
||||
|
||||
To use 21Tipbot or Bittip, you need a Telegram username.
|
||||
|
||||
Please set a username in Telegram settings, or enter a custom Lightning Address:`,
|
||||
|
||||
firstTimeSuccess: (address: string) =>
|
||||
`✅ *Perfect!* I'll use \`${address}\` to send any winnings.
|
||||
|
||||
@@ -153,16 +221,14 @@ Confirm this purchase?`,
|
||||
invoiceCaption: (
|
||||
ticketCount: number,
|
||||
totalAmount: string,
|
||||
paymentRequest: string,
|
||||
expiryMinutes: number
|
||||
) =>
|
||||
`🎟 *${ticketCount} ticket${ticketCount > 1 ? 's' : ''}*
|
||||
💰 *Amount:* ${totalAmount} sats
|
||||
⏳ Expires in ${expiryMinutes} minutes`,
|
||||
|
||||
\`${paymentRequest}\`
|
||||
|
||||
⏳ This invoice expires in ${expiryMinutes} minutes.
|
||||
I'll notify you when payment is received!`,
|
||||
invoiceString: (paymentRequest: string) =>
|
||||
`\`${paymentRequest}\``,
|
||||
|
||||
paymentReceived: (ticketNumbers: string, drawTime: string) =>
|
||||
`🎉 *Payment Received!*
|
||||
@@ -180,13 +246,13 @@ Good luck! 🍀 I'll notify you after the draw!`,
|
||||
|
||||
No payment was received in time. No tickets were issued.
|
||||
|
||||
Use /buy to try again.`,
|
||||
Use /buyticket to try again.`,
|
||||
|
||||
invoiceExpiredShort: `❌ *Invoice Expired*
|
||||
|
||||
This invoice has expired. No tickets were issued.
|
||||
|
||||
Use /buy to try again.`,
|
||||
Use /buyticket to try again.`,
|
||||
|
||||
jackpotUnavailable: '❌ Jackpot is no longer available.',
|
||||
},
|
||||
@@ -201,13 +267,13 @@ Use /buy to try again.`,
|
||||
|
||||
You haven't purchased any tickets yet!
|
||||
|
||||
Use /buy to get started! 🎟`,
|
||||
Use /buyticket to get started! 🎟`,
|
||||
|
||||
notFound: `🧾 *Your Tickets*
|
||||
|
||||
No ticket purchases found. Purchase history may have expired.
|
||||
|
||||
Use /buy to get new tickets! 🎟`,
|
||||
Use /buyticket to get new tickets! 🎟`,
|
||||
|
||||
tapForDetails: `\nTap a ticket below for details:`,
|
||||
|
||||
@@ -266,13 +332,13 @@ ${statusSection}`,
|
||||
|
||||
You haven't purchased any tickets yet, so no wins to show!
|
||||
|
||||
Use /buy to get started! 🎟`,
|
||||
Use /buyticket to get started! 🎟`,
|
||||
|
||||
noWinsYet: `🏆 *Your Wins*
|
||||
|
||||
You haven't won any jackpots yet. Keep playing!
|
||||
|
||||
Use /buy to get more tickets! 🎟🍀`,
|
||||
Use /buyticket to get more tickets! 🎟🍀`,
|
||||
|
||||
header: (totalWinnings: string, paidWinnings: string) =>
|
||||
`🏆 *Your Wins*
|
||||
@@ -300,20 +366,47 @@ This is the Lightning Jackpot lottery bot! Buy tickets with Bitcoin Lightning, a
|
||||
5️⃣ If you win, sats are sent to your address instantly!
|
||||
|
||||
*Commands:*
|
||||
• /buy — Buy lottery tickets
|
||||
• /buyticket — Buy lottery tickets
|
||||
• /tickets — View your tickets
|
||||
• /wins — View your past wins
|
||||
• /address — Update Lightning Address
|
||||
• /menu — Show main menu
|
||||
• /help — Show this help
|
||||
• /lottoaddress — Update Lightning Address
|
||||
• /lottomenu — Show main menu
|
||||
• /lottohelp — Show this help
|
||||
• /jackpot — View current jackpot info
|
||||
|
||||
*Settings (use ⚙️ Settings button):*
|
||||
• Set your display name (shown if you win)
|
||||
• Enable/disable draw reminders
|
||||
• Enable/disable draw results notifications
|
||||
• Enable/disable new jackpot alerts
|
||||
|
||||
*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
|
||||
🔔 You'll be notified after every draw you participate in
|
||||
|
||||
Good luck! 🍀`,
|
||||
|
||||
groupMessage: `⚡🎰 *Lightning Jackpot Bot - Group Help* 🎰⚡
|
||||
|
||||
*User Commands:*
|
||||
• /jackpot — View current jackpot info
|
||||
• /buyticket — Buy lottery tickets
|
||||
• /lottohelp — Show this help
|
||||
|
||||
*Admin Commands:*
|
||||
• /lottosettings — Configure bot settings for this group
|
||||
|
||||
*Admin Settings:*
|
||||
👉 *Bot Enabled* — Enable/disable the bot in this group
|
||||
👉 *Draw Announcements* — Announce draw winners in this group
|
||||
👉 *Draw Reminders* — Send reminders before draws
|
||||
👉 *Ticket Purchases* — Allow /buyticket command in group
|
||||
|
||||
💡 *Tip:* For privacy, ticket purchases in groups can be disabled. Users can always buy tickets by messaging the bot directly.
|
||||
|
||||
To buy tickets privately, message me directly!`,
|
||||
},
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
@@ -371,18 +464,104 @@ Good luck next round! 🍀`,
|
||||
|
||||
Congratulations to the winner! ⚡
|
||||
|
||||
Use /buy to enter the next draw! 🍀`,
|
||||
Use /buyticket to enter the next draw! 🍀`,
|
||||
|
||||
drawReminder: (potSats: string, drawTime: string, timeLeft: string) =>
|
||||
`⏰ *Draw Reminder!*
|
||||
drawCompleted: (potSats: number, hasWinner: boolean) =>
|
||||
hasWinner
|
||||
? `🎰 *JACKPOT DRAW COMPLETE!* 🎰
|
||||
|
||||
🎰 The next Lightning Jackpot draw is coming up!
|
||||
💰 *Jackpot:* ${potSats.toLocaleString()} sats
|
||||
🏆 A winner has been selected!
|
||||
|
||||
💰 *Current Prize Pool:* ${potSats} sats
|
||||
🕐 *Draw Time:* ${drawTime}
|
||||
⏳ *Time Left:* ${timeLeft}
|
||||
Use /buyticket to enter the next round! 🍀`
|
||||
: `🎰 *JACKPOT DRAW COMPLETE!* 🎰
|
||||
|
||||
Don't miss your chance to win! Use /buy to get your tickets! 🎟`,
|
||||
No tickets were sold this round.
|
||||
The jackpot rolls over to the next draw!
|
||||
|
||||
Use /buyticket to be the first to enter! 🍀`,
|
||||
|
||||
drawReminder: (value: number, unit: string, drawTime: Date, potSats: number) => {
|
||||
const timeStr = unit === 'days'
|
||||
? `${value} day${value > 1 ? 's' : ''}`
|
||||
: unit === 'hours'
|
||||
? `${value} hour${value > 1 ? 's' : ''}`
|
||||
: `${value} minute${value > 1 ? 's' : ''}`;
|
||||
|
||||
const drawTimeStr = drawTime.toLocaleString('en-US', {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
timeZoneName: 'short',
|
||||
});
|
||||
|
||||
return `⏰ *Draw Reminder!*
|
||||
|
||||
🎰 The next Lightning Jackpot draw is in *${timeStr}*!
|
||||
|
||||
💰 *Current Prize Pool:* ${potSats.toLocaleString()} sats
|
||||
🕐 *Draw Time:* ${drawTimeStr}
|
||||
|
||||
Don't miss your chance to win! Use /buyticket to get your tickets! 🎟`;
|
||||
},
|
||||
|
||||
newJackpot: (lotteryName: string, ticketPrice: number, drawTime: Date) => {
|
||||
const drawTimeStr = drawTime.toLocaleString('en-US', {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
timeZoneName: 'short',
|
||||
});
|
||||
|
||||
return `🎉 *NEW JACKPOT STARTED!* 🎉
|
||||
|
||||
⚡ *${lotteryName}*
|
||||
|
||||
A new lottery round has begun!
|
||||
|
||||
🎟 *Ticket Price:* ${ticketPrice} sats
|
||||
🕐 *Draw Time:* ${drawTimeStr}
|
||||
|
||||
Be first to enter! Use /buyticket to buy tickets! 🍀`;
|
||||
},
|
||||
},
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// USER SETTINGS
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
settings: {
|
||||
overview: (displayName: string, notifications: { drawReminders: boolean; drawResults: boolean; newJackpotAlerts: boolean }) =>
|
||||
`⚙️ *Your Settings*
|
||||
|
||||
👤 *Display Name:* ${displayName}
|
||||
_(Used when announcing winners)_
|
||||
|
||||
*Notifications:*
|
||||
${notifications.drawReminders ? '✅' : '❌'} Draw Reminders _(15 min before draws)_
|
||||
${notifications.drawResults ? '✅' : '❌'} Draw Results _(when your tickets are drawn)_
|
||||
${notifications.newJackpotAlerts ? '✅' : '❌'} New Jackpot Alerts _(when new rounds start)_
|
||||
|
||||
Tap buttons below to change settings:`,
|
||||
|
||||
enterDisplayName: `👤 *Set Display Name*
|
||||
|
||||
Enter your display name (max 20 characters).
|
||||
This name will be shown if you win!
|
||||
|
||||
_Send "Anon" to stay anonymous._`,
|
||||
|
||||
nameTooLong: '❌ Display name must be 20 characters or less. Please try again:',
|
||||
|
||||
nameUpdated: (name: string) =>
|
||||
`✅ *Display Name Updated!*
|
||||
|
||||
Your display name is now: *${name}*
|
||||
|
||||
This will be shown when announcing winners.`,
|
||||
},
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
@@ -397,16 +576,16 @@ 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
|
||||
• /lottosettings — Configure bot settings for this group
|
||||
|
||||
*User Commands:*
|
||||
• /buy — Buy lottery tickets (in DM)
|
||||
• /buyticket — Buy lottery tickets
|
||||
• /jackpot — View current jackpot info
|
||||
• /help — Get help
|
||||
• /lottohelp — Get help
|
||||
|
||||
To buy tickets, message me directly @LightningLottoBot! 🎟`,
|
||||
To buy tickets privately, message me directly! 🎟`,
|
||||
|
||||
privateChat: '❌ This command only works in groups. Use /menu to see available commands.',
|
||||
privateChat: '❌ This command only works in groups. Use /lottomenu to see available commands.',
|
||||
|
||||
adminOnly: '⚠️ Only group administrators can change these settings.',
|
||||
|
||||
@@ -415,19 +594,59 @@ To buy tickets, message me directly @LightningLottoBot! 🎟`,
|
||||
enabled: boolean;
|
||||
drawAnnouncements: boolean;
|
||||
reminders: boolean;
|
||||
newJackpotAnnouncement?: boolean;
|
||||
ticketPurchaseAllowed: boolean;
|
||||
}) =>
|
||||
`⚙️ *Group Settings*
|
||||
reminder1Enabled?: boolean;
|
||||
reminder1Time?: { value: number; unit: string };
|
||||
reminder2Enabled?: boolean;
|
||||
reminder2Time?: { value: number; unit: string };
|
||||
reminder3Enabled?: boolean;
|
||||
reminder3Time?: { value: number; unit: string };
|
||||
announcementDelaySeconds?: number;
|
||||
}) => {
|
||||
const announceDelay = settings.announcementDelaySeconds ?? 10;
|
||||
const formatAnnounce = announceDelay === 0
|
||||
? 'Immediately'
|
||||
: announceDelay >= 60
|
||||
? `${announceDelay / 60} min after draw`
|
||||
: `${announceDelay}s after draw`;
|
||||
|
||||
// Format helper for reminder times
|
||||
const formatTime = (t?: { value: number; unit: string }) => {
|
||||
if (!t) return '?';
|
||||
if (t.unit === 'minutes') return `${t.value}m`;
|
||||
if (t.unit === 'hours') return t.value === 1 ? '1h' : `${t.value}h`;
|
||||
return t.value === 1 ? '1d' : `${t.value}d`;
|
||||
};
|
||||
|
||||
// Format reminder times (3-tier system)
|
||||
const r1 = settings.reminder1Enabled !== false;
|
||||
const r2 = settings.reminder2Enabled === true;
|
||||
const r3 = settings.reminder3Enabled === true;
|
||||
const activeReminders: string[] = [];
|
||||
if (r1) activeReminders.push(formatTime(settings.reminder1Time) || '1h');
|
||||
if (r2) activeReminders.push(formatTime(settings.reminder2Time) || '1d');
|
||||
if (r3) activeReminders.push(formatTime(settings.reminder3Time) || '6d');
|
||||
const formatReminderList = activeReminders.length > 0
|
||||
? activeReminders.join(', ')
|
||||
: 'None';
|
||||
|
||||
const newJackpot = settings.newJackpotAnnouncement !== false;
|
||||
|
||||
return `⚙️ *Group Settings*
|
||||
|
||||
📍 *Group:* ${settings.groupTitle}
|
||||
|
||||
*Current Configuration:*
|
||||
${settings.enabled ? '✅' : '❌'} Bot Enabled
|
||||
${settings.drawAnnouncements ? '✅' : '❌'} Draw Announcements
|
||||
${settings.reminders ? '✅' : '❌'} Draw Reminders
|
||||
${newJackpot ? '✅' : '❌'} New Jackpot Announcements
|
||||
${settings.drawAnnouncements ? '✅' : '❌'} Draw Announcements ${settings.drawAnnouncements ? `_(${formatAnnounce})_` : ''}
|
||||
${settings.reminders ? '✅' : '❌'} Draw Reminders ${settings.reminders ? `_(${formatReminderList})_` : ''}
|
||||
${settings.ticketPurchaseAllowed ? '✅' : '❌'} Ticket Purchases in Group
|
||||
|
||||
Tap a button below to toggle settings:`,
|
||||
_Tap buttons to toggle features or adjust times._
|
||||
_This message will auto-delete in 2 minutes._`;
|
||||
},
|
||||
|
||||
settingUpdated: (setting: string, enabled: boolean) =>
|
||||
`✅ *${setting}* has been ${enabled ? 'enabled' : 'disabled'}.`,
|
||||
|
||||
Reference in New Issue
Block a user