summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFuwn <[email protected]>2025-09-07 03:26:30 -0700
committerFuwn <[email protected]>2025-09-07 03:26:30 -0700
commit087895920eb1610ab72c056138ef406e3f971044 (patch)
tree5e9d2be5cf8113aeab9fe98dcf85ab7e3d6fef92
parentbuild: Switch to TypeScript (diff)
downloadumabotdiscord-087895920eb1610ab72c056138ef406e3f971044.tar.xz
umabotdiscord-087895920eb1610ab72c056138ef406e3f971044.zip
fix: Add rate-limit mitigation
-rw-r--r--src/reddit.ts52
-rw-r--r--src/register.ts2
-rw-r--r--src/server.ts22
3 files changed, 58 insertions, 18 deletions
diff --git a/src/reddit.ts b/src/reddit.ts
index 79475b1..5c5ae0b 100644
--- a/src/reddit.ts
+++ b/src/reddit.ts
@@ -1,4 +1,4 @@
-import type { TimePeriod } from './commands.js';
+import type { TimePeriod } from './commands.ts';
export interface RedditPost {
id: string;
@@ -35,26 +35,58 @@ export interface RedditResponse {
type SortType = 'hot' | 'top';
-async function fetchRedditPosts(
+async function fetchWithRetry(url: string, maxRetries: number = 3): Promise<Response> {
+ for (let attempt = 0; attempt < maxRetries; attempt++)
+ try {
+ await new Promise(resolve => setTimeout(resolve, Math.random() * 1000 + 500));
+
+ 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',
+ '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',
+ },
+ });
+
+ return response;
+ } catch (error) {
+ if (attempt === maxRetries - 1) throw error;
+
+ const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000;
+
+ console.log(`Attempt ${attempt + 1} failed, retrying in ${delay}ms ...`);
+
+ await new Promise(resolve => setTimeout(resolve, delay));
+ }
+
+ throw new Error('Max retries exceeded');
+}
+
+export async function fetchRedditPosts(
sort: SortType = 'hot',
time: TimePeriod = 'day',
): Promise<RedditPost[]> {
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',
- },
- });
+ const response = await fetchWithRetry(url);
if (!response.ok) {
let errorText = `Error fetching ${response.url}: ${response.status} ${response.statusText}`;
try {
const error = await response.text();
+
+ if (error.includes('You\'ve been 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.');
if (error) errorText = `${errorText} \n\n ${error}`;
- } catch {
- //
+ } catch (err) {
+ if (err instanceof Error && err.message.includes('blocked by network security'))
+ throw err;
}
throw new Error(errorText);
@@ -65,7 +97,7 @@ async function fetchRedditPosts(
return data.data.children.map((post) => post.data);
}
-function filterPostsByFlair(
+export function filterPostsByFlair(
posts: RedditPost[],
excludedFlairs: string[] = [],
includedFlairs: string[] = [],
diff --git a/src/register.ts b/src/register.ts
index 632b2b8..94bb420 100644
--- a/src/register.ts
+++ b/src/register.ts
@@ -4,7 +4,7 @@ import {
ROLEPLAY_COMMAND,
TOP_COMMAND,
type DiscordCommand,
-} from './commands.js';
+} from './commands.ts';
import dotenv from 'dotenv';
import process from 'node:process';
diff --git a/src/server.ts b/src/server.ts
index 3f54f63..bb0c458 100644
--- a/src/server.ts
+++ b/src/server.ts
@@ -9,15 +9,15 @@ import {
ROLEPLAY_COMMAND,
NSFW_COMMAND,
TOP_COMMAND,
-} from './commands.js';
+} from './commands.ts';
import {
getCutePost,
getRoleplayPost,
getNSFWPost,
getTopPost,
type RedditPost,
-} from './reddit.js';
-import type { TimePeriod } from './commands.js';
+} from './reddit.ts';
+import type { TimePeriod } from './commands.ts';
interface Environment {
DISCORD_APPLICATION_ID: string;
@@ -167,7 +167,9 @@ router.post('/', async (request: Request, environment: Environment) => {
embeds: [embed],
},
});
- } catch {
+ } catch (error) {
+ console.error('Error in hot command:', error);
+
return new JSONResponse({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
@@ -189,7 +191,9 @@ router.post('/', async (request: Request, environment: Environment) => {
embeds: [embed],
},
});
- } catch {
+ } catch (error) {
+ console.error('Error in roleplay command:', error);
+
return new JSONResponse({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
@@ -221,7 +225,9 @@ router.post('/', async (request: Request, environment: Environment) => {
embeds: [embed],
},
});
- } catch {
+ } catch (error) {
+ console.error('Error in NSFW command:', error);
+
return new JSONResponse({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
@@ -245,7 +251,9 @@ router.post('/', async (request: Request, environment: Environment) => {
embeds: [embed],
},
});
- } catch {
+ } catch (error) {
+ console.error('Error in top command:', error);
+
return new JSONResponse({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {