summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2025-09-09 18:16:44 -0700
committerFuwn <[email protected]>2025-09-09 18:16:44 -0700
commit8baec3564fccac1a7743ac9de481f54e0808c0c9 (patch)
treee29b09fb158539a15df8a5a10bb484666fb22c2d /src
parentrefactor(src): Use arrow functions everywhere (diff)
downloadumabotdiscord-8baec3564fccac1a7743ac9de481f54e0808c0c9.tar.xz
umabotdiscord-8baec3564fccac1a7743ac9de481f54e0808c0c9.zip
style: Use base prettier:recommended rules
Diffstat (limited to 'src')
-rw-r--r--src/discord/commands.ts46
-rw-r--r--src/discord/embeds.ts42
-rw-r--r--src/discord/responses.ts4
-rw-r--r--src/discord/types.ts2
-rw-r--r--src/discord/verification.ts8
-rw-r--r--src/reddit.ts62
-rw-r--r--src/register.ts22
-rw-r--r--src/server.ts52
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(/&amp;/g, '&')
- .replace(/&lt;/g, '<')
- .replace(/&gt;/g, '>')
+ .replace(/&amp;/g, "&")
+ .replace(/&lt;/g, "<")
+ .replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&#x27;/g, "'")
- .replace(/&#x2F;/g, '/')
- .replace(/&#x60;/g, '`')
- .replace(/&#x3D;/g, '=');
+ .replace(/&#x2F;/g, "/")
+ .replace(/&#x60;/g, "`")
+ .replace(/&#x3D;/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,