Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ZIP Stats

A two-part toolkit for analyzing your LinkedIn ZIP head-to-head against a friend.

  1. A Chrome extension that pulls a conversation's full message history from LinkedIn's GraphQL endpoint and saves it as JSON.
  2. A Python analyzer that parses every ZIP result (English and Polish message formats) and produces a designed PDF report with KPIs, narrative analysis, and charts for wins, speed, and backtracks.

Cover


Why this exists

If you and a friend share ZIP results in a LinkedIn chat every day, the chat thread becomes a years-long dataset of solve times, flawless rates, backtrack counts, and streaks. This tool surfaces the lifetime head-to-head — who's faster, who's cleaner, who's on a hot streak — as a single shareable PDF.

Features

  • Direct GraphQL extraction — pulls the full conversation via LinkedIn's voyagerMessagingGraphQL endpoint with proper cursor pagination. No DOM scraping, no manual scrolling.
  • Bilingual parsing — handles both English (flawless, with N backtracks, N-day win streak) and Polish (bezbłędnie, Z N cofnięciami, N-dniową serię zwycięstw) LinkedIn message templates.
  • Designed PDF report (5 pages): cover with verdict and KPIs, head-to-head wins race, speed analysis with rolling averages, quality/backtracks breakdown, highlights (closest games, biggest blowouts, recent form).
  • Auto-generated narrative — paragraph-form analysis that adapts to your data ("X is the faster solver by 8s on average", "Y backtracks half as often", "Form check: both have gotten faster…").
  • Resilient extraction — auto-saves partial progress to chrome.storage.local so a mid-flight error doesn't lose pages; popup offers a one-click Restore of the last partial fetch.
  • Auto-captured queryId — a background service worker watches for LinkedIn's GraphQL request and stashes the current queryId (which LinkedIn rotates on every web deploy), so you never have to copy/paste it from devtools.

Screenshots

Cover (KPIs + narrative) Head-to-head race Speed (with rolling avg)
Cover Head-to-head Speed
Quality (backtracks) Highlights
Quality Highlights

The PDF version with all five pages: docs/screenshots/report.pdf.

How it works

┌──────────────────────────────────────────────────────────────────┐
│   Chrome (logged into LinkedIn)                                  │
│                                                                  │
│   extension/         background.js  ◄── webRequest hook         │
│   ┌───────────────┐  popup.js       ─── scripting.executeScript │
│   │ ZIP Extractor │       │                                      │
│   │  popup        │       ▼                                      │
│   └───────────────┘  pageFetchAll()  ── GraphQL paginated fetch │
│                          │                                       │
│                          ▼                                       │
│                    zip-messages.json  ◄─ saved via downloads API │
└──────────────────────────────────────────────────────────────────┘
                          │
                          ▼
┌──────────────────────────────────────────────────────────────────┐
│   Terminal                                                       │
│                                                                  │
│   analyze.py ── parse(text)   ── matches EN + PL regexes        │
│       │                                                          │
│       ├─ compute_insights()  ── stats per player                │
│       ├─ narrate(insights)   ── paragraph generator             │
│       ├─ page_cover/...      ── matplotlib pages                │
│       └─ PdfPages            ─► out/report.pdf                  │
└──────────────────────────────────────────────────────────────────┘

Setup

Prerequisites

  • Chrome (or any Chromium-based browser that loads MV3 unpacked extensions)
  • Python 3.10+

Install the extension

  1. Clone the repo.
  2. Open chrome://extensions → enable Developer mode (top-right toggle) → Load unpacked → select the extension/ directory.
  3. The ZIP Stats Extractor icon appears in your toolbar.

Install the analyzer

git clone https://github.com/YOUR-USERNAME/zip-stats.git
cd zip-stats
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

Usage

1. Extract messages

  1. Open the LinkedIn conversation with your friend in a tab. The URL should look like https://www.linkedin.com/messaging/thread/2-…/.
  2. Scroll up in the conversation once so older messages load. This triggers LinkedIn's cursor-pagination GraphQL request, which the extension's background listener captures (it needs the current queryId, which LinkedIn rotates on each web deploy).
  3. Click the ZIP Stats Extractor icon. The popup shows the auto-detected queryId and a save path (default: zip-messages.json in your Downloads folder).
  4. Click Fetch all messages. The extension paginates through the full history, persisting partial state after each page. When done, the JSON saves to disk.

If something goes wrong, reopen the popup and click Restore previous fetch — the last successful partial state is downloadable.

2. Analyze

.venv/bin/python analyze.py ~/Downloads/zip-messages.json

This prints a terminal summary and writes to out/:

  • report.pdf — the 5-page designed report (the main artifact)
  • 01_cover.png05_highlights.png — each page as a standalone image

Useful flags:

  • --you "Your Name" / --friend "Their Name" — explicitly pick which sender is which (otherwise auto-detected as whichever has more ZIP results)
  • --out path/to/dir — write to a different output directory
  • --debug — print per-message parse trace, useful if you suspect a new message format the regex doesn't cover

Try it with the synthetic sample

.venv/bin/python analyze.py sample.json
open out/report.pdf

This generates the same screenshots you see above — 30 days of fictional head-to-head between Alex Rivers (English messages) and Ola Nowak (Polish messages).

Caveats

LinkedIn ToS. This tool reads messages via LinkedIn's internal GraphQL endpoint using your own logged-in session cookies. It does not log in for you, scrape other users' data, or use credentials. That said, automated access to LinkedIn data — even of your own conversations — sits in a gray area of LinkedIn's User Agreement. Use at your own risk; this is provided for personal-data-export use cases only. Don't run it at high volume.

Rotating queryId. LinkedIn rebuilds the messaging GraphQL hash (messengerMessages.<hash>) on every web deploy, sometimes multiple times a week. The extension auto-captures the current hash by listening for it in network traffic. If extraction fails with HTTP 400, scroll up in the conversation once to fire a fresh request, then retry — the listener will have re-captured the new hash.

Message format changes. LinkedIn occasionally re-localizes their ZIP result strings. The current regexes cover all formats observed in English and Polish. If your dataset includes other languages, run --debug and the parser will log every match — add new patterns to FLAWLESS_RE, NO_BACKTRACK_RE, WITH_BACKTRACK_RE, and STREAK_RE near the top of analyze.py.

Repository layout

zip-stats/
├── extension/
│   ├── manifest.json       # MV3 manifest, host_permissions for linkedin.com
│   ├── background.js       # webRequest listener that captures the GraphQL queryId
│   ├── popup.html          # Popup UI (path input, fetch / restore buttons)
│   └── popup.js            # GraphQL pagination, partial-state persistence
├── analyze.py              # Single-file analyzer (parse → insights → charts → PDF)
├── scripts/
│   └── gen_sample.py       # Generates the synthetic sample.json
├── sample.json             # Anonymized 30-day demo conversation
├── requirements.txt        # matplotlib, numpy
├── docs/screenshots/       # README images regenerated from sample.json
├── LICENSE                 # MIT
└── README.md

Contributing

Issues and PRs welcome. Some directions if you're looking for somewhere to start:

  • Additional language support (Spanish, German, French — same message structure, just translated strings)
  • Other LinkedIn games — Queens, Tango, Pinpoint — share the same messaging plumbing
  • HTML report variant (single self-contained HTML alongside the PDF)
  • Time-of-day analysis (use deliveredAt timestamps to bucket by hour)

License

MIT — see LICENSE.

About

Chrome extension + Python report generator that extracts your LinkedIn ZIP head-to-head history with a friend and turns it into a designed PDF - wins race, speed trends, backtracks breakdown, narrative analysis. Bilingual EN/PL parsing.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages