tap
Back

reddit/thread

redditRead-only

获取 Reddit 帖子的完整讨论树

www.reddit.com
Last 7 days
0
Last 30 days
0
All time
0
reddit/thread.js
/* @meta
{
  "name": "reddit/thread",
  "description": "获取 Reddit 帖子的完整讨论树",
  "domain": "www.reddit.com",
  "args": {
    "url": {"required": true, "description": "Reddit post URL"},
    "depth": {"required": false, "description": "Comment tree depth (default: 10, max: 10)"},
    "count": {"required": false, "description": "Comment fetch limit (default: 500, max: 500)"}
  },
  "params": {
    "url": {"type": "string", "required": true, "description": "Reddit post URL"},
    "depth": {"type": "number", "required": false, "description": "Comment tree depth (default: 10, max: 10)"},
    "count": {"type": "number", "required": false, "description": "Comment fetch limit (default: 500, max: 500)"}
  },
  "auth": "optional",
  "profile": "required",
  "side_effect": "read_only",
  "retry_safety": "safe_with_backoff",
  "max_concurrency": 1,
  "serialization_key": "site:reddit:{profile}",
  "output_modes": ["legacy", "envelope_v1"],
  "timeout_class": "standard",
  "envelope_versions": ["pinix.site-result-envelope.v1"],
  "capabilities": ["network"],
  "readOnly": true,
  "example": "bb-browser site reddit/thread https://www.reddit.com/r/LocalLLaMA/comments/1rrisqn/..."
}
*/

async function(args) {
  if (!args.url) return {error: 'Missing argument: url', hint: 'Provide a Reddit post URL'};
  const parsedLimit = parseInt(args.count);
  const parsedDepth = parseInt(args.depth);
  const limit = Math.min(Math.max(Number.isFinite(parsedLimit) ? parsedLimit : 500, 1), 500);
  const depthLimit = Math.min(Math.max(Number.isFinite(parsedDepth) ? parsedDepth : 10, 0), 10);
  let path = args.url.replace(/https?:\/\/[^/]*/, '').replace(/\?.*/, '').replace(/\/*$/, '/');
  // Normalize to /r/sub/comments/POST_ID/ — strip slug and any comment suffixes
  // Handles: .../comments/ID/slug/, .../comments/ID/comment/CID/, .../comments/ID/slug/CID/
  const m = path.match(/(\/r\/[^/]+\/comments\/[^/]+\/)/);
  if (m) path = m[1];
  const canonicalUrl = 'https://www.reddit.com' + path;
  const apiUrl = path + '.json?limit=' + limit + '&depth=' + depthLimit + '&raw_json=1';
  const resp = await fetch(apiUrl, {credentials: 'include'});
  if (!resp.ok) return {error: 'HTTP ' + resp.status};
  const d = await resp.json();
  if (!d[0]?.data?.children?.[0]?.data) return {error: 'Unexpected response', hint: 'Post may be deleted or URL is incorrect'};
  const post = d[0].data.children[0].data;
  const stats = {more: 0, depth_truncated: 0, limit_truncated: 0};

  function flatten(children, depth) {
    let result = [];
    for (const child of children) {
      if (child.kind === 'more') {
        stats.more += Array.isArray(child.data?.children) ? child.data.children.length : 1;
        continue;
      }
      if (child.kind !== 't1') continue;
      const c = child.data;
      result.push({id: c.name, parent_id: c.parent_id, author: c.author, score: c.score, body: c.body, depth});
      if (c.replies?.data?.children) {
        if (depth >= depthLimit) stats.depth_truncated += c.replies.data.children.length;
        else
        result = result.concat(flatten(c.replies.data.children, depth + 1));
      }
    }
    return result;
  }

  let comments = flatten(d[1]?.data?.children || [], 0);
  if (comments.length > limit) {
    stats.limit_truncated = comments.length - limit;
    comments = comments.slice(0, limit);
  }
  const data = {
    post: {id: post.name, title: post.title, author: post.author, subreddit: post.subreddit_name_prefixed,
      score: post.score, num_comments: post.num_comments, selftext: post.selftext, url: post.url, created_utc: post.created_utc},
    comments_total: comments.length,
    comments
  };
  const partial = stats.more > 0 || stats.depth_truncated > 0 || stats.limit_truncated > 0;

  return {
    __pinix_site_result: {
      version: 'pinix.site-adapter-result.v1',
      metadata: {
        effective_args: {url: canonicalUrl, depth: depthLimit, count: limit},
        completeness: partial ? 'partial' : 'complete',
        reason: partial ? 'comments_omitted' : 'complete',
        source: {url: 'https://www.reddit.com' + apiUrl},
        pagination: {
          limit,
          depth: depthLimit,
          comments_returned: comments.length,
          more_children_omitted: stats.more,
          depth_truncated: stats.depth_truncated,
          limit_truncated: stats.limit_truncated
        },
        auth: {authenticated_as: 'unknown'},
        warnings: partial ? [{code: 'PARTIAL_COMMENTS', message: 'Some comments were omitted because Reddit returned more placeholders or adapter depth/limit was reached.'}] : undefined
      }
    },
    data
  };
}
Updated Sep 5, 2026Created Sep 5, 2026SHA-256: 03d789b6d8e1