Benzinga Market Data: Fetches Benzinga news, analyst ratings, earnings, guidance, dividends, splits, IPO, and economic calendars via API, with a news list view.
| 1 | import { defineSource } from '@jaspers-ai/sdk' |
| 2 | import { z } from 'zod' |
| 3 | import { HOST, benzingaGet, isoDay } from './client' |
| 4 | |
| 5 | /** One story as /api/v2/news answers it. */ |
| 6 | interface NewsItem { |
| 7 | id: number |
| 8 | author?: string |
| 9 | created?: string |
| 10 | updated?: string |
| 11 | title?: string |
| 12 | teaser?: string |
| 13 | url?: string |
| 14 | channels?: { name: string }[] |
| 15 | stocks?: { name: string }[] |
| 16 | } |
| 17 | |
| 18 | /** Benzinga writes dates like "Wed, 17 May 2023 14:20:15 -0400"; rows carry ISO. */ |
| 19 | function iso(value: string | undefined): string { |
| 20 | if (!value) return '' |
| 21 | const date = new Date(value) |
| 22 | return Number.isNaN(date.getTime()) ? value : date.toISOString() |
| 23 | } |
| 24 | |
| 25 | function stripTags(html: string): string { |
| 26 | return html.replace(/<[^>]*>/g, '').replace(/ /g, ' ').replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, "'").trim() |
| 27 | } |
| 28 | |
| 29 | export const news = defineSource({ |
| 30 | description: |
| 31 | 'Benzinga news, newest first. Filter by tickers, channels, and date range. Rows: id, created, updated (ISO), title, teaser, author, url, tickers, channels (comma joined).', |
| 32 | hosts: [HOST], |
| 33 | input: z.object({ |
| 34 | tickers: z.string().optional().describe('Comma-separated tickers, like AAPL,MSFT.'), |
| 35 | channels: z.string().optional().describe('Comma-separated channel names, like Earnings,Analyst Ratings.'), |
| 36 | dateFrom: z.string().optional().describe('Start date YYYY-MM-DD. Default 7 days ago.'), |
| 37 | dateTo: z.string().optional().describe('End date YYYY-MM-DD. Default today.'), |
| 38 | pageSize: z.number().int().min(1).max(100).default(25).describe('Items to return, up to 100.'), |
| 39 | displayOutput: z.enum(['headline', 'abstract', 'full']).default('abstract').describe('How much of each story.'), |
| 40 | }), |
| 41 | async run(args, ctx) { |
| 42 | const body = await benzingaGet(ctx, 'news', '/api/v2/news', { |
| 43 | tickers: args.tickers?.replace(/\s+/g, '').toUpperCase(), |
| 44 | channels: args.channels, |
| 45 | dateFrom: args.dateFrom ?? isoDay(-7), |
| 46 | dateTo: args.dateTo ?? isoDay(0), |
| 47 | pageSize: args.pageSize, |
| 48 | displayOutput: args.displayOutput, |
| 49 | sort: 'created:desc', |
| 50 | }) |
| 51 | const items = (Array.isArray(body) ? body : []) as NewsItem[] |
| 52 | return items |
| 53 | .map((item) => ({ |
| 54 | id: item.id, |
| 55 | created: iso(item.created), |
| 56 | updated: iso(item.updated), |
| 57 | title: item.title ?? '', |
| 58 | teaser: stripTags(item.teaser ?? ''), |
| 59 | author: item.author ?? '', |
| 60 | url: item.url ?? '', |
| 61 | tickers: (item.stocks ?? []).map((s) => s.name).join(','), |
| 62 | channels: (item.channels ?? []).map((c) => c.name).join(','), |
| 63 | })) |
| 64 | .sort((a, b) => b.created.localeCompare(a.created)) |
| 65 | }, |
| 66 | }) |