Files
last30days-skill/vendor/package/dist/commands/news.js
T
Matt Van Horn 32992834ee Merge PR #48: feat: add Xiaohongshu source + Reddit public fallback
- Xiaohongshu search via local MCP service (opt-in, zero impact if service not running)
- Reddit public JSON fallback (works with zero API keys)
- Reddit priority: ScrapeCreators -> OpenAI -> public fallback
- Updated env.py: Reddit always available via public fallback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 16:11:35 -08:00

131 lines
5.2 KiB
JavaScript

import { TwitterClient } from '../lib/twitter-client.js';
function formatPostCount(count) {
if (count >= 1_000_000) {
return `${(count / 1_000_000).toFixed(1)}M`;
}
if (count >= 1_000) {
return `${(count / 1_000).toFixed(1)}K`;
}
return String(count);
}
function printNewsItems(items, ctx, opts = {}) {
if (opts.json) {
console.log(JSON.stringify(items, null, 2));
return;
}
if (items.length === 0) {
console.log(opts.emptyMessage ?? 'No news items found.');
return;
}
for (const item of items) {
const categoryLabel = item.category ? `[${item.category}]` : '';
console.log(`\n${ctx.colors.accent(categoryLabel)} ${ctx.colors.command(item.headline)}`);
if (item.description) {
console.log(` ${ctx.colors.muted(item.description)}`);
}
const meta = [];
if (item.timeAgo) {
meta.push(item.timeAgo);
}
if (item.postCount) {
meta.push(`${formatPostCount(item.postCount)} posts`);
}
if (meta.length > 0) {
console.log(` ${ctx.colors.muted(meta.join(' | '))}`);
}
if (item.url) {
console.log(` ${ctx.l('url')}${item.url}`);
}
// Print related tweets if available
if (item.tweets && item.tweets.length > 0) {
console.log(` ${ctx.colors.section('Related tweets:')}`);
const tweetLimit = opts.tweetLimit ?? item.tweets.length;
for (const tweet of item.tweets.slice(0, tweetLimit)) {
console.log(` @${tweet.author.username}: ${tweet.text.slice(0, 100)}${tweet.text.length > 100 ? '...' : ''}`);
}
}
console.log(ctx.colors.muted('─'.repeat(50)));
}
}
export function registerNewsCommand(program, ctx) {
program
.command('news')
.alias('trending')
.description('Fetch AI-curated news and trending topics from Explore tabs')
.option('-n, --count <number>', 'Number of items to fetch', '10')
.option('--ai-only', 'Show only AI-curated news items')
.option('--with-tweets', 'Also fetch related tweets for each news item')
.option('--tweets-per-item <number>', 'Number of tweets to fetch per news item (default: 5)', '5')
.option('--for-you', 'Fetch only from For You tab')
.option('--news-only', 'Fetch only from News tab')
.option('--sports', 'Fetch only from Sports tab')
.option('--entertainment', 'Fetch only from Entertainment tab')
.option('--trending-only', 'Fetch only from Trending tab')
.option('--json', 'Output as JSON')
.option('--json-full', 'Output as JSON with full raw API response in _raw field')
.action(async (cmdOpts) => {
const opts = program.opts();
const timeoutMs = ctx.resolveTimeoutFromOptions(opts);
const quoteDepth = ctx.resolveQuoteDepthFromOptions(opts);
const count = Number.parseInt(cmdOpts.count || '10', 10);
const tweetsPerItem = Number.parseInt(cmdOpts.tweetsPerItem || '5', 10);
const { cookies, warnings } = await ctx.resolveCredentialsFromOptions(opts);
for (const warning of warnings) {
console.error(`${ctx.p('warn')}${warning}`);
}
if (Number.isNaN(count) || count < 1) {
console.error(`${ctx.p('err')}--count must be a positive number`);
process.exit(1);
}
if (Number.isNaN(tweetsPerItem) || tweetsPerItem < 1) {
console.error(`${ctx.p('err')}--tweets-per-item must be a positive number`);
process.exit(1);
}
if (!cookies.authToken || !cookies.ct0) {
console.error(`${ctx.p('err')}Missing required credentials`);
process.exit(1);
}
// Determine which tabs to fetch from
const tabs = [];
if (cmdOpts.forYou) {
tabs.push('forYou');
}
if (cmdOpts.newsOnly) {
tabs.push('news');
}
if (cmdOpts.sports) {
tabs.push('sports');
}
if (cmdOpts.entertainment) {
tabs.push('entertainment');
}
if (cmdOpts.trendingOnly) {
tabs.push('trending');
}
// If no specific tabs selected, use defaults (all tabs except trending)
const tabsToFetch = tabs.length > 0 ? tabs : undefined;
const client = new TwitterClient({ cookies, timeoutMs, quoteDepth });
const includeRaw = cmdOpts.jsonFull ?? false;
const withTweets = cmdOpts.withTweets ?? false;
const aiOnly = cmdOpts.aiOnly ?? false;
const result = await client.getNews(count, {
includeRaw,
withTweets,
tweetsPerItem,
aiOnly,
tabs: tabsToFetch,
});
if (result.success) {
printNewsItems(result.items, ctx, {
json: cmdOpts.json || cmdOpts.jsonFull,
emptyMessage: 'No news items found.',
tweetLimit: withTweets ? tweetsPerItem : undefined,
});
}
else {
console.error(`${ctx.p('err')}Failed to fetch news: ${result.error}`);
process.exit(1);
}
});
}
//# sourceMappingURL=news.js.map