tangled
alpha
login
or
join now
indexx.dev
/
tweets2bsky
forked from
j4ck.xyz/tweets2bsky
0
fork
atom
A simple tool which lets you scrape twitter accounts and crosspost them to bluesky accounts! Comes with a CLI and a webapp for managing profiles! Works with images/videos/link embeds/threads.
0
fork
atom
overview
issues
pulls
pipelines
fix: robust t.co link expansion fallback for link cards
jack
2 months ago
aa76ee80
67ed40fd
+10
-64
2 changed files
expand all
collapse all
unified
split
src
index.ts
test-link.ts
+10
-1
src/index.ts
···
833
833
if (tco && expanded) text = text.replace(tco, expanded);
834
834
}
835
835
836
836
+
// Fallback: Regex for t.co links (if entities failed or missed one)
836
837
const tcoRegex = /https:\/\/t\.co\/[a-zA-Z0-9]+/g;
837
838
const matches = text.match(tcoRegex) || [];
838
839
for (const tco of matches) {
840
840
+
// Avoid re-resolving if we already handled it via entities
841
841
+
if (urls.some(u => u.url === tco)) continue;
842
842
+
843
843
+
console.log(`[${twitterUsername}] 🔍 Resolving fallback link: ${tco}`);
839
844
const resolved = await expandUrl(tco);
840
840
-
if (resolved !== tco) text = text.replace(tco, resolved);
845
845
+
if (resolved !== tco) {
846
846
+
text = text.replace(tco, resolved);
847
847
+
// Add to urls array so it can be used for card embedding later
848
848
+
urls.push({ url: tco, expanded_url: resolved });
849
849
+
}
841
850
}
842
851
843
852
// 2. Media Handling
-63
src/test-link.ts
···
1
1
-
import 'dotenv/config';
2
2
-
import { TwitterClient } from '@steipete/bird/dist/lib/twitter-client.js';
3
3
-
4
4
-
// Provided credentials
5
5
-
const AUTH_TOKEN = '30f5905989c9984bef1ca849910db8c84ae31c04';
6
6
-
const CT0 = '012bae351eeebe4a19df0a21c73d5705d887246beee09c96da2a5a6935495688d3479b8e3fa963433a43b3b37675c16f4a192d018dd388108a6d6aa3ed3d9ba3233238bc71830583a1613ec042d3ba55';
7
7
-
8
8
-
async function main() {
9
9
-
console.log('🧪 Starting Link Extraction Test...');
10
10
-
11
11
-
const client = new TwitterClient({
12
12
-
cookies: {
13
13
-
authToken: AUTH_TOKEN,
14
14
-
ct0: CT0,
15
15
-
},
16
16
-
});
17
17
-
18
18
-
const username = 'NVIDIANetworkng';
19
19
-
// Searching for the specific tweet ID: 2003547578848206861
20
20
-
// Note: search usually doesn't find by ID directly unless we use specific operators or search from user
21
21
-
// Let's try searching from user and look for the ID.
22
22
-
23
23
-
console.log(`🔍 Fetching tweets for @${username}...`);
24
24
-
25
25
-
try {
26
26
-
const result = (await client.search(`from:${username}`, 20)) as any;
27
27
-
28
28
-
if (!result.success || !result.tweets) {
29
29
-
console.error('❌ Failed to fetch tweets:', result.error);
30
30
-
return;
31
31
-
}
32
32
-
33
33
-
const targetId = '2003547578848206861';
34
34
-
const targetTweet = result.tweets.find((t: any) => (t.id_str || t.id) === targetId);
35
35
-
36
36
-
if (targetTweet) {
37
37
-
console.log('✅ Found target tweet!');
38
38
-
console.log('--- RAW TWEET OBJECT ---');
39
39
-
console.log(JSON.stringify(targetTweet, null, 2));
40
40
-
console.log('------------------------');
41
41
-
42
42
-
console.log('--- Entities ---');
43
43
-
console.log(JSON.stringify(targetTweet.entities, null, 2));
44
44
-
45
45
-
if (targetTweet.card) {
46
46
-
console.log('--- Card Data (Bird internal?) ---');
47
47
-
console.log(JSON.stringify(targetTweet.card, null, 2));
48
48
-
}
49
49
-
} else {
50
50
-
console.log(`❌ Target tweet ${targetId} not found in last 20 results.`);
51
51
-
// Just print the first one to see structure anyway
52
52
-
if (result.tweets.length > 0) {
53
53
-
console.log('Printing first tweet for structure analysis:');
54
54
-
console.log(JSON.stringify(result.tweets[0], null, 2));
55
55
-
}
56
56
-
}
57
57
-
58
58
-
} catch (err) {
59
59
-
console.error('❌ Error:', err);
60
60
-
}
61
61
-
}
62
62
-
63
63
-
main();