From 188c714f43635fb57eac70b167dba682d6b93a2f Mon Sep 17 00:00:00 2001 From: Fuwn Date: Sun, 7 Sep 2025 02:28:34 -0700 Subject: build: Switch to TypeScript --- src/reddit.ts | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 src/reddit.ts (limited to 'src/reddit.ts') diff --git a/src/reddit.ts b/src/reddit.ts new file mode 100644 index 0000000..79475b1 --- /dev/null +++ b/src/reddit.ts @@ -0,0 +1,146 @@ +import type { TimePeriod } from './commands.js'; + +export interface RedditPost { + id: string; + title: string; + author: string; + score: number; + num_comments: number; + created_utc: number; + permalink: string; + url: string; + selftext: string; + is_gallery?: boolean; + over_18: boolean; + link_flair_text?: string; + media?: { + reddit_video?: { + fallback_url: string; + }; + }; + secure_media?: { + reddit_video?: { + fallback_url: string; + }; + }; +} + +export interface RedditResponse { + data: { + children: Array<{ + data: RedditPost; + }>; + }; +} + +type SortType = 'hot' | 'top'; + +async function fetchRedditPosts( + sort: SortType = 'hot', + time: TimePeriod = 'day', +): Promise { + const url = `https://www.reddit.com/r/okbuddyumamusume/${sort}.json${sort === 'top' ? `?t=${time}` : ''}`; + const response = await fetch(url, { + headers: { + 'User-Agent': 'UmaBot/0.1.0', + }, + }); + + if (!response.ok) { + let errorText = `Error fetching ${response.url}: ${response.status} ${response.statusText}`; + + try { + const error = await response.text(); + + if (error) errorText = `${errorText} \n\n ${error}`; + } catch { + // + } + + throw new Error(errorText); + } + + const data: RedditResponse = await response.json(); + + return data.data.children.map((post) => post.data); +} + +function filterPostsByFlair( + posts: RedditPost[], + excludedFlairs: string[] = [], + includedFlairs: string[] = [], +): RedditPost[] { + return posts.filter((post) => { + if (post.is_gallery) return false; + + const hasMedia = + post.media?.reddit_video?.fallback_url || + post.secure_media?.reddit_video?.fallback_url || + post.url; + + if (!hasMedia) return false; + + const postFlair = post.link_flair_text?.toLowerCase() || ''; + const isNSFW = post.over_18 || postFlair.includes('nsfw'); + + if ( + includedFlairs.length > 0 && + includedFlairs.some((flair) => flair.toLowerCase() === 'nsfw') + ) + if (includedFlairs.some((flair) => flair.toLowerCase() === 'nsfw')) + return isNSFW; + + if (isNSFW) return false; + + if (includedFlairs.length > 0) + return includedFlairs.some((flair) => + postFlair.includes(flair.toLowerCase()), + ); + + if (excludedFlairs.length > 0) + return !excludedFlairs.some((flair) => + postFlair.includes(flair.toLowerCase()), + ); + + return true; + }); +} + +function getRandomPost(posts: RedditPost[]): RedditPost { + if (posts.length === 0) + throw new Error('No posts found matching the criteria'); + + const randomIndex = Math.floor(Math.random() * posts.length); + + return posts[randomIndex]; +} + +export async function getCutePost(): Promise { + const posts = await fetchRedditPosts('hot'); + const filteredPosts = filterPostsByFlair(posts, ['roleplay', 'announcement']); + + return getRandomPost(filteredPosts); +} + +export async function getRoleplayPost(): Promise { + const posts = await fetchRedditPosts('hot'); + const filteredPosts = filterPostsByFlair(posts, [], ['roleplay']); + + return getRandomPost(filteredPosts); +} + +export async function getNSFWPost(): Promise { + const posts = await fetchRedditPosts('hot'); + const filteredPosts = filterPostsByFlair(posts, [], ['nsfw']); + + return getRandomPost(filteredPosts); +} + +export async function getTopPost( + time: TimePeriod = 'day', +): Promise { + const posts = await fetchRedditPosts('top', time); + const filteredPosts = filterPostsByFlair(posts, ['roleplay', 'announcement']); + + return getRandomPost(filteredPosts); +} -- cgit v1.2.3