diff --git a/apps/sim/blocks/blocks/reddit.ts b/apps/sim/blocks/blocks/reddit.ts index 8d95a7309f..a99bf88947 100644 --- a/apps/sim/blocks/blocks/reddit.ts +++ b/apps/sim/blocks/blocks/reddit.ts @@ -84,7 +84,7 @@ export const RedditBlock: BlockConfig< }, { id: 'limit', - title: 'Number of Posts', + title: 'Max Posts', type: 'short-input', layout: 'full', placeholder: '10', diff --git a/apps/sim/tools/reddit/get_comments.ts b/apps/sim/tools/reddit/get_comments.ts index 38044f553c..c95a945cd9 100644 --- a/apps/sim/tools/reddit/get_comments.ts +++ b/apps/sim/tools/reddit/get_comments.ts @@ -40,73 +40,124 @@ export const getCommentsTool: ToolConfig ({ - '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}` }, } diff --git a/apps/sim/tools/reddit/get_posts.ts b/apps/sim/tools/reddit/get_posts.ts index a7450dc5c1..09c5315555 100644 --- a/apps/sim/tools/reddit/get_posts.ts +++ b/apps/sim/tools/reddit/get_posts.ts @@ -40,7 +40,7 @@ export const getPostsTool: ToolConfig = 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 = }, 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}` }, } diff --git a/apps/sim/tools/reddit/hot_posts.ts b/apps/sim/tools/reddit/hot_posts.ts index 510d463927..873c31127e 100644 --- a/apps/sim/tools/reddit/hot_posts.ts +++ b/apps/sim/tools/reddit/hot_posts.ts @@ -26,60 +26,101 @@ export const hotPostsTool: ToolConfig = }, 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}` }, }