From c5ef08fea2658173366cdb72d55735b911b5edfc Mon Sep 17 00:00:00 2001 From: Koki Takahashi Date: Wed, 19 Jun 2024 12:00:10 +0000 Subject: [PATCH] sunrise: Update imports and function calls to integrate JMA weather forecast data received from https://weather.tsukumijima.net/api/forecast --- sunrise/fetch.ts | 27 +++++++++++++++++++++++++++ sunrise/forecast.ts | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/sunrise/fetch.ts b/sunrise/fetch.ts index 4b245a31..e5d11cb2 100644 --- a/sunrise/fetch.ts +++ b/sunrise/fetch.ts @@ -170,6 +170,23 @@ const accuweatherCurrentConditionsResponseSchema = z.array( export type AccuweatherCurrentConditionsResponse = z.infer; +const jmaForecastSchema = z.object({ + publicTime: z.string(), + publicTimeFormatted: z.string(), + publishingOffice: z.string(), + title: z.string(), + link: z.string(), + description: z.object({ + publicTime: z.string(), + publicTimeFormatted: z.string(), + headlineText: z.string(), + bodyText: z.string(), + text: z.string(), + }), +}); + +export type JmaForecast = z.infer; + const getTayoriEntries = async () => { const {data} = await scrapeIt<{articles: Article[]}>('http://www.i-nekko.jp/hibinotayori/', { articles: { @@ -323,3 +340,13 @@ export const getMinuteCast = async (location: [number, number]) => { return accuweatherMinuteCastResponseSchema.parse(data); }; + +export const getJmaForecast = async (firstAreaCode: string) => { + const {data: forecastData} = await axios.get('https://weather.tsukumijima.net/api/forecast', { + params: { + city: firstAreaCode, + }, + }); + + return {data: jmaForecastSchema.parse(forecastData)}; +}; diff --git a/sunrise/forecast.ts b/sunrise/forecast.ts index 405d3b1f..9f6a8876 100644 --- a/sunrise/forecast.ts +++ b/sunrise/forecast.ts @@ -1,7 +1,15 @@ -import type {WebClient} from '@slack/web-api'; -import {AccuweatherMultiUnit, getCurrentWeather, getMinuteCast, getWeather} from './fetch'; +import type {MessageAttachment, WebClient} from '@slack/web-api'; +import {Attachment} from '@slack/web-api/dist/response/ChatPostMessageResponse'; +import {FeatureCollection, MultiPolygon, points as Points, pointsWithinPolygon} from '@turf/turf'; +import {Loader} from '../lib/utils'; +import {AccuweatherMultiUnit, getCurrentWeather, getJmaForecast, getMinuteCast, getWeather} from './fetch'; import type {Point} from './index'; +const firstAreaGeojsonLoader = new Loader>(() => { + const url = 'https://raw.githubusercontent.com/tmiyachi/jma-gis/master/geojson/firstarea.geojson'; + return fetch(url).then((res) => res.json()); +}); + export const postRainMinuteCast = async (point: Point, slack: WebClient, threadTimestamp?: string) => { const weatherData = await getMinuteCast([point.latitude, point.longitude]); @@ -109,11 +117,31 @@ export const postWeatherCast = async (point: Point, slack: WebClient, threadTime const text = [headlineText, percipitationText, windText, '\n', forecastText].join(''); + const firstAreaGeojson = await firstAreaGeojsonLoader.load(); + const points = Points([[point.longitude, point.latitude]]); + const featureContainingPoints = firstAreaGeojson.features.find((feature) => ( + pointsWithinPolygon(points, feature)?.features?.length > 0 + )); + + const attachments: MessageAttachment[] = []; + if (featureContainingPoints !== undefined) { + const firstAreaCode = featureContainingPoints.properties.firstareacode; + const jmaForecast = await getJmaForecast(firstAreaCode); + const text = jmaForecast.data.description.text.split('【')[0]?.replace(/\s+/g, ''); + attachments.push({ + title: `${jmaForecast.data.publishingOffice}発表: ${jmaForecast.data.title}`, + title_link: jmaForecast.data.link, + text, + color: '#36a64f', + }); + } + await slack.chat.postMessage({ channel: process.env.CHANNEL_SANDBOX, username: 'sunrise', icon_emoji: ':sunrise:', text, + attachments, ...(threadTimestamp ? {thread_ts: threadTimestamp} : {}), blocks: [ {