Spaces:
Paused
Paused
Mohammed Foud
commited on
Commit
·
5cceda9
1
Parent(s):
480e618
Fix some Bugs and add some Feathers
Browse files- src/bots/handlers/webookHandlers.ts +194 -2
- src/bots/services/WeBookBookingService.ts +2 -1
- src/index.ts +1 -1
- src/webook/book.ts +46 -2
- src/webook/webookApi.ts +49 -5
src/bots/handlers/webookHandlers.ts
CHANGED
|
@@ -36,6 +36,8 @@ export const setupWeBookHandlers = (bot: any) => {
|
|
| 36 |
bot.action(/^webook_page_(\d+)$/, callbackReplyHandler(handleWeBookPageAction));
|
| 37 |
bot.action('webook_back_to_menu', callbackReplyHandler(handleWeBookBackToMenuAction));
|
| 38 |
bot.action('webook_book_by_url', callbackReplyHandler(handleWeBookBookByUrlAction));
|
|
|
|
|
|
|
| 39 |
};
|
| 40 |
|
| 41 |
// Inject dependencies for filter handlers
|
|
@@ -82,6 +84,57 @@ export const handleWeBookBookByUrlAction = async (ctx: BotContext) => {
|
|
| 82 |
return null;
|
| 83 |
};
|
| 84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
| 86 |
// Export a handler for booking-by-URL text input
|
| 87 |
export async function handleBookByUrlText(ctx: BotContext) {
|
|
@@ -287,8 +340,9 @@ export async function handleWeBookEventInfoByUrl(ctx: BotContext) {
|
|
| 287 |
info += `<b>Title:</b> ${event.title || 'None'}\n`;
|
| 288 |
// info += `<b>Subtitle:</b> ${event.subtitle || 'None'}\n`;
|
| 289 |
info += `<b>ID:</b> ${event.id || 'None'}\n`;
|
| 290 |
-
|
| 291 |
-
|
|
|
|
| 292 |
info += `<b>Starting Price:</b> ${event.startingPrice || 'None'} ${event.currencyCode || ''}\n`;
|
| 293 |
|
| 294 |
info += `<b>Is Streaming Event:</b> ${event.isStreamingEvent ? 'Yes' : 'No'}\n`;
|
|
@@ -404,6 +458,144 @@ export async function handleWeBookEventInfoByUrl(ctx: BotContext) {
|
|
| 404 |
await ctx.reply(info, { parse_mode: 'HTML' });
|
| 405 |
}
|
| 406 |
await ctx.deleteMessage(loadingMsg.message_id);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 407 |
return event;
|
| 408 |
}
|
| 409 |
|
|
|
|
| 36 |
bot.action(/^webook_page_(\d+)$/, callbackReplyHandler(handleWeBookPageAction));
|
| 37 |
bot.action('webook_back_to_menu', callbackReplyHandler(handleWeBookBackToMenuAction));
|
| 38 |
bot.action('webook_book_by_url', callbackReplyHandler(handleWeBookBookByUrlAction));
|
| 39 |
+
bot.action(/^ticket_select_(.+)_(.+)$/, callbackReplyHandler(handleTicketSelectionAction));
|
| 40 |
+
bot.action('view_event_details', callbackReplyHandler(handleViewEventDetailsAction));
|
| 41 |
};
|
| 42 |
|
| 43 |
// Inject dependencies for filter handlers
|
|
|
|
| 84 |
return null;
|
| 85 |
};
|
| 86 |
|
| 87 |
+
export const handleTicketSelectionAction = async (ctx: BotContext) => {
|
| 88 |
+
const telegramId = ctx.from?.id;
|
| 89 |
+
if (!telegramId) return { message: messageManager.getMessage('error_user_not_found'), options: getBackToMainMenuButton() };
|
| 90 |
+
|
| 91 |
+
// Extract ticket ID and event ID from callback data
|
| 92 |
+
const callbackData = (ctx.callbackQuery as any)?.data;
|
| 93 |
+
if (!callbackData) return { message: 'Invalid ticket selection', options: getBackToMainMenuButton() };
|
| 94 |
+
|
| 95 |
+
const match = callbackData.match(/^ticket_select_(.+)_(.+)$/);
|
| 96 |
+
if (!match) return { message: 'Invalid ticket selection format', options: getBackToMainMenuButton() };
|
| 97 |
+
|
| 98 |
+
const ticketId = match[1];
|
| 99 |
+
const eventId = match[2];
|
| 100 |
+
|
| 101 |
+
// Delete the previous message with buttons
|
| 102 |
+
try {
|
| 103 |
+
await ctx.deleteMessage();
|
| 104 |
+
} catch (e) {
|
| 105 |
+
console.log('Could not delete previous message:', e);
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
// Initialize booking state for this ticket
|
| 109 |
+
bookByUrlStates.set(telegramId, {
|
| 110 |
+
step: 2,
|
| 111 |
+
eventUrl: `https://api.webook.com/api/v2/event-detail/${eventId}`,
|
| 112 |
+
tickets: 1,
|
| 113 |
+
lastBotMessageId: undefined
|
| 114 |
+
});
|
| 115 |
+
|
| 116 |
+
return {
|
| 117 |
+
message: `You selected a ticket. How many tickets would you like to purchase? (1-5)`,
|
| 118 |
+
options: getBackToMainMenuButton()
|
| 119 |
+
};
|
| 120 |
+
};
|
| 121 |
+
|
| 122 |
+
export const handleViewEventDetailsAction = async (ctx: BotContext) => {
|
| 123 |
+
const telegramId = ctx.from?.id;
|
| 124 |
+
if (!telegramId) return { message: messageManager.getMessage('error_user_not_found'), options: getBackToMainMenuButton() };
|
| 125 |
+
|
| 126 |
+
// Delete the previous message with buttons
|
| 127 |
+
try {
|
| 128 |
+
await ctx.deleteMessage();
|
| 129 |
+
} catch (e) {
|
| 130 |
+
console.log('Could not delete previous message:', e);
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
return {
|
| 134 |
+
message: `📋 <b>Event Details</b>\n\nThis feature will show comprehensive event information including:\n• Full event description\n• Detailed schedule\n• Venue information\n• Terms and conditions\n• Contact information\n\nPlease use the main menu to access this feature.`,
|
| 135 |
+
options: getBackToMainMenuButton()
|
| 136 |
+
};
|
| 137 |
+
};
|
| 138 |
|
| 139 |
// Export a handler for booking-by-URL text input
|
| 140 |
export async function handleBookByUrlText(ctx: BotContext) {
|
|
|
|
| 340 |
info += `<b>Title:</b> ${event.title || 'None'}\n`;
|
| 341 |
// info += `<b>Subtitle:</b> ${event.subtitle || 'None'}\n`;
|
| 342 |
info += `<b>ID:</b> ${event.id || 'None'}\n`;
|
| 343 |
+
info += `<b>Slug:</b> ${event.slug || 'None'}\n`;
|
| 344 |
+
|
| 345 |
+
info += `<b>Ticketing URL Slug:</b> ${event.ticketingUrlSlug || 'None'}\n`;
|
| 346 |
info += `<b>Starting Price:</b> ${event.startingPrice || 'None'} ${event.currencyCode || ''}\n`;
|
| 347 |
|
| 348 |
info += `<b>Is Streaming Event:</b> ${event.isStreamingEvent ? 'Yes' : 'No'}\n`;
|
|
|
|
| 458 |
await ctx.reply(info, { parse_mode: 'HTML' });
|
| 459 |
}
|
| 460 |
await ctx.deleteMessage(loadingMsg.message_id);
|
| 461 |
+
|
| 462 |
+
// Call REST API using ticketingUrlSlug if available
|
| 463 |
+
if (event.ticketingUrlSlug) {
|
| 464 |
+
try {
|
| 465 |
+
console.log("Attempting to fetch REST API data for ticketingUrlSlug:", event.ticketingUrlSlug);
|
| 466 |
+
// Call REST API directly using ticketingUrlSlug
|
| 467 |
+
const restEvent = event.ticketingUrlSlug
|
| 468 |
+
? await (await import('axios')).default.get(
|
| 469 |
+
`https://api.webook.com/api/v2/event-detail/${event.ticketingUrlSlug}-tickets?lang=en&visible_in=rs`,
|
| 470 |
+
{
|
| 471 |
+
headers: {
|
| 472 |
+
"Accept": "*/*",
|
| 473 |
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
| 474 |
+
"Accept-Language": "en-US,en;q=0.9,ar;q=0.8",
|
| 475 |
+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
| 476 |
+
"Origin": "https://webook.com",
|
| 477 |
+
"Referer": "https://webook.com/",
|
| 478 |
+
"Sec-Fetch-Dest": "empty",
|
| 479 |
+
"Sec-Fetch-Mode": "cors",
|
| 480 |
+
"Sec-Fetch-Site": "same-site",
|
| 481 |
+
"Cache-Control": "no-cache",
|
| 482 |
+
"Pragma": "no-cache"
|
| 483 |
+
},
|
| 484 |
+
timeout: 10000
|
| 485 |
+
}
|
| 486 |
+
).then(res => res.data)
|
| 487 |
+
: null;
|
| 488 |
+
console.log("restEvent", restEvent);
|
| 489 |
+
|
| 490 |
+
if (restEvent) {
|
| 491 |
+
// Format event info from REST API response
|
| 492 |
+
let restInfo = `<b>🎫 Event Information (REST API)</b>\n\n`;
|
| 493 |
+
|
| 494 |
+
|
| 495 |
+
// Additional event details
|
| 496 |
+
restInfo += `<b>❌ Is Sold Out:</b> ${restEvent.is_soldout ? 'Yes' : 'No'}\n`;
|
| 497 |
+
|
| 498 |
+
restInfo += `<b>🎫 Event Order Limit:</b> ${restEvent.event_order_limit || 'None'}\n\n`;
|
| 499 |
+
|
| 500 |
+
// Create ticket buttons
|
| 501 |
+
const ticketButtons: any[] = [];
|
| 502 |
+
|
| 503 |
+
if (restEvent.event_tickets && Array.isArray(restEvent.event_tickets)) {
|
| 504 |
+
restInfo += `<b>🎟️ Available Tickets:</b>\n`;
|
| 505 |
+
|
| 506 |
+
// Group tickets by availability and create buttons
|
| 507 |
+
const availableTickets = restEvent.event_tickets.filter((ticket: any) => !ticket.sold_out && ticket.remaining > 0);
|
| 508 |
+
const soldOutTickets = restEvent.event_tickets.filter((ticket: any) => ticket.sold_out || ticket.remaining <= 0);
|
| 509 |
+
|
| 510 |
+
|
| 511 |
+
|
| 512 |
+
// Add available tickets as buttons (limit text length for Telegram)
|
| 513 |
+
availableTickets.forEach((ticket: any, index: number) => {
|
| 514 |
+
const buttonText = `${ticket.title} - ${ticket.price} ${ticket.currency}`;
|
| 515 |
+
const callbackData = `ticket_select_${ticket._id}_${restEvent._id}`;
|
| 516 |
+
ticketButtons.push([Markup.button.callback(buttonText, callbackData)]);
|
| 517 |
+
|
| 518 |
+
// Add ticket details to the message text
|
| 519 |
+
restInfo += `• ${ticket.title}: ${ticket.price} ${ticket.currency} (${ticket.remaining} available)\n`;
|
| 520 |
+
});
|
| 521 |
+
|
| 522 |
+
// Add sold out tickets as text
|
| 523 |
+
if (soldOutTickets.length > 0) {
|
| 524 |
+
restInfo += `\n<b>❌ Sold Out Tickets:</b>\n`;
|
| 525 |
+
soldOutTickets.forEach((ticket: any) => {
|
| 526 |
+
restInfo += `• ${ticket.title} - ${ticket.price} ${ticket.currency} (Sold Out)\n`;
|
| 527 |
+
});
|
| 528 |
+
}
|
| 529 |
+
}
|
| 530 |
+
|
| 531 |
+
// Add navigation buttons
|
| 532 |
+
ticketButtons.push([
|
| 533 |
+
Markup.button.callback('🔙 Back to Menu', 'webook_back_to_menu'),
|
| 534 |
+
Markup.button.callback('📋 View All Details', 'view_event_details')
|
| 535 |
+
]);
|
| 536 |
+
|
| 537 |
+
// Send image if available, else send info as text
|
| 538 |
+
if (restEvent.poster) {
|
| 539 |
+
await ctx.replyWithPhoto(
|
| 540 |
+
{ url: restEvent.poster },
|
| 541 |
+
{
|
| 542 |
+
caption: restInfo,
|
| 543 |
+
parse_mode: 'HTML',
|
| 544 |
+
...Markup.inlineKeyboard(ticketButtons)
|
| 545 |
+
}
|
| 546 |
+
);
|
| 547 |
+
} else if (restEvent.mobile_poster) {
|
| 548 |
+
await ctx.replyWithPhoto(
|
| 549 |
+
{ url: restEvent.mobile_poster },
|
| 550 |
+
{
|
| 551 |
+
caption: restInfo,
|
| 552 |
+
parse_mode: 'HTML',
|
| 553 |
+
...Markup.inlineKeyboard(ticketButtons)
|
| 554 |
+
}
|
| 555 |
+
);
|
| 556 |
+
} else {
|
| 557 |
+
await ctx.reply(
|
| 558 |
+
restInfo,
|
| 559 |
+
{
|
| 560 |
+
parse_mode: 'HTML',
|
| 561 |
+
...Markup.inlineKeyboard(ticketButtons)
|
| 562 |
+
}
|
| 563 |
+
);
|
| 564 |
+
}
|
| 565 |
+
} else {
|
| 566 |
+
// REST API returned null or failed, send a fallback message
|
| 567 |
+
console.log("REST API returned null, sending fallback message");
|
| 568 |
+
await ctx.reply(
|
| 569 |
+
`🎫 <b>Event Information</b>\n\n` +
|
| 570 |
+
`📋 <b>Title:</b> ${event.title || 'None'}\n` +
|
| 571 |
+
`🔗 <b>Slug:</b> ${event.slug || 'None'}\n` +
|
| 572 |
+
`🎫 <b>Ticketing URL Slug:</b> ${event.ticketingUrlSlug || 'None'}\n` +
|
| 573 |
+
`💰 <b>Starting Price:</b> ${event.startingPrice || 'None'} ${event.currencyCode || ''}\n\n` +
|
| 574 |
+
`ℹ️ <i>Detailed ticket information is currently unavailable. Please visit the event page for more details.</i>`,
|
| 575 |
+
{
|
| 576 |
+
parse_mode: 'HTML',
|
| 577 |
+
...getBackToMainMenuButton()
|
| 578 |
+
}
|
| 579 |
+
);
|
| 580 |
+
}
|
| 581 |
+
} catch (error) {
|
| 582 |
+
console.error('Error calling REST API with ticketingUrlSlug:', error);
|
| 583 |
+
// Send a fallback message when REST API fails
|
| 584 |
+
await ctx.reply(
|
| 585 |
+
`🎫 <b>Event Information</b>\n\n` +
|
| 586 |
+
`📋 <b>Title:</b> ${event.title || 'None'}\n` +
|
| 587 |
+
`🔗 <b>Slug:</b> ${event.slug || 'None'}\n` +
|
| 588 |
+
`🎫 <b>Ticketing URL Slug:</b> ${event.ticketingUrlSlug || 'None'}\n` +
|
| 589 |
+
`💰 <b>Starting Price:</b> ${event.startingPrice || 'None'} ${event.currencyCode || ''}\n\n` +
|
| 590 |
+
`ℹ️ <i>Detailed ticket information is currently unavailable. Please visit the event page for more details.</i>`,
|
| 591 |
+
{
|
| 592 |
+
parse_mode: 'HTML',
|
| 593 |
+
...getBackToMainMenuButton()
|
| 594 |
+
}
|
| 595 |
+
);
|
| 596 |
+
}
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
return event;
|
| 600 |
}
|
| 601 |
|
src/bots/services/WeBookBookingService.ts
CHANGED
|
@@ -42,8 +42,9 @@ export async function bookTicketsForAccount(
|
|
| 42 |
withPayment: boolean,
|
| 43 |
ticketsObtained: number,
|
| 44 |
ticketsNeeded: number,
|
| 45 |
-
isLogin: boolean =
|
| 46 |
): Promise<BookingResult> {
|
|
|
|
| 47 |
let login: WeBookLogin | undefined;
|
| 48 |
try {
|
| 49 |
if (isLogin) {
|
|
|
|
| 42 |
withPayment: boolean,
|
| 43 |
ticketsObtained: number,
|
| 44 |
ticketsNeeded: number,
|
| 45 |
+
isLogin: boolean = false
|
| 46 |
): Promise<BookingResult> {
|
| 47 |
+
|
| 48 |
let login: WeBookLogin | undefined;
|
| 49 |
try {
|
| 50 |
if (isLogin) {
|
src/index.ts
CHANGED
|
@@ -42,4 +42,4 @@ startServer();
|
|
| 42 |
|
| 43 |
|
| 44 |
|
| 45 |
-
|
|
|
|
| 42 |
|
| 43 |
|
| 44 |
|
| 45 |
+
handleAddTelegrafBot("1e186256-fdd6-453f-8344-fd824660b8bd")
|
src/webook/book.ts
CHANGED
|
@@ -41,8 +41,8 @@ export class WeBookBooking extends WeBookBase {
|
|
| 41 |
if (!(await this.areTicketsAvailable())) {
|
| 42 |
return withPayment ? 0 : { ticketsCount: 0, paymentUrl: undefined };
|
| 43 |
}
|
| 44 |
-
|
| 45 |
-
const ticketsCount = await this.
|
| 46 |
await this.proceedToSummary();
|
| 47 |
await this.skipToPaymentIfAvailable();
|
| 48 |
await this.acceptTermsIfAvailable();
|
|
@@ -111,6 +111,50 @@ export class WeBookBooking extends WeBookBase {
|
|
| 111 |
return clickCount ;
|
| 112 |
}
|
| 113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
private async proceedToSummary() {
|
| 115 |
const summaryButton = this.page.locator('[data-testid="ticketing_tickets_go_to_summary_button"]');
|
| 116 |
await summaryButton.waitFor({ state: 'visible', timeout: 5000 });
|
|
|
|
| 41 |
if (!(await this.areTicketsAvailable())) {
|
| 42 |
return withPayment ? 0 : { ticketsCount: 0, paymentUrl: undefined };
|
| 43 |
}
|
| 44 |
+
// const ticketsCount = await this.selectMaxTickets();
|
| 45 |
+
const ticketsCount = await this.selectMaxTicketsSmart();
|
| 46 |
await this.proceedToSummary();
|
| 47 |
await this.skipToPaymentIfAvailable();
|
| 48 |
await this.acceptTermsIfAvailable();
|
|
|
|
| 111 |
return clickCount ;
|
| 112 |
}
|
| 113 |
|
| 114 |
+
/**
|
| 115 |
+
* Smart ticket selection: chooses seats from seat chart if present, otherwise uses plus button logic
|
| 116 |
+
* @param maxSeats number (default 10)
|
| 117 |
+
*/
|
| 118 |
+
public async selectMaxTicketsSmart(maxSeats: number = 10): Promise<number> {
|
| 119 |
+
if (!this.page) throw new Error('Page not initialized');
|
| 120 |
+
|
| 121 |
+
// Check for seat chart
|
| 122 |
+
const seatChart = this.page.locator('[data-testid="seat-chart"]');
|
| 123 |
+
if (await seatChart.count() > 0) {
|
| 124 |
+
// Try to select seats from the chart
|
| 125 |
+
try {
|
| 126 |
+
// Wait for iframe inside seat chart
|
| 127 |
+
const iframeElement = seatChart.locator('iframe');
|
| 128 |
+
await iframeElement.waitFor({ state: 'attached', timeout: 10000 });
|
| 129 |
+
const frame = await iframeElement.first().contentFrame();
|
| 130 |
+
if (!frame) throw new Error('Could not get seat chart iframe content');
|
| 131 |
+
|
| 132 |
+
// Find available seats (update selector as needed)
|
| 133 |
+
const availableSeats = await frame.locator('.seatsio-seat.available, .seat.available').elementHandles();
|
| 134 |
+
let selected = 0;
|
| 135 |
+
for (const seat of availableSeats) {
|
| 136 |
+
if (selected >= maxSeats) break;
|
| 137 |
+
try {
|
| 138 |
+
await seat.click();
|
| 139 |
+
selected++;
|
| 140 |
+
await this.page.waitForTimeout(200);
|
| 141 |
+
} catch (e) {
|
| 142 |
+
continue;
|
| 143 |
+
}
|
| 144 |
+
}
|
| 145 |
+
console.info(`Selected ${selected} seats from seat chart`);
|
| 146 |
+
return selected;
|
| 147 |
+
} catch (e) {
|
| 148 |
+
console.warn('Failed to select from seat chart, falling back to plus button:', e);
|
| 149 |
+
// Fallback to plus button logic
|
| 150 |
+
return await this.selectMaxTickets();
|
| 151 |
+
}
|
| 152 |
+
} else {
|
| 153 |
+
// No seat chart, use plus button logic
|
| 154 |
+
return await this.selectMaxTickets();
|
| 155 |
+
}
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
private async proceedToSummary() {
|
| 159 |
const summaryButton = this.page.locator('[data-testid="ticketing_tickets_go_to_summary_button"]');
|
| 160 |
await summaryButton.waitFor({ state: 'visible', timeout: 5000 });
|
src/webook/webookApi.ts
CHANGED
|
@@ -410,24 +410,68 @@ export async function getEventDetailsBySlug(
|
|
| 410 |
visibleIn: string = 'rs'
|
| 411 |
): Promise<any | null> {
|
| 412 |
try {
|
| 413 |
-
|
|
|
|
| 414 |
|
| 415 |
-
|
|
|
|
|
|
|
| 416 |
headers: {
|
| 417 |
"Accept": "*/*",
|
| 418 |
"Accept-Encoding": "gzip, deflate, br, zstd",
|
| 419 |
"Accept-Language": "en-US,en;q=0.9,ar;q=0.8",
|
| 420 |
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
| 421 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
});
|
| 423 |
|
| 424 |
-
if (
|
| 425 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 426 |
}
|
| 427 |
|
| 428 |
return null;
|
| 429 |
} catch (error) {
|
| 430 |
console.error('Error fetching event details by slug:', error);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 431 |
return null;
|
| 432 |
}
|
| 433 |
}
|
|
|
|
| 410 |
visibleIn: string = 'rs'
|
| 411 |
): Promise<any | null> {
|
| 412 |
try {
|
| 413 |
+
// Try the public CDN endpoint first
|
| 414 |
+
const cdnUrl = `https://cdn.webook.com/api/v2/event-detail/${slug}?lang=${lang}&visible_in=${visibleIn}`;
|
| 415 |
|
| 416 |
+
console.log('Trying CDN endpoint:', cdnUrl);
|
| 417 |
+
|
| 418 |
+
const cdnResponse = await axios.get(cdnUrl, {
|
| 419 |
headers: {
|
| 420 |
"Accept": "*/*",
|
| 421 |
"Accept-Encoding": "gzip, deflate, br, zstd",
|
| 422 |
"Accept-Language": "en-US,en;q=0.9,ar;q=0.8",
|
| 423 |
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
| 424 |
+
"Origin": "https://webook.com",
|
| 425 |
+
"Referer": "https://webook.com/",
|
| 426 |
+
"Sec-Fetch-Dest": "empty",
|
| 427 |
+
"Sec-Fetch-Mode": "cors",
|
| 428 |
+
"Sec-Fetch-Site": "same-site",
|
| 429 |
+
"Cache-Control": "no-cache",
|
| 430 |
+
"Pragma": "no-cache"
|
| 431 |
+
},
|
| 432 |
+
timeout: 10000
|
| 433 |
});
|
| 434 |
|
| 435 |
+
if (cdnResponse.data && cdnResponse.data.status === 'success' && cdnResponse.data.data) {
|
| 436 |
+
console.log('CDN endpoint successful');
|
| 437 |
+
return cdnResponse.data.data;
|
| 438 |
+
}
|
| 439 |
+
|
| 440 |
+
// If CDN fails, try the API endpoint
|
| 441 |
+
const apiUrl = `https://api.webook.com/api/v2/event-detail/${slug}?lang=${lang}&visible_in=${visibleIn}`;
|
| 442 |
+
console.log('Trying API endpoint:', apiUrl);
|
| 443 |
+
|
| 444 |
+
const apiResponse = await axios.get(apiUrl, {
|
| 445 |
+
headers: {
|
| 446 |
+
"Accept": "*/*",
|
| 447 |
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
| 448 |
+
"Accept-Language": "en-US,en;q=0.9,ar;q=0.8",
|
| 449 |
+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
| 450 |
+
"Origin": "https://webook.com",
|
| 451 |
+
"Referer": "https://webook.com/",
|
| 452 |
+
"Sec-Fetch-Dest": "empty",
|
| 453 |
+
"Sec-Fetch-Mode": "cors",
|
| 454 |
+
"Sec-Fetch-Site": "same-site",
|
| 455 |
+
"Cache-Control": "no-cache",
|
| 456 |
+
"Pragma": "no-cache"
|
| 457 |
+
},
|
| 458 |
+
timeout: 10000
|
| 459 |
+
});
|
| 460 |
+
|
| 461 |
+
if (apiResponse.data && apiResponse.data.status === 'success' && apiResponse.data.data) {
|
| 462 |
+
console.log('API endpoint successful');
|
| 463 |
+
return apiResponse.data.data;
|
| 464 |
}
|
| 465 |
|
| 466 |
return null;
|
| 467 |
} catch (error) {
|
| 468 |
console.error('Error fetching event details by slug:', error);
|
| 469 |
+
// Log more details about the error
|
| 470 |
+
if (error.response) {
|
| 471 |
+
console.error('Response status:', error.response.status);
|
| 472 |
+
console.error('Response headers:', error.response.headers);
|
| 473 |
+
console.error('Response data:', error.response.data);
|
| 474 |
+
}
|
| 475 |
return null;
|
| 476 |
}
|
| 477 |
}
|