aboutsummaryrefslogtreecommitdiff
path: root/src/lib/Data/AniList/activity.ts
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-03-01 16:20:51 -0800
committerFuwn <[email protected]>2026-03-01 16:21:02 -0800
commiteae5d24d9e79e59a19d4721caaeaa0ca650ecb33 (patch)
tree1b685bb248e051dfa26d2bfdebe6689402dd93c5 /src/lib/Data/AniList/activity.ts
parentchore(tooling): remove legacy eslint and prettier (diff)
downloaddue.moe-eae5d24d9e79e59a19d4721caaeaa0ca650ecb33.tar.xz
due.moe-eae5d24d9e79e59a19d4721caaeaa0ca650ecb33.zip
chore(biome): drop formatter style overrides
Diffstat (limited to 'src/lib/Data/AniList/activity.ts')
-rw-r--r--src/lib/Data/AniList/activity.ts526
1 files changed, 285 insertions, 241 deletions
diff --git a/src/lib/Data/AniList/activity.ts b/src/lib/Data/AniList/activity.ts
index 9a8d13ba..11466cc5 100644
--- a/src/lib/Data/AniList/activity.ts
+++ b/src/lib/Data/AniList/activity.ts
@@ -1,309 +1,353 @@
-import { database } from '$lib/Database/IDB/activities';
-import type { User } from './follow';
-import type { AniListAuthorisation, UserIdentity } from './identity';
+import { database } from "$lib/Database/IDB/activities";
+import type { User } from "./follow";
+import type { AniListAuthorisation, UserIdentity } from "./identity";
export interface ActivityHistoryEntry {
- date: number;
- amount: number;
+ date: number;
+ amount: number;
}
interface ActivityHistoryOptions {
- stats: {
- activityHistory: ActivityHistoryEntry[];
- };
- options: {
- timezone: string;
- };
+ stats: {
+ activityHistory: ActivityHistoryEntry[];
+ };
+ options: {
+ timezone: string;
+ };
}
interface LastActivity {
- date: Date;
- timezone: string;
+ date: Date;
+ timezone: string;
}
export const fillMissingDays = (
- inputActivities: ActivityHistoryEntry[],
- startOfYear = false,
- year = new Date().getFullYear()
+ inputActivities: ActivityHistoryEntry[],
+ startOfYear = false,
+ year = new Date().getFullYear(),
): ActivityHistoryEntry[] => {
- const yearDate = new Date(year, 0, 0);
-
- if (inputActivities.length === 0)
- return startOfYear
- ? fillDateRange(
- new Date(yearDate.getUTCFullYear(), 0, 1),
- new Date(yearDate.getUTCFullYear() + 1, 0, 1)
- )
- : [];
-
- const sortedActivities = [...inputActivities].sort((a, b) => a.date - b.date);
- const endDate = new Date(sortedActivities[sortedActivities.length - 1].date * 1000);
-
- endDate.setUTCDate(endDate.getUTCDate() + 1);
-
- return fillDateRange(
- startOfYear
- ? new Date(yearDate.getUTCFullYear(), 0, 1)
- : new Date(sortedActivities[0].date * 1000),
- endDate,
- sortedActivities
- );
+ const yearDate = new Date(year, 0, 0);
+
+ if (inputActivities.length === 0)
+ return startOfYear
+ ? fillDateRange(
+ new Date(yearDate.getUTCFullYear(), 0, 1),
+ new Date(yearDate.getUTCFullYear() + 1, 0, 1),
+ )
+ : [];
+
+ const sortedActivities = [...inputActivities].sort((a, b) => a.date - b.date);
+ const endDate = new Date(
+ sortedActivities[sortedActivities.length - 1].date * 1000,
+ );
+
+ endDate.setUTCDate(endDate.getUTCDate() + 1);
+
+ return fillDateRange(
+ startOfYear
+ ? new Date(yearDate.getUTCFullYear(), 0, 1)
+ : new Date(sortedActivities[0].date * 1000),
+ endDate,
+ sortedActivities,
+ );
};
const fillDateRange = (
- startDate: Date,
- endDate: Date,
- existingActivities: ActivityHistoryEntry[] = []
+ startDate: Date,
+ endDate: Date,
+ existingActivities: ActivityHistoryEntry[] = [],
): ActivityHistoryEntry[] => {
- const outputActivities: ActivityHistoryEntry[] = [];
-
- for (let dt = new Date(startDate); dt < endDate; dt.setUTCDate(dt.getUTCDate() + 1)) {
- const dateString = dt.toDateString();
-
- if (
- !new Set(
- existingActivities.map((activity) => new Date(activity.date * 1000).toDateString())
- ).has(dateString)
- ) {
- outputActivities.push({ date: Math.floor(dt.getTime() / 1000), amount: 0 });
- } else {
- const activity = existingActivities.find(
- (activity) => new Date(activity.date * 1000).toDateString() === dateString
- );
-
- if (activity) outputActivities.push(activity);
- }
- }
-
- return outputActivities;
+ const outputActivities: ActivityHistoryEntry[] = [];
+
+ for (
+ let dt = new Date(startDate);
+ dt < endDate;
+ dt.setUTCDate(dt.getUTCDate() + 1)
+ ) {
+ const dateString = dt.toDateString();
+
+ if (
+ !new Set(
+ existingActivities.map((activity) =>
+ new Date(activity.date * 1000).toDateString(),
+ ),
+ ).has(dateString)
+ ) {
+ outputActivities.push({
+ date: Math.floor(dt.getTime() / 1000),
+ amount: 0,
+ });
+ } else {
+ const activity = existingActivities.find(
+ (activity) =>
+ new Date(activity.date * 1000).toDateString() === dateString,
+ );
+
+ if (activity) outputActivities.push(activity);
+ }
+ }
+
+ return outputActivities;
};
export const activityHistoryOptions = async (
- userIdentity: UserIdentity,
- authorisation?: AniListAuthorisation
+ userIdentity: UserIdentity,
+ authorisation?: AniListAuthorisation,
): Promise<ActivityHistoryOptions> => {
- return (
- await (
- await fetch('https://graphql.anilist.co', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Accept: 'application/json',
- ...(authorisation
- ? { Authorization: `${authorisation.tokenType} ${authorisation.accessToken}` }
- : {})
- },
- body: JSON.stringify({
- query: `{ User(id: ${userIdentity.id}) {
+ return (
+ await (
+ await fetch("https://graphql.anilist.co", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ ...(authorisation
+ ? {
+ Authorization: `${authorisation.tokenType} ${authorisation.accessToken}`,
+ }
+ : {}),
+ },
+ body: JSON.stringify({
+ query: `{ User(id: ${userIdentity.id}) {
stats { activityHistory { date amount } }
- ${authorisation ? 'options { timezone }' : ''}
- } }`
- })
- })
- ).json()
- )['data']['User'];
+ ${authorisation ? "options { timezone }" : ""}
+ } }`,
+ }),
+ })
+ ).json()
+ )["data"]["User"];
};
const convertToTimezoneOffset = (timeStr: string) => {
- const [hours, minutes] = timeStr.split(':');
- let totalMinutes = parseInt(hours, 10) * 60 + parseInt(minutes, 10);
+ const [hours, minutes] = timeStr.split(":");
+ let totalMinutes = parseInt(hours, 10) * 60 + parseInt(minutes, 10);
- totalMinutes = -totalMinutes;
+ totalMinutes = -totalMinutes;
- return totalMinutes;
+ return totalMinutes;
};
export const activityHistory = async (
- userIdentity: UserIdentity,
- authorisation?: AniListAuthorisation
+ userIdentity: UserIdentity,
+ authorisation?: AniListAuthorisation,
): Promise<ActivityHistoryEntry[]> =>
- (await activityHistoryOptions(userIdentity, authorisation)).stats.activityHistory;
+ (await activityHistoryOptions(userIdentity, authorisation)).stats
+ .activityHistory;
export const lastActivityDate = async (
- userIdentity: UserIdentity,
- authorisation: AniListAuthorisation
+ userIdentity: UserIdentity,
+ authorisation: AniListAuthorisation,
): Promise<LastActivity> => {
- if (userIdentity.id === -1 || userIdentity.id === -2)
- return { date: new Date(8640000000000000), timezone: '' };
-
- const history = await activityHistoryOptions(userIdentity, authorisation);
- const date = new Date(
- Number(history.stats.activityHistory[history.stats.activityHistory.length - 1].date) * 1000 +
- convertToTimezoneOffset(history.options.timezone)
- );
-
- date.setDate(date.getDate() + 1);
-
- return { date, timezone: history.options.timezone };
+ if (userIdentity.id === -1 || userIdentity.id === -2)
+ return { date: new Date(8640000000000000), timezone: "" };
+
+ const history = await activityHistoryOptions(userIdentity, authorisation);
+ const date = new Date(
+ Number(
+ history.stats.activityHistory[history.stats.activityHistory.length - 1]
+ .date,
+ ) *
+ 1000 +
+ convertToTimezoneOffset(history.options.timezone),
+ );
+
+ date.setDate(date.getDate() + 1);
+
+ return { date, timezone: history.options.timezone };
};
export interface ActivitiesPage {
- data: {
- Page: {
- pageInfo: {
- hasNextPage: boolean;
- };
- activities: {
- createdAt: number;
- }[];
- };
- };
+ data: {
+ Page: {
+ pageInfo: {
+ hasNextPage: boolean;
+ };
+ activities: {
+ createdAt: number;
+ }[];
+ };
+ };
}
const activitiesPage = async (
- page: number,
- anilistAuthorisation: AniListAuthorisation,
- userIdentity: UserIdentity,
- year = new Date().getFullYear()
+ page: number,
+ anilistAuthorisation: AniListAuthorisation,
+ userIdentity: UserIdentity,
+ year = new Date().getFullYear(),
): Promise<ActivitiesPage> =>
- 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: `{
+ 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: `{
Page(page: ${page}) {
pageInfo { hasNextPage }
activities(userId: ${userIdentity.id}, createdAt_greater: ${Math.floor(
- new Date(year, 0, 1).getTime() / 1000
- )}, createdAt_lesser: ${Math.floor(new Date(year, 7, 1).getTime() / 1000)}) {
+ new Date(year, 0, 1).getTime() / 1000,
+ )}, createdAt_lesser: ${Math.floor(new Date(year, 7, 1).getTime() / 1000)}) {
... on TextActivity { createdAt }
... on ListActivity { createdAt }
... on MessageActivity { createdAt }
}
}
- }`
- })
- })
- ).json();
+ }`,
+ }),
+ })
+ ).json();
export const fullActivityHistory = async (
- anilistAuthorisation: AniListAuthorisation,
- userIdentity: UserIdentity,
- year = new Date().getFullYear(),
- disableLoopingActivityCounter = false
+ anilistAuthorisation: AniListAuthorisation,
+ userIdentity: UserIdentity,
+ year = new Date().getFullYear(),
+ disableLoopingActivityCounter = false,
): Promise<ActivityHistoryEntry[]> => {
- const activities = [];
- let page = 1;
- let currentDatabasePage = await database.activities.get(page);
- let currentPage: ActivitiesPage;
-
- if (currentDatabasePage) currentPage = currentDatabasePage.data;
- else {
- currentPage = await activitiesPage(page, anilistAuthorisation, userIdentity, year);
- database.activities.add({
- page,
- data: currentPage
- });
- }
-
- for (const activity of currentPage.data.Page.activities) activities.push(activity);
-
- while (currentPage['data']['Page']['pageInfo']['hasNextPage']) {
- if (disableLoopingActivityCounter) break;
-
- for (const activity of currentPage.data.Page.activities) activities.push(activity);
-
- page += 1;
- currentDatabasePage = await database.activities.get(page);
-
- if (currentDatabasePage) currentPage = currentDatabasePage.data;
- else {
- currentPage = await activitiesPage(page, anilistAuthorisation, userIdentity, year);
- database.activities.add({
- page,
- data: currentPage
- });
- }
- }
-
- for (const activity of currentPage.data.Page.activities) activities.push(activity);
-
- let fullLocalActivityHistory: ActivityHistoryEntry[] = [];
-
- for (const activity of activities) {
- const date = new Date(activity.createdAt * 1000);
- const dateString = date.toDateString();
-
- const activityHistoryEntry = fullLocalActivityHistory.find(
- (activityHistoryEntry) =>
- new Date(activityHistoryEntry.date * 1000).toDateString() === dateString
- );
-
- if (activityHistoryEntry) activityHistoryEntry.amount += 1;
- else fullLocalActivityHistory.push({ date: Math.floor(date.getTime() / 1000), amount: 1 });
- }
-
- fullLocalActivityHistory = fullLocalActivityHistory.filter((a) => !isNaN(a.date));
-
- if (new Date().getMonth() > 6)
- fullLocalActivityHistory.push(...(await activityHistory(userIdentity)));
-
- fullLocalActivityHistory = fullLocalActivityHistory.filter(
- (activityHistoryEntry, index, self) =>
- self.findIndex(
- (a) =>
- new Date(a.date * 1000).toDateString() ===
- new Date(activityHistoryEntry.date * 1000).toDateString()
- ) === index
- );
-
- fullLocalActivityHistory = fullLocalActivityHistory.filter(
- (activityHistoryEntry) => new Date(activityHistoryEntry.date * 1000).getFullYear() === year
- );
-
- return fullLocalActivityHistory;
+ const activities = [];
+ let page = 1;
+ let currentDatabasePage = await database.activities.get(page);
+ let currentPage: ActivitiesPage;
+
+ if (currentDatabasePage) currentPage = currentDatabasePage.data;
+ else {
+ currentPage = await activitiesPage(
+ page,
+ anilistAuthorisation,
+ userIdentity,
+ year,
+ );
+ database.activities.add({
+ page,
+ data: currentPage,
+ });
+ }
+
+ for (const activity of currentPage.data.Page.activities)
+ activities.push(activity);
+
+ while (currentPage["data"]["Page"]["pageInfo"]["hasNextPage"]) {
+ if (disableLoopingActivityCounter) break;
+
+ for (const activity of currentPage.data.Page.activities)
+ activities.push(activity);
+
+ page += 1;
+ currentDatabasePage = await database.activities.get(page);
+
+ if (currentDatabasePage) currentPage = currentDatabasePage.data;
+ else {
+ currentPage = await activitiesPage(
+ page,
+ anilistAuthorisation,
+ userIdentity,
+ year,
+ );
+ database.activities.add({
+ page,
+ data: currentPage,
+ });
+ }
+ }
+
+ for (const activity of currentPage.data.Page.activities)
+ activities.push(activity);
+
+ let fullLocalActivityHistory: ActivityHistoryEntry[] = [];
+
+ for (const activity of activities) {
+ const date = new Date(activity.createdAt * 1000);
+ const dateString = date.toDateString();
+
+ const activityHistoryEntry = fullLocalActivityHistory.find(
+ (activityHistoryEntry) =>
+ new Date(activityHistoryEntry.date * 1000).toDateString() ===
+ dateString,
+ );
+
+ if (activityHistoryEntry) activityHistoryEntry.amount += 1;
+ else
+ fullLocalActivityHistory.push({
+ date: Math.floor(date.getTime() / 1000),
+ amount: 1,
+ });
+ }
+
+ fullLocalActivityHistory = fullLocalActivityHistory.filter(
+ (a) => !isNaN(a.date),
+ );
+
+ if (new Date().getMonth() > 6)
+ fullLocalActivityHistory.push(...(await activityHistory(userIdentity)));
+
+ fullLocalActivityHistory = fullLocalActivityHistory.filter(
+ (activityHistoryEntry, index, self) =>
+ self.findIndex(
+ (a) =>
+ new Date(a.date * 1000).toDateString() ===
+ new Date(activityHistoryEntry.date * 1000).toDateString(),
+ ) === index,
+ );
+
+ fullLocalActivityHistory = fullLocalActivityHistory.filter(
+ (activityHistoryEntry) =>
+ new Date(activityHistoryEntry.date * 1000).getFullYear() === year,
+ );
+
+ return fullLocalActivityHistory;
};
export const activityLikes = async (id: number): Promise<Partial<User>[]> => {
- const activityResponse = await (
- await fetch('https://graphql.anilist.co', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Accept: 'application/json'
- },
- body: JSON.stringify({
- query: `{
+ const activityResponse = await (
+ await fetch("https://graphql.anilist.co", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ },
+ body: JSON.stringify({
+ query: `{
Activity(id: ${id}) {
__typename
... on TextActivity { likes { name avatar { large } } }
... on ListActivity { likes { name avatar { large } } }
... on MessageActivity { likes { name avatar { large } } }
}
- }`
- })
- })
- ).json();
+ }`,
+ }),
+ })
+ ).json();
- return activityResponse['data']['Activity']['likes'];
+ return activityResponse["data"]["Activity"]["likes"];
};
-export const activityText = async (id: number, replies = false): Promise<string> => {
- const activityResponse = await (
- await fetch('https://graphql.anilist.co', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Accept: 'application/json'
- },
- body: JSON.stringify({
- query: `{
+export const activityText = async (
+ id: number,
+ replies = false,
+): Promise<string> => {
+ const activityResponse = await (
+ await fetch("https://graphql.anilist.co", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ },
+ body: JSON.stringify({
+ query: `{
Activity(id: ${id}) {
- ... on TextActivity { text(asHtml: true) ${replies ? 'replies { text(asHtml: true) }' : ''} }
+ ... on TextActivity { text(asHtml: true) ${replies ? "replies { text(asHtml: true) }" : ""} }
}
- }`
- })
- })
- ).json();
- let text = activityResponse['data']['Activity']['text'];
+ }`,
+ }),
+ })
+ ).json();
+ let text = activityResponse["data"]["Activity"]["text"];
- if (replies)
- for (const reply of activityResponse['data']['Activity']['replies']) text += reply.text;
+ if (replies)
+ for (const reply of activityResponse["data"]["Activity"]["replies"])
+ text += reply.text;
- return text;
+ return text;
};