-
-
Notifications
You must be signed in to change notification settings - Fork 86
Expand file tree
/
Copy pathbookmarkOrganizer.js
More file actions
127 lines (107 loc) · 4.43 KB
/
Copy pathbookmarkOrganizer.js
File metadata and controls
127 lines (107 loc) · 4.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright (c) 2024-2026 nich (@nichxbt). Licensed under the Apache License, Version 2.0.
// scripts/bookmarkOrganizer.js
// Browser console script for categorizing bookmarks by keywords on X/Twitter
// Paste in DevTools console on x.com/i/bookmarks
// by nichxbt
(() => {
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
// =============================================
// CONFIGURATION
// =============================================
const CONFIG = {
maxBookmarks: 100,
categories: {
tech: ['code', 'programming', 'dev', 'javascript', 'python', 'software', 'ai', 'ml'],
crypto: ['bitcoin', 'eth', 'web3', 'crypto', 'defi', 'blockchain', 'btc'],
news: ['breaking', 'report', 'update', 'announced', 'confirmed'],
business: ['startup', 'revenue', 'funding', 'investor', 'market', 'growth'],
},
exportFormat: 'json', // 'json' | 'csv'
scrollDelay: 1500,
maxScrollRetries: 5,
};
// =============================================
const download = (data, filename) => {
const a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([typeof data === 'string' ? data : JSON.stringify(data, null, 2)], { type: typeof data === 'string' ? 'text/csv' : 'application/json' }));
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
console.log(`📥 Downloaded: ${filename}`);
};
const categorize = (text) => {
const lc = text.toLowerCase();
const matched = [];
for (const [cat, keywords] of Object.entries(CONFIG.categories)) {
if (keywords.some(kw => lc.includes(kw))) matched.push(cat);
}
return matched.length > 0 ? matched : ['uncategorized'];
};
const run = async () => {
console.log('🔖 BOOKMARK ORGANIZER — XActions by nichxbt\n');
if (!window.location.href.includes('/bookmarks')) {
console.error('❌ Navigate to x.com/i/bookmarks first!');
return;
}
console.log('📊 Scanning bookmarks...');
const bookmarks = new Map();
let retries = 0;
while (bookmarks.size < CONFIG.maxBookmarks && retries < CONFIG.maxScrollRetries) {
const prevSize = bookmarks.size;
document.querySelectorAll('article[data-testid="tweet"]').forEach(tweet => {
const linkEl = tweet.querySelector('a[href*="/status/"]');
if (!linkEl || bookmarks.has(linkEl.href)) return;
const text = tweet.querySelector('[data-testid="tweetText"]')?.textContent || '';
const userLink = tweet.querySelector('a[href^="/"][role="link"]');
const username = userLink?.href?.match(/x\.com\/([^/]+)/)?.[1] || '';
const time = tweet.querySelector('time')?.getAttribute('datetime') || '';
bookmarks.set(linkEl.href, {
text: text.substring(0, 300),
url: linkEl.href,
username,
time,
categories: categorize(text),
});
});
if (bookmarks.size === prevSize) retries++;
else retries = 0;
window.scrollTo(0, document.body.scrollHeight);
await sleep(CONFIG.scrollDelay);
}
const all = [...bookmarks.values()];
// Group by category
const byCategory = {};
all.forEach(b => {
b.categories.forEach(cat => {
if (!byCategory[cat]) byCategory[cat] = [];
byCategory[cat].push(b);
});
});
// Print report
console.log(`\n📚 Total bookmarks: ${all.length}\n`);
console.log('📁 BY CATEGORY:');
Object.entries(byCategory)
.sort((a, b) => b[1].length - a[1].length)
.forEach(([cat, items]) => {
console.log(`\n 📂 ${cat} (${items.length})`);
items.slice(0, 3).forEach(item => {
console.log(` • @${item.username}: "${item.text.substring(0, 50)}..."`);
});
if (items.length > 3) console.log(` ... and ${items.length - 3} more`);
});
// Export
const date = new Date().toISOString().slice(0, 10);
if (CONFIG.exportFormat === 'csv') {
const header = 'URL,Username,Text,Categories,Time';
const rows = all.map(b =>
`"${b.url}","${b.username}","${b.text.replace(/"/g, '""')}","${b.categories.join(';')}","${b.time}"`
);
download([header, ...rows].join('\n'), `xactions-bookmarks-organized-${date}.csv`);
} else {
download({ exportedAt: new Date().toISOString(), total: all.length, byCategory, bookmarks: all }, `xactions-bookmarks-organized-${date}.json`);
}
console.log('\n🏁 Done!');
};
run();
})();