Adding speedy mode for reservations

This commit is contained in:
Collin Duncan 2024-04-30 10:27:10 +02:00
parent 1fcd49af2a
commit c4fcb38df8
No known key found for this signature in database
2 changed files with 154 additions and 5 deletions

View file

@ -43,7 +43,7 @@ export class ReservationsWorker {
reservation.dateRangeStart,
reservation.dateRangeEnd,
)
await this.performReservation(reservation, job.attemptsMade, false)
await this.performReservation(reservation, job.attemptsMade, true, true)
}
private async handleReservationErrors(
@ -78,9 +78,14 @@ export class ReservationsWorker {
reservation: Reservation,
attemptsMade: number,
timeSensitive = true,
speedyMode = true,
) {
try {
await this.brService.performReservation(reservation)
if (speedyMode) {
await this.brService.performSpeedyReservation(reservation)
} else {
await this.brService.performReservation(reservation, timeSensitive)
}
await this.reservationsService.deleteById(reservation.id)
} catch (error: unknown) {
await this.handleReservationErrors(

View file

@ -49,7 +49,7 @@ enum CourtSlot {
Thirteen = '63',
}
const CourtSlotToNumber = {
const CourtSlotToNumber: Record<CourtSlot, number> = {
[CourtSlot.One]: 1,
[CourtSlot.Two]: 2,
[CourtSlot.Three]: 3,
@ -66,7 +66,7 @@ const CourtSlotToNumber = {
} as const
// Lower is better
const CourtRank = {
const CourtRank: Record<CourtSlot, number> = {
[CourtSlot.One]: 0,
[CourtSlot.Two]: 0,
[CourtSlot.Three]: 0,
@ -82,6 +82,105 @@ const CourtRank = {
[CourtSlot.Thirteen]: 1, // no one likes upstairs
} as const
enum StartTimeClass {
First = 'first',
Second = 'second',
Third = 'third',
}
const StartTimeClassCourtSlots: Record<StartTimeClass, readonly CourtSlot[]> = {
[StartTimeClass.First]: [
CourtSlot.One,
CourtSlot.Two,
CourtSlot.Three,
CourtSlot.Four,
CourtSlot.Five,
],
[StartTimeClass.Second]: [
CourtSlot.Six,
CourtSlot.Seven,
CourtSlot.Eight,
CourtSlot.Nine,
CourtSlot.Ten,
],
[StartTimeClass.Third]: [
CourtSlot.Eleven,
CourtSlot.Twelve,
CourtSlot.Thirteen,
],
} as const
const StartTimeClassStartTimes = {
[StartTimeClass.First]: [
'07:15',
'08:00',
'08:45',
'09:30',
'10:15',
'11:00',
'11:45',
'12:30',
'13:15',
'14:00',
'14:45',
'15:30',
'16:15',
'17:00',
'17:45',
'18:30',
'19:15',
'20:00',
'20:45',
'21:30',
'22:15',
],
[StartTimeClass.Second]: [
'07:45',
'08:30',
'09:15',
'10:00',
'10:45',
'11:30',
'12:15',
'13:00',
'13:45',
'14:30',
'15:15',
'16:00',
'16:45',
'17:30',
'18:15',
'19:00',
'19:45',
'20:30',
'21:15',
'22:00',
],
[StartTimeClass.Third]: [
'07:30',
'08:15',
'09:00',
'09:45',
'10:30',
'11:15',
'12:00',
'12:45',
'13:30',
'14:15',
'15:00',
'15:45',
'16:30',
'17:15',
'18:00',
'18:45',
'19:30',
'20:15',
'21:00',
'21:45',
'22:30',
],
}
const TYPING_DELAY_MS = 2
@Injectable()
@ -234,6 +333,17 @@ export class BaanReserverenService {
return lastDayOfMonth.add(daysToAdd, 'day')
}
private async navigateToReservationPrompt(courtSlot: CourtSlot, date: Dayjs) {
this.loggerService.debug('Navigating to reservation prompt', {
courtSlot,
date,
})
const utcSeconds = date.valueOf() / 1000
await this.page.goto(
`${BAAN_RESERVEREN_ROOT_URL}/reservations/make/${courtSlot}/${utcSeconds}`,
)
}
private async navigateToDay(date: Dayjs) {
this.loggerService.debug('Navigating to day', { date })
if (this.getLastVisibleDay().isBefore(date)) {
@ -473,7 +583,7 @@ export class BaanReserverenService {
throw new RunnerReservationConfirmButtonError(e)
})
await this.page
.waitForSelector('input#__make_submit2')
.waitForSelector('input#__make_submit2', { timeout: 500 }) // quick timeout here because all it is checking is that the reservation is still available
.then((b) => b?.click())
.catch((e: Error) => {
throw new RunnerReservationConfirmSubmitError(e)
@ -565,6 +675,15 @@ export class BaanReserverenService {
return courtStatuses
}
private getCourtSlotsForDate(date: Dayjs) {
const time = date.format('HH:mm')
for (const [timeClass, times] of Object.entries(StartTimeClassStartTimes)) {
if (times.includes(time)) {
return StartTimeClassCourtSlots[timeClass as StartTimeClass]
}
}
}
public async performReservation(
reservation: Reservation,
timeSensitive = true,
@ -582,6 +701,31 @@ export class BaanReserverenService {
}
}
public async performSpeedyReservation(reservation: Reservation) {
await this.init()
const courtSlots = this.getCourtSlotsForDate(reservation.dateRangeStart)
if (courtSlots == null) {
throw new NoCourtAvailableError('Improper time for courts')
}
for (const courtSlot of courtSlots) {
await this.navigateToReservationPrompt(
courtSlot,
reservation.dateRangeStart,
)
await this.selectOwner(reservation.ownerId)
await this.selectOpponents(reservation.opponents)
await this.confirmReservation().catch((error: Error) => {
if (error instanceof RunnerReservationConfirmSubmitError) {
this.loggerService.warn('Court taken, retrying', { courtSlot })
return
}
throw error
})
}
}
public async addReservationToWaitList(
reservation: Reservation,
timeSensitive = true,