improvement(reddit): better error logging

This commit is contained in:
Emir Karabeg
2025-05-20 01:27:39 -07:00
parent 47090713ef
commit 1ebcaf5bfd
4 changed files with 259 additions and 125 deletions

View File

@@ -84,7 +84,7 @@ export const RedditBlock: BlockConfig<
},
{
id: 'limit',
title: 'Number of Posts',
title: 'Max Posts',
type: 'short-input',
layout: 'full',
placeholder: '10',

View File

@@ -40,73 +40,124 @@ export const getCommentsTool: ToolConfig<RedditCommentsParams, RedditCommentsRes
const limit = Math.min(Math.max(1, params.limit || 50), 100)
// Build URL
return `https://www.reddit.com/r/${subreddit}/comments/${params.postId}.json?sort=${sort}&limit=${limit}`
return `https://www.reddit.com/r/${subreddit}/comments/${params.postId}.json?sort=${sort}&limit=${limit}&raw_json=1`
},
method: 'GET',
headers: () => ({
'User-Agent': 'Sim Studio Reddit Tool/1.0',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36',
'Accept': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
transformResponse: async (response: Response, requestParams?: RedditCommentsParams) => {
try {
// Check if response is OK
if (!response.ok) {
if (response.status === 403 || response.status === 429) {
throw new Error('Reddit API access blocked or rate limited. Please try again later.')
}
throw new Error(`Reddit API returned ${response.status}: ${response.statusText}`)
}
// Extract post data (first element in the array)
const postData = data[0]?.data?.children[0]?.data || {}
// Attempt to parse JSON
let data
try {
data = await response.json()
} catch (error) {
throw new Error('Failed to parse Reddit API response: Response was not valid JSON')
}
// Extract and transform comments (second element in the array)
const commentsData = data[1]?.data?.children || []
// Validate data structure
if (!Array.isArray(data) || data.length < 2) {
throw new Error('Invalid response format from Reddit API')
}
// Recursive function to process nested comments
const processComments = (comments: any[]): any[] => {
return comments
.map((comment) => {
const commentData = comment.data
// Extract post data (first element in the array)
const postData = data[0]?.data?.children[0]?.data || {}
// Skip non-comment items like "more" items
if (!commentData || comment.kind !== 't1') {
return null
}
// Extract and transform comments (second element in the array)
const commentsData = data[1]?.data?.children || []
// Process nested replies if they exist
const replies =
commentData.replies && commentData.replies.data && commentData.replies.data.children
? processComments(commentData.replies.data.children)
: []
// Recursive function to process nested comments
const processComments = (comments: any[]): any[] => {
return comments
.map((comment) => {
const commentData = comment.data
return {
id: commentData.id,
author: commentData.author,
body: commentData.body,
created_utc: commentData.created_utc,
score: commentData.score,
permalink: `https://www.reddit.com${commentData.permalink}`,
replies: replies.filter(Boolean),
}
})
.filter(Boolean)
}
// Skip non-comment items like "more" items
if (!commentData || comment.kind !== 't1') {
return null
}
const comments = processComments(commentsData)
// Process nested replies if they exist
const replies =
commentData.replies && commentData.replies.data && commentData.replies.data.children
? processComments(commentData.replies.data.children)
: []
return {
success: true,
output: {
post: {
id: postData.id,
title: postData.title,
author: postData.author,
selftext: postData.selftext,
created_utc: postData.created_utc,
score: postData.score,
permalink: `https://www.reddit.com${postData.permalink}`,
return {
id: commentData.id || '',
author: commentData.author || '[deleted]',
body: commentData.body || '',
created_utc: commentData.created_utc || 0,
score: commentData.score || 0,
permalink: commentData.permalink ? `https://www.reddit.com${commentData.permalink}` : '',
replies: replies.filter(Boolean),
}
})
.filter(Boolean)
}
const comments = processComments(commentsData)
return {
success: true,
output: {
post: {
id: postData.id || '',
title: postData.title || '',
author: postData.author || '[deleted]',
selftext: postData.selftext || '',
created_utc: postData.created_utc || 0,
score: postData.score || 0,
permalink: postData.permalink ? `https://www.reddit.com${postData.permalink}` : '',
},
comments: comments,
},
comments: comments,
},
}
} catch (error: any) {
const errorMessage = error.message || 'Unknown error'
return {
success: false,
output: {
post: {
id: '',
title: '',
author: '',
selftext: '',
created_utc: 0,
score: 0,
permalink: '',
},
comments: [],
},
error: errorMessage
}
}
},
transformError: (error) => {
return `Error fetching Reddit comments: ${error.message}`
transformError: (error): string => {
// Create detailed error message
let errorMessage = error.message || 'Unknown error'
if (errorMessage.includes('blocked') || errorMessage.includes('rate limited')) {
errorMessage = `Reddit access is currently unavailable: ${errorMessage}. Consider reducing request frequency or using the official Reddit API with authentication.`
}
if (errorMessage.includes('not valid JSON')) {
errorMessage = 'Unable to process Reddit response: Received non-JSON response, which typically happens when Reddit blocks automated access.'
}
return `Error fetching Reddit comments: ${errorMessage}`
},
}

View File

@@ -40,7 +40,7 @@ export const getPostsTool: ToolConfig<RedditPostsParams, RedditPostsResponse> =
const limit = Math.min(Math.max(1, params.limit || 10), 100)
// Build URL with appropriate parameters
let url = `https://www.reddit.com/r/${subreddit}/${sort}.json?limit=${limit}`
let url = `https://www.reddit.com/r/${subreddit}/${sort}.json?limit=${limit}&raw_json=1`
// Add time parameter only for 'top' sorting
if (sort === 'top' && params.time) {
@@ -51,46 +51,88 @@ export const getPostsTool: ToolConfig<RedditPostsParams, RedditPostsResponse> =
},
method: 'GET',
headers: () => ({
'User-Agent': 'Sim Studio Reddit Tool/1.0',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36',
'Accept': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
transformResponse: async (response: Response, requestParams?: RedditPostsParams) => {
try {
// Check if response is OK
if (!response.ok) {
if (response.status === 403 || response.status === 429) {
throw new Error('Reddit API access blocked or rate limited. Please try again later.')
}
throw new Error(`Reddit API returned ${response.status}: ${response.statusText}`)
}
// Extract subreddit name from response
const subredditName = data.data?.children[0]?.data?.subreddit || 'unknown'
// Attempt to parse JSON
let data
try {
data = await response.json()
} catch (error) {
throw new Error('Failed to parse Reddit API response: Response was not valid JSON')
}
// Transform posts data
const posts =
data.data?.children.map((child: any) => {
const post = child.data
// Check if response contains error
if (data.error || !data.data) {
throw new Error(data.message || 'Invalid response from Reddit API')
}
// Extract subreddit name from response (with fallback)
const subredditName = data.data?.children[0]?.data?.subreddit || requestParams?.subreddit || 'unknown'
// Transform posts data with proper error handling
const posts = data.data?.children?.map((child: any) => {
const post = child.data || {}
return {
id: post.id,
title: post.title,
author: post.author,
url: post.url,
permalink: `https://www.reddit.com${post.permalink}`,
created_utc: post.created_utc,
score: post.score,
num_comments: post.num_comments,
is_self: post.is_self,
selftext: post.selftext,
thumbnail: post.thumbnail,
subreddit: post.subreddit,
id: post.id || '',
title: post.title || '',
author: post.author || '[deleted]',
url: post.url || '',
permalink: post.permalink ? `https://www.reddit.com${post.permalink}` : '',
created_utc: post.created_utc || 0,
score: post.score || 0,
num_comments: post.num_comments || 0,
is_self: !!post.is_self,
selftext: post.selftext || '',
thumbnail: post.thumbnail || '',
subreddit: post.subreddit || subredditName,
}
}) || []
return {
success: true,
output: {
subreddit: subredditName,
posts,
},
return {
success: true,
output: {
subreddit: subredditName,
posts,
},
}
} catch (error: any) {
const errorMessage = error.message || 'Unknown error'
return {
success: false,
output: {
subreddit: requestParams?.subreddit || 'unknown',
posts: [],
},
error: errorMessage
}
}
},
transformError: (error) => {
return `Error fetching Reddit posts: ${error.message}`
transformError: (error): string => {
// Create detailed error message
let errorMessage = error.message || 'Unknown error'
if (errorMessage.includes('blocked') || errorMessage.includes('rate limited')) {
errorMessage = `Reddit access is currently unavailable: ${errorMessage}. Consider reducing request frequency or using the official Reddit API with authentication.`
}
if (errorMessage.includes('not valid JSON')) {
errorMessage = 'Unable to process Reddit response: Received non-JSON response, which typically happens when Reddit blocks automated access.'
}
return `Error fetching Reddit posts: ${errorMessage}`
},
}

View File

@@ -26,60 +26,101 @@ export const hotPostsTool: ToolConfig<HotPostsParams, RedditHotPostsResponse> =
},
request: {
url: (params) =>
`https://www.reddit.com/r/${params.subreddit}/hot.json?limit=${params.limit || 10}`,
url: (params) => {
// Sanitize inputs and enforce limits
const subreddit = params.subreddit.trim().replace(/^r\//, '')
const limit = Math.min(Math.max(1, params.limit || 10), 100)
return `https://www.reddit.com/r/${subreddit}/hot.json?limit=${limit}&raw_json=1`
},
method: 'GET',
headers: () => ({
'User-Agent': 'SimStudio/1.0.0',
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36',
'Accept': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.message || 'Failed to fetch Reddit posts')
}
if (data.error) {
throw new Error(data.message || 'Reddit API error')
}
const posts: RedditPost[] = data.data.children.map((child: any) => {
const post = child.data
return {
id: post.id,
title: post.title,
author: post.author,
url: post.url,
permalink: `https://www.reddit.com${post.permalink}`,
created_utc: post.created_utc,
score: post.score,
num_comments: post.num_comments,
selftext: post.selftext,
thumbnail:
post.thumbnail !== 'self' && post.thumbnail !== 'default' ? post.thumbnail : undefined,
is_self: post.is_self,
subreddit: post.subreddit,
subreddit_name_prefixed: post.subreddit_name_prefixed,
transformResponse: async (response: Response, requestParams?: HotPostsParams) => {
try {
// Check if response is OK
if (!response.ok) {
if (response.status === 403 || response.status === 429) {
throw new Error('Reddit API access blocked or rate limited. Please try again later.')
}
throw new Error(`Reddit API returned ${response.status}: ${response.statusText}`)
}
})
// Extract the subreddit name from the response data
const subreddit =
data.data?.children?.[0]?.data?.subreddit || (posts.length > 0 ? posts[0].subreddit : '')
// Attempt to parse JSON
let data
try {
data = await response.json()
} catch (error) {
throw new Error('Failed to parse Reddit API response: Response was not valid JSON')
}
return {
success: true,
output: {
subreddit,
posts,
},
// Check if response contains error
if (data.error || !data.data) {
throw new Error(data.message || 'Invalid response from Reddit API')
}
// Process the posts data with proper error handling
const posts: RedditPost[] = data.data.children.map((child: any) => {
const post = child.data || {}
return {
id: post.id || '',
title: post.title || '',
author: post.author || '[deleted]',
url: post.url || '',
permalink: post.permalink ? `https://www.reddit.com${post.permalink}` : '',
created_utc: post.created_utc || 0,
score: post.score || 0,
num_comments: post.num_comments || 0,
selftext: post.selftext || '',
thumbnail:
post.thumbnail !== 'self' && post.thumbnail !== 'default' ? post.thumbnail : undefined,
is_self: !!post.is_self,
subreddit: post.subreddit || requestParams?.subreddit || '',
subreddit_name_prefixed: post.subreddit_name_prefixed || '',
}
})
// Extract the subreddit name from the response data with fallback
const subreddit =
data.data?.children?.[0]?.data?.subreddit ||
(posts.length > 0 ? posts[0].subreddit : requestParams?.subreddit || '')
return {
success: true,
output: {
subreddit,
posts,
},
}
} catch (error: any) {
const errorMessage = error.message || 'Unknown error'
return {
success: false,
output: {
subreddit: requestParams?.subreddit || '',
posts: [],
},
error: errorMessage
}
}
},
transformError: (error) => {
return error instanceof Error ? error.message : 'An error occurred while fetching Reddit posts'
transformError: (error): string => {
// Create detailed error message
let errorMessage = error.message || 'Unknown error'
if (errorMessage.includes('blocked') || errorMessage.includes('rate limited')) {
errorMessage = `Reddit access is currently unavailable: ${errorMessage}. Consider reducing request frequency or using the official Reddit API with authentication.`
}
if (errorMessage.includes('not valid JSON')) {
errorMessage = 'Unable to process Reddit response: Received non-JSON response, which typically happens when Reddit blocks automated access.'
}
return `Error fetching Reddit posts: ${errorMessage}`
},
}