import { defineSource } from '@jaspers-ai/sdk' import { z } from 'zod' import { HOST, benzingaGet, isoDay } from './client' /** One story as /api/v2/news answers it. */ interface NewsItem { id: number author?: string created?: string updated?: string title?: string teaser?: string url?: string channels?: { name: string }[] stocks?: { name: string }[] } /** Benzinga writes dates like "Wed, 17 May 2023 14:20:15 -0400"; rows carry ISO. */ function iso(value: string | undefined): string { if (!value) return '' const date = new Date(value) return Number.isNaN(date.getTime()) ? value : date.toISOString() } function stripTags(html: string): string { return html.replace(/<[^>]*>/g, '').replace(/ /g, ' ').replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, "'").trim() } export const news = defineSource({ description: 'Benzinga news, newest first. Filter by tickers, channels, and date range. Rows: id, created, updated (ISO), title, teaser, author, url, tickers, channels (comma joined).', hosts: [HOST], input: z.object({ tickers: z.string().optional().describe('Comma-separated tickers, like AAPL,MSFT.'), channels: z.string().optional().describe('Comma-separated channel names, like Earnings,Analyst Ratings.'), dateFrom: z.string().optional().describe('Start date YYYY-MM-DD. Default 7 days ago.'), dateTo: z.string().optional().describe('End date YYYY-MM-DD. Default today.'), pageSize: z.number().int().min(1).max(100).default(25).describe('Items to return, up to 100.'), displayOutput: z.enum(['headline', 'abstract', 'full']).default('abstract').describe('How much of each story.'), }), async run(args, ctx) { const body = await benzingaGet(ctx, 'news', '/api/v2/news', { tickers: args.tickers?.replace(/\s+/g, '').toUpperCase(), channels: args.channels, dateFrom: args.dateFrom ?? isoDay(-7), dateTo: args.dateTo ?? isoDay(0), pageSize: args.pageSize, displayOutput: args.displayOutput, sort: 'created:desc', }) const items = (Array.isArray(body) ? body : []) as NewsItem[] return items .map((item) => ({ id: item.id, created: iso(item.created), updated: iso(item.updated), title: item.title ?? '', teaser: stripTags(item.teaser ?? ''), author: item.author ?? '', url: item.url ?? '', tickers: (item.stocks ?? []).map((s) => s.name).join(','), channels: (item.channels ?? []).map((c) => c.name).join(','), })) .sort((a, b) => b.created.localeCompare(a.created)) }, })