diff --git a/.vscode/settings.json b/.vscode/settings.json index 68ecdba..9397f56 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "typescript.tsdk": "node_modules\\typescript\\lib" -} \ No newline at end of file + "typescript.tsdk": "node_modules\\typescript\\lib", + "C_Cpp.errorSquiggles": "Disabled" +} diff --git a/src/config.ts b/src/config.ts index 1918b4f..81892a6 100644 --- a/src/config.ts +++ b/src/config.ts @@ -12,6 +12,7 @@ type Config = { mecab?: string; mecabDic?: string; memoryDir?: string; + earthQuakeMonitorPort?: number; }; const config = require('../config.json'); 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..3237657 --- /dev/null +++ b/src/modules/earthquake/index.ts @@ -0,0 +1,156 @@ +import autobind from "autobind-decorator"; +import Module from "@/module"; +import config from "@/config"; +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 = ""; + + private thresholdVal = 5; // 下の配列の添え字に相当する値。しきい値以上のものについて通知を出す。 + private earthquakeIntensityIndex: string[] = [ + "0未満", + "0", + "1", + "2", + "3", + "4", + "5弱", + "5強", + "6弱", + "6強", + "7", + ]; + + @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}/) || true) { + const rawDataJSON = JSON.parse( + rawDataString.replace(/\\u([\d\w]{4})/g, (match, p1) => { + return String.fromCharCode(parseInt(p1, 16)); + }), + ); + + if (rawDataJSON.type == "intensity_report") { + if (rawDataJSON.max_index >= this.thresholdVal - 1) { + const data: 震度レポート = { + type: rawDataJSON.type, + time: new Date(parseInt(rawDataJSON.time)), + max_index: rawDataJSON.max_index, + intensity_list: rawDataJSON.intensity_list, + }; + this.message = + `地震かも?\n\n震度レポート\n${data.time.toLocaleString()}\n最大震度: ${ + this.earthquakeIntensityIndex[data.max_index + 1] + }\n\n${ + data.intensity_list.map((intensity) => + `震度${this.earthquakeIntensityIndex[intensity.index + 1]}: ${ + intensity.region_list.join(" ") + }` + ).join("\n") + }`; + } + } + if (rawDataJSON.type == "eew" && false) { // これ使わなさそうだしとりあえず入らないようにした + const data: 緊急地震速報 = { + type: rawDataJSON.type, + time: new Date(parseInt(rawDataJSON.time)), + report: rawDataJSON.report, + epicenter: rawDataJSON.epicenter, + depth: rawDataJSON.depth, + magnitude: rawDataJSON.magnitude, + latitude: rawDataJSON.latitude, + longitude: rawDataJSON.longitude, + intensity: rawDataJSON.intensity, + index: rawDataJSON.index, + }; + + if (data.report == "1") { + this.message = + `**TEST TEST TEST TEST**\n地震かも?\n\n緊急地震速報\n${data.time.toLocaleString()}\n\n第${data.report}報\n震源地: ${data.epicenter}\n震源の深さ: ${data.depth}\n地震の規模(M): ${data.magnitude}\n緯度: ${data.latitude}\n経度: ${data.longitude}\n予想される最大震度(?): ${data.intensity}\n`; + } + } + + console.table(rawDataJSON); // デバッグ用 + if (rawDataJSON.type == 'intensity_report') { + console.table(rawDataJSON.intensity_list); // デバッグ用 + } + + this.returnResponse(res, "ok"); + if (this.message) { + this.ai.post({ + cw: "試験運用中!!!!!", + visibility: "home", + text: this.message, + }); + } + } else { + this.returnResponse(res, "debobigego"); + } + }).listen(config.earthQuakeMonitorPort || 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.c b/src/modules/earthquake/typeMemo.c new file mode 100644 index 0000000..e8c6b52 --- /dev/null +++ b/src/modules/earthquake/typeMemo.c @@ -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,,,,]}, +,,] +} diff --git a/src/modules/earthquake/テスト用生データ.txt b/src/modules/earthquake/テスト用生データ.txt new file mode 100644 index 0000000..9d4696d --- /dev/null +++ b/src/modules/earthquake/テスト用生データ.txt @@ -0,0 +1,4 @@ +# テスト用生データ +{"type":"pga_alert","time":"1649085285968","max_pga":-0.531,"new":true,"estimated_intensity":0,"region_list":["\u8328\u57ce"]} +{"type":"intensity_report","time":"1649085285968","max_index":-1,"intensity_list":[{"intensity":"0\u672a\u6e80","index":-1,"region_list":["\u8328\u57ce"]}]} +{"type": "eew","report": "1","epicenter": "伊予灘","depth": "60km","magnitude": 3.5,"latitude": 33.8,"longitude": 132.1,"intensity": "2","index": 2}