diff --git a/src/index.ts b/src/index.ts index 353db8b..5f5b4c0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -35,6 +35,7 @@ import SleepReportModule from './modules/sleep-report'; import NotingModule from './modules/noting'; // import PollModule from './modules/poll'; import ReminderModule from './modules/reminder'; +import earthquake from './modules/earthquake'; import DicModule from './modules/dic'; import menuModule from './modules/menu'; import GetColorModule from './modules/color'; @@ -96,6 +97,7 @@ promiseRetry(retry => { new DicModule(), new menuModule(), new GetColorModule(), + new earthquake(), ]); }).catch(e => { log(chalk.red('Failed to fetch the account')); diff --git a/src/modules/earthquake/index.ts b/src/modules/earthquake/index.ts new file mode 100644 index 0000000..0ee8742 --- /dev/null +++ b/src/modules/earthquake/index.ts @@ -0,0 +1,116 @@ +import autobind from "autobind-decorator"; +import Module from "@/module"; +import Message from "@/message"; +import * as http from "http"; + +// 基本的に生データはstringばっかり。都合のいい形に加工済みの状態の型定義を書いています。 +// ここでいくらか言及されてる(https://bultar.bbs.fc2.com/?act=reply&tid=5645851); +interface 緊急地震速報 { + type: 'eew'; + time: Date; + report: string; // 第n報 最終報はstringで'final'となるので、とりあえずstring型 + epicenter: string; // 震源地 + depth: string; // 震源の深さ + magnitude: string; // 地震の規模を示すマグニチュード + latitude: string; // 緯度らしいが謎 + longitude: string; // 経度らしいが謎 + intensity: string; // 地震の強さ + index: number; // 謎 +} + +interface 緊急地震速報キャンセル { + type: 'pga_alert_cancel'; + time: Date; +} + +interface 震度レポート { + type: 'intensity_report'; + time: Date; + max_index: number; + intensity_list: { + intensity: string; + index: number; + region_list: string[]; + }[]; +} + +interface 地震検知 { + type: 'pga_alert'; + time: Date; + max_pga: number; + new: boolean; + estimated_intensity: number; + region_list: string[]; +} + +export default class extends Module { + public readonly name = "earthquake"; + private message: string = ""; + + @autobind + public install() { + this.createListenServer(); + return {}; + } + + @autobind + private async createListenServer() { + http.createServer(async (req, res) => { + const buffers: Buffer[] = []; + for await (const chunk of req) { + buffers.push(chunk); + } + + const rawDataString = Buffer.concat(buffers).toString(); + // rawDataString について、Unicodeエスケープシーケンスが含まれていたら通常の文字列に変換する + // JSONでなければreturn falseする + if (rawDataString.match(/\\u[0-9a-f]{4}/)) { + const rawDataJSON = JSON.parse( + rawDataString.replace(/\\u([\d\w]{4})/g, (match, p1) => { + return String.fromCharCode(parseInt(p1, 16)); + }), + ); + + if (rawDataJSON.type == "pga_alert") { + const data: 地震検知 = { + type: rawDataJSON.type, + time: new Date(parseInt(rawDataJSON.time)), + max_pga: rawDataJSON.max_pga, + new: rawDataJSON.new, + estimated_intensity: rawDataJSON.estimated_intensity, + region_list: rawDataJSON.region_list, + }; + this.message = + // region_listはオブジェクトなので、2行改行してから列挙する + `PGA Alert\n${data.time.toLocaleString()}\n${data.max_pga}\n${data.estimated_intensity}\n\n${data.region_list.join("\n")}`; + }else if (rawDataJSON.type == 'intensity_report'){ + const data: 震度レポート = { + type: rawDataJSON.type, + time: new Date(parseInt(rawDataJSON.time)), + max_index: rawDataJSON.max_index, + intensity_list: rawDataJSON.intensity_list, + } + this.message = + `Intensity Report\n${data.time.toLocaleString()}\n\n${data.intensity_list.map(intensity => `震度${intensity.intensity} ${intensity.region_list.join(" ")}`).join("\n")}`; + } + this.returnResponse(res, "ok"); + if (this.message) { + this.ai.post({ + visibility: "home", + text: this.message, + }); + } + } else { + this.returnResponse(res, "debobigego"); + } + }).listen(process.env.EARTHQUAKE_PORT || 9999); + } + + @autobind + private returnResponse(res: http.ServerResponse, text: string) { + res.writeHead(200, { + "Content-Type": "text/plain", + }); + res.end(text); + } +} diff --git a/src/modules/earthquake/typeMemo.cs b/src/modules/earthquake/typeMemo.cs new file mode 100644 index 0000000..e8c6b52 --- /dev/null +++ b/src/modules/earthquake/typeMemo.cs @@ -0,0 +1,38 @@ +# 緊急地震速報 +{"type":"eew", +"time":long, +"report":int, +"epicenter":String, +"depth":String, +"magnitude":String, +"latitude":String, +"longitude":String, +"intensity":String, +"index":int +} + +# 地震検知 +{"type":"pga_alert", +"time":long, +"max_pga":float, +"new":boolean, +"estimated_intensity":int, +"region_list":[String,String,,,] +} + +# 地震検知キャンセル +{"type":"pga_alert_cancel", "time":long } + +# 震度レポート +{"type":"intensity_report", +"time":long, +"max_index":int, +"intensity_list":[ +{"intensity":String, +"index":int, +"region_list":[String,String,,,,]}, +{"intensity":String, +"index":int, +"region_list":[String,String,,,,]}, +,,] +}