@sns-parse/core 0.4.0-alpha.3 → 0.4.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -138,6 +138,36 @@ function pick(...vals) {
138
138
  return v;
139
139
  return '';
140
140
  }
141
+ /** BFS 深取子树中第一个非空的指定键值(X GraphQL 用户结构随版本漂移:legacy ↔ core/avatar) */
142
+ function deepPick(node, key) {
143
+ const queue = [node];
144
+ while (queue.length) {
145
+ const cur = queue.shift();
146
+ if (!cur || typeof cur !== 'object')
147
+ continue;
148
+ const v = cur[key];
149
+ if (v !== undefined && v !== null && v !== '')
150
+ return v;
151
+ for (const k of Object.keys(cur))
152
+ queue.push(cur[k]);
153
+ }
154
+ return undefined;
155
+ }
156
+ /** BFS 找子树中最长的 text 字符串(笔记正文位置随版本漂移,取最长以避开截断副本) */
157
+ function deepFindLongestText(node) {
158
+ let best;
159
+ const queue = [node];
160
+ while (queue.length) {
161
+ const cur = queue.shift();
162
+ if (!cur || typeof cur !== 'object')
163
+ continue;
164
+ if (typeof cur.text === 'string' && cur.text && (!best || cur.text.length > best.length))
165
+ best = cur.text;
166
+ for (const k of Object.keys(cur))
167
+ queue.push(cur[k]);
168
+ }
169
+ return best;
170
+ }
141
171
  /** 清理描述中的 t.co 短链(Twitter 自动附加的截断 URL,无实际内容价值) */
142
172
  function cleanDesc(text) {
143
173
  if (!text)
@@ -273,8 +303,10 @@ function mapGraphql(rawResult) {
273
303
  }
274
304
  const legacy = result.legacy || {};
275
305
  const user = result.core?.user_results?.result;
276
- const ulegacy = user?.legacy || {};
277
- const text = String(pick(legacy.full_text, result.note_tweet?.note_results?.note?.text, ''));
306
+ const ures = user || {};
307
+ // 长推(note tweet):legacy.full_text 是截断版,全文在 note_tweet 子树(位置随版本漂移,深取最长 text)
308
+ // 用户字段同理:legacy ↔ core/avatar/profile_bio 漂移,深取键值
309
+ const text = String(pick(deepFindLongestText(result.note_tweet), legacy.full_text, ''));
278
310
  const p = baseParsed();
279
311
  const media = Array.isArray(legacy.entities?.media) ? legacy.entities.media : [];
280
312
  const photos = [];
@@ -318,9 +350,9 @@ function mapGraphql(rawResult) {
318
350
  p.title = cleaned.slice(0, 100);
319
351
  p.desc = cleaned;
320
352
  p.lang = legacy.lang ? String(legacy.lang) : undefined;
321
- p.author = String(pick(ulegacy.name, ulegacy.screen_name, ''));
322
- p.uid = String(pick(ulegacy.screen_name, user?.rest_id, ''));
323
- p.avatar = String(pick(ulegacy.profile_image_url_https, ''));
353
+ p.author = String(pick(deepPick(ures, 'name'), ''));
354
+ p.uid = String(pick(deepPick(ures, 'screen_name'), user?.rest_id, ''));
355
+ p.avatar = String(pick(deepPick(ures, 'profile_image_url_https'), deepPick(ures, 'image_url'), ''));
324
356
  p.like = Number(pick(legacy.favorite_count, 0)) || 0;
325
357
  p.comment = Number(pick(legacy.reply_count, 0)) || 0;
326
358
  p.share = Number(pick(legacy.retweet_count, 0)) || 0;
@@ -331,8 +363,8 @@ function mapGraphql(rawResult) {
331
363
  if (!isNaN(t))
332
364
  p.publishTime = t;
333
365
  }
334
- p.author_followers = Number(pick(ulegacy.followers_count, 0)) || 0;
335
- p.author_signature = String(pick(ulegacy.description, ''));
366
+ p.author_followers = Number(pick(deepPick(ures, 'followers_count'), 0)) || 0;
367
+ p.author_signature = String(pick(deepPick(ures, 'description'), ''));
336
368
  if (p.title && p.desc && p.desc.startsWith(p.title))
337
369
  p.title = '';
338
370
  return p;
@@ -387,7 +419,19 @@ async function parseTwitter(url, http, creds, getGraphql) {
387
419
  });
388
420
  const tw = res.data;
389
421
  if (tw && tw.__typename === 'Tweet' && tw.user) {
390
- return mapSyndication(tw);
422
+ const p = mapSyndication(tw);
423
+ // 长推:syndication 现仅返回 note_tweet 的 id 引用(无 text),tw.text 被
424
+ // display_text_range 截断。有登录态时改走 GraphQL 取全文;失败回退截断结果。
425
+ const noteTruncated = tw.note_tweet && !tw.note_tweet.text;
426
+ if (noteTruncated && creds && creds.authToken && creds.ct0) {
427
+ try {
428
+ return await fetchGraphqlTweet(id, creds, getGraphql || tls_client_1.tlsGet);
429
+ }
430
+ catch {
431
+ return p;
432
+ }
433
+ }
434
+ return p;
391
435
  }
392
436
  // 2) tombstone(需登录):回退到鉴权 GraphQL(TLS 指纹模拟)
393
437
  if (creds && creds.authToken && creds.ct0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sns-parse/core",
3
- "version": "0.4.0-alpha.3+upstream.1.6.7",
3
+ "version": "0.4.0-alpha.5+upstream.1.6.7",
4
4
  "description": "sns-parse 核心:契约、宿主抽象、配置 DSL 与解析引擎(fetcher/parser/翻译/X 原生解析/合并/GIF/TLS 指纹客户端)",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",