import { convertRelativeDateToString } from "https://esm.town/v/sarahxc/convertRelativeDateToString";
import { searchParams } from "https://esm.town/v/stevekrouse/searchParams";
import { twitterJSON } from "https://esm.town/v/stevekrouse/twitterJSON";
import { twitterUser } from "https://esm.town/v/stevekrouse/twitterUser";
interface Website {
source: string;
url: string;
title?: string;
date_published: string;
}
function formatTweetText(text: string): string {
text = text.replace(/https?:\/\/\S+/g, "");
text = text.replace(/@\w+/g, "");
text = text.replace(/#\w+/g, "");
text = text.replace(/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}]/gu, "");
text = text.replace(/\s+/g, " ").trim();
text = text.replace(/([&<>])/g, "\\$1");
const maxLength = 200;
if (text.length > maxLength) {
text = text.substring(0, maxLength - 3) + "...";
}
return text;
}
export async function twitterSearch({
query,
maxResults = 4,
daysBack = 1,
apiKey,
}: {
query: string;
maxResults?: number;
daysBack?: number;
apiKey: string;
}): Promise<Website[]> {
const startDate = new Date();
startDate.setDate(startDate.getDate() - daysBack);
const formattedQuery = encodeURIComponent(`${query} -is:retweet lang:en`);
let allTweets: Website[] = [];
try {
console.log(`Searching Twitter for query: "${query}", start date: ${startDate.toISOString()}`);
const res = await twitterJSON({
url: `https://api.twitter.com/2/tweets/search/recent?query=${formattedQuery}&${await searchParams(
{
start_time: startDate.toISOString(),
expansions: ["author_id"],
["tweet.fields"]: ["created_at", "public_metrics"],
"user.fields": ["username"],
max_results: 100,
sort_order: "relevancy",
},
)}`,
bearerToken: apiKey,
});
if (res.errors) {
console.error("twitterSearch error", JSON.stringify(res));
throw new Error(JSON.stringify(res.errors));
}
const filteredTweets = (res.data || []).filter(tweet => {
return !/#/.test(tweet.text)
&& !/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}]/u.test(tweet.text);
});
const sortedTweets = filteredTweets
.sort((a, b) => {
const scoreA = (a.public_metrics?.like_count || 0)
+ (a.public_metrics?.retweet_count || 0) * 2
+ (a.public_metrics?.reply_count || 0) * 3;
const scoreB = (b.public_metrics?.like_count || 0)
+ (b.public_metrics?.retweet_count || 0) * 2
+ (b.public_metrics?.reply_count || 0) * 3;
return scoreB - scoreA;
})
.slice(0, maxResults);
allTweets = await Promise.all(
sortedTweets.map(async (tweet) => {
let author;
try {
const authorResponse = await twitterUser({
id: tweet.author_id,
bearerToken: apiKey,
});
author = authorResponse.data;
} catch (error) {