aboutsummaryrefslogtreecommitdiff
path: root/src/lib/AniList
diff options
context:
space:
mode:
authorFuwn <[email protected]>2023-09-27 23:33:01 -0700
committerFuwn <[email protected]>2023-09-27 23:33:01 -0700
commit8562ba4280c575b3f04df598b7954a2d28b19e50 (patch)
treeeaa43530441522b5104a1fd1e404ce6b663fc9b0 /src/lib/AniList
parentfix(anime): template increment render (diff)
downloaddue.moe-8562ba4280c575b3f04df598b7954a2d28b19e50.tar.xz
due.moe-8562ba4280c575b3f04df598b7954a2d28b19e50.zip
feat(wrapped): initial wrapped prototype
Diffstat (limited to 'src/lib/AniList')
-rw-r--r--src/lib/AniList/activity.ts39
-rw-r--r--src/lib/AniList/media.ts12
-rw-r--r--src/lib/AniList/wrapped.ts113
3 files changed, 161 insertions, 3 deletions
diff --git a/src/lib/AniList/activity.ts b/src/lib/AniList/activity.ts
index bd4bafc0..df150c96 100644
--- a/src/lib/AniList/activity.ts
+++ b/src/lib/AniList/activity.ts
@@ -5,6 +5,45 @@ export interface ActivityHistoryEntry {
amount: number;
}
+export const fillMissingDays = (
+ inputActivities: ActivityHistoryEntry[],
+ startOfYear = false
+): ActivityHistoryEntry[] => {
+ const timezoneOffset = new Date().getTimezoneOffset() * 60 * 1000;
+ const activities = inputActivities;
+ const firstDate = startOfYear
+ ? new Date(new Date().getFullYear(), 0, 1, 0, 0, 0, 0)
+ : new Date(activities[0].date * 1000 + timezoneOffset);
+ const lastDate = new Date(activities[activities.length - 1].date * 1000 + timezoneOffset);
+ const currentDate = firstDate;
+
+ while (currentDate <= lastDate) {
+ const current_unix_timestamp = currentDate.getTime();
+ let found = false;
+
+ for (let i = 0; i < activities.length; i++) {
+ if (activities[i].date * 1000 + timezoneOffset === current_unix_timestamp) {
+ found = true;
+
+ break;
+ }
+ }
+
+ if (!found) {
+ activities.push({
+ date: current_unix_timestamp / 1000,
+ amount: 0
+ });
+ }
+
+ currentDate.setDate(currentDate.getDate() + 1);
+ }
+
+ // activities.sort((a: { date: number }, b: { date: number }) => a.date - b.date);
+
+ return activities;
+};
+
export const activityHistory = async (
userIdentity: UserIdentity
): Promise<ActivityHistoryEntry[]> => {
diff --git a/src/lib/AniList/media.ts b/src/lib/AniList/media.ts
index 6187094d..d9ce5e57 100644
--- a/src/lib/AniList/media.ts
+++ b/src/lib/AniList/media.ts
@@ -28,10 +28,14 @@ export interface Media {
mediaListEntry?: {
progress: number;
status: string;
+ score: number;
};
startDate: {
year: number;
};
+ coverImage: {
+ extraLarge: string;
+ };
}
export const flattenLists = (lists: { entries: { media: Media }[] }[]) => {
@@ -59,7 +63,8 @@ export const mediaListCollection = async (
type: Type,
mediaCache: string | undefined,
currentLastPruneAt: string | number,
- forcePrune = false
+ forcePrune = false,
+ includeCompleted = false
): Promise<Media[]> => {
let currentCacheMinutes;
@@ -106,7 +111,7 @@ export const mediaListCollection = async (
body: JSON.stringify({
query: `{ MediaListCollection(userId: ${userIdentity.id}, type: ${
type === Type.Anime ? 'ANIME' : 'MANGA'
- }, status_not_in: [ COMPLETED ]) {
+ }${includeCompleted ? '' : ', status_not_in: [ COMPLETED ]'}) {
lists { entries { media {
id
status
@@ -115,8 +120,9 @@ export const mediaListCollection = async (
format
title { romaji english native }
nextAiringEpisode { episode timeUntilAiring }
- mediaListEntry { progress status }
+ mediaListEntry { progress status score(format: POINT_100) }
startDate { year }
+ coverImage { extraLarge }
} } }
}
}`
diff --git a/src/lib/AniList/wrapped.ts b/src/lib/AniList/wrapped.ts
new file mode 100644
index 00000000..bd700101
--- /dev/null
+++ b/src/lib/AniList/wrapped.ts
@@ -0,0 +1,113 @@
+import type { AniListAuthorisation, UserIdentity } from './identity';
+
+export interface Wrapped {
+ statistics: {
+ anime: {
+ startYears: {
+ startYear: number;
+ minutesWatched: number;
+ count: number;
+ }[];
+ };
+ manga: {
+ startYears: {
+ startYear: number;
+ chaptersRead: number;
+ count: number;
+ }[];
+ };
+ };
+ activities: {
+ statusCount: number;
+ messageCount: number;
+ };
+ avatar: {
+ large: string;
+ };
+}
+
+const profileActivities = async (user: AniListAuthorisation, identity: UserIdentity) => {
+ const get = async (page: number) =>
+ await (
+ await fetch('https://graphql.anilist.co', {
+ method: 'POST',
+ headers: {
+ Authorization: `${user.tokenType} ${user.accessToken}`,
+ 'Content-Type': 'application/json',
+ Accept: 'application/json'
+ },
+ body: JSON.stringify({
+ query: `{
+ Page(page: ${page}) {
+ activities(userId: ${identity.id}, type_in: [ TEXT, MESSAGE ]) {
+ ... on TextActivity {
+ type
+ }
+ ... on MessageActivity {
+ type
+ }
+ }
+ pageInfo {
+ hasNextPage
+ }
+ }
+}`
+ })
+ })
+ ).json();
+
+ const pages = [];
+ let page = 1;
+ let response = await get(page);
+
+ pages.push(response['data']['Page']['activities']);
+
+ while (response['data']['Page']['pageInfo']['hasNextPage']) {
+ page += 1;
+ response = await get(page);
+
+ pages.push(response['data']['Page']['activities']);
+ }
+
+ return {
+ statusCount: pages.flat().filter((activity) => activity.type == 'TEXT').length,
+ messageCount: pages.flat().filter((activity) => activity.type == 'MESSAGE').length
+ };
+};
+
+export const wrapped = async (
+ anilistAuthorisation: AniListAuthorisation,
+ identity: UserIdentity
+): Promise<Wrapped> => {
+ const wrappedResponse = await (
+ await fetch('https://graphql.anilist.co', {
+ method: 'POST',
+ headers: {
+ Authorization: `${anilistAuthorisation.tokenType} ${anilistAuthorisation.accessToken}`,
+ 'Content-Type': 'application/json',
+ Accept: 'application/json'
+ },
+ body: JSON.stringify({
+ query: `{
+ User(name: "${identity.name}") {
+ avatar { large }
+ statistics {
+ anime { startYears { startYear minutesWatched count } }
+ manga { startYears { startYear chaptersRead count } }
+ }
+ }
+}`
+ })
+ })
+ ).json();
+ const { statusCount, messageCount } = await profileActivities(anilistAuthorisation, identity);
+
+ return {
+ statistics: wrappedResponse['data']['User']['statistics'],
+ activities: {
+ statusCount,
+ messageCount
+ },
+ avatar: wrappedResponse['data']['User']['avatar']
+ };
+};