import { type Notification, notifications, } from "$lib/Data/AniList/notifications"; import { refreshAniListToken } from "$lib/Utility/anilistOauth"; import { siteUrl } from "$lib/Utility/appOrigin"; import { decryptFeedToken } from "$lib/Utility/feedToken"; const htmlEncode = (input: string) => { return input.replace(/[\u00A0-\u9999<>&]/g, (i) => `&#${i.charCodeAt(0)};`); }; const channel = (items: string) => ` AniList Notifications • due.moe ${siteUrl("/")} Instantly view your AniList notifications via RSS! ${new Date().toUTCString()} ${new Date().toUTCString()} en-US ${siteUrl("/favicon-196x196.png")} ${items} `; const render = (posts: Notification[] = []) => channel( posts .filter((notification: Notification) => notification.type !== undefined) .map((notification: Notification) => { let title = `${notification.user.name}${notification.context}`; let link = `https://anilist.co/user/${notification.user.name}`; const prettyType = notification.type .toString() .replace(/_/g, " ") .toLowerCase() .replace(/\w\S*/g, (text) => { return text.charAt(0).toUpperCase() + text.substr(1).toLowerCase(); }); try { if ( !["FOLLOWING", "ACTIVITY_MESSAGE"].includes( notification.type.toString(), ) && !notification.type.toString().includes("THREAD") ) { link = `https://anilist.co/activity/${notification.activity.id}`; } else if (notification.type.toString().includes("THREAD")) { title += `${notification.thread.title}`; link = `https://anilist.co/forum/thread/${notification.thread.id}`; } } catch { return ""; } return ` ${notification.id} ${htmlEncode(title)} ${link} ${notification.user.name} ${prettyType} ${new Date(notification.createdAt * 1000).toUTCString()} `; }) .join(""), ); // Old feed URLs carried the tokens in clear (?token=&refresh=); they no longer // resolve, so degrade to a single item nudging the user to re-copy the link // instead of silently going empty. const staleNotice = () => channel(` due-moe-feed-url-outdated ${htmlEncode("Your due.moe RSS feed URL is outdated — re-copy it from Settings")} ${siteUrl("/settings#feeds")} ${htmlEncode("This feed link no longer works. Open due.moe → Settings → RSS Feeds and copy the new URL into your reader.")} ${new Date().toUTCString()} `); const feed = (body: string) => new Response(body, { headers: { "Cache-Control": "max-age=0", "Content-Type": "application/xml", }, }); export const GET = async ({ url }) => { const sealed = url.searchParams.get("feed"); const refreshToken = sealed ? await decryptFeedToken(sealed) : null; if (!refreshToken) return feed(staleNotice()); const accessToken = await refreshAniListToken(refreshToken); if (!accessToken) return feed(render()); const notification = await notifications(accessToken); return feed(render(notification || [])); };