aboutsummaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorYash <[email protected]>2024-04-11 11:02:41 +0000
committerYash <[email protected]>2024-04-11 11:02:41 +0000
commitfc3130d5629bca33da267db08be8e56ee140751d (patch)
treebef1dbbe754530a4b2ac5befbbdaf8ac437c5c35 /apps
parentok (diff)
parentprepare statement (diff)
downloadsupermemory-fc3130d5629bca33da267db08be8e56ee140751d.tar.xz
supermemory-fc3130d5629bca33da267db08be8e56ee140751d.zip
Merge branch 'main' of https://github.com/Dhravya/supermemory
Diffstat (limited to 'apps')
-rw-r--r--apps/cf-ai-backend/src/env.d.ts9
-rw-r--r--apps/cf-ai-backend/src/routes/chat.ts3
-rw-r--r--apps/cf-ai-backend/wrangler.toml4
-rw-r--r--apps/extension/src/App.tsx233
-rw-r--r--apps/extension/src/SideBar.tsx168
-rw-r--r--apps/extension/src/background.ts32
-rw-r--r--apps/web/db/prepare.sql4
-rw-r--r--apps/web/src/app/api/store/route.ts2
8 files changed, 444 insertions, 11 deletions
diff --git a/apps/cf-ai-backend/src/env.d.ts b/apps/cf-ai-backend/src/env.d.ts
index acbd6c43..65def4fd 100644
--- a/apps/cf-ai-backend/src/env.d.ts
+++ b/apps/cf-ai-backend/src/env.d.ts
@@ -4,4 +4,13 @@ interface Env {
SECURITY_KEY: string;
OPENAI_API_KEY: string;
GOOGLE_AI_API_KEY: string;
+ MY_QUEUE: Queue<TweetData>;
+}
+
+interface TweetData {
+ tweetText: string;
+ postUrl: string;
+ authorName: string;
+ handle: string;
+ time: string;
}
diff --git a/apps/cf-ai-backend/src/routes/chat.ts b/apps/cf-ai-backend/src/routes/chat.ts
index 75e298b8..980e46a3 100644
--- a/apps/cf-ai-backend/src/routes/chat.ts
+++ b/apps/cf-ai-backend/src/routes/chat.ts
@@ -60,8 +60,9 @@ export async function POST(request: Request, _: CloudflareVectorizeStore, embedd
// if (responses.count === 0) {
// return new Response(JSON.stringify({ message: "No Results Found" }), { status: 404 });
// }
+ console.log(responses.matches);
- const highScoreIds = responses.matches.filter(({ score }) => score > 0.35).map(({ id }) => id);
+ const highScoreIds = responses.matches.filter(({ score }) => score > 0.3).map(({ id }) => id);
if (sourcesOnly === 'true') {
return new Response(JSON.stringify({ ids: highScoreIds }), { status: 200 });
diff --git a/apps/cf-ai-backend/wrangler.toml b/apps/cf-ai-backend/wrangler.toml
index 9e3ee21b..9a9effb6 100644
--- a/apps/cf-ai-backend/wrangler.toml
+++ b/apps/cf-ai-backend/wrangler.toml
@@ -9,6 +9,10 @@ index_name = "any-vector"
[ai]
binding = "AI"
+[[queues.producers]]
+ queue = "batch-vector-queue"
+ binding = "MY_QUEUE"
+
# Variable bindings. These are arbitrary, plaintext strings (similar to environment variables)
# Note: Use secrets to store sensitive data.
# Docs: https://developers.cloudflare.com/workers/platform/environment-variables
diff --git a/apps/extension/src/App.tsx b/apps/extension/src/App.tsx
index f563664f..89227432 100644
--- a/apps/extension/src/App.tsx
+++ b/apps/extension/src/App.tsx
@@ -13,7 +13,7 @@ function App() {
null,
);
- const doStuff = () => {
+ const getUserData = () => {
chrome.runtime.sendMessage({ type: "getJwt" }, (response) => {
const jwt = response.jwt;
const loginButton = document.getElementById("login");
@@ -41,9 +41,69 @@ function App() {
};
useEffect(() => {
- doStuff();
+ getUserData();
}, []);
+ // TODO: Implement getting bookmarks from API directly
+ // const [status, setStatus] = useState('');
+ // const [bookmarks, setBookmarks] = useState<TweetData[]>([]);
+
+ // const fetchBookmarks = (e: React.MouseEvent<HTMLButtonElement>) => {
+ // e.preventDefault();
+
+ // chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
+ // chrome.tabs.sendMessage(tabs[0].id!, { action: 'showProgressIndicator' });
+ // });
+
+ // chrome.tabs.create(
+ // { url: 'https://twitter.com/i/bookmarks/all' },
+ // function (tab) {
+ // chrome.tabs.onUpdated.addListener(function listener(tabId, info) {
+ // if (tabId === tab.id && info.status === 'complete') {
+ // chrome.tabs.onUpdated.removeListener(listener);
+
+ // chrome.runtime.sendMessage(
+ // { action: 'getAuthData' },
+ // function (response) {
+ // const authorizationHeader = response.authorizationHeader;
+ // const csrfToken = response.csrfToken;
+ // const cookies = response.cookies;
+
+ // if (authorizationHeader && csrfToken && cookies) {
+ // fetchAllBookmarks(authorizationHeader, csrfToken, cookies)
+ // .then((bookmarks) => {
+ // console.log('Bookmarks data:', bookmarks);
+ // setBookmarks(bookmarks);
+ // chrome.tabs.sendMessage(tabId, {
+ // action: 'hideProgressIndicator',
+ // });
+ // setStatus(
+ // `Fetched ${bookmarks.length} bookmarked tweets.`,
+ // );
+ // })
+ // .catch((error) => {
+ // console.error('Error:', error);
+ // chrome.tabs.sendMessage(tabId, {
+ // action: 'hideProgressIndicator',
+ // });
+ // setStatus(
+ // 'Error fetching bookmarks. Please check the console for details.',
+ // );
+ // });
+ // } else {
+ // chrome.tabs.sendMessage(tabId, {
+ // action: 'hideProgressIndicator',
+ // });
+ // setStatus('Missing authentication data');
+ // }
+ // },
+ // );
+ // }
+ // });
+ // },
+ // );
+ // };
+
return (
<div className="p-8">
<button
@@ -69,6 +129,19 @@ function App() {
<h3>{userData.data.user.name}</h3>
<p>{userData.data.user.email}</p>
</div>
+ {/* TODO: Implement getting bookmarks from API directly */}
+ {/* <button onClick={(e) => fetchBookmarks(e)}>Fetch Bookmarks</button>
+ <div>{status}</div>
+
+ <div>
+ {bookmarks.map((bookmark) => (
+ <div key={bookmark.tweet_id}>
+ <p>{bookmark.author}</p>
+ <p>{bookmark.date}</p>
+ <p>{bookmark.full_text}</p>
+ </div>
+ ))}
+ </div> */}
</div>
)}
</div>
@@ -76,4 +149,160 @@ function App() {
);
}
+// TODO: Implement getting bookmarks from API directly
+// async function fetchAllBookmarks(
+// authorizationHeader: string,
+// csrfToken: string,
+// cookies: string,
+// ): Promise<TweetData[]> {
+// const baseUrl =
+// 'https://twitter.com/i/api/graphql/uJEL6XARgGmo2EAsO2Pfkg/Bookmarks';
+// const params = new URLSearchParams({
+// variables: JSON.stringify({
+// count: 100,
+// includePromotedContent: true,
+// }),
+// features: JSON.stringify({
+// graphql_timeline_v2_bookmark_timeline: true,
+// rweb_tipjar_consumption_enabled: false,
+// responsive_web_graphql_exclude_directive_enabled: true,
+// verified_phone_label_enabled: true,
+// creator_subscriptions_tweet_preview_api_enabled: true,
+// responsive_web_graphql_timeline_navigation_enabled: true,
+// responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
+// communities_web_enable_tweet_community_results_fetch: true,
+// c9s_tweet_anatomy_moderator_badge_enabled: true,
+// tweetypie_unmention_optimization_enabled: true,
+// responsive_web_edit_tweet_api_enabled: true,
+// graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
+// view_counts_everywhere_api_enabled: true,
+// longform_notetweets_consumption_enabled: true,
+// responsive_web_twitter_article_tweet_consumption_enabled: true,
+// tweet_awards_web_tipping_enabled: false,
+// creator_subscriptions_quote_tweet_preview_enabled: false,
+// freedom_of_speech_not_reach_fetch_enabled: true,
+// standardized_nudges_misinfo: true,
+// tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled:
+// true,
+// tweet_with_visibility_results_prefer_gql_media_interstitial_enabled:
+// false,
+// rweb_video_timestamps_enabled: true,
+// longform_notetweets_rich_text_read_enabled: true,
+// longform_notetweets_inline_media_enabled: true,
+// responsive_web_enhance_cards_enabled: false,
+// }),
+// });
+
+// const requestUrl = `${baseUrl}?${params}`;
+
+// const headers = {
+// Authorization: authorizationHeader,
+// 'X-Csrf-Token': csrfToken,
+// Cookie: cookies,
+// };
+
+// const bookmarks: TweetData[] = [];
+// let nextCursor = null;
+// let requestCount = 0;
+// const maxRequestsPerWindow = 450;
+// const windowDuration = 15 * 60 * 1000; // 15 minutes in milliseconds
+// let windowStartTime = Date.now();
+
+// do {
+// if (nextCursor) {
+// params.set(
+// 'variables',
+// JSON.stringify({
+// count: 100,
+// cursor: nextCursor,
+// includePromotedContent: true,
+// }),
+// );
+// }
+
+// // Check if the rate limit is exceeded
+// if (requestCount >= maxRequestsPerWindow) {
+// const elapsedTime = Date.now() - windowStartTime;
+// if (elapsedTime < windowDuration) {
+// const waitTime = windowDuration - elapsedTime;
+// await new Promise((resolve) => setTimeout(resolve, waitTime));
+// }
+// requestCount = 0;
+// windowStartTime = Date.now();
+// }
+
+// try {
+// const response = await fetch(requestUrl, {
+// method: 'GET',
+// headers: headers,
+// });
+
+// requestCount++;
+
+// if (!response.ok) {
+// throw new Error(`HTTP error! status: ${response.status}`);
+// }
+
+// const data = await response.json();
+// const timeline = data.data.bookmark_timeline_v2.timeline;
+
+// timeline.instructions.forEach(
+// (instruction: {
+// type: string;
+// entries: {
+// content: {
+// entryType: string;
+// itemContent: {
+// tweet_results: {
+// result: {
+// legacy: {
+// full_text: string;
+// created_at: string;
+// };
+// core: {
+// user_results: {
+// result: {
+// legacy: {
+// screen_name: string;
+// };
+// };
+// };
+// };
+// rest_id: string;
+// };
+// };
+// };
+// };
+// }[];
+// }) => {
+// if (instruction.type === 'TimelineAddEntries') {
+// instruction.entries.forEach((entry) => {
+// if (entry.content.entryType === 'TimelineTimelineItem') {
+// const tweet = entry.content.itemContent.tweet_results.result;
+// const tweetData = {
+// full_text: tweet.legacy.full_text,
+// url: `https://twitter.com/${tweet.core.user_results.result.legacy.screen_name}/status/${tweet.rest_id}`,
+// author: tweet.core.user_results.result.legacy.screen_name,
+// date: tweet.legacy.created_at,
+// tweet_id: tweet.rest_id,
+// };
+// bookmarks.push(tweetData);
+// }
+// });
+// }
+// },
+// );
+
+// nextCursor = timeline.instructions.find(
+// (instruction: { type: string }) =>
+// instruction.type === 'TimelineTerminateTimeline',
+// )?.direction?.cursor;
+// } catch (error) {
+// console.error('Error fetching bookmarks:', error);
+// throw error;
+// }
+// } while (nextCursor);
+
+// return bookmarks;
+// }
export default App;
diff --git a/apps/extension/src/SideBar.tsx b/apps/extension/src/SideBar.tsx
index 07d9b9f5..b3bfb08a 100644
--- a/apps/extension/src/SideBar.tsx
+++ b/apps/extension/src/SideBar.tsx
@@ -25,20 +25,176 @@ function sendUrlToAPI() {
}
function SideBar() {
+ // TODO: Implement getting bookmarks from API directly
+ // chrome.runtime.onMessage.addListener(function (request) {
+ // if (request.action === 'showProgressIndicator') {
+ // // TODO: SHOW PROGRESS INDICATOR
+ // // showProgressIndicator();
+ // } else if (request.action === 'hideProgressIndicator') {
+ // // hideProgressIndicator();
+ // }
+ // });
+
const [savedWebsites, setSavedWebsites] = useState<string[]>([]);
const [isSendingData, setIsSendingData] = useState(false);
+ interface TweetData {
+ tweetText: string;
+ postUrl: string;
+ authorName: string;
+ handle: string;
+ time: string;
+ }
+
+ const fetchBookmarks = () => {
+ const tweets: TweetData[] = []; // Initialize an empty array to hold all tweet elements
+
+ const scrollInterval = 1000;
+ const scrollStep = 5000; // Pixels to scroll on each step
+
+ let previousTweetCount = 0;
+ let unchangedCount = 0;
+
+ const scrollToEndIntervalID = setInterval(() => {
+ window.scrollBy(0, scrollStep);
+ const currentTweetCount = tweets.length;
+ if (currentTweetCount === previousTweetCount) {
+ unchangedCount++;
+ if (unchangedCount >= 2) {
+ // Stop if the count has not changed 5 times
+ console.log("Scraping complete");
+ console.log("Total tweets scraped: ", tweets.length);
+ console.log("Downloading tweets as JSON...");
+ clearInterval(scrollToEndIntervalID); // Stop scrolling
+ observer.disconnect(); // Stop observing DOM changes
+ downloadTweetsAsJson(tweets); // Download the tweets list as a JSON file
+ }
+ } else {
+ unchangedCount = 0; // Reset counter if new tweets were added
+ }
+ previousTweetCount = currentTweetCount; // Update previous count for the next check
+ }, scrollInterval);
+
+ function updateTweets() {
+ document
+ .querySelectorAll('article[data-testid="tweet"]')
+ .forEach((tweetElement) => {
+ const authorName = (
+ tweetElement.querySelector(
+ '[data-testid="User-Name"]',
+ ) as HTMLElement
+ )?.innerText;
+
+ const handle = (
+ tweetElement.querySelector('[role="link"]') as HTMLLinkElement
+ ).href
+ .split("/")
+ .pop();
+
+ const tweetText = (
+ tweetElement.querySelector(
+ '[data-testid="tweetText"]',
+ ) as HTMLElement
+ )?.innerText;
+ const time = (
+ tweetElement.querySelector("time") as HTMLTimeElement
+ ).getAttribute("datetime");
+ const postUrl = (
+ tweetElement.querySelector(
+ ".css-175oi2r.r-18u37iz.r-1q142lx a",
+ ) as HTMLLinkElement
+ )?.href;
+
+ const isTweetNew = !tweets.some((tweet) => tweet.postUrl === postUrl);
+ if (isTweetNew) {
+ tweets.push({
+ authorName,
+ handle: handle ?? "",
+ tweetText,
+ time: time ?? "",
+ postUrl,
+ });
+ console.log("Tweets capturados: ", tweets.length);
+ }
+ });
+ }
+
+ // Initially populate the tweets array
+ updateTweets();
+
+ // Create a MutationObserver to observe changes in the DOM
+ const observer = new MutationObserver((mutations) => {
+ mutations.forEach((mutation) => {
+ if (mutation.addedNodes.length) {
+ updateTweets(); // Call updateTweets whenever new nodes are added to the DOM
+ }
+ });
+ });
+
+ // Start observing the document body for child list changes
+ observer.observe(document.body, { childList: true, subtree: true });
+
+ function downloadTweetsAsJson(tweetsArray: TweetData[]) {
+ const jsonData = JSON.stringify(tweetsArray); // Convert the array to JSON
+
+ // TODO: SEND jsonData to server
+ console.log(jsonData);
+ }
+ };
+
return (
<>
<TooltipProvider>
- <div className="anycontext-flex anycontext-flex-col anycontext-gap-2 anycontext-fixed anycontext-bottom-12 anycontext-right-0 anycontext-z-[99999] anycontext-font-sans">
- {/* <Tooltip delayDuration={300}>
- <TooltipContent side="left">
- <p>Open Sidebar</p>
- </TooltipContent>
- </Tooltip> */}
+ <div className="anycontext-flex anycontext-group anycontext-flex-col anycontext-gap-2 anycontext-fixed anycontext-bottom-12 anycontext-right-0 anycontext-z-[99999] anycontext-font-sans">
+ {window.location.href.includes("twitter.com") ||
+ window.location.href.includes("x.com") ? (
+ <Tooltip delayDuration={300}>
+ <TooltipTrigger
+ className="anycontext-bg-transparent
+ anycontext-border-none anycontext-m-0 anycontext-p-0"
+ >
+ <button
+ onClick={() => {
+ if (window.location.href.endsWith("/i/bookmarks/all")) {
+ fetchBookmarks();
+ } else {
+ window.location.href =
+ "https://twitter.com/i/bookmarks/all";
+ setTimeout(() => {
+ fetchBookmarks();
+ }, 2500);
+ }
+ }}
+ className="anycontext-open-button disabled:anycontext-opacity-30 anycontext-bg-transparent
+ anycontext-border-none anycontext-m-0 anycontext-p-0"
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ strokeWidth={1.5}
+ stroke="currentColor"
+ className="anycontext-w-6 anycontext-h-6"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M17.593 3.322c1.1.128 1.907 1.077 1.907 2.185V21L12 17.25 4.5 21V5.507c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0 1 11.186 0Z"
+ />
+ </svg>
+ </button>
+ </TooltipTrigger>
+ <TooltipContent className="anycontext-p-0" side="left">
+ <p className="anycontext-p-0 anycontext-m-0">
+ Import twitter bookmarks
+ </p>
+ </TooltipContent>
+ </Tooltip>
+ ) : (
+ <></>
+ )}
<Tooltip delayDuration={300}>
<TooltipTrigger
className="anycontext-bg-transparent
diff --git a/apps/extension/src/background.ts b/apps/extension/src/background.ts
index 2cf29f40..b3030e74 100644
--- a/apps/extension/src/background.ts
+++ b/apps/extension/src/background.ts
@@ -5,6 +5,30 @@ const backendUrl =
? "http://localhost:3000"
: "https://supermemory.dhr.wtf";
+// TODO: Implement getting bookmarks from API directly
+// let authorizationHeader: string | null = null;
+// let csrfToken: string | null = null;
+// let cookies: string | null = null;
+
+// chrome.webRequest.onBeforeSendHeaders.addListener(
+// (details) => {
+// for (let i = 0; i < details.requestHeaders!.length; ++i) {
+// const header = details.requestHeaders![i];
+// if (header.name.toLowerCase() === 'authorization') {
+// authorizationHeader = header.value || null;
+// } else if (header.name.toLowerCase() === 'x-csrf-token') {
+// csrfToken = header.value || null;
+// } else if (header.name.toLowerCase() === 'cookie') {
+// cookies = header.value || null;
+// }
+
+// console.log(header, authorizationHeader, csrfToken, cookies)
+// }
+// },
+// { urls: ['https://twitter.com/*', 'https://x.com/*'] },
+// ['requestHeaders']
+// );
+
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type === "getJwt") {
chrome.storage.local.get(["jwt"], ({ jwt }) => {
@@ -73,4 +97,12 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
return true;
})();
}
+ // TODO: Implement getting bookmarks from API directly
+ // else if (request.action === 'getAuthData') {
+ // sendResponse({
+ // authorizationHeader: authorizationHeader,
+ // csrfToken: csrfToken,
+ // cookies: cookies
+ // });
+ // }
});
diff --git a/apps/web/db/prepare.sql b/apps/web/db/prepare.sql
index a4f9951d..dcba4d40 100644
--- a/apps/web/db/prepare.sql
+++ b/apps/web/db/prepare.sql
@@ -34,7 +34,7 @@ CREATE TABLE `session` (
--> statement-breakpoint
CREATE TABLE `space` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
- `name` text DEFAULT 'all' NOT NULL,
+ `name` text DEFAULT 'none' NOT NULL,
`user` text(255),
FOREIGN KEY (`user`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
);
@@ -47,6 +47,7 @@ CREATE TABLE `storedContent` (
`url` text NOT NULL,
`savedAt` integer NOT NULL,
`baseUrl` text(255),
+ `type` text DEFAULT 'page',
`image` text(255),
`user` text(255),
FOREIGN KEY (`user`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
@@ -69,6 +70,7 @@ CREATE TABLE `verificationToken` (
--> statement-breakpoint
CREATE INDEX `account_userId_idx` ON `account` (`userId`);--> statement-breakpoint
CREATE INDEX `session_userId_idx` ON `session` (`userId`);--> statement-breakpoint
+CREATE UNIQUE INDEX `space_name_unique` ON `space` (`name`);--> statement-breakpoint
CREATE INDEX `spaces_name_idx` ON `space` (`name`);--> statement-breakpoint
CREATE INDEX `spaces_user_idx` ON `space` (`user`);--> statement-breakpoint
CREATE INDEX `storedContent_url_idx` ON `storedContent` (`url`);--> statement-breakpoint
diff --git a/apps/web/src/app/api/store/route.ts b/apps/web/src/app/api/store/route.ts
index ebe23077..ca6921c4 100644
--- a/apps/web/src/app/api/store/route.ts
+++ b/apps/web/src/app/api/store/route.ts
@@ -67,7 +67,7 @@ export async function POST(req: NextRequest) {
let storeToSpace = data.space;
if (!storeToSpace) {
- storeToSpace = "all";
+ storeToSpace = "none";
}
const storedContentId = await db.insert(storedContent).values({