ipl-ticket-watcher
Version:
IPL ticket watcher (RCB-optimized) - Notifies when tickets go live
117 lines (100 loc) • 3.44 kB
JavaScript
const EventEmitter = require('events');
const nodemailer = require('nodemailer');
const dotenv = require('dotenv');
const axios = require('axios');
const cheerio = require('cheerio');
const { Webhook, MessageBuilder } = require('discord-webhook-node');
class TicketWatcher extends EventEmitter {
constructor(config = {}) {
super();
this.config = {
watchInterval: config.watchInterval || parseInt(process.env.WATCH_INTERVAL_MIN || '5') * 60 * 1000,
matchDates: config.matchDates || process.env.MATCH_DATES?.split(',') || [],
ticketUrl: config.ticketUrl || 'https://shop.royalchallengers.com/ticket',
discordWebhook: config.discordWebhook || process.env.DISCORD_WEBHOOK_URL,
emailConfig: config.emailConfig || {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
receiver: process.env.EMAIL_RECEIVER
}
};
this.transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: this.config.emailConfig.user,
pass: this.config.emailConfig.pass,
},
});
this.hook = this.config.discordWebhook ? new Webhook(this.config.discordWebhook) : null;
}
async sendEmail(subject, text) {
await this.transporter.sendMail({
from: `RCB Ticket Notifier <${this.config.emailConfig.user}>`,
to: this.config.emailConfig.receiver,
subject,
text,
});
}
async sendDiscordNotification(message) {
if (!this.hook) return;
const embed = new MessageBuilder()
.setTitle('🚨 RCB Tickets Alert!')
.setDescription(message)
.setURL(this.config.ticketUrl)
.setColor('#FF0000')
.setTimestamp();
await this.hook.send(embed);
}
async checkForTickets() {
try {
const { data: html } = await axios.get(this.config.ticketUrl);
const $ = cheerio.load(html);
const dateElements = $('p.css-1nm99ps');
const availableDates = [];
dateElements.each((_, elem) => {
availableDates.push($(elem).text().trim());
});
const matched = availableDates.filter(date => {
return this.config.matchDates.some(match =>
date.toLowerCase().includes(match.toLowerCase())
);
});
if (matched.length > 0) {
const msg = `🚨 Tickets available for: ${matched.join(', ')}\n\nBook here: ${this.config.ticketUrl}`;
this.emit('ticketsAvailable', { dates: matched, message: msg });
await Promise.all([
this.sendEmail('🚨 RCB Tickets LIVE!', msg),
this.sendDiscordNotification(msg)
]);
} else {
this.emit('noTickets', { time: new Date() });
}
return matched;
} catch (error) {
this.emit('error', error);
throw error;
}
}
start() {
this.emit('start');
this.checkForTickets();
this.interval = setInterval(() => this.checkForTickets(), this.config.watchInterval);
return this;
}
stop() {
clearInterval(this.interval);
this.emit('stop');
return this;
}
}
// Maintain CLI functionality
if (require.main === module) {
dotenv.config();
const watcher = new TicketWatcher();
watcher
.on('ticketsAvailable', ({message}) => console.log(message))
.on('noTickets', ({time}) => console.log(`[${time.toLocaleTimeString()}] Tickets not yet available.`))
.on('error', console.error)
.start();
}
module.exports = TicketWatcher;