|
|
@@ -1,4 +1,4 @@
|
|
|
-import { Context } from "grammy";
|
|
|
+import { Context, InlineKeyboard } from "grammy";
|
|
|
import { CosmoeClient } from "../../client/cosmoe";
|
|
|
|
|
|
export interface Env {
|
|
|
@@ -63,52 +63,164 @@ export async function handleBookEvent(ctx: Context, env: Env): Promise<void> {
|
|
|
}
|
|
|
|
|
|
// Check for available coupons for this event
|
|
|
- let couponCode: string | undefined;
|
|
|
+ let coupons: any[] = [];
|
|
|
try {
|
|
|
const couponsResult = await cosmoeClient.getAvailableCoupons(eventId);
|
|
|
if (couponsResult.code === 200 && couponsResult.data && couponsResult.data.length > 0) {
|
|
|
- // Use the first available coupon (you could implement logic to select the best one)
|
|
|
- const availableCoupon = couponsResult.data[0];
|
|
|
- couponCode = availableCoupon.code;
|
|
|
- console.log(`Using coupon ${couponCode} for event ${eventId}`);
|
|
|
+ coupons = couponsResult.data;
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.warn('Failed to fetch coupons, proceeding without coupon:', error);
|
|
|
}
|
|
|
-
|
|
|
- // Attempt to book the event with coupon if available
|
|
|
- const bookingRequest: any = {
|
|
|
- event_id: eventId,
|
|
|
- time_slot: selectedSlot.range,
|
|
|
- };
|
|
|
-
|
|
|
- if (couponCode) {
|
|
|
- bookingRequest.coupon_code = couponCode;
|
|
|
- }
|
|
|
-
|
|
|
- const bookingResult = await cosmoeClient.bookEvent(bookingRequest);
|
|
|
-
|
|
|
- if (bookingResult.code === 200) {
|
|
|
- // Extract final price and booking ID from the response data if available
|
|
|
- const finalPrice = bookingResult.data?.final_price || selectedSlot.price;
|
|
|
- const bookingId = bookingResult.data?.id;
|
|
|
-
|
|
|
- let successMessage = `成功预约活动 *${event.name}*,时间段 *${selectedSlot.range}*`;
|
|
|
- successMessage += `\n*最终价格* ¥${finalPrice}`;
|
|
|
- if (bookingId) {
|
|
|
- successMessage += `\n*预约编号* ${bookingId}`;
|
|
|
- }
|
|
|
- if (couponCode) {
|
|
|
- successMessage += ` \n*使用优惠码* ${couponCode}`;
|
|
|
- }
|
|
|
- await ctx.reply(successMessage, { parse_mode: "Markdown" });
|
|
|
- } else {
|
|
|
- let errorMessage = `预约失败: ${bookingResult.msg || '未知错误'}`;
|
|
|
- await ctx.reply(errorMessage);
|
|
|
+
|
|
|
+ // If multiple coupons available, show selection keyboard
|
|
|
+ if (coupons.length > 1) {
|
|
|
+ const keyboard = new InlineKeyboard();
|
|
|
+ coupons.forEach((coupon, index) => {
|
|
|
+ const discountText = coupon.type === 'percentage'
|
|
|
+ ? `${coupon.value}%`
|
|
|
+ : `¥${coupon.value}`;
|
|
|
+ keyboard.text(
|
|
|
+ `${coupon.code} (${discountText})`,
|
|
|
+ `select_coupon_${eventId}_${slotIndex}_${coupon.code}`
|
|
|
+ );
|
|
|
+ if (index < coupons.length - 1) {
|
|
|
+ keyboard.row();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // Add option to book without coupon
|
|
|
+ keyboard.row();
|
|
|
+ keyboard.text('不使用优惠码', `select_coupon_${eventId}_${slotIndex}_none`);
|
|
|
+
|
|
|
+ await ctx.reply(
|
|
|
+ `请选择要使用的优惠码:\n\n` +
|
|
|
+ `*活动* ${event.name}\n` +
|
|
|
+ `*时间段* ${selectedSlot.range}\n` +
|
|
|
+ `*原价* ¥${selectedSlot.price}`,
|
|
|
+ {
|
|
|
+ parse_mode: "Markdown",
|
|
|
+ reply_markup: keyboard,
|
|
|
+ }
|
|
|
+ );
|
|
|
+ return;
|
|
|
}
|
|
|
+
|
|
|
+ // If only one coupon, use it automatically; if none, proceed without
|
|
|
+ const couponCode = coupons.length === 1 ? coupons[0].code : undefined;
|
|
|
+
|
|
|
+ // Proceed with booking
|
|
|
+ await proceedWithBooking(ctx, cosmoeClient, event, selectedSlot, couponCode);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
- console.error("Error handling booking request:", error);
|
|
|
- await ctx.reply("处理预约请求时出错,请稍后再试")
|
|
|
+ console.error("Error handling booking request:", error);
|
|
|
+ await ctx.reply("处理预约请求时出错,请稍后再试");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Helper function to proceed with booking
|
|
|
+async function proceedWithBooking(
|
|
|
+ ctx: Context,
|
|
|
+ cosmoeClient: CosmoeClient,
|
|
|
+ event: any,
|
|
|
+ selectedSlot: any,
|
|
|
+ couponCode: string | undefined
|
|
|
+): Promise<void> {
|
|
|
+ const bookingRequest: any = {
|
|
|
+ event_id: event.id,
|
|
|
+ time_slot: selectedSlot.range,
|
|
|
+ };
|
|
|
+
|
|
|
+ if (couponCode) {
|
|
|
+ bookingRequest.coupon_code = couponCode;
|
|
|
+ }
|
|
|
+
|
|
|
+ const bookingResult = await cosmoeClient.bookEvent(bookingRequest);
|
|
|
+
|
|
|
+ if (bookingResult.code === 200) {
|
|
|
+ // Extract final price and booking ID from the response data if available
|
|
|
+ const finalPrice = bookingResult.data?.final_price || selectedSlot.price;
|
|
|
+ const bookingId = bookingResult.data?.id;
|
|
|
+
|
|
|
+ let successMessage = `成功预约活动 *${event.name}*,时间段 *${selectedSlot.range}*`;
|
|
|
+ successMessage += `\n*最终价格* ¥${finalPrice}`;
|
|
|
+ if (bookingId) {
|
|
|
+ successMessage += `\n*预约编号* ${bookingId}`;
|
|
|
+ }
|
|
|
+ if (couponCode) {
|
|
|
+ successMessage += ` \n*使用优惠码* ${couponCode}`;
|
|
|
+ }
|
|
|
+ await ctx.reply(successMessage, { parse_mode: "Markdown" });
|
|
|
+ } else {
|
|
|
+ let errorMessage = `预约失败: ${bookingResult.msg || '未知错误'}`;
|
|
|
+ await ctx.reply(errorMessage);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Handler for coupon selection callback
|
|
|
+export async function handleCouponSelection(
|
|
|
+ ctx: Context,
|
|
|
+ env: Env,
|
|
|
+ action: string
|
|
|
+): Promise<void> {
|
|
|
+ try {
|
|
|
+ // Parse action: select_coupon_{event_id}_{slot_index}_{coupon_code}
|
|
|
+ const parts = action.split('_');
|
|
|
+ if (parts.length < 5 || parts[0] !== 'select' || parts[1] !== 'coupon') {
|
|
|
+ await ctx.editMessageText("无效的优惠码选择");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const eventId = parts[2];
|
|
|
+ const slotIndex = parseInt(parts[3]);
|
|
|
+ const couponCode = parts[4] === 'none' ? undefined : parts[4];
|
|
|
+
|
|
|
+ // Get user credentials from KV storage
|
|
|
+ const telegramUserId = ctx.from?.id.toString();
|
|
|
+ if (!telegramUserId) {
|
|
|
+ await ctx.editMessageText("无法获取你的 Telegram 用户身份");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const storedCredentials = await env.COSMOE_CREDENTIALS.get(telegramUserId);
|
|
|
+ if (!storedCredentials) {
|
|
|
+ await ctx.editMessageText("请先使用 /login 命令登录后再进行预约");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const credentials = JSON.parse(storedCredentials);
|
|
|
+
|
|
|
+ // Initialize Cosmoe API client and set credentials
|
|
|
+ const cosmoeClient = new CosmoeClient();
|
|
|
+ cosmoeClient.setCredentials(credentials.user_id, credentials.token);
|
|
|
+
|
|
|
+ // Fetch event details to get time slots
|
|
|
+ const eventResult = await cosmoeClient.getEventDetail(eventId);
|
|
|
+ if (eventResult.code !== 200) {
|
|
|
+ await ctx.editMessageText(`获取活动详情时出错: ${eventResult.msg || '未知错误'}`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const event = eventResult.data;
|
|
|
+ const sortedSlots = [...event.slots].sort((a, b) => a.range.localeCompare(b.range));
|
|
|
+
|
|
|
+ if (slotIndex < 0 || slotIndex >= sortedSlots.length) {
|
|
|
+ await ctx.editMessageText(`无效的时间段编号`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const selectedSlot = sortedSlots[slotIndex];
|
|
|
+
|
|
|
+ // Update message to show selection
|
|
|
+ let selectionMessage = `已选择: ${couponCode ? `优惠码 ${couponCode}` : '不使用优惠码'}\n\n`;
|
|
|
+ selectionMessage += `*活动* ${event.name}\n`;
|
|
|
+ selectionMessage += `*时间段* ${selectedSlot.range}`;
|
|
|
+ await ctx.editMessageText(selectionMessage, { parse_mode: "Markdown" });
|
|
|
+
|
|
|
+ // Proceed with booking
|
|
|
+ await proceedWithBooking(ctx, cosmoeClient, event, selectedSlot, couponCode);
|
|
|
+ await ctx.answerCallbackQuery();
|
|
|
+ } catch (error) {
|
|
|
+ console.error("Error handling coupon selection:", error);
|
|
|
+ await ctx.editMessageText("处理优惠码选择时出错,请稍后再试");
|
|
|
}
|
|
|
}
|