diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/discord/commands.ts | 46 | ||||
| -rw-r--r-- | src/discord/embeds.ts | 42 | ||||
| -rw-r--r-- | src/discord/responses.ts | 4 | ||||
| -rw-r--r-- | src/discord/types.ts | 2 | ||||
| -rw-r--r-- | src/discord/verification.ts | 8 | ||||
| -rw-r--r-- | src/reddit.ts | 62 | ||||
| -rw-r--r-- | src/register.ts | 22 | ||||
| -rw-r--r-- | src/server.ts | 52 |
8 files changed, 119 insertions, 119 deletions
diff --git a/src/discord/commands.ts b/src/discord/commands.ts index dec18e6..b4436d6 100644 --- a/src/discord/commands.ts +++ b/src/discord/commands.ts @@ -1,57 +1,57 @@ -import type { DiscordCommand } from './interfaces.ts'; +import type { DiscordCommand } from "./interfaces.ts"; export type { DiscordCommand }; export const HOT_COMMAND: DiscordCommand = { - name: 'hot', - description: 'Fetch a random hot post from r/okbuddyumamusume', + name: "hot", + description: "Fetch a random hot post from r/okbuddyumamusume", }; export const ROLEPLAY_COMMAND: DiscordCommand = { - name: 'roleplay', - description: 'Fetch a random hot roleplay post from r/okbuddyumamusume', + name: "roleplay", + description: "Fetch a random hot roleplay post from r/okbuddyumamusume", }; export const NSFW_COMMAND: DiscordCommand = { - name: 'nsfw', + name: "nsfw", description: - 'Fetch a random NSFW post from r/okbuddyumamusume (NSFW channels only)', + "Fetch a random NSFW post from r/okbuddyumamusume (NSFW channels only)", }; export const TOP_COMMAND: DiscordCommand = { - name: 'top', + name: "top", description: - 'Fetch a random top post from r/okbuddyumamusume (defaults to today)', + "Fetch a random top post from r/okbuddyumamusume (defaults to today)", options: [ { type: 3, - name: 'time', - description: 'Time period for top posts (defaults to today)', + name: "time", + description: "Time period for top posts (defaults to today)", required: false, choices: [ { - name: 'Now', - value: 'hour', + name: "Now", + value: "hour", }, { - name: 'Today', - value: 'day', + name: "Today", + value: "day", }, { - name: 'This Week', - value: 'week', + name: "This Week", + value: "week", }, { - name: 'This Month', - value: 'month', + name: "This Month", + value: "month", }, { - name: 'This Year', - value: 'year', + name: "This Year", + value: "year", }, { - name: 'All Time', - value: 'all', + name: "All Time", + value: "all", }, ], }, diff --git a/src/discord/embeds.ts b/src/discord/embeds.ts index 833c831..1fad102 100644 --- a/src/discord/embeds.ts +++ b/src/discord/embeds.ts @@ -1,16 +1,16 @@ -import type { DiscordEmbed } from './interfaces.ts'; -import type { RedditPost } from '../reddit.ts'; +import type { DiscordEmbed } from "./interfaces.ts"; +import type { RedditPost } from "../reddit.ts"; const decodeHtmlEntities = (str: string): string => { return str - .replace(/&/g, '&') - .replace(/</g, '<') - .replace(/>/g, '>') + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") .replace(/"/g, '"') .replace(/'/g, "'") - .replace(///g, '/') - .replace(/`/g, '`') - .replace(/=/g, '='); + .replace(///g, "/") + .replace(/`/g, "`") + .replace(/=/g, "="); }; export const createPostEmbed = (post: RedditPost): DiscordEmbed => { @@ -19,10 +19,10 @@ export const createPostEmbed = (post: RedditPost): DiscordEmbed => { post.secure_media?.reddit_video?.fallback_url || post.url; - let description = post.selftext || ''; + let description = post.selftext || ""; if (description.length > 1000) - description = description.substring(0, 997).trim() + ' ...'; + description = description.substring(0, 997).trim() + " ..."; const embed: DiscordEmbed = { title: post.title, @@ -35,30 +35,30 @@ export const createPostEmbed = (post: RedditPost): DiscordEmbed => { }, fields: [ { - name: 'Score', + name: "Score", value: `${post.score} ā¬ļø`, inline: true, }, { - name: 'Comments', + name: "Comments", value: `${post.num_comments} š¬`, inline: true, }, ], timestamp: new Date(post.created_utc * 1000).toISOString(), footer: { - text: 'r/okbuddyumamusume', + text: "r/okbuddyumamusume", }, }; if (mediaUrl) if (post.media?.reddit_video || post.secure_media?.reddit_video) { - if (!description) description = ''; + if (!description) description = ""; description += - '\n\nš¹ **This post contains a video** - [Click here to view](' + + "\n\nš¹ **This post contains a video** - [Click here to view](" + mediaUrl + - ')'; + ")"; embed.description = description; if (post.preview?.images?.[0]?.source?.url) { @@ -66,21 +66,21 @@ export const createPostEmbed = (post: RedditPost): DiscordEmbed => { post.preview.images[0].source.url, ); - console.log('Using preview image:', decodedURL); + console.log("Using preview image:", decodedURL); embed.image = { url: decodedURL }; } else if ( post.thumbnail && - post.thumbnail !== 'self' && - post.thumbnail !== 'default' + post.thumbnail !== "self" && + post.thumbnail !== "default" ) { const decodedThumbnail = decodeHtmlEntities(post.thumbnail); - console.log('Using thumbnail:', decodedThumbnail); + console.log("Using thumbnail:", decodedThumbnail); embed.image = { url: decodedThumbnail }; } else { - console.log('No suitable thumbnail found for video post'); + console.log("No suitable thumbnail found for video post"); } } else { embed.image = { url: mediaUrl }; diff --git a/src/discord/responses.ts b/src/discord/responses.ts index da72967..4dcc777 100644 --- a/src/discord/responses.ts +++ b/src/discord/responses.ts @@ -1,4 +1,4 @@ -import type { DiscordResponse } from './interfaces.ts'; +import type { DiscordResponse } from "./interfaces.ts"; export class JSONResponse extends Response { constructor(body: DiscordResponse | { error: string }, init?: ResponseInit) { @@ -6,7 +6,7 @@ export class JSONResponse extends Response { init = init || { headers: { - 'content-type': 'application/json;charset=UTF-8', + "content-type": "application/json;charset=UTF-8", }, }; diff --git a/src/discord/types.ts b/src/discord/types.ts index 9b1d6c5..4f6e85e 100644 --- a/src/discord/types.ts +++ b/src/discord/types.ts @@ -1 +1 @@ -export type TimePeriod = 'hour' | 'day' | 'week' | 'month' | 'year' | 'all'; +export type TimePeriod = "hour" | "day" | "week" | "month" | "year" | "all"; diff --git a/src/discord/verification.ts b/src/discord/verification.ts index e4679db..89d26db 100644 --- a/src/discord/verification.ts +++ b/src/discord/verification.ts @@ -1,12 +1,12 @@ -import { verifyKey } from 'discord-interactions'; -import type { Environment, DiscordInteraction } from './interfaces.ts'; +import { verifyKey } from "discord-interactions"; +import type { Environment, DiscordInteraction } from "./interfaces.ts"; export const verifyDiscordRequest = async ( request: Request, environment: Environment, ): Promise<{ isValid: boolean; interaction?: DiscordInteraction }> => { - const signature = request.headers.get('x-signature-ed25519'); - const timestamp = request.headers.get('x-signature-timestamp'); + const signature = request.headers.get("x-signature-ed25519"); + const timestamp = request.headers.get("x-signature-timestamp"); const body = await request.text(); const isValidRequest = signature && diff --git a/src/reddit.ts b/src/reddit.ts index f57cf75..5b4ded7 100644 --- a/src/reddit.ts +++ b/src/reddit.ts @@ -1,4 +1,4 @@ -import type { TimePeriod } from './discord/types.ts'; +import type { TimePeriod } from "./discord/types.ts"; export interface RedditPost { id: string; @@ -49,7 +49,7 @@ export interface RedditResponse { }; } -type SortType = 'hot' | 'top'; +type SortType = "hot" | "top"; const fetchWithRetry = async ( url: string, @@ -63,15 +63,15 @@ const fetchWithRetry = async ( const response = await fetch(url, { headers: { - 'User-Agent': - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + "User-Agent": + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", Accept: - 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', - 'Accept-Language': 'en-US,en;q=0.5', - 'Accept-Encoding': 'gzip, deflate, br', - DNT: '1', - Connection: 'keep-alive', - 'Upgrade-Insecure-Requests': '1', + "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.5", + "Accept-Encoding": "gzip, deflate, br", + DNT: "1", + Connection: "keep-alive", + "Upgrade-Insecure-Requests": "1", }, }); @@ -86,14 +86,14 @@ const fetchWithRetry = async ( await new Promise((resolve) => setTimeout(resolve, delay)); } - throw new Error('Max retries exceeded'); + throw new Error("Max retries exceeded"); }; export const fetchRedditPosts = async ( - sort: SortType = 'hot', - time: TimePeriod = 'day', + sort: SortType = "hot", + time: TimePeriod = "day", ): Promise<RedditPost[]> => { - const url = `https://www.reddit.com/r/okbuddyumamusume/${sort}.json${sort === 'top' ? `?t=${time}` : ''}`; + const url = `https://www.reddit.com/r/okbuddyumamusume/${sort}.json${sort === "top" ? `?t=${time}` : ""}`; const response = await fetchWithRetry(url); if (!response.ok) { @@ -104,17 +104,17 @@ export const fetchRedditPosts = async ( if ( error.includes("You've been blocked by network security") || - error.includes('blocked by network security') + error.includes("blocked by network security") ) throw new Error( - 'Reddit is blocking requests due to network security. This may be due to rate limiting or bot detection. Please try again later.', + "Reddit is blocking requests due to network security. This may be due to rate limiting or bot detection. Please try again later.", ); if (error) errorText = `${errorText} \n\n ${error}`; } catch (err) { if ( err instanceof Error && - err.message.includes('blocked by network security') + err.message.includes("blocked by network security") ) throw err; } @@ -142,14 +142,14 @@ export const filterPostsByFlair = ( if (!hasMedia) return false; - const postFlair = post.link_flair_text?.toLowerCase() || ''; - const isNSFW = post.over_18 || postFlair.includes('nsfw'); + 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') + includedFlairs.some((flair) => flair.toLowerCase() === "nsfw") ) - if (includedFlairs.some((flair) => flair.toLowerCase() === 'nsfw')) + if (includedFlairs.some((flair) => flair.toLowerCase() === "nsfw")) return isNSFW; if (isNSFW) return false; @@ -170,7 +170,7 @@ export const filterPostsByFlair = ( const getRandomPost = (posts: RedditPost[]): RedditPost => { if (posts.length === 0) - throw new Error('No posts found matching the criteria'); + throw new Error("No posts found matching the criteria"); const randomIndex = Math.floor(Math.random() * posts.length); @@ -178,31 +178,31 @@ const getRandomPost = (posts: RedditPost[]): RedditPost => { }; export const getCutePost = async (): Promise<RedditPost> => { - const posts = await fetchRedditPosts('hot'); - const filteredPosts = filterPostsByFlair(posts, ['roleplay', 'announcement']); + const posts = await fetchRedditPosts("hot"); + const filteredPosts = filterPostsByFlair(posts, ["roleplay", "announcement"]); return getRandomPost(filteredPosts); }; export const getRoleplayPost = async (): Promise<RedditPost> => { - const posts = await fetchRedditPosts('hot'); - const filteredPosts = filterPostsByFlair(posts, [], ['roleplay']); + const posts = await fetchRedditPosts("hot"); + const filteredPosts = filterPostsByFlair(posts, [], ["roleplay"]); return getRandomPost(filteredPosts); }; export const getNSFWPost = async (): Promise<RedditPost> => { - const posts = await fetchRedditPosts('hot'); - const filteredPosts = filterPostsByFlair(posts, [], ['nsfw']); + const posts = await fetchRedditPosts("hot"); + const filteredPosts = filterPostsByFlair(posts, [], ["nsfw"]); return getRandomPost(filteredPosts); }; export const getTopPost = async ( - time: TimePeriod = 'day', + time: TimePeriod = "day", ): Promise<RedditPost> => { - const posts = await fetchRedditPosts('top', time); - const filteredPosts = filterPostsByFlair(posts, ['roleplay', 'announcement']); + const posts = await fetchRedditPosts("top", time); + const filteredPosts = filterPostsByFlair(posts, ["roleplay", "announcement"]); return getRandomPost(filteredPosts); }; diff --git a/src/register.ts b/src/register.ts index 319b054..6cd2bac 100644 --- a/src/register.ts +++ b/src/register.ts @@ -4,21 +4,21 @@ import { ROLEPLAY_COMMAND, TOP_COMMAND, type DiscordCommand, -} from './discord/commands.ts'; -import dotenv from 'dotenv'; -import process from 'node:process'; +} from "./discord/commands.ts"; +import dotenv from "dotenv"; +import process from "node:process"; -dotenv.config({ path: '.dev.vars' }); +dotenv.config({ path: ".dev.vars" }); const token = process.env.DISCORD_TOKEN; const applicationID = process.env.DISCORD_APPLICATION_ID; if (!token) - throw new Error('The DISCORD_TOKEN environment variable is required.'); + throw new Error("The DISCORD_TOKEN environment variable is required."); if (!applicationID) throw new Error( - 'The DISCORD_APPLICATION_ID environment variable is required.', + "The DISCORD_APPLICATION_ID environment variable is required.", ); const url = `https://discord.com/api/v10/applications/${applicationID}/commands`; @@ -32,21 +32,21 @@ const commands: DiscordCommand[] = [ const response = await fetch(url, { headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", Authorization: `Bot ${token}`, }, - method: 'PUT', + method: "PUT", body: JSON.stringify(commands), }); if (response.ok) { - console.log('Registered all commands'); + console.log("Registered all commands"); const data = await response.json(); console.log(JSON.stringify(data, null, 2)); } else { - console.error('Error registering commands'); + console.error("Error registering commands"); let errorText = `Error registering commands \n ${response.url}: ${response.status} ${response.statusText}`; @@ -55,7 +55,7 @@ if (response.ok) { if (error) errorText = `${errorText} \n\n ${error}`; } catch (error) { - console.error('Error reading body from request:', error); + console.error("Error reading body from request:", error); } console.error(errorText); diff --git a/src/server.ts b/src/server.ts index 5f43763..bf3acf2 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,37 +1,37 @@ -import { AutoRouter } from 'itty-router'; -import { InteractionResponseType, InteractionType } from 'discord-interactions'; +import { AutoRouter } from "itty-router"; +import { InteractionResponseType, InteractionType } from "discord-interactions"; import { HOT_COMMAND, ROLEPLAY_COMMAND, NSFW_COMMAND, TOP_COMMAND, -} from './discord/commands.ts'; +} from "./discord/commands.ts"; import { getCutePost, getRoleplayPost, getNSFWPost, getTopPost, -} from './reddit.ts'; -import type { TimePeriod } from './discord/types.ts'; -import type { Environment } from './discord/interfaces.ts'; -import { createPostEmbed } from './discord/embeds.ts'; -import { JSONResponse } from './discord/responses.ts'; -import { verifyDiscordRequest } from './discord/verification.ts'; +} from "./reddit.ts"; +import type { TimePeriod } from "./discord/types.ts"; +import type { Environment } from "./discord/interfaces.ts"; +import { createPostEmbed } from "./discord/embeds.ts"; +import { JSONResponse } from "./discord/responses.ts"; +import { verifyDiscordRequest } from "./discord/verification.ts"; const router = AutoRouter(); -router.get('/', (_request: Request, environment: Environment) => { +router.get("/", (_request: Request, environment: Environment) => { return new Response(`š ${environment.DISCORD_APPLICATION_ID}`); }); -router.post('/', async (request: Request, environment: Environment) => { +router.post("/", async (request: Request, environment: Environment) => { const { isValid, interaction } = await server.verifyDiscordRequest( request, environment, ); if (!isValid || !interaction) - return new Response('Bad request signature.', { status: 401 }); + return new Response("Bad request signature.", { status: 401 }); if (interaction.type === InteractionType.PING) return new JSONResponse({ @@ -52,12 +52,12 @@ router.post('/', async (request: Request, environment: Environment) => { }, }); } catch (error) { - console.error('Error in hot command:', error); + console.error("Error in hot command:", error); return new JSONResponse({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { - content: 'ā No posts found. Try again later!', + content: "ā No posts found. Try again later!", flags: 64, }, }); @@ -76,12 +76,12 @@ router.post('/', async (request: Request, environment: Environment) => { }, }); } catch (error) { - console.error('Error in roleplay command:', error); + console.error("Error in roleplay command:", error); return new JSONResponse({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { - content: 'ā No roleplay posts found. Try again later!', + content: "ā No roleplay posts found. Try again later!", flags: 64, }, }); @@ -93,7 +93,7 @@ router.post('/', async (request: Request, environment: Environment) => { return new JSONResponse({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { - content: 'ā This command can only be used in NSFW channels.', + content: "ā This command can only be used in NSFW channels.", flags: 64, }, }); @@ -110,12 +110,12 @@ router.post('/', async (request: Request, environment: Environment) => { }, }); } catch (error) { - console.error('Error in NSFW command:', error); + console.error("Error in NSFW command:", error); return new JSONResponse({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { - content: 'ā No NSFW posts found. Try again later!', + content: "ā No NSFW posts found. Try again later!", flags: 64, }, }); @@ -125,7 +125,7 @@ router.post('/', async (request: Request, environment: Environment) => { case TOP_COMMAND.name.toLowerCase(): { try { const time = - (interaction.data.options?.[0]?.value as TimePeriod) || 'day'; + (interaction.data.options?.[0]?.value as TimePeriod) || "day"; const post = await getTopPost(time); const embed = createPostEmbed(post); @@ -136,12 +136,12 @@ router.post('/', async (request: Request, environment: Environment) => { }, }); } catch (error) { - console.error('Error in top command:', error); + console.error("Error in top command:", error); return new JSONResponse({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, data: { - content: 'ā No top posts found. Try again later!', + content: "ā No top posts found. Try again later!", flags: 64, }, }); @@ -149,16 +149,16 @@ router.post('/', async (request: Request, environment: Environment) => { } default: - return new JSONResponse({ error: 'Unknown Type' }, { status: 400 }); + return new JSONResponse({ error: "Unknown Type" }, { status: 400 }); } } - console.error('Unknown Type'); + console.error("Unknown Type"); - return new JSONResponse({ error: 'Unknown Type' }, { status: 400 }); + return new JSONResponse({ error: "Unknown Type" }, { status: 400 }); }); -router.all('*', () => new Response('Not Found.', { status: 404 })); +router.all("*", () => new Response("Not Found.", { status: 404 })); const server = { verifyDiscordRequest, |