feat(route)!: qweather api host change (#20042)
* feat(route)!: qweather api host change * chore: typo
This commit is contained in:
parent
b67b4f6afb
commit
a7469fe8a0
|
|
@ -187,6 +187,7 @@ export type Config = {
|
|||
};
|
||||
hefeng: {
|
||||
key?: string;
|
||||
apiHost?: string;
|
||||
};
|
||||
infzm: {
|
||||
cookie?: string;
|
||||
|
|
@ -663,6 +664,7 @@ const calculateValue = () => {
|
|||
},
|
||||
hefeng: {
|
||||
key: envs.HEFENG_KEY,
|
||||
apiHost: envs.HEFENG_API_HOST,
|
||||
},
|
||||
infzm: {
|
||||
cookie: envs.INFZM_COOKIE,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,9 @@
|
|||
import { Route } from '@/types';
|
||||
|
||||
import cache from '@/utils/cache';
|
||||
import got from '@/utils/got';
|
||||
import { art } from '@/utils/render';
|
||||
import path from 'node:path';
|
||||
import { config } from '@/config';
|
||||
import ConfigNotFoundError from '@/errors/types/config-not-found';
|
||||
|
||||
const WEATHER_API = 'https://devapi.qweather.com/v7/weather/3d';
|
||||
const AIR_QUALITY_API = 'https://devapi.qweather.com/v7/air/5d';
|
||||
const CIRY_LOOKUP_API = 'https://geoapi.qweather.com/v2/city/lookup';
|
||||
import { render3DaysDescription, type WeatherForecastItem } from './util';
|
||||
const author = 'QWeather';
|
||||
|
||||
export const route: Route = {
|
||||
|
|
@ -21,7 +15,11 @@ export const route: Route = {
|
|||
requireConfig: [
|
||||
{
|
||||
name: 'HEFENG_KEY',
|
||||
description: '',
|
||||
description: 'QWeather API KEY',
|
||||
},
|
||||
{
|
||||
name: 'HEFENG_API_HOST',
|
||||
description: 'This is required after 2026/01/01: https://blog.qweather.com/announce/public-api-domain-change-to-api-host/',
|
||||
},
|
||||
],
|
||||
requirePuppeteer: false,
|
||||
|
|
@ -37,10 +35,14 @@ export const route: Route = {
|
|||
};
|
||||
|
||||
async function handler(ctx) {
|
||||
if (!config.hefeng.key) {
|
||||
if (!config.hefeng.key || !config.hefeng.apiHost) {
|
||||
throw new ConfigNotFoundError('QWeather RSS is disabled due to the lack of <a href="https://docs.rsshub.app/zh/install/config#%E5%92%8C%E9%A3%8E%E5%A4%A9%E6%B0%94">relevant config</a>');
|
||||
}
|
||||
|
||||
const WEATHER_API = `https://${config.hefeng.apiHost}/v7/weather/3d`;
|
||||
const AIR_QUALITY_API = `https://${config.hefeng.apiHost}/v7/air/5d`;
|
||||
const CIRY_LOOKUP_API = `https://${config.hefeng.apiHost}/geo/v2/city/lookup`;
|
||||
|
||||
const id = await cache.tryGet('qweather:' + ctx.req.param('location') + ':id', async () => {
|
||||
const response = await got(`${CIRY_LOOKUP_API}?location=${ctx.req.param('location')}&key=${config.hefeng.key}`);
|
||||
return response.data.location[0].id;
|
||||
|
|
@ -67,7 +69,7 @@ async function handler(ctx) {
|
|||
const combined = {
|
||||
updateTime: weatherData.updateTime,
|
||||
fxLink: weatherData.fxLink,
|
||||
daily: weatherData.daily.map((weatherItem) => {
|
||||
daily: weatherData.daily.map((weatherItem: WeatherForecastItem) => {
|
||||
const dailyAirQuality = airQualityData.daily.find((airQualityItem) => airQualityItem.fxDate === weatherItem.fxDate);
|
||||
if (dailyAirQuality) {
|
||||
return {
|
||||
|
|
@ -81,11 +83,9 @@ async function handler(ctx) {
|
|||
return weatherItem;
|
||||
}),
|
||||
};
|
||||
const items = combined.daily.map((item) => ({
|
||||
const items = combined.daily.map((item: WeatherForecastItem) => ({
|
||||
title: `${item.fxDate}: ${item.textDay === item.textNight ? item.textDay : item.textDay + '转' + item.textNight} ${item.tempMin}~${item.tempMax}℃`,
|
||||
description: art(path.join(__dirname, 'templates/3days.art'), {
|
||||
item,
|
||||
}),
|
||||
description: render3DaysDescription(item),
|
||||
pubDate: combined.updateTime,
|
||||
guid: '位置:' + ctx.req.param('location') + '--日期:' + item.fxDate,
|
||||
link: combined.fxLink,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
import { Route } from '@/types';
|
||||
|
||||
import cache from '@/utils/cache';
|
||||
import got from '@/utils/got';
|
||||
import { art } from '@/utils/render';
|
||||
import path from 'node:path';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
import { config } from '@/config';
|
||||
import ConfigNotFoundError from '@/errors/types/config-not-found';
|
||||
|
||||
const rootUrl = 'https://devapi.qweather.com/v7/weather/now?';
|
||||
import { renderNowDescription, type NowItem } from './util';
|
||||
|
||||
export const route: Route = {
|
||||
path: '/now/:location',
|
||||
|
|
@ -21,6 +17,10 @@ export const route: Route = {
|
|||
name: 'HEFENG_KEY',
|
||||
description: '访问 `https://www.qweather.com/` 注册开发 API Key。',
|
||||
},
|
||||
{
|
||||
name: 'HEFENG_API_HOST',
|
||||
description: 'This is required after 2026/01/01: https://blog.qweather.com/announce/public-api-domain-change-to-api-host/',
|
||||
},
|
||||
],
|
||||
requirePuppeteer: false,
|
||||
antiCrawler: false,
|
||||
|
|
@ -34,16 +34,18 @@ export const route: Route = {
|
|||
};
|
||||
|
||||
async function handler(ctx) {
|
||||
if (!config.hefeng.key) {
|
||||
if (!config.hefeng.key || !config.hefeng.apiHost) {
|
||||
throw new ConfigNotFoundError('QWeather RSS is disabled due to the lack of <a href="https://docs.rsshub.app/zh/install/config#%E5%92%8C%E9%A3%8E%E5%A4%A9%E6%B0%94">relevant config</a>');
|
||||
}
|
||||
const NOW_WEATHER_API = `https://${config.hefeng.apiHost}/v7/weather/now`;
|
||||
const CIRY_LOOKUP_API = `https://${config.hefeng.apiHost}/geo/v2/city/lookup`;
|
||||
|
||||
const id = await cache.tryGet('qweather:' + ctx.req.param('location') + ':id', async () => {
|
||||
const response = await got(`https://geoapi.qweather.com/v2/city/lookup?location=${ctx.req.param('location')}&key=${config.hefeng.key}`);
|
||||
const response = await got(`${CIRY_LOOKUP_API}?location=${ctx.req.param('location')}&key=${config.hefeng.key}`);
|
||||
const data = response.data.location.map((loc) => loc);
|
||||
return data[0].id;
|
||||
});
|
||||
const requestUrl = rootUrl + 'key=' + config.hefeng.key + '&location=' + id;
|
||||
const requestUrl = `${NOW_WEATHER_API}?key=${config.hefeng.key}&location=${id}`;
|
||||
const responseData = await cache.tryGet(
|
||||
'qweather:' + ctx.req.param('location') + ':now',
|
||||
async () => {
|
||||
|
|
@ -63,9 +65,9 @@ async function handler(ctx) {
|
|||
return {
|
||||
title: ctx.req.param('location') + '实时天气',
|
||||
description: ctx.req.param('location') + '实时天气状况',
|
||||
item: data.map((item) => ({
|
||||
item: data.map((item: NowItem) => ({
|
||||
title: '观测时间:' + time_show,
|
||||
description: art(path.join(__dirname, 'templates/now.art'), { item }),
|
||||
description: renderNowDescription(item),
|
||||
pubDate: timeObj,
|
||||
guid: '位置:' + ctx.req.param('location') + '--时间:' + time_show,
|
||||
link: responseData.fxLink,
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
<text>白天:{{item.textDay}}——夜间:{{item.textNight}}</text>
|
||||
<br>
|
||||
<text>气温:{{item.tempMin}}℃~{{item.tempMax}}℃</text>
|
||||
<br>
|
||||
<text>相对湿度:{{item.humidity}}%</text>
|
||||
<br>
|
||||
<text>空气质量指数:{{item.aqi}} ({{item.aqiCategory}})</text>
|
||||
<br>
|
||||
<text>大气压强:{{item.pressure}}百帕</text>
|
||||
<br>
|
||||
<text>紫外线强度:{{item.uvIndex}}</text>
|
||||
<br>
|
||||
<text>白天风向:{{item.windDirDay}} 风力:{{item.windScaleDay}}级 风速:{{item.windSpeedDay}}公里/小时</text>
|
||||
<br>
|
||||
<text>夜间风向:{{item.windDirNight}} 风力:{{item.windScaleNight}}级 风速:{{item.windSpeedNight}}公里/小时</text>
|
||||
<br>
|
||||
<text>能见度:{{item.vis}}公里</text>
|
||||
<br>
|
||||
<text>日出:{{item.sunrise}} 日落: {{item.sunset}}</text>
|
||||
<br>
|
||||
<text>月相:{{item.moonPhase}} 月出:{{item.sunrise}} 月落:{{item.moonset}}
|
||||
<br>
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<text>天气:{{item.text}}</text>
|
||||
<br>
|
||||
<text>气温:{{item.temp}}℃</text>
|
||||
<br>
|
||||
<text>体感温度:{{item.feelsLike}}℃</text>
|
||||
<br>
|
||||
<text>风向:{{item.windDir}}</text>
|
||||
<br>
|
||||
<text>风力:{{item.windScale}}级 风速:{{item.windSpeed}}km/h</text>
|
||||
<br>
|
||||
<text>湿度:{{item.humidity}}% 大气压强:{{item.pressure}}hPa</text>
|
||||
<br>
|
||||
<text>本小时降水量:{{item.precip}}mm</text>
|
||||
<br>
|
||||
<text>能见度:{{item.vis}}km</text>
|
||||
<br>
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import { renderToString } from 'hono/jsx/dom/server';
|
||||
|
||||
interface WeatherForecastItem {
|
||||
fxDate: string;
|
||||
textDay: string;
|
||||
textNight: string;
|
||||
tempMin: number;
|
||||
tempMax: number;
|
||||
humidity: number;
|
||||
aqi: number;
|
||||
aqiCategory: string;
|
||||
pressure: number;
|
||||
uvIndex: number;
|
||||
windDirDay: string;
|
||||
windScaleDay: number;
|
||||
windSpeedDay: number;
|
||||
windDirNight: string;
|
||||
windScaleNight: number;
|
||||
windSpeedNight: number;
|
||||
vis: number;
|
||||
sunrise: string;
|
||||
sunset: string;
|
||||
moonPhase: string;
|
||||
moonset: string;
|
||||
}
|
||||
|
||||
interface NowItem {
|
||||
text: string;
|
||||
temp: number;
|
||||
feelsLike: number;
|
||||
windDir: string;
|
||||
windScale: number;
|
||||
windSpeed: number;
|
||||
humidity: number;
|
||||
pressure: number;
|
||||
precip: number;
|
||||
vis: number;
|
||||
}
|
||||
|
||||
const render3DaysDescription = (item: WeatherForecastItem) => renderToString(<WeatherForecast item={item} />);
|
||||
const renderNowDescription = (item: NowItem) => renderToString(<Now item={item} />);
|
||||
|
||||
const WeatherForecast = ({ item }: { item: WeatherForecastItem }) => (
|
||||
<p>
|
||||
白天:{item.textDay}——夜间:{item.textNight}
|
||||
<br />
|
||||
气温:{item.tempMin}℃~{item.tempMax}℃
|
||||
<br />
|
||||
相对湿度:{item.humidity}%
|
||||
<br />
|
||||
空气质量指数:{item.aqi} ({item.aqiCategory})
|
||||
<br />
|
||||
大气压强:{item.pressure}百帕
|
||||
<br />
|
||||
紫外线强度:{item.uvIndex}
|
||||
<br />
|
||||
白天风向:{item.windDirDay} 风力:{item.windScaleDay}级 风速:{item.windSpeedDay}公里/小时
|
||||
<br />
|
||||
夜间风向:{item.windDirNight} 风力:{item.windScaleNight}级 风速:{item.windSpeedNight}公里/小时
|
||||
<br />
|
||||
能见度:{item.vis}公里
|
||||
<br />
|
||||
日出:{item.sunrise} 日落: {item.sunset}
|
||||
<br />
|
||||
月相:{item.moonPhase} 月出:{item.sunrise} 月落:{item.moonset}
|
||||
</p>
|
||||
);
|
||||
|
||||
const Now = ({ item }: { item: NowItem }) => (
|
||||
<p>
|
||||
天气:{item.text}
|
||||
<br />
|
||||
气温:{item.temp}℃
|
||||
<br />
|
||||
体感温度:{item.feelsLike}℃
|
||||
<br />
|
||||
风向:{item.windDir}
|
||||
<br />
|
||||
风力:{item.windScale}级 风速:{item.windSpeed}km/h
|
||||
<br />
|
||||
湿度:{item.humidity}% 大气压强:{item.pressure}hPa
|
||||
<br />
|
||||
本小时降水量:{item.precip}mm
|
||||
<br />
|
||||
能见度:{item.vis}km
|
||||
</p>
|
||||
);
|
||||
|
||||
export { render3DaysDescription, renderNowDescription, type WeatherForecastItem, type NowItem };
|
||||
Loading…
Reference in New Issue