From 64afe1d441945cf009cc4a026a2ecdeb582fa7fc Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Tue, 13 Jun 2023 22:31:48 -0700 Subject: [PATCH] feat: add weather api service --- src/llms/llm-utils.ts | 1 + src/services/index.ts | 1 + src/services/weather.ts | 125 ++++++++++++++++++++++++++++++++++ test/services/weather.test.ts | 34 +++++++++ 4 files changed, 161 insertions(+) create mode 100644 src/services/weather.ts create mode 100644 test/services/weather.test.ts diff --git a/src/llms/llm-utils.ts b/src/llms/llm-utils.ts index bde0025..2a5ef6e 100644 --- a/src/llms/llm-utils.ts +++ b/src/llms/llm-utils.ts @@ -8,6 +8,7 @@ import { isValidTaskIdentifier } from '@/utils' // TODO: this needs work + testing // TODO: move to isolated module +// TODO compare with https://gist.github.com/rileytomasek/4811b5fdd9c82c4730c191317ea76411 export async function getNumTokensForChatMessages({ messages, model, diff --git a/src/services/index.ts b/src/services/index.ts index 201ca09..9d0610a 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -5,3 +5,4 @@ export * from './novu' export * from './serpapi' export * from './slack' export * from './twilio-conversation' +export * from './weather' diff --git a/src/services/weather.ts b/src/services/weather.ts new file mode 100644 index 0000000..9157eb7 --- /dev/null +++ b/src/services/weather.ts @@ -0,0 +1,125 @@ +import defaultKy from 'ky' + +export const WEATHER_API_BASE_URL = 'http://api.weatherapi.com/v1' + +interface CurrentWeatherResponse { + current: CurrentWeather + location: WeatherLocation +} + +interface CurrentWeather { + cloud: number + condition: WeatherCondition + feelslike_c: number + feelslike_f: number + gust_kph: number + gust_mph: number + humidity: number + is_day: number + last_updated: string + last_updated_epoch: number + precip_in: number + precip_mm: number + pressure_in: number + pressure_mb: number + temp_c: number + temp_f: number + uv: number + vis_km: number + vis_miles: number + wind_degree: number + wind_dir: string + wind_kph: number + wind_mph: number +} + +interface WeatherCondition { + code: number + icon: string + text: string +} + +interface WeatherLocation { + country: string + lat: number + localtime: string + localtime_epoch: number + lon: number + name: string + region: string + tz_id: string +} + +interface WeatherIPInfoResponse { + ip: string + type: string + continent_code: string + continent_name: string + country_code: string + country_name: string + is_eu: string + geoname_id: number + city: string + region: string + lat: number + lon: number + tz_id: string + localtime_epoch: number + localtime: string +} + +export class WeatherClient { + api: typeof defaultKy + + apiKey: string + apiBaseUrl: string + + constructor({ + apiKey = process.env.WEATHER_API_KEY, + apiBaseUrl = WEATHER_API_BASE_URL, + ky = defaultKy + }: { + apiKey?: string + apiBaseUrl?: string + ky?: typeof defaultKy + } = {}) { + if (!apiKey) { + throw new Error(`Error WeatherClient missing required "apiKey"`) + } + + this.apiKey = apiKey + this.apiBaseUrl = apiBaseUrl + + this.api = ky.extend({ prefixUrl: apiBaseUrl }) + } + + async getCurrentWeather(queryOrOptions: string | { q: string }) { + const options = + typeof queryOrOptions === 'string' + ? { q: queryOrOptions } + : queryOrOptions + + return this.api + .get('current.json', { + searchParams: { + key: this.apiKey, + ...options + } + }) + .json() + } + + async ipInfo(ipOrOptions: string | { q: string }) { + const options = + typeof ipOrOptions === 'string' ? { q: ipOrOptions } : ipOrOptions + + return this.api + .get('ip.json', { + searchParams: { + key: this.apiKey, + ...options + } + }) + .json() + } +} diff --git a/test/services/weather.test.ts b/test/services/weather.test.ts new file mode 100644 index 0000000..b80cb4d --- /dev/null +++ b/test/services/weather.test.ts @@ -0,0 +1,34 @@ +import test from 'ava' + +import { WeatherClient } from '@/services' + +import { ky } from '../_utils' + +test('WeatherClient.getCurrentWeather', async (t) => { + if (!process.env.WEATHER_API_KEY) { + return t.pass() + } + + t.timeout(2 * 60 * 1000) + const client = new WeatherClient({ ky }) + + const result = await client.getCurrentWeather('Seattle') + // console.log(result) + t.truthy(result.current) + t.truthy(result.location) +}) + +test('WeatherClient.ipInfo', async (t) => { + if (!process.env.WEATHER_API_KEY) { + return t.pass() + } + + t.timeout(2 * 60 * 1000) + const client = new WeatherClient({ ky }) + + const res = await client.ipInfo('52.119.125.215') + t.truthy(res.ip) + t.truthy(res.city) + t.truthy(res.lat) + t.truthy(res.lon) +})