/ clis / sinafinance / news.js
news.js
 1  /**
 2   * Sina Finance 7x24 live news feed.
 3   *
 4   * Uses the public CJ API — no key or browser required.
 5   * https://app.cj.sina.com.cn/api/news/pc
 6   */
 7  import { cli, Strategy } from '@jackwener/opencli/registry';
 8  import { CliError } from '@jackwener/opencli/errors';
 9  // User-facing type (0-9) → Sina API tag ID
10  const TYPE_MAP = [
11      0, // 0: 全部
12      10, // 1: A股
13      1, // 2: 宏观
14      3, // 3: 公司
15      4, // 4: 数据
16      5, // 5: 市场
17      102, // 6: 国际
18      6, // 7: 观点
19      6, // 8: 央行
20      8, // 9: 其它
21  ];
22  function stripHtml(html) {
23      return html.replace(/<[^>]+>/g, '').trim();
24  }
25  cli({
26      site: 'sinafinance',
27      name: 'news',
28      description: '新浪财经 7x24 小时实时快讯',
29      domain: 'app.cj.sina.com.cn',
30      strategy: Strategy.PUBLIC,
31      browser: false,
32      args: [
33          { name: 'limit', type: 'int', default: 20, help: 'Max results (max 50)' },
34          { name: 'type', type: 'int', default: 0, help: 'News type: 0=全部 1=A股 2=宏观 3=公司 4=数据 5=市场 6=国际 7=观点 8=央行 9=其它' },
35      ],
36      columns: ['id', 'time', 'content', 'views'],
37      func: async (_page, args) => {
38          const limit = Math.max(1, Math.min(Number(args.limit), 50));
39          const apiTag = TYPE_MAP[args.type] ?? 0;
40          const params = new URLSearchParams({
41              page: '1',
42              size: String(limit),
43              tag: String(apiTag),
44          });
45          const res = await fetch(`https://app.cj.sina.com.cn/api/news/pc?${params}`);
46          if (!res.ok) {
47              throw new CliError('FETCH_ERROR', `Sina Finance API HTTP ${res.status}`, 'Check your network connection');
48          }
49          const json = await res.json();
50          const list = json?.result?.data?.feed?.list ?? [];
51          if (!list.length) {
52              throw new CliError('NOT_FOUND', 'No news found', 'Try a different type or increase limit');
53          }
54          return list.map((item) => ({
55              id: item.id ?? '',
56              time: item.create_time ?? '',
57              content: stripHtml(item.rich_text ?? ''),
58              views: item.view_num ?? 0,
59          }));
60      },
61  });