| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- /**
- * Fetch token IDs from Gamma events API (temperature markets).
- * Used as fallback when MarketSnapshot table is empty.
- */
- import { CITY_ALIASES } from '@poly-weather/core';
- const GAMMA_BASE = 'https://gamma-api.polymarket.com';
- interface GammaEventMarket {
- id: string;
- groupItemTitle: string;
- clobTokenIds: string;
- closed: boolean;
- }
- interface GammaEvent {
- id: string;
- title: string;
- markets: GammaEventMarket[];
- }
- export interface TokenPair {
- yesTokenId: string;
- noTokenId: string;
- }
- // In-memory cache, refreshed every minute
- let cache: Map<string, TokenPair> | null = null;
- let cacheTime = 0;
- const CACHE_TTL = 60_000;
- /**
- * Fetch ALL temperature event markets from Gamma.
- * Returns a map of "city_slug|bucketLabel" -> { yesTokenId, noTokenId }
- */
- export async function fetchAllGammaTokens(): Promise<Map<string, TokenPair>> {
- if (cache && Date.now() - cacheTime < CACHE_TTL) {
- return cache;
- }
- const tokenMap = new Map<string, TokenPair>();
- let offset = 0;
- try {
- while (offset <= 500) {
- const params = new URLSearchParams({
- tag_slug: 'temperature',
- active: 'true',
- closed: 'false',
- limit: '50',
- offset: offset.toString(),
- });
- const res = await fetch(`${GAMMA_BASE}/events?${params}`, {
- headers: { Accept: 'application/json' },
- signal: AbortSignal.timeout(15000),
- });
- if (!res.ok) break;
- const events = (await res.json()) as GammaEvent[];
- if (events.length === 0) break;
- for (const event of events) {
- // Parse city from event title: "Highest temperature in Atlanta on March 18?"
- const titleMatch = event.title.match(/temperature in (.+?) on /i);
- if (!titleMatch) continue;
- const rawCity = titleMatch[1].trim();
- // Use CITY_ALIASES to map Gamma titles (e.g. "NYC") to bot slugs (e.g. "new_york")
- const citySlug = CITY_ALIASES[rawCity] ?? rawCity.toLowerCase().replace(/\s+/g, '_');
- for (const m of event.markets || []) {
- if (m.closed) continue;
- const bucketLabel = m.groupItemTitle;
- if (!bucketLabel) continue;
- try {
- const tokenIds = JSON.parse(m.clobTokenIds || '[]');
- if (tokenIds.length >= 1) {
- tokenMap.set(`${citySlug}|${bucketLabel}`, {
- yesTokenId: tokenIds[0] || '',
- noTokenId: tokenIds[1] || '',
- });
- }
- } catch {
- // skip
- }
- }
- }
- offset += 50;
- }
- } catch (err) {
- console.error('Gamma events fetch failed:', err);
- }
- console.log(`[gamma-tokens] Cache loaded: ${tokenMap.size} buckets`);
- cache = tokenMap;
- cacheTime = Date.now();
- return tokenMap;
- }
- /**
- * Look up token pair for a specific city + bucket.
- * Tries MarketSnapshot DB first, then Gamma API fallback.
- */
- export async function lookupTokenPair(
- city: string,
- bucket: string,
- prismaClient: { marketSnapshot: { findFirst: (...args: any[]) => Promise<any> } },
- ): Promise<TokenPair | null> {
- // Try DB first
- const snapshot = await prismaClient.marketSnapshot.findFirst({
- where: { city, bucketLabel: bucket },
- orderBy: { createdAt: 'desc' },
- select: { yesTokenId: true, noTokenId: true },
- });
- if (snapshot?.yesTokenId) {
- return {
- yesTokenId: snapshot.yesTokenId,
- noTokenId: snapshot.noTokenId || '',
- };
- }
- // Fallback to Gamma
- const gammaTokens = await fetchAllGammaTokens();
- return gammaTokens.get(`${city}|${bucket}`) || null;
- }
|