Handle unavailable ChatGPT shares
This commit is contained in:
+12
-1
@@ -26,6 +26,9 @@ function publicErrorFor(error: unknown): string {
|
||||
if (/captcha|verify|robot/i.test(message)) {
|
||||
return "ChatGPT asked for verification, so this public share could not be captured automatically.";
|
||||
}
|
||||
if (/unavailable|no longer public|not public|redirected away/i.test(message)) {
|
||||
return "This ChatGPT share is not available publicly anymore, so it cannot be captured.";
|
||||
}
|
||||
if (/timeout/i.test(message)) {
|
||||
return "The public ChatGPT share took too long to load.";
|
||||
}
|
||||
@@ -271,9 +274,11 @@ async function extractConversation(sourceUrl: string): Promise<ExtractedConversa
|
||||
if (!page) throw new Error("Chromium CDP page target was not created.");
|
||||
await page.setViewportSize({ width: 1360, height: 900 }).catch(() => undefined);
|
||||
page.setDefaultTimeout(config.captureTimeoutMs);
|
||||
const expectedPath = new URL(sourceUrl).pathname;
|
||||
await page
|
||||
.waitForFunction(
|
||||
() => {
|
||||
(sharePath) => {
|
||||
if (!location.pathname.startsWith(sharePath)) return true;
|
||||
const bodyText = document.body?.innerText || document.body?.textContent || "";
|
||||
if (/verify you are human|captcha|unusual activity/i.test(bodyText)) return true;
|
||||
if (document.querySelectorAll("[data-message-author-role]").length > 0) return true;
|
||||
@@ -281,12 +286,18 @@ async function extractConversation(sourceUrl: string): Promise<ExtractedConversa
|
||||
if ((markdown?.textContent || "").trim().length > 0) return true;
|
||||
return /(?:^|\s)(You said:|ChatGPT said:)/.test(bodyText);
|
||||
},
|
||||
expectedPath,
|
||||
{ timeout: config.captureTimeoutMs }
|
||||
)
|
||||
.catch(() => undefined);
|
||||
await page.waitForLoadState("networkidle", { timeout: 5_000 }).catch(() => undefined);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const actualUrl = page.url();
|
||||
if (!new URL(actualUrl).pathname.startsWith(expectedPath)) {
|
||||
throw new Error(`ChatGPT share is unavailable or no longer public: page redirected away to ${actualUrl}.`);
|
||||
}
|
||||
|
||||
const extracted = await withTimeout(page.evaluate(() => {
|
||||
function cleanHtml(root: Element): string {
|
||||
const clone = root.cloneNode(true) as Element;
|
||||
|
||||
@@ -90,6 +90,17 @@ export class Store {
|
||||
.run(status, detail?.errorPublic ?? null, detail?.errorDetail ?? null, new Date().toISOString(), id);
|
||||
}
|
||||
|
||||
retryCapture(id: string): void {
|
||||
this.db
|
||||
.prepare(
|
||||
`UPDATE captures
|
||||
SET status = 'queued', title = NULL, error_public = NULL, error_detail = NULL,
|
||||
data_json = NULL, metadata_json = NULL, captured_at = NULL, updated_at = ?
|
||||
WHERE id = ?`
|
||||
)
|
||||
.run(new Date().toISOString(), id);
|
||||
}
|
||||
|
||||
completeCapture(id: string, input: { slug: string; title: string; dataJson: string; metadataJson: string }): void {
|
||||
const now = new Date().toISOString();
|
||||
this.db
|
||||
|
||||
+1
-1
@@ -130,7 +130,7 @@ app.post(route("/api/captures/:id/retry"), requireAdmin, (req, res) => {
|
||||
res.status(409).json({ error: "Successful captures are immutable. Create a new capture instead." });
|
||||
return;
|
||||
}
|
||||
store.updateStatus(row.id, "queued");
|
||||
store.retryCapture(row.id);
|
||||
captureQueue.enqueue(row.id);
|
||||
res.json({ capture: publicCapture(store.getCapture(row.id)) });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user