aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2023-09-17 19:58:24 -0700
committerFuwn <[email protected]>2023-09-17 19:58:24 -0700
commitcd49dd513c27cb8d856d822dcc778a77cf7d670e (patch)
treedf75c622e91c7f3877cb740bbcabfd2aab121f1d /src
parentfeat(oauth): refresh token route (diff)
downloaddue.moe-cd49dd513c27cb8d856d822dcc778a77cf7d670e.tar.xz
due.moe-cd49dd513c27cb8d856d822dcc778a77cf7d670e.zip
feat(routes): rss feed git status
Diffstat (limited to 'src')
-rw-r--r--src/lib/AniList/notifications.ts62
-rw-r--r--src/routes/feeds/notifications/+server.ts58
2 files changed, 120 insertions, 0 deletions
diff --git a/src/lib/AniList/notifications.ts b/src/lib/AniList/notifications.ts
new file mode 100644
index 00000000..8640e105
--- /dev/null
+++ b/src/lib/AniList/notifications.ts
@@ -0,0 +1,62 @@
+export interface Notification {
+ user: {
+ name: string;
+ avatar: {
+ large: string;
+ };
+ };
+ context: string;
+ id: number;
+ createdAt: number;
+ type:
+ | 'FOLLOWING'
+ | 'ACTIVITY_MESSAGE'
+ | 'ACTIVITY_MENTION'
+ | 'ACTIVITY_REPLY'
+ | 'ACTIVITY_REPLY_SUBSCRIBED'
+ | 'ACTIVITY_LIKE'
+ | 'ACTIVITY_REPLY_LIKE'
+ | 'THREAD_COMMENT_MENTION'
+ | 'THREAD_COMMENT_REPLY'
+ | 'THREAD_SUBSCRIBED'
+ | 'THREAD_COMMENT_LIKE'
+ | 'THREAD_LIKE';
+}
+
+export const notifications = async (accessToken: string): Promise<Notification[]> => {
+ const activityNotification = (type: string) => `... on ${type} {
+ id user { name avatar { large } } context createdAt type
+ }`;
+ const threadNotification = (type: string) => `... on ${type} {
+ id user { name avatar { large } } context createdAt type thread { title }
+ }`;
+
+ return (
+ await (
+ await fetch('https://graphql.anilist.co', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`,
+ Accept: 'application/json'
+ },
+ body: JSON.stringify({
+ query: `{ Page(page: 1, perPage: 50) { notifications {
+ ${activityNotification('FollowingNotification')}
+ ${activityNotification('ActivityMessageNotification')}
+ ${activityNotification('ActivityMentionNotification')}
+ ${activityNotification('ActivityReplyNotification')}
+ ${activityNotification('ActivityReplySubscribedNotification')}
+ ${activityNotification('ActivityLikeNotification')}
+ ${activityNotification('ActivityReplyLikeNotification')}
+ ${threadNotification('ThreadCommentMentionNotification')}
+ ${threadNotification('ThreadCommentReplyNotification')}
+ ${threadNotification('ThreadCommentSubscribedNotification')}
+ ${threadNotification('ThreadCommentLikeNotification')}
+ ${threadNotification('ThreadLikeNotification')}
+ } } }`
+ })
+ })
+ ).json()
+ )['data']['Page']['notifications'];
+};
diff --git a/src/routes/feeds/notifications/+server.ts b/src/routes/feeds/notifications/+server.ts
new file mode 100644
index 00000000..b06aa1f3
--- /dev/null
+++ b/src/routes/feeds/notifications/+server.ts
@@ -0,0 +1,58 @@
+import { notifications, type Notification } from '$lib/AniList/notifications';
+
+export const GET = async ({ url }) => {
+ const data = await notifications(url.searchParams.get('token') || 'null');
+ const render = (posts: Notification[]) => `<?xml version="1.0" encoding="UTF-8" ?>
+<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
+ <channel>
+ <atom:link href="https://due.moe/feeds/notifications" rel="self" type="application/rss+xml" />
+ <title>期限 | AniList Notifications</title>
+ <link>https://due.moe</link>
+ <description>Instantly view your AniList notifications via RSS!</description>
+ <pubDate>${new Date().toUTCString()}</pubDate>
+ <lastBuildDate>${new Date().toUTCString()}</lastBuildDate>
+ <language>en-US</language>
+ <image>
+ <url>https://due.moe/favicon-192x192.png</url>
+ <link>https://due.moe</link>
+ <title>期限 Logo</title>
+ </image>
+ ${posts
+ .map((notification: Notification) => {
+ let title = `${notification.user.name} ${notification.context}`;
+ const prettyType = notification.type
+ .toString()
+ .replace(/_/g, ' ')
+ .toLowerCase()
+ .replace(/\w\S*/g, (text) => {
+ return text.charAt(0).toUpperCase() + text.substr(1).toLowerCase();
+ });
+
+ if (notification.type.toString().includes('THREAD')) {
+ title += `${notification.thread.title}`;
+ }
+
+ return `<item>
+<guid>${notification.id}</guid>
+<title>${title}</title>
+<link>https://anilist.co/user/${notification.user.name}</link>
+<author>${notification.user.name}</author>
+<enclosure url="${notification.user.avatar.large}" type="image/png" />
+<category>${prettyType}</category>
+<pubDate>${new Date(
+ notification.createdAt * 1000 + new Date().getTimezoneOffset()
+ ).toUTCString()}</pubDate>
+</item>`;
+ })
+ .join('')}
+ </channel>
+</rss>
+`;
+
+ return new Response(render(data), {
+ headers: {
+ 'Cache-Control': `max-age=0, s-max-age=${600}`,
+ 'Content-Type': 'application/xml'
+ }
+ });
+};