import { graphql } from "gql.tada"; import { GraphQLClient } from "graphql-request"; import { DateTime } from "luxon"; import { getValue, setValue } from "~/models/kv"; import type { Env } from "~/types/env"; import type { Title } from "~/types/title"; import { MediaFragment } from "~/types/title/mediaFragment"; const GetUpcomingTitlesQuery = graphql( ` query GetUpcomingTitles( $page: Int! $airingAtLowerBound: Int! $airingAtUpperBound: Int! ) { Page(page: $page) { airingSchedules( notYetAired: true sort: TIME airingAt_lesser: $airingAtUpperBound airingAt_greater: $airingAtLowerBound ) { id airingAt timeUntilAiring episode media { ...Media } } pageInfo { hasNextPage } } } `, [MediaFragment], ); type AiringSchedule = { media: Title; episode: number; timeUntilAiring: number; airingAt: number; id: number; }; export async function getUpcomingTitlesFromAnilist(env: Env) { const client = new GraphQLClient("https://graphql.anilist.co/"); const lastCheckedScheduleAt = await getValue( env, "schedule_last_checked_at", ).then((value) => (value ? Number(value) : DateTime.now().toUnixInteger())); console.log(lastCheckedScheduleAt); const twoDaysFromNow = DateTime.now().plus({ days: 2 }).toUnixInteger(); let currentPage = 1; let scheduleList: AiringSchedule[] = []; let shouldContinue = true; do { const { Page } = await client.request(GetUpcomingTitlesQuery, { page: currentPage++, airingAtLowerBound: lastCheckedScheduleAt, airingAtUpperBound: twoDaysFromNow, }); const { airingSchedules, pageInfo } = Page!; scheduleList = scheduleList.concat( airingSchedules!.filter( (schedule): schedule is AiringSchedule => !!schedule, ), ); shouldContinue = pageInfo?.hasNextPage ?? false; } while (shouldContinue); if (scheduleList.length === 0) { return []; } await setValue( env, "schedule_last_checked_at", scheduleList[scheduleList.length - 1].airingAt.toString(), ); return scheduleList; }