MCPcopy
hub / github.com/afar1/fieldtheory-cli / syncTwitterBookmarks

Function syncTwitterBookmarks

src/bookmarks.ts:170–246  ·  view source on GitHub ↗
(
  mode: 'full' | 'incremental',
  options: { targetAdds?: number } = {}
)

Source from the content-addressed store, hash-verified

168}
169
170export async function syncTwitterBookmarks(
171 mode: 'full' | 'incremental',
172 options: { targetAdds?: number } = {}
173): Promise<BookmarkSyncResult> {
174 const token = await loadTwitterOAuthToken();
175 if (!token?.access_token) {
176 throw new Error('Missing user-context OAuth token. Run: ft auth');
177 }
178
179 const me = await fetchCurrentUserId(token.access_token);
180 if (!me.ok || !me.id) {
181 throw new Error(`Could not resolve current user id: ${me.detail}`);
182 }
183
184 ensureDataDir();
185 const cachePath = twitterBookmarksCachePath();
186 const metaPath = twitterBookmarksMetaPath();
187 const now = new Date().toISOString();
188 const existing = await readJsonLines<BookmarkRecord>(cachePath);
189 const existingById = new Map(existing.map((item) => [item.id, item]));
190
191 const allFetched: BookmarkRecord[] = [];
192 let nextToken: string | undefined;
193 let pages = 0;
194 const maxPages = mode === 'full' ? 200 : 2;
195
196 while (pages < maxPages) {
197 const pageResult = await fetchBookmarksPage(token.access_token, me.id, nextToken);
198 if (!pageResult.ok || !pageResult.page) {
199 throw new Error(`Bookmark fetch failed (${pageResult.status}): ${pageResult.detail}`);
200 }
201
202 const normalized = normalizeBookmarkPage(pageResult.page, now);
203 allFetched.push(...normalized);
204 nextToken = pageResult.page.meta?.next_token;
205 pages += 1;
206
207 if (!nextToken) break;
208 if (mode === 'incremental' && normalized.every((item) => existingById.has(item.id))) break;
209 if (typeof options.targetAdds === 'number') {
210 const uniqueAddsSoFar = allFetched.filter((item, index, arr) => arr.findIndex((x) => x.id === item.id) === index).filter((item) => !existingById.has(item.id)).length;
211 if (uniqueAddsSoFar >= options.targetAdds) break;
212 }
213 }
214
215 const merged = [...existing];
216 let added = 0;
217 for (const record of allFetched) {
218 if (!existingById.has(record.id)) {
219 merged.push(record);
220 existingById.set(record.id, record);
221 added += 1;
222 if (typeof options.targetAdds === 'number' && added >= options.targetAdds) break;
223 }
224 }
225
226 merged.sort((a, b) => String(b.bookmarkedAt ?? b.syncedAt).localeCompare(String(a.bookmarkedAt ?? a.syncedAt)));
227 await writeJsonLines(cachePath, merged);

Callers 1

buildCliFunction · 0.85

Calls 10

loadTwitterOAuthTokenFunction · 0.85
fetchCurrentUserIdFunction · 0.85
ensureDataDirFunction · 0.85
twitterBookmarksMetaPathFunction · 0.85
fetchBookmarksPageFunction · 0.85
normalizeBookmarkPageFunction · 0.85
writeJsonLinesFunction · 0.85
writeJsonFunction · 0.85
pathExistsFunction · 0.70

Tested by

no test coverage detected