Skip to content

Instantly share code, notes, and snippets.

@indexzero
Created October 9, 2025 03:53
Show Gist options
  • Save indexzero/4e9cc864f1140dfa5d85bed2b4cae883 to your computer and use it in GitHub Desktop.
Save indexzero/4e9cc864f1140dfa5d85bed2b4cae883 to your computer and use it in GitHub Desktop.
Extract threaded comments from Github Discussions pages in DevTools

GitHub Discussion Thread Extractor

A Chrome DevTools script to extract structured comment data from GitHub discussion threads.

Usage

  1. Navigate to a GitHub discussion page
  2. Open Chrome DevTools Console (Cmd+Option+J on Mac, Ctrl+Shift+J on Windows)
  3. Paste the extraction script
  4. Run it to get JSON structured data

Script

See: ./gh-discussion-threads.js

Output Schema

interface GitHubDiscussionThread {
  id: string;           // GitHub internal ID (e.g., "DC_kwDOEfmk4M4Azg4f")
  anchor: string;       // URL anchor (e.g., "discussioncomment-13044518")
  author: string;       // GitHub username
  timestamp: string;    // ISO 8601 datetime
  content: string;      // Full comment text
  reactions: string[];  // Emoji reactions with counts (e.g., ["πŸ‘ 6", "πŸŽ‰ 1"])
  replies: Reply[];     // Nested replies
}

interface Reply {
  id: string;           // GitHub internal ID
  anchor: string;       // URL anchor
  author: string;       // GitHub username
  timestamp: string;    // ISO 8601 datetime
  content: string;      // Full reply text
}

Example Output

{
  "id": "DC_kwDOEfmk4M4Azg4f",
  "anchor": "discussioncomment-13044518",
  "author": "jhiesey",
  "timestamp": "2025-09-19T17:01:13Z",
  "content": "Is the replication feed down/blocked?...",
  "reactions": ["πŸ‘ 6", "πŸŽ‰ 1"],
  "replies": [
    {
      "id": "DC_kwDOEfmk4M4A3KH4",
      "anchor": "discussioncomment-12647262",
      "author": "michelleqyun",
      "timestamp": "2025-09-19T17:04:34Z",
      "content": "I'm seeing the same issue"
    }
  ]
}

Direct Link Construction

const discussionUrl = 'https://github.com/orgs/community/discussions/152515';
const directLink = `${discussionUrl}#${comment.anchor}`;
function extractDiscussionThreads() {
const comments = [];
const nestedIds = new Set();
// First pass: collect all nested comment IDs
document.querySelectorAll('.discussion-nested-comment-timeline-item [data-gid]').forEach(el => {
nestedIds.add(el.dataset.gid);
});
// Second pass: process only parent-level comments
document.querySelectorAll('.timeline-comment').forEach(commentEl => {
const container = commentEl.closest('[data-gid]');
if (!container) return;
const commentId = container.dataset.gid;
// Skip if this is a nested comment
if (nestedIds.has(commentId)) return;
// Find the anchor ID
const permalinkEl = commentEl.querySelector('[id^="discussioncomment-"][id$="-permalink"]');
const anchorId = permalinkEl?.id.replace('-permalink', '') ||
commentEl.querySelector('[href^="#discussioncomment-"]')?.href.split('#')[1] ||
container.id;
const comment = {
id: commentId,
anchor: anchorId, // e.g., "discussioncomment-13044518"
author: commentEl.querySelector('[data-hovercard-type="user"]')?.textContent?.trim(),
timestamp: commentEl.querySelector('relative-time')?.getAttribute('datetime'),
content: commentEl.querySelector('.comment-body')?.textContent?.trim(),
reactions: [],
replies: []
};
// Get reactions
commentEl.querySelectorAll('.social-reaction-summary-item').forEach(reaction => {
const emoji = reaction.querySelector('g-emoji')?.textContent || '';
const count = reaction.querySelector('span[class*="count"]')?.textContent ||
reaction.textContent.match(/\d+/)?.[0] || '';
if (emoji) {
comment.reactions.push(`${emoji} ${count}`.trim());
}
});
// Find the reply container for this comment
const parentContainer = commentEl.closest('.js-comment-container');
if (parentContainer) {
const replies = parentContainer.querySelectorAll('.discussion-nested-comment-timeline-item');
replies.forEach(replyEl => {
// Get anchor for reply
const replyPermalink = replyEl.querySelector('[id^="discussioncomment-"][id$="-permalink"]');
const replyAnchor = replyPermalink?.id.replace('-permalink', '') ||
replyEl.querySelector('[href^="#discussioncomment-"]')?.href.split('#')[1] ||
replyEl.id;
const replyData = {
id: replyEl.querySelector('[data-gid]')?.dataset.gid,
anchor: replyAnchor, // e.g., "discussioncomment-12647262"
author: replyEl.querySelector('[data-hovercard-type="user"]')?.textContent?.trim(),
timestamp: replyEl.querySelector('relative-time')?.getAttribute('datetime'),
content: replyEl.querySelector('.comment-body')?.textContent?.trim()
};
if (replyData.id) {
comment.replies.push(replyData);
}
});
}
comments.push(comment);
});
return comments;
}
const threads = extractDiscussionThreads();
console.log(JSON.stringify(threads, null, 2));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment