import { PuppeteerDeno } from "https://deno.land/x/puppeteer@16.2.0/src/deno/Puppeteer.ts";
import { convertRelativeDateToString } from "https://esm.town/v/sarahxc/convertRelativeDateToString";
interface ThreadResult {
source: string;
url: string;
date_published: string;
title: string;
}
interface RedditSearchOptions {
query: string;
apiKey?: string;
}
export async function redditSearch({
query,
apiKey = Deno.env.get("BROWSERBASE_API_KEY"),
}: RedditSearchOptions): Promise<ThreadResult[]> {
if (!apiKey) {
throw new Error("BrowserBase API key is required");
}
const puppeteer = new PuppeteerDeno({ productName: "chrome" });
const browser = await puppeteer.connect({
browserWSEndpoint: `wss://connect.browserbase.com?apiKey=${apiKey}&enableProxy=true`,
ignoreHTTPSErrors: true,
});
try {
const page = await browser.newPage();
const url = constructSearchUrl(query);
await page.goto(url, { waitUntil: "networkidle0" });
await page.waitForSelector("div[data-testid=\"search-post-unit\"]", { timeout: 30000 });
const postData = await extractPostData(page);
return await processPostData(postData);
} catch (error) {
console.error("Error occurred during Reddit search:", error);
const noResultsFound: ThreadResult[] = [];
return noResultsFound;
} finally {
await browser.close();
}
}
function constructSearchUrl(query: string): string {
const encodedQuery = encodeURIComponent(query).replace(/%20/g, "+");
return `https://www.reddit.com/search/?q=${encodedQuery}&type=link&t=week`;
}
async function extractPostData(page: any): Promise<Partial<ThreadResult>[]> {
return page.evaluate(() => {
const posts = document.querySelectorAll("div[data-testid=\"search-post-unit\"]");
return Array.from(posts).map(post => {
const titleElement = post.querySelector("a[id^=\"search-post-title\"]");
const timeElement = post.querySelector("faceplate-timeago");
return {
source: "Reddit",
title: titleElement?.textContent?.trim() || "",
url: titleElement?.href || "",
date_published: timeElement?.textContent?.trim() || "",
};
});
});
}
async function processPostData(postData: Partial<ThreadResult>[]): Promise<ThreadResult[]> {
const processedData: ThreadResult[] = [];
for (const post of postData) {
if (post.title && post.url && post.date_published) {
const date_published = await convertRelativeDateToString({ relativeDate: post.date_published });
processedData.push({
source: "Reddit",
title: post.title,
url: post.url,
date_published,
});
}
}
return processedData;
}