diff --git a/blocks/blocks/reddit.ts b/blocks/blocks/reddit.ts new file mode 100644 index 0000000000..1828ca648a --- /dev/null +++ b/blocks/blocks/reddit.ts @@ -0,0 +1,58 @@ +import { RedditIcon } from '@/components/icons' +import { RedditHotPostsResponse } from '@/tools/reddit/types' +import { BlockConfig } from '../types' + +export const RedditBlock: BlockConfig = { + type: 'reddit', + name: 'Reddit', + description: 'Fetch popular posts from Reddit', + longDescription: + 'Access Reddit data to retrieve the most popular (hot) posts from any subreddit. Get post titles, content, authors, scores, and more.', + category: 'tools', + bgColor: '#FF5700', + icon: RedditIcon, + subBlocks: [ + // Subreddit input + { + id: 'subreddit', + title: 'Subreddit', + type: 'short-input', + layout: 'full', + placeholder: 'Enter subreddit name (without r/)', + }, + // Limit input + { + id: 'limit', + title: 'Number of Posts', + type: 'short-input', + layout: 'full', + placeholder: '10', + }, + ], + tools: { + access: ['reddit_hot_posts'], + config: { + tool: () => 'reddit_hot_posts', + }, + }, + inputs: { + subreddit: { + type: 'string', + required: true, + description: 'The name of the subreddit to fetch posts from (without the r/ prefix)', + }, + limit: { + type: 'number', + required: false, + description: 'Maximum number of posts to return (default: 10, max: 100)', + }, + }, + outputs: { + response: { + type: { + subreddit: 'string', + posts: 'json', + }, + }, + }, +} diff --git a/blocks/index.ts b/blocks/index.ts index 0b0843dbc9..40ecb37626 100644 --- a/blocks/index.ts +++ b/blocks/index.ts @@ -13,6 +13,7 @@ import { JinaBlock } from './blocks/jina' import { NotionBlock } from './blocks/notion' import { OpenAIBlock } from './blocks/openai' import { PineconeBlock } from './blocks/pinecone' +import { RedditBlock } from './blocks/reddit' import { RouterBlock } from './blocks/router' import { SerperBlock } from './blocks/serper' import { SlackBlock } from './blocks/slack' @@ -47,6 +48,7 @@ export { PineconeBlock, OpenAIBlock, ExaBlock, + RedditBlock, } // Registry of all block configurations, alphabetically sorted @@ -65,6 +67,7 @@ const blocks: Record = { notion: NotionBlock, openai: OpenAIBlock, pinecone: PineconeBlock, + reddit: RedditBlock, router: RouterBlock, serper: SerperBlock, slack: SlackBlock, diff --git a/components/icons.tsx b/components/icons.tsx index 457fb5ddb9..38a1f09b82 100644 --- a/components/icons.tsx +++ b/components/icons.tsx @@ -1445,3 +1445,22 @@ export function ExaAIIcon(props: SVGProps) { ) } + +export function RedditIcon(props: SVGProps) { + return ( + + + + ) +} diff --git a/tools/index.ts b/tools/index.ts index 86ed05d6d6..8afb4f7bc0 100644 --- a/tools/index.ts +++ b/tools/index.ts @@ -25,6 +25,7 @@ import { generateEmbeddingsTool as pineconeGenerateEmbeddingsTool } from './pine import { searchTextTool as pineconeSearchTextTool } from './pinecone/searchText' import { searchVectorTool as pineconeSearchVectorTool } from './pinecone/searchVector' import { upsertTextTool as pineconeUpsertTextTool } from './pinecone/upsertText' +import { hotPostsTool as redditHotPosts } from './reddit/hot' import { opportunitiesTool as salesforceOpportunities } from './salesforce/opportunities' import { searchTool as serperSearch } from './serper/search' import { slackMessageTool } from './slack/message' @@ -75,6 +76,7 @@ export const tools: Record = { exa_get_contents: exaGetContents, exa_find_similar_links: exaFindSimilarLinks, exa_answer: exaAnswer, + reddit_hot_posts: redditHotPosts, } // Get a tool by its ID diff --git a/tools/reddit/hot.ts b/tools/reddit/hot.ts new file mode 100644 index 0000000000..510d463927 --- /dev/null +++ b/tools/reddit/hot.ts @@ -0,0 +1,85 @@ +import { ToolConfig, ToolResponse } from '../types' +import { RedditHotPostsResponse, RedditPost } from './types' + +interface HotPostsParams { + subreddit: string + limit?: number +} + +export const hotPostsTool: ToolConfig = { + id: 'reddit_hot_posts', + name: 'Reddit Hot Posts', + description: 'Fetch the most popular (hot) posts from a specified subreddit.', + version: '1.0.0', + + params: { + subreddit: { + type: 'string', + required: true, + description: 'The name of the subreddit to fetch posts from (without the r/ prefix)', + }, + limit: { + type: 'number', + required: false, + description: 'Maximum number of posts to return (default: 10, max: 100)', + }, + }, + + request: { + url: (params) => + `https://www.reddit.com/r/${params.subreddit}/hot.json?limit=${params.limit || 10}`, + method: 'GET', + headers: () => ({ + 'User-Agent': 'SimStudio/1.0.0', + 'Content-Type': '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, + } + }) + + // Extract the subreddit name from the response data + const subreddit = + data.data?.children?.[0]?.data?.subreddit || (posts.length > 0 ? posts[0].subreddit : '') + + return { + success: true, + output: { + subreddit, + posts, + }, + } + }, + + transformError: (error) => { + return error instanceof Error ? error.message : 'An error occurred while fetching Reddit posts' + }, +} diff --git a/tools/reddit/types.ts b/tools/reddit/types.ts new file mode 100644 index 0000000000..47d91efadc --- /dev/null +++ b/tools/reddit/types.ts @@ -0,0 +1,24 @@ +import { ToolResponse } from '../types' + +export interface RedditPost { + id: string + title: string + author: string + url: string + permalink: string + created_utc: number + score: number + num_comments: number + selftext?: string + thumbnail?: string + is_self: boolean + subreddit: string + subreddit_name_prefixed: string +} + +export interface RedditHotPostsResponse extends ToolResponse { + output: { + subreddit: string + posts: RedditPost[] + } +}