feat(clients/weather): yandex weather integration
This commit is contained in:
parent
1499e46a0f
commit
9a83871571
1 changed files with 175 additions and 0 deletions
175
src/lib/clients/weather.ts
Normal file
175
src/lib/clients/weather.ts
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
import crypto from "node:crypto";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User agent header.
|
||||||
|
*/
|
||||||
|
const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token secret. Used to hash the token.
|
||||||
|
*/
|
||||||
|
const WEATHER_TOKEN_SECRET = "gravisfrontem";
|
||||||
|
|
||||||
|
interface YandexWeatherData {
|
||||||
|
/**
|
||||||
|
* Message from the server.
|
||||||
|
*/
|
||||||
|
message?: string | null;
|
||||||
|
|
||||||
|
info: {
|
||||||
|
/**
|
||||||
|
* URL of the location page on Yandex Weather
|
||||||
|
**/
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
fact: {
|
||||||
|
/**
|
||||||
|
* Current air temperature in °C
|
||||||
|
**/
|
||||||
|
temp: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Water temperature in °C (may be missing)
|
||||||
|
**/
|
||||||
|
temp_water?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perceived (“feels like”) temperature in °C
|
||||||
|
**/
|
||||||
|
feels_like: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather condition code (e.g., "clear", "rain")
|
||||||
|
**/
|
||||||
|
condition: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wind speed in m/s
|
||||||
|
**/
|
||||||
|
wind_speed: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wind gust speed in m/s (may be missing)
|
||||||
|
**/
|
||||||
|
wind_gust?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wind direction code (e.g., "nw", "s")
|
||||||
|
**/
|
||||||
|
wind_dir: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atmospheric pressure in mmHg
|
||||||
|
**/
|
||||||
|
pressure_mm: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Air humidity percentage
|
||||||
|
**/
|
||||||
|
humidity: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FetchWeatherResult {
|
||||||
|
/**
|
||||||
|
* Link to the weather in location.
|
||||||
|
**/
|
||||||
|
url: string;
|
||||||
|
|
||||||
|
temperature: {
|
||||||
|
/**
|
||||||
|
* Current temperature in °C.
|
||||||
|
**/
|
||||||
|
current: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Water temperature in °C.
|
||||||
|
**/
|
||||||
|
water?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perceived ("feels like") temperature in °C.
|
||||||
|
**/
|
||||||
|
feelsLike: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather condition.
|
||||||
|
**/
|
||||||
|
condition: string;
|
||||||
|
|
||||||
|
wind: {
|
||||||
|
/**
|
||||||
|
* Wind speed in m/s.
|
||||||
|
**/
|
||||||
|
speed: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wind gust speed in m/s.
|
||||||
|
**/
|
||||||
|
gust?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wind direction code.
|
||||||
|
**/
|
||||||
|
direction: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atmospheric pressure in mmHg.
|
||||||
|
**/
|
||||||
|
pressure: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Air humidity percentage.
|
||||||
|
**/
|
||||||
|
humidity: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWeatherHeaders() {
|
||||||
|
const timestamp = Date.now();
|
||||||
|
|
||||||
|
return {
|
||||||
|
// NOTE: no one would pay ~$7k to have a weather on their personal website
|
||||||
|
'x-yandex-weather-timestamp': timestamp.toString(),
|
||||||
|
'x-yandex-weather-token':
|
||||||
|
crypto.createHash('md5').update(`${WEATHER_TOKEN_SECRET}${timestamp}`).digest('hex'),
|
||||||
|
'x-yandex-weather-client': 'YandexWeatherFront2',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function fetchWeather(lat: number, lon: number): Promise<FetchWeatherResult> {
|
||||||
|
const response = await fetch(
|
||||||
|
`https://api.weather.yandex.ru/v2/informers?lat=${lat}&lon=${lon}&lang=en_US`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'user-agent': USER_AGENT,
|
||||||
|
...getWeatherHeaders(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const result: YandexWeatherData = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Yandex Weather API error: ${result.message}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: result.info.url,
|
||||||
|
temperature: {
|
||||||
|
current: result.fact.temp,
|
||||||
|
water: result.fact.temp_water,
|
||||||
|
feelsLike: result.fact.feels_like,
|
||||||
|
},
|
||||||
|
condition: result.fact.condition,
|
||||||
|
wind: {
|
||||||
|
speed: result.fact.wind_speed,
|
||||||
|
gust: result.fact.wind_gust,
|
||||||
|
direction: result.fact.wind_dir,
|
||||||
|
},
|
||||||
|
pressure: result.fact.pressure_mm,
|
||||||
|
humidity: result.fact.humidity,
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue