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
feat: add support for custom backfill limits in web UI
jack
2 months ago
90b70a02
435cc19b
+21
-12
3 changed files
expand all
collapse all
unified
split
public
index.html
src
index.ts
server.ts
+5
-3
public/index.html
···
207
};
208
209
const runBackfill = async (id, twitterUsername) => {
210
-
if (!confirm(`Run full history backfill for @${twitterUsername}? This may take a while.`)) return;
0
0
211
try {
212
-
await axios.post(`/api/backfill/${id}`, {}, {
213
headers: { Authorization: `Bearer ${token}` }
214
});
215
alert(`Backfill queued for @${twitterUsername}`);
···
280
);
281
}
282
283
-
const isBackfillQueued = (id) => status.pendingBackfills?.includes(id);
284
285
return (
286
<div>
···
207
};
208
209
const runBackfill = async (id, twitterUsername) => {
210
+
const limit = prompt(`How many tweets to backfill for @${twitterUsername}?`, "100");
211
+
if (limit === null) return; // Cancelled
212
+
213
try {
214
+
await axios.post(`/api/backfill/${id}`, { limit: parseInt(limit) || 100 }, {
215
headers: { Authorization: `Bearer ${token}` }
216
});
217
alert(`Backfill queued for @${twitterUsername}`);
···
282
);
283
}
284
285
+
const isBackfillQueued = (id) => status.pendingBackfills?.some(b => (b.id || b) === id);
286
287
return (
288
<div>
+5
-3
src/index.ts
···
582
const agent = await getAgent(mapping);
583
if (!agent) continue;
584
585
-
if (forceBackfill || pendingBackfills.includes(mapping.id)) {
586
-
console.log(`[${mapping.twitterUsername}] Running backfill (limit 100)...`);
587
-
await importHistory(mapping.twitterUsername, 100, dryRun);
0
0
588
clearBackfill(mapping.id);
589
console.log(`[${mapping.twitterUsername}] Backfill complete.`);
590
} else {
···
582
const agent = await getAgent(mapping);
583
if (!agent) continue;
584
585
+
const backfillReq = pendingBackfills.find(b => b.id === mapping.id);
586
+
if (forceBackfill || backfillReq) {
587
+
const limit = backfillReq?.limit || 100;
588
+
console.log(`[${mapping.twitterUsername}] Running backfill (limit ${limit})...`);
589
+
await importHistory(mapping.twitterUsername, limit, dryRun);
590
clearBackfill(mapping.id);
591
console.log(`[${mapping.twitterUsername}] Backfill complete.`);
592
} else {
+11
-6
src/server.ts
···
17
// In-memory state for triggers and scheduling
18
let lastCheckTime = Date.now();
19
let nextCheckTime = Date.now() + (getConfig().checkIntervalMinutes || 5) * 60 * 1000;
20
-
let pendingBackfills: string[] = [];
0
0
0
0
21
22
app.use(cors());
23
app.use(express.json());
···
176
177
app.post('/api/backfill/:id', authenticateToken, requireAdmin, (req, res) => {
178
const { id } = req.params;
0
179
const config = getConfig();
180
const mapping = config.mappings.find((m) => m.id === id);
181
···
184
return;
185
}
186
187
-
if (!pendingBackfills.includes(id)) {
188
-
pendingBackfills.push(id);
189
}
190
191
lastCheckTime = 0;
···
195
196
app.delete('/api/backfill/:id', authenticateToken, (req, res) => {
197
const { id } = req.params;
198
-
pendingBackfills = pendingBackfills.filter((bid) => bid !== id);
199
res.json({ success: true });
200
});
201
···
206
nextCheckTime = lastCheckTime + (config.checkIntervalMinutes || 5) * 60 * 1000;
207
}
208
209
-
export function getPendingBackfills(): string[] {
210
return [...pendingBackfills];
211
}
212
···
215
}
216
217
export function clearBackfill(id: string) {
218
-
pendingBackfills = pendingBackfills.filter((bid) => bid !== id);
219
}
220
221
// Serve the frontend for any other route (middleware approach for Express 5)
···
17
// In-memory state for triggers and scheduling
18
let lastCheckTime = Date.now();
19
let nextCheckTime = Date.now() + (getConfig().checkIntervalMinutes || 5) * 60 * 1000;
20
+
interface PendingBackfill {
21
+
id: string;
22
+
limit?: number;
23
+
}
24
+
let pendingBackfills: PendingBackfill[] = [];
25
26
app.use(cors());
27
app.use(express.json());
···
180
181
app.post('/api/backfill/:id', authenticateToken, requireAdmin, (req, res) => {
182
const { id } = req.params;
183
+
const { limit } = req.body;
184
const config = getConfig();
185
const mapping = config.mappings.find((m) => m.id === id);
186
···
189
return;
190
}
191
192
+
if (!pendingBackfills.find(b => b.id === id)) {
193
+
pendingBackfills.push({ id, limit: limit ? Number(limit) : undefined });
194
}
195
196
lastCheckTime = 0;
···
200
201
app.delete('/api/backfill/:id', authenticateToken, (req, res) => {
202
const { id } = req.params;
203
+
pendingBackfills = pendingBackfills.filter((bid) => bid.id !== id);
204
res.json({ success: true });
205
});
206
···
211
nextCheckTime = lastCheckTime + (config.checkIntervalMinutes || 5) * 60 * 1000;
212
}
213
214
+
export function getPendingBackfills(): PendingBackfill[] {
215
return [...pendingBackfills];
216
}
217
···
220
}
221
222
export function clearBackfill(id: string) {
223
+
pendingBackfills = pendingBackfills.filter((bid) => bid.id !== id);
224
}
225
226
// Serve the frontend for any other route (middleware approach for Express 5)