import type { RedditPostsResponse, RedditSearchParams } from '@/tools/reddit/types' import { normalizeSubreddit } from '@/tools/reddit/utils' import type { ToolConfig } from '@/tools/types' export const searchTool: ToolConfig = { id: 'reddit_search', name: 'Search Reddit', description: 'Search for posts within a subreddit', version: '1.0.0', oauth: { required: true, provider: 'reddit', }, params: { accessToken: { type: 'string', required: true, visibility: 'hidden', description: 'Access token for Reddit API', }, subreddit: { type: 'string', required: true, visibility: 'user-or-llm', description: 'The subreddit to search in (e.g., "technology", "programming")', }, query: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Search query text (e.g., "artificial intelligence", "machine learning tutorial")', }, sort: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Sort method for search results (e.g., "relevance", "hot", "top", "new", "comments"). Default: "relevance"', }, time: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Time filter for search results: "hour", "day", "week", "month", "year", or "all" (default: "all")', }, limit: { type: 'number', required: false, visibility: 'user-or-llm', description: 'Maximum number of posts to return (e.g., 25). Default: 10, max: 100', }, restrict_sr: { type: 'boolean', required: false, visibility: 'user-or-llm', description: 'Restrict search to the specified subreddit only (default: true)', }, after: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Fullname of a thing to fetch items after (for pagination)', }, before: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Fullname of a thing to fetch items before (for pagination)', }, count: { type: 'number', required: false, visibility: 'user-or-llm', description: 'A count of items already seen in the listing (used for numbering)', }, show: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Show items that would normally be filtered (e.g., "all")', }, type: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Type of search results: "link" (posts), "sr" (subreddits), or "user" (users). Default: "link"', }, sr_detail: { type: 'boolean', required: false, visibility: 'user-or-llm', description: 'Expand subreddit details in the response', }, }, request: { url: (params: RedditSearchParams) => { const subreddit = normalizeSubreddit(params.subreddit) const sort = params.sort || 'relevance' const limit = Math.min(Math.max(1, params.limit ?? 10), 100) const restrict_sr = params.restrict_sr !== false const urlParams = new URLSearchParams({ q: params.query, sort: sort, limit: limit.toString(), restrict_sr: restrict_sr.toString(), raw_json: '1', }) if (params.time) { urlParams.append('t', params.time) } if (params.after) urlParams.append('after', params.after) if (params.before) urlParams.append('before', params.before) if (params.count !== undefined) urlParams.append('count', Number(params.count).toString()) if (params.show) urlParams.append('show', params.show) if (params.type) urlParams.append('type', params.type) if (params.sr_detail !== undefined) urlParams.append('sr_detail', params.sr_detail.toString()) return `https://oauth.reddit.com/r/${subreddit}/search?${urlParams.toString()}` }, method: 'GET', headers: (params: RedditSearchParams) => { if (!params.accessToken) { throw new Error('Access token is required for Reddit API') } return { Authorization: `Bearer ${params.accessToken}`, 'User-Agent': 'sim-studio/1.0 (https://github.com/simstudioai/sim)', Accept: 'application/json', } }, }, transformResponse: async (response: Response, requestParams?: RedditSearchParams) => { const data = await response.json() const subredditName = data.data?.children?.[0]?.data?.subreddit || requestParams?.subreddit || 'unknown' const posts = data.data?.children?.map((child: any) => { const post = child.data || {} return { id: post.id ?? '', name: post.name ?? '', 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, after: data.data?.after ?? null, before: data.data?.before ?? null, }, } }, outputs: { subreddit: { type: 'string', description: 'Name of the subreddit where search was performed', }, posts: { type: 'array', description: 'Array of search result posts with title, author, URL, score, comments count, and metadata', items: { type: 'object', properties: { id: { type: 'string', description: 'Post ID' }, name: { type: 'string', description: 'Thing fullname (t3_xxxxx)' }, title: { type: 'string', description: 'Post title' }, author: { type: 'string', description: 'Author username' }, url: { type: 'string', description: 'Post URL' }, permalink: { type: 'string', description: 'Reddit permalink' }, score: { type: 'number', description: 'Post score (upvotes - downvotes)' }, num_comments: { type: 'number', description: 'Number of comments' }, created_utc: { type: 'number', description: 'Creation timestamp (UTC)' }, is_self: { type: 'boolean', description: 'Whether this is a text post' }, selftext: { type: 'string', description: 'Text content for self posts' }, thumbnail: { type: 'string', description: 'Thumbnail URL' }, subreddit: { type: 'string', description: 'Subreddit name' }, }, }, }, after: { type: 'string', description: 'Fullname of the last item for forward pagination', optional: true, }, before: { type: 'string', description: 'Fullname of the first item for backward pagination', optional: true, }, }, }