Greasy Fork

Greasy Fork is available in English.

ChatGPT Exporter

轻松导出 ChatGPT 聊天记录,以便进一步分析或分享。

这些是此脚本变更过代码的版本。 显示所有版本。

  • v2.31.0 2026-05-03

    chore: ci build

  • v2.30.0 2026-05-03

    feat: batched export, smart rate-limit backoff, filter chips, and dialog UX improvements (#348)

    • feat(export): batch export in 100-conv waves and date-range filter

    Closes #347 (feature request on upstream pionxzh/chatgpt-exporter).

    Changes:

    • src/constants.ts: add EXPORT_OPERATION_BATCH = 100 (fixed, non-tunable)
    • src/utils/download.ts: add PartInfo type, part-suffix support to buildZipFileName, and new buildJsonBatchFileName for official JSON
    • src/exporter/{html,json,markdown}.ts: accept optional partIndex / totalParts params; each exportAllTo* uses PartInfo to suffix its output filename when multiple batches are produced
    • src/ui/ExportDialog.tsx:
      • Remove hardcoded EXPORT_LIMIT = 100 selection cap; users may now select all loaded conversations (up to exportAllLimit from settings)
      • Add DateFilter component with from/to date inputs that filter the conversation list by create_time (inclusive, end-of-day for dateTo)
      • Wave-based export pipeline: splits selected conversations into chunks of EXPORT_OPERATION_BATCH (100), fetches each chunk via RequestQueue, downloads the completed batch, pauses 400 ms, then continues with the next chunk — keeping memory bounded and API pressure low
      • Progress bar now shows global count across all batches plus "Batch X/N" indicator when multiple batches are active
      • Local-file source also batched in 100-conversation waves with the same 400 ms inter-batch delay
      • Shift-click range selection and Select All no longer impose a hard cap (handled by the batch pipeline instead)
    • src/locales/en.json: add Date Filter Label/Hint, Selected count, Export batch info, Exporting batch strings; update Export All Limit Description to mention 100-conversation waves

    Made-with: Cursor

    • fix(export): correct date filter for ISO string timestamps + add update_time field

    The list endpoint returns create_time/update_time as ISO 8601 strings (e.g. "2025-07-10T14:53:58.103234Z"), not Unix numbers. The previous filter compared a string to a number, always producing NaN comparisons and filtering out every conversation.

    • Add toMs() helper that handles both ISO strings and legacy Unix numbers
    • Fix filter comparisons to use toMs() and compare in milliseconds
    • Update ApiConversationItem type: create_time is number|string, update_time is optional number|string
    • Add filterField state ('create_time' | 'update_time') with a Created/Updated selector in the DateFilter UI so users can filter by either timestamp
    • Add i18n strings for the new selector labels

    Made-with: Cursor

    • feat: redesign date filter layout with presets, auto-load conversations, batch label on export

    • Date filter redesigned to two-row layout: field selector + quick preset buttons (7d / 30d / 90d / This year / Clear) on row 1; From–To date inputs on row 2, eliminating the wrapping issue

    • Auto-load all conversations when dialog opens (default project null instead of undefined) so search box, Last 100 button, and counter are immediately visible without needing to manually pick a project

    • Export button shows batch count when selection > 100, e.g. "Export · 3 files" with a tooltip explaining "3 separate downloads, 100 conversations each"

    • Progress display shows "Batch X/Y · completed/total" for multi-batch exports

    • Remove redundant "Export from API" static text (project selector implies it)

    • Add locale strings: Date Preset 7d/30d/90d/Year, Clear filter, Batch progress

    Made-with: Cursor

    • fix: show batch download count inline below export button

    Made-with: Cursor

    • feat: filter chips, date column, All conversations, starred indicator

    • Filter chips via # trigger: type # in search box to open a popover with available filters; click to add as a removable chip: ★ Starred only | 💾 Saved only (skip temp) | ⏱ Long conversation (active ≥ N days, editable inline) | 🤖 GPT/custom AI | 💬 Regular chats

    • Date column: each conversation row now shows a compact relative date (Today / Yesterday / Xd ago / Mon DD / MMM YYYY) on the right edge

    • Starred indicator: ★ shown inline for starred conversations

    • Project dropdown: new "📂 All conversations" option fetches the main list plus every project's conversations, deduplicated; uses new fetchAllConversationsAll() API function

    • ApiConversationItem: add is_starred, is_temporary_chat, gizmo_id fields from the list endpoint payload

    • CSS: SelectItem flex layout, new SelectChip / SelectChips / SelectFilterPopover / SelectItemMeta classes

    Made-with: Cursor

    • feat: add Load More button to fetch conversations beyond the initial limit

    • Export fetchConversationsPage from api.ts for single-page incremental loading

    • Add onHasMore callback to fetchAllConversations so callers know when the fetch was stopped by the user-configured limit (vs the API having no more data)

    • DialogContent tracks hasMore / totalAvailable / loadingMore state

    • A "Load N more" button appears below the conversation list when more pages exist; shows "N remaining" count once the API total is known

    • Resets cleanly on project change

    Made-with: Cursor

    • feat: comprehensive filter chip system with chat class, origin, status, and mode logic

    • Add conversation_origin, pinned_time, is_archived, is_do_not_remember to ApiConversationItem

    • Replace simple chip flags with typed FilterChipDef union: chat_class (Regular/GPT/Project), origin (dynamic from data), status (starred/temporary/pinned), duration_gte, recency_lte

    • Each chip has an include/exclude mode toggle (click badge to flip)

    • AND/OR logic toggle appears in chip bar when 2+ chips are active

    • Text search supports * and ? wildcards alongside chips

    • Picker grouped by category with dynamic Origin values from loaded conversations

    • Project classification cross-references fetched project IDs against gizmo_id

    • Remove ProjectSelect dropdown: always auto-load all conversations on open

    • Chat class chip: Regular (no gizmo), GPT (store gizmo), Project (matched gizmo)

    Made-with: Cursor

    • refactor(filters): redesign chips as multi-select with two-stage picker

    • Chat class, Origin, Project, Status are now single chips that open a sub-view where users tick multiple values (Regular + GPT, etc.)

    • New Project chip lets users pick specific project(s) by name

    • Date filter moved from top-of-dialog into the chip bar as a full-width date-range chip (Created/Updated selector + date inputs + presets)

    • Picker uses two-stage UX: root list → sub-view with checkboxes + Apply

    • Clicking a chip's value label reopens the picker for in-place editing

    • AND/OR logic toggle only appears for 2+ non-date chips

    • Remove standalone DateFilter component and related state from DialogContent

    Made-with: Cursor

    • fix(filters): guard gizmo_id null check for project chip

    Made-with: Cursor

    • fix: prevent export dialog close mid-run and add resume-from-offset control

    • Block ESC, outside-click, and X button while an export is in progress; module-level exportingRef bridges DialogContent to ExportDialog without lifting state.

    • Stale-fetch prevention: fetchGenRef generation counter discards callbacks from a previous dialog mount after remount, eliminating the console errors seen when reopening the dialog mid-run.

    • Cleanup on unmount: clear all three RequestQueues when DialogContent unmounts so orphaned background work stops immediately.

    • Add "→ 100 from #N" resume control in the toolbar: a number input sets the starting offset and the arrow button selects the next 100 convs from that position, letting users continue a partial export (e.g. set offset to 200 to resume after 2 finished batches).

    Made-with: Cursor

    • perf: use Web Worker for sleep() to bypass background-tab timer throttle

    Chrome throttles setTimeout in non-focused tabs to >=1 s minimum, which causes the export queue (200 ms between requests * 100 convs = 20 s/batch) to stall entirely when the user switches away from the tab.

    Fix: run all timers inside a single persistent Web Worker. Workers are exempt from background-tab throttling, so exports run at full speed regardless of tab focus. A graceful fallback to regular setTimeout is retained for environments where blob-URL Worker creation fails (CSP etc.) so the change is entirely non-breaking.

    Made-with: Cursor

    • fix: proper 429 backoff — wait Retry-After secs, cap retries, skip when exhausted

    Previously the queue retried forever with a max backoff of 1600 ms, causing the 429 storm seen in the console (the same request hammering the API continuously with no meaningful pause).

    Changes:

    • api.ts: export RateLimitError with retryAfterMs read from Retry-After header (defaults to 30 s when header is absent or unparseable)
    • queue.ts: on RateLimitError, sleep max(retryAfterMs, 30 s) before retrying; give up after 8 consecutive 429s and skip the request; on regular errors give up after 5 retries and skip; add rate_limited status so the UI can show what is happening
    • ExportDialog.tsx: show "Rate limited — waiting Xs…" with an amber progress bar when status is rate_limited; clear progress bar to blue once the pause ends

    Made-with: Cursor

    • fix: global queue pause on 429 instead of per-item backoff

    The previous design retried each item individually with a 30s wait, so if all 100 conversations in a batch were rate-limited the theoretical worst case was 100 x 8 x 30s = 24,000s.

    New design:

    • On the first 429 the entire queue is frozen (pauseUntil timestamp). Every subsequent process() call checks pauseUntil and waits out the remaining time before making any new request — one shared wait for all queued items, not N separate waits.
    • The pause length is max(Retry-After header, 60s * pauseCount) so it backs off exponentially if the first pause was not long enough: pause 1 = 60s, pause 2 = 120s, pause 3 = 180s, etc.
    • After MAX_GLOBAL_PAUSES (5) the queue aborts rather than looping forever (total max wait ~15 min before giving up).
    • The failed item is always returned to the front of the queue to be retried after the pause clears rather than being counted as a skip.
    • Removed per-item rateRetries counter (no longer needed).

    Made-with: Cursor

    • feat: add Test API button and rate-limit header logging

    • probeApi(): make one minimal request (1 conversation) to check whether the API is currently accepting traffic. Returns ok/rate_limited status plus any X-RateLimit-* or Retry-After headers in the response.

    • fetchApi(): logs all known rate-limit response headers to console on every call (success or failure) so we can discover which headers ChatGPT actually sends.

    • ExportDialog: "Test API" button next to the upload icon. Shows "Testing... / API ready / Rate limited · wait Xs / Error" inline. Hovering the button after a successful test shows any rate-limit headers as a tooltip (useful for debugging).

    Made-with: Cursor

    • fix: add Cancel button during export; replace useless dimmed X

    Previously the X button was grayed out and non-functional while an export was running, with no way to stop it short of refreshing the page.

    Changes:

    • Cancel button (red, top-right) appears while processing. Clicking it sets cancelledRef, calls queue.stop() on all three queues, and lets the in-flight request finish before cleanly stopping the loop.
    • The on('done') handler checks cancelledRef: if set, it skips the download and the next-batch kickoff and just resets processing state.
    • cancelledRef is cleared at the start of each new export so back-to-back exports work correctly.
    • The dimmed X is kept (slightly more transparent) as a visual reminder that the dialog cannot be closed mid-export; its tooltip now says "Click Cancel to stop the export".

    Made-with: Cursor

    • feat: sortable column headers, both date columns, unambiguous date format

    Problems fixed:

    • Date column only showed create_time with no label — confusing when the user had filtered or sorted by update_time.
    • Dates < 1 year omitted the year ("Jun 17" vs "Jun 17, 2025"), making it impossible to tell which year a conversation belonged to.
    • No way to sort the list — order was whatever the API returned.

    Changes:

    • formatConvDate: always includes the year (removes the < 365-day branch). Still shows "Today" / "Yesterday" for very recent items.
    • ConversationSelect: two date columns (Created, Updated). The active-sort column is visually highlighted (bold, blue) so the user always knows what order the list is in.
    • Sortable header row (SelectListHeader): click Title / Created / Updated to sort; click again to toggle asc/desc. Arrow indicator (↑/↓/↕) shows current state. Defaults to Created ↓ (newest first) matching the original API order.
    • Sorting applied in the filtered memo after chip/text filtering.

    Made-with: Cursor


    Co-authored-by: Pionxzh [email protected]

  • v2.30.0 2026-03-27 Imported from URL
  • v2.29.1 2025-07-25

    chore: ci build

  • v2.29.0 2025-07-03

    chore: ci build

  • v2.28.1 2025-06-08

    chore: ci build

  • v2.28.0 2025-05-11

    chore: ci build

  • v2.27.2 2025-05-04

    chore: ci build

  • v2.27.1 2025-02-02

    chore: ci build

  • v2.27.0 2024-12-22

    chore: ci build

  • v2.26.0 2024-11-29

    chore: ci build

  • v2.25.0 2024-09-12

    chore: ci build

  • v2.24.6 2024-09-08

    chore: ci build

  • v2.24.5 2024-09-07

    chore: ci build

  • v2.24.4 2024-06-29

    chore: ci build

  • v2.24.3 2024-06-17

    chore: ci build

  • v2.24.1 2024-05-21

    chore: ci build

  • v2.24.0 2024-05-21

    fix: add @match for new ChatGPT domain with query parameter

    Added @match for https://chatgpt.com/?oai-dm=1 to the Tampermonkey script manifest. This is to handle the new domain for ChatGPT, as the old domain https://chat.openai.com/ now redirects to the new address with the '?oai-dm=1' query parameter. The manifest already includes all variations of the new address except this one.

  • v2.24.0 2024-05-18

    chore: ci build

  • v2.23.0 2024-05-04

    chore: ci build

  • v2.22.1 2024-04-23

    chore: ci build

  • v2.22.0 2024-04-04

    chore: ci build

  • v2.21.2 2024-03-30

    chore: ci build

  • v2.21.1 2024-03-13

    chore: ci build

  • v2.21.0 2024-02-24

    chore: ci build

  • v2.20.0 2024-02-13 Imported from URL
  • v2.19.0 2024-01-07 Imported from URL
  • v2.18.1 2023-12-24 Imported from URL
  • v2.18.0 2023-11-22 Imported from URL
  • v2.17.2 2023-11-14 Imported from URL
  • v2.17.1 2023-11-12 Imported from URL
  • v2.17.0 2023-11-08 Imported from URL
  • v2.16.1 2023-11-07 Imported from URL
  • v2.16.0 2023-11-05 Imported from URL
  • v2.15.0 2023-10-16 Imported from URL
  • v2.14.5 2023-09-25 Imported from URL
  • v2.14.4 2023-09-19 Imported from URL
  • v2.14.3 2023-08-29 Imported from URL
  • v2.14.2 2023-08-21 Imported from URL
  • v2.14.1 2023-08-20 Imported from URL
  • v2.14.1 2023-08-20
  • v2.14.0 2023-08-20 Imported from URL
  • v2.13.0 2023-07-09 Imported from URL
  • v2.12.1 2023-06-15 Imported from URL
  • v2.12.0 2023-06-08 Imported from URL
  • v2.11.0 2023-06-07 Imported from URL
  • v2.11.0 2023-05-31 Imported from URL
  • v2.10.0 2023-05-08 Imported from URL
  • v2.9.2 2023-05-01 Imported from URL
  • v2.9.1 2023-04-24 Imported from URL

显示所有脚本版本