From de3c9707129a4b7c321f69c78142defeeda7d12d Mon Sep 17 00:00:00 2001 From: Last2014 Date: Tue, 1 Jul 2025 00:01:35 +0900 Subject: [PATCH] =?UTF-8?q?=E5=9C=B0=E9=9C=87=E6=83=85=E5=A0=B1=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0(=E6=9C=80=E4=BD=8E=E9=99=90=E6=83=85?= =?UTF-8?q?=E5=A0=B1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logexample | 166 +++++++++++++++++++++++ main.ts | 3 + package.json | 7 +- scripts/earthquakeNotice.ts | 254 ++++++++++++++++++++++++++++++++++++ tsconfig.json | 127 +++--------------- types/ws.d.ts | 68 ++++++++++ 6 files changed, 515 insertions(+), 110 deletions(-) create mode 100644 logexample create mode 100644 scripts/earthquakeNotice.ts create mode 100644 types/ws.d.ts diff --git a/logexample b/logexample new file mode 100644 index 0000000..db1b0d1 --- /dev/null +++ b/logexample @@ -0,0 +1,166 @@ +地震情報サーバーに接続します +P2P地震情報に接続中 +サーバーが起動しました +P2P地震情報に接続しました +受信したメッセージ: { + _id: '6862177ac5875700073d7010', + areas: [ + { id: 900, peer: 211 }, { id: 250, peer: 266 }, { id: 70, peer: 4 }, + { id: 905, peer: 13 }, { id: 150, peer: 16 }, { id: 460, peer: 48 }, + { id: 125, peer: 19 }, { id: 601, peer: 8 }, { id: 275, peer: 28 }, + { id: 231, peer: 69 }, { id: 205, peer: 15 }, { id: 405, peer: 14 }, + { id: 411, peer: 12 }, { id: 425, peer: 60 }, { id: 416, peer: 11 }, + { id: 570, peer: 1 }, { id: 301, peer: 5 }, { id: 351, peer: 8 }, + { id: 410, peer: 5 }, { id: 270, peer: 122 }, { id: 225, peer: 26 }, + { id: 475, peer: 33 }, { id: 130, peer: 14 }, { id: 455, peer: 14 }, + { id: 430, peer: 15 }, { id: 230, peer: 15 }, { id: 420, peer: 14 }, + { id: 241, peer: 60 }, { id: 215, peer: 14 }, { id: 115, peer: 13 }, + { id: 560, peer: 12 }, { id: 665, peer: 14 }, { id: 465, peer: 23 }, + { id: 550, peer: 4 }, { id: 240, peer: 8 }, { id: 105, peer: 7 }, + { id: 525, peer: 15 }, { id: 210, peer: 3 }, { id: 310, peer: 4 }, + { id: 620, peer: 2 }, { id: 100, peer: 14 }, { id: 575, peer: 4 }, + { id: 315, peer: 4 }, { id: 480, peer: 6 }, { id: 300, peer: 6 }, + { id: 200, peer: 6 }, { id: 641, peer: 3 }, { id: 302, peer: 10 }, + { id: 705, peer: 2 }, { id: 625, peer: 2 }, { id: 535, peer: 20 }, + { id: 415, peer: 17 }, { id: 515, peer: 3 }, { id: 651, peer: 6 }, + { id: 10, peer: 26 }, { id: 55, peer: 9 }, { id: 701, peer: 7 }, + { id: 20, peer: 2 }, { id: 345, peer: 3 }, { id: 335, peer: 1 }, + { id: 545, peer: 5 }, { id: 325, peer: 10 }, { id: 45, peer: 1 }, + { id: 350, peer: 4 }, { id: 901, peer: 2 }, { id: 600, peer: 14 }, + { id: 151, peer: 3 }, { id: 670, peer: 4 }, { id: 581, peer: 5 }, + { id: 330, peer: 4 }, { id: 355, peer: 7 }, { id: 135, peer: 7 }, + { id: 152, peer: 5 }, { id: 242, peer: 4 }, { id: 440, peer: 1 }, + { id: 120, peer: 15 }, { id: 646, peer: 2 }, { id: 65, peer: 8 }, + { id: 60, peer: 1 }, { id: 35, peer: 2 }, { id: 320, peer: 6 }, + { id: 110, peer: 2 }, { id: 220, peer: 1 }, { id: 400, peer: 2 }, + { id: 656, peer: 1 }, { id: 530, peer: 2 }, { id: 50, peer: 3 }, + { id: 490, peer: 8 }, { id: 605, peer: 5 }, { id: 645, peer: 1 }, + { id: 541, peer: 2 }, { id: 450, peer: 1 }, { id: 675, peer: 2 }, + { id: 142, peer: 9 }, { id: 685, peer: 1 }, { id: 576, peer: 1 }, + { id: 30, peer: 2 }, { id: 15, peer: 3 }, { id: 615, peer: 2 }, + { id: 305, peer: 1 }, + ... 13 more items + ], + code: 555, + created_at: '2025/06/30 13:50:02.640', + expire: '2025/06/30 13:55:02', + hop: 4, + time: '2025/06/30 13:50:02.634', + uid: '2025/06/30 13:55:02', + ver: '20150406' +} +震度情報を受信しました +{"_id":"6862177ac5875700073d7010","areas":[{"id":900,"peer":211},{"id":250,"peer":266},{"id":70,"peer":4},{"id":905,"peer":13},{"id":150,"peer":16},{"id":460,"peer":48},{"id":125,"peer":19},{"id":601,"peer":8},{"id":275,"peer":28},{"id":231,"peer":69},{"id":205,"peer":15},{"id":405,"peer":14},{"id":411,"peer":12},{"id":425,"peer":60},{"id":416,"peer":11},{"id":570,"peer":1},{"id":301,"peer":5},{"id":351,"peer":8},{"id":410,"peer":5},{"id":270,"peer":122},{"id":225,"peer":26},{"id":475,"peer":33},{"id":130,"peer":14},{"id":455,"peer":14},{"id":430,"peer":15},{"id":230,"peer":15},{"id":420,"peer":14},{"id":241,"peer":60},{"id":215,"peer":14},{"id":115,"peer":13},{"id":560,"peer":12},{"id":665,"peer":14},{"id":465,"peer":23},{"id":550,"peer":4},{"id":240,"peer":8},{"id":105,"peer":7},{"id":525,"peer":15},{"id":210,"peer":3},{"id":310,"peer":4},{"id":620,"peer":2},{"id":100,"peer":14},{"id":575,"peer":4},{"id":315,"peer":4},{"id":480,"peer":6},{"id":300,"peer":6},{"id":200,"peer":6},{"id":641,"peer":3},{"id":302,"peer":10},{"id":705,"peer":2},{"id":625,"peer":2},{"id":535,"peer":20},{"id":415,"peer":17},{"id":515,"peer":3},{"id":651,"peer":6},{"id":10,"peer":26},{"id":55,"peer":9},{"id":701,"peer":7},{"id":20,"peer":2},{"id":345,"peer":3},{"id":335,"peer":1},{"id":545,"peer":5},{"id":325,"peer":10},{"id":45,"peer":1},{"id":350,"peer":4},{"id":901,"peer":2},{"id":600,"peer":14},{"id":151,"peer":3},{"id":670,"peer":4},{"id":581,"peer":5},{"id":330,"peer":4},{"id":355,"peer":7},{"id":135,"peer":7},{"id":152,"peer":5},{"id":242,"peer":4},{"id":440,"peer":1},{"id":120,"peer":15},{"id":646,"peer":2},{"id":65,"peer":8},{"id":60,"peer":1},{"id":35,"peer":2},{"id":320,"peer":6},{"id":110,"peer":2},{"id":220,"peer":1},{"id":400,"peer":2},{"id":656,"peer":1},{"id":530,"peer":2},{"id":50,"peer":3},{"id":490,"peer":8},{"id":605,"peer":5},{"id":645,"peer":1},{"id":541,"peer":2},{"id":450,"peer":1},{"id":675,"peer":2},{"id":142,"peer":9},{"id":685,"peer":1},{"id":576,"peer":1},{"id":30,"peer":2},{"id":15,"peer":3},{"id":615,"peer":2},{"id":305,"peer":1},{"id":580,"peer":1},{"id":706,"peer":1},{"id":140,"peer":1},{"id":445,"peer":4},{"id":143,"peer":3},{"id":650,"peer":1},{"id":25,"peer":2},{"id":340,"peer":1},{"id":660,"peer":2},{"id":510,"peer":1},{"id":470,"peer":1},{"id":661,"peer":1},{"id":265,"peer":1}],"code":555,"created_at":"2025/06/30 13:50:02.640","expire":"2025/06/30 13:55:02","hop":4,"time":"2025/06/30 13:50:02.634","uid":"2025/06/30 13:55:02","ver":"20150406"} +=== 地震速報検知 === +時刻: 2025/06/30 13:50:02.634 +コード: 555 +対象地域: [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object] +📊 震度情報が更新されました +受信したメッセージ: { + _id: '686217a9c5875700073d7011', + areas: [ + { id: 10, peer: 26 }, { id: 15, peer: 3 }, { id: 20, peer: 2 }, + { id: 25, peer: 2 }, { id: 30, peer: 2 }, { id: 35, peer: 2 }, + { id: 45, peer: 1 }, { id: 50, peer: 3 }, { id: 55, peer: 9 }, + { id: 60, peer: 1 }, { id: 65, peer: 9 }, { id: 70, peer: 4 }, + { id: 100, peer: 14 }, { id: 105, peer: 7 }, { id: 110, peer: 2 }, + { id: 115, peer: 13 }, { id: 120, peer: 15 }, { id: 125, peer: 19 }, + { id: 130, peer: 14 }, { id: 135, peer: 7 }, { id: 140, peer: 1 }, + { id: 142, peer: 9 }, { id: 143, peer: 3 }, { id: 150, peer: 16 }, + { id: 151, peer: 3 }, { id: 152, peer: 5 }, { id: 200, peer: 6 }, + { id: 205, peer: 15 }, { id: 210, peer: 3 }, { id: 215, peer: 14 }, + { id: 220, peer: 1 }, { id: 225, peer: 26 }, { id: 230, peer: 15 }, + { id: 231, peer: 69 }, { id: 240, peer: 8 }, { id: 241, peer: 60 }, + { id: 242, peer: 4 }, { id: 250, peer: 267 }, { id: 265, peer: 1 }, + { id: 270, peer: 122 }, { id: 275, peer: 28 }, { id: 300, peer: 6 }, + { id: 301, peer: 5 }, { id: 302, peer: 10 }, { id: 305, peer: 1 }, + { id: 310, peer: 4 }, { id: 315, peer: 4 }, { id: 320, peer: 5 }, + { id: 325, peer: 10 }, { id: 330, peer: 4 }, { id: 335, peer: 1 }, + { id: 340, peer: 1 }, { id: 345, peer: 3 }, { id: 350, peer: 4 }, + { id: 351, peer: 8 }, { id: 355, peer: 7 }, { id: 400, peer: 2 }, + { id: 405, peer: 14 }, { id: 410, peer: 5 }, { id: 411, peer: 12 }, + { id: 415, peer: 17 }, { id: 416, peer: 11 }, { id: 420, peer: 14 }, + { id: 425, peer: 61 }, { id: 430, peer: 15 }, { id: 440, peer: 1 }, + { id: 445, peer: 4 }, { id: 450, peer: 1 }, { id: 455, peer: 14 }, + { id: 460, peer: 48 }, { id: 465, peer: 23 }, { id: 470, peer: 1 }, + { id: 475, peer: 32 }, { id: 480, peer: 6 }, { id: 490, peer: 8 }, + { id: 510, peer: 1 }, { id: 515, peer: 3 }, { id: 525, peer: 15 }, + { id: 530, peer: 2 }, { id: 535, peer: 20 }, { id: 541, peer: 2 }, + { id: 545, peer: 5 }, { id: 550, peer: 4 }, { id: 560, peer: 12 }, + { id: 570, peer: 1 }, { id: 575, peer: 4 }, { id: 576, peer: 1 }, + { id: 580, peer: 1 }, { id: 581, peer: 5 }, { id: 600, peer: 14 }, + { id: 601, peer: 8 }, { id: 605, peer: 5 }, { id: 615, peer: 2 }, + { id: 620, peer: 2 }, { id: 625, peer: 2 }, { id: 641, peer: 3 }, + { id: 645, peer: 1 }, { id: 646, peer: 2 }, { id: 650, peer: 1 }, + { id: 651, peer: 6 }, + ... 13 more items + ], + code: 555, + created_at: '2025/06/30 13:50:49.437', + expire: '2025/06/30 13:53:48', + hop: 11, + time: '2025/06/30 13:50:49.434', + uid: '2025/06/30 13:53:48', + ver: '20150406' +} +震度情報を受信しました +{"_id":"686217a9c5875700073d7011","areas":[{"id":10,"peer":26},{"id":15,"peer":3},{"id":20,"peer":2},{"id":25,"peer":2},{"id":30,"peer":2},{"id":35,"peer":2},{"id":45,"peer":1},{"id":50,"peer":3},{"id":55,"peer":9},{"id":60,"peer":1},{"id":65,"peer":9},{"id":70,"peer":4},{"id":100,"peer":14},{"id":105,"peer":7},{"id":110,"peer":2},{"id":115,"peer":13},{"id":120,"peer":15},{"id":125,"peer":19},{"id":130,"peer":14},{"id":135,"peer":7},{"id":140,"peer":1},{"id":142,"peer":9},{"id":143,"peer":3},{"id":150,"peer":16},{"id":151,"peer":3},{"id":152,"peer":5},{"id":200,"peer":6},{"id":205,"peer":15},{"id":210,"peer":3},{"id":215,"peer":14},{"id":220,"peer":1},{"id":225,"peer":26},{"id":230,"peer":15},{"id":231,"peer":69},{"id":240,"peer":8},{"id":241,"peer":60},{"id":242,"peer":4},{"id":250,"peer":267},{"id":265,"peer":1},{"id":270,"peer":122},{"id":275,"peer":28},{"id":300,"peer":6},{"id":301,"peer":5},{"id":302,"peer":10},{"id":305,"peer":1},{"id":310,"peer":4},{"id":315,"peer":4},{"id":320,"peer":5},{"id":325,"peer":10},{"id":330,"peer":4},{"id":335,"peer":1},{"id":340,"peer":1},{"id":345,"peer":3},{"id":350,"peer":4},{"id":351,"peer":8},{"id":355,"peer":7},{"id":400,"peer":2},{"id":405,"peer":14},{"id":410,"peer":5},{"id":411,"peer":12},{"id":415,"peer":17},{"id":416,"peer":11},{"id":420,"peer":14},{"id":425,"peer":61},{"id":430,"peer":15},{"id":440,"peer":1},{"id":445,"peer":4},{"id":450,"peer":1},{"id":455,"peer":14},{"id":460,"peer":48},{"id":465,"peer":23},{"id":470,"peer":1},{"id":475,"peer":32},{"id":480,"peer":6},{"id":490,"peer":8},{"id":510,"peer":1},{"id":515,"peer":3},{"id":525,"peer":15},{"id":530,"peer":2},{"id":535,"peer":20},{"id":541,"peer":2},{"id":545,"peer":5},{"id":550,"peer":4},{"id":560,"peer":12},{"id":570,"peer":1},{"id":575,"peer":4},{"id":576,"peer":1},{"id":580,"peer":1},{"id":581,"peer":5},{"id":600,"peer":14},{"id":601,"peer":8},{"id":605,"peer":5},{"id":615,"peer":2},{"id":620,"peer":2},{"id":625,"peer":2},{"id":641,"peer":3},{"id":645,"peer":1},{"id":646,"peer":2},{"id":650,"peer":1},{"id":651,"peer":6},{"id":656,"peer":1},{"id":660,"peer":2},{"id":661,"peer":1},{"id":665,"peer":14},{"id":670,"peer":4},{"id":675,"peer":2},{"id":685,"peer":1},{"id":701,"peer":7},{"id":705,"peer":2},{"id":706,"peer":1},{"id":900,"peer":211},{"id":901,"peer":2},{"id":905,"peer":13}],"code":555,"created_at":"2025/06/30 13:50:49.437","expire":"2025/06/30 13:53:48","hop":11,"time":"2025/06/30 13:50:49.434","uid":"2025/06/30 13:53:48","ver":"20150406"} +=== 地震速報検知 === +時刻: 2025/06/30 13:50:49.434 +コード: 555 +対象地域: [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object] +📊 震度情報が更新されました +受信したメッセージ: { + _id: '68621858c5875700073d7012', + areas: [ + { id: 10, peer: 26 }, { id: 15, peer: 3 }, { id: 20, peer: 2 }, + { id: 25, peer: 2 }, { id: 30, peer: 2 }, { id: 35, peer: 2 }, + { id: 45, peer: 1 }, { id: 50, peer: 3 }, { id: 55, peer: 9 }, + { id: 60, peer: 1 }, { id: 65, peer: 9 }, { id: 70, peer: 4 }, + { id: 100, peer: 13 }, { id: 105, peer: 7 }, { id: 110, peer: 2 }, + { id: 115, peer: 13 }, { id: 120, peer: 15 }, { id: 125, peer: 19 }, + { id: 130, peer: 14 }, { id: 135, peer: 7 }, { id: 140, peer: 1 }, + { id: 142, peer: 9 }, { id: 143, peer: 3 }, { id: 150, peer: 16 }, + { id: 151, peer: 3 }, { id: 152, peer: 5 }, { id: 200, peer: 6 }, + { id: 205, peer: 15 }, { id: 210, peer: 3 }, { id: 215, peer: 14 }, + { id: 220, peer: 1 }, { id: 225, peer: 26 }, { id: 230, peer: 15 }, + { id: 231, peer: 69 }, { id: 240, peer: 8 }, { id: 241, peer: 60 }, + { id: 242, peer: 4 }, { id: 250, peer: 264 }, { id: 265, peer: 1 }, + { id: 270, peer: 123 }, { id: 275, peer: 28 }, { id: 300, peer: 5 }, + { id: 301, peer: 5 }, { id: 302, peer: 10 }, { id: 305, peer: 1 }, + { id: 310, peer: 4 }, { id: 315, peer: 4 }, { id: 320, peer: 5 }, + { id: 325, peer: 9 }, { id: 330, peer: 4 }, { id: 335, peer: 1 }, + { id: 340, peer: 1 }, { id: 345, peer: 3 }, { id: 350, peer: 4 }, + { id: 351, peer: 8 }, { id: 355, peer: 7 }, { id: 400, peer: 2 }, + { id: 405, peer: 14 }, { id: 410, peer: 5 }, { id: 411, peer: 12 }, + { id: 415, peer: 17 }, { id: 416, peer: 12 }, { id: 420, peer: 14 }, + { id: 425, peer: 60 }, { id: 430, peer: 14 }, { id: 440, peer: 1 }, + { id: 445, peer: 4 }, { id: 450, peer: 1 }, { id: 455, peer: 14 }, + { id: 460, peer: 48 }, { id: 465, peer: 22 }, { id: 470, peer: 1 }, + { id: 475, peer: 32 }, { id: 480, peer: 6 }, { id: 490, peer: 8 }, + { id: 510, peer: 1 }, { id: 515, peer: 3 }, { id: 525, peer: 15 }, + { id: 530, peer: 2 }, { id: 535, peer: 20 }, { id: 541, peer: 2 }, + { id: 545, peer: 5 }, { id: 550, peer: 4 }, { id: 560, peer: 12 }, + { id: 570, peer: 1 }, { id: 575, peer: 4 }, { id: 576, peer: 1 }, + { id: 580, peer: 1 }, { id: 581, peer: 5 }, { id: 600, peer: 14 }, + { id: 601, peer: 8 }, { id: 605, peer: 5 }, { id: 615, peer: 2 }, + { id: 620, peer: 2 }, { id: 625, peer: 2 }, { id: 641, peer: 3 }, + { id: 645, peer: 1 }, { id: 646, peer: 2 }, { id: 650, peer: 1 }, + { id: 651, peer: 6 }, + ... 13 more items + ], + code: 555, + created_at: '2025/06/30 13:53:44.508', + expire: '2025/06/30 13:56:43', + hop: 6, + time: '2025/06/30 13:53:44.505', + uid: '2025/06/30 13:56:43', + ver: '20150406' +} +震度情報を受信しました +{"_id":"68621858c5875700073d7012","areas":[{"id":10,"peer":26},{"id":15,"peer":3},{"id":20,"peer":2},{"id":25,"peer":2},{"id":30,"peer":2},{"id":35,"peer":2},{"id":45,"peer":1},{"id":50,"peer":3},{"id":55,"peer":9},{"id":60,"peer":1},{"id":65,"peer":9},{"id":70,"peer":4},{"id":100,"peer":13},{"id":105,"peer":7},{"id":110,"peer":2},{"id":115,"peer":13},{"id":120,"peer":15},{"id":125,"peer":19},{"id":130,"peer":14},{"id":135,"peer":7},{"id":140,"peer":1},{"id":142,"peer":9},{"id":143,"peer":3},{"id":150,"peer":16},{"id":151,"peer":3},{"id":152,"peer":5},{"id":200,"peer":6},{"id":205,"peer":15},{"id":210,"peer":3},{"id":215,"peer":14},{"id":220,"peer":1},{"id":225,"peer":26},{"id":230,"peer":15},{"id":231,"peer":69},{"id":240,"peer":8},{"id":241,"peer":60},{"id":242,"peer":4},{"id":250,"peer":264},{"id":265,"peer":1},{"id":270,"peer":123},{"id":275,"peer":28},{"id":300,"peer":5},{"id":301,"peer":5},{"id":302,"peer":10},{"id":305,"peer":1},{"id":310,"peer":4},{"id":315,"peer":4},{"id":320,"peer":5},{"id":325,"peer":9},{"id":330,"peer":4},{"id":335,"peer":1},{"id":340,"peer":1},{"id":345,"peer":3},{"id":350,"peer":4},{"id":351,"peer":8},{"id":355,"peer":7},{"id":400,"peer":2},{"id":405,"peer":14},{"id":410,"peer":5},{"id":411,"peer":12},{"id":415,"peer":17},{"id":416,"peer":12},{"id":420,"peer":14},{"id":425,"peer":60},{"id":430,"peer":14},{"id":440,"peer":1},{"id":445,"peer":4},{"id":450,"peer":1},{"id":455,"peer":14},{"id":460,"peer":48},{"id":465,"peer":22},{"id":470,"peer":1},{"id":475,"peer":32},{"id":480,"peer":6},{"id":490,"peer":8},{"id":510,"peer":1},{"id":515,"peer":3},{"id":525,"peer":15},{"id":530,"peer":2},{"id":535,"peer":20},{"id":541,"peer":2},{"id":545,"peer":5},{"id":550,"peer":4},{"id":560,"peer":12},{"id":570,"peer":1},{"id":575,"peer":4},{"id":576,"peer":1},{"id":580,"peer":1},{"id":581,"peer":5},{"id":600,"peer":14},{"id":601,"peer":8},{"id":605,"peer":5},{"id":615,"peer":2},{"id":620,"peer":2},{"id":625,"peer":2},{"id":641,"peer":3},{"id":645,"peer":1},{"id":646,"peer":2},{"id":650,"peer":1},{"id":651,"peer":6},{"id":656,"peer":1},{"id":660,"peer":2},{"id":661,"peer":1},{"id":665,"peer":14},{"id":670,"peer":4},{"id":675,"peer":2},{"id":685,"peer":1},{"id":701,"peer":7},{"id":705,"peer":2},{"id":706,"peer":1},{"id":900,"peer":211},{"id":901,"peer":2},{"id":905,"peer":12}],"code":555,"created_at":"2025/06/30 13:53:44.508","expire":"2025/06/30 13:56:43","hop":6,"time":"2025/06/30 13:53:44.505","uid":"2025/06/30 13:56:43","ver":"20150406"} +=== 地震速報検知 === +時刻: 2025/06/30 13:53:44.505 +コード: 555 +対象地域: [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], [object Object] +📊 震度情報が更新されました diff --git a/main.ts b/main.ts index 8713de4..418fb05 100644 --- a/main.ts +++ b/main.ts @@ -2,9 +2,12 @@ import * as cron from "node-cron"; import timeNotice from "./scripts/timeNotice.js"; import weatherNotice from "./scripts/weatherNotice.js"; +import earthquakeNotice from "./scripts/earthquakeNotice.js" import followBack from "./scripts/followBack.js"; +earthquakeNotice(); + // 時報・フォローバック(毎時) cron.schedule("0 * * * *", () => { timeNotice(); diff --git a/package.json b/package.json index 5052a97..064f2e7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,11 @@ "dotenv": "^17.0.0", "node-cron": "^4.1.1", "tsx": "^4.20.3", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "ws": "^8.18.3" + }, + "devDependencies": { + "@types/node": "^24.0.7", + "@types/ws": "^8.18.1" } } diff --git a/scripts/earthquakeNotice.ts b/scripts/earthquakeNotice.ts new file mode 100644 index 0000000..ba2c814 --- /dev/null +++ b/scripts/earthquakeNotice.ts @@ -0,0 +1,254 @@ +import WebSocket from "ws"; +import * as dotenv from "dotenv"; + +dotenv.config(); + +class P2PEarthquakeClient { + private ws: WebSocket | null = null; + private reconnectInterval: number = 5000; + private reconnectTimer: NodeJS.Timeout | null = null; + private isConnecting: boolean = false; + + public start(): void { + this.connect(); + this.setupCleanup(); + } + + private connect(): void { + if (this.isConnecting) return; + + this.isConnecting = true; + console.log("P2P地震情報に接続中"); + + try { + this.ws = new WebSocket("wss://api.p2pquake.net/v2/ws"); + + this.ws.on("open", () => { + console.log("P2P地震情報に接続しました"); + this.isConnecting = false; + + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = null; + } + }); + + this.ws.on("message", (data: WebSocket) => { + try { + const message = JSON.parse(data.toString()); + this.handleMessage(message); + } catch (error) { + console.error("メッセージのパースに失敗:", error); + } + }); + + this.ws.on("close", (code: number, reason: Buffer) => { + console.log(`接続が閉じられました: ${code} - ${reason.toString()}`); + this.isConnecting = false; + this.scheduleReconnect(); + }); + + this.ws.on("error", (error: Error) => { + console.error("WebSocketエラー:", error); + this.isConnecting = false; + this.scheduleReconnect(); + }); + } catch (error) { + console.error("接続エラー:", error); + this.isConnecting = false; + this.scheduleReconnect(); + } + } + + private handleMessage(message: any): void { + switch (message.code) { + case 551: // 地震情報 + console.log("地震情報を受信しました"); + this.executeEventFunc(message); + break; + case 554: // 緊急地震速報 + console.log("緊急地震速報を受信しました"); + this.executeEventFunc(message); + break; + case 555: // 震度情報 + console.log("震度情報を受信しました"); + this.executeEventFunc(message); + break; + default: + console.log(`その他の情報 (コード: ${message.code})`); + break; + } + } + + private executeEventFunc(earthquakeInfo: any): void { + event(earthquakeInfo); + } + + private scheduleReconnect(): void { + if (this.reconnectTimer) return; + + console.log(`${this.reconnectInterval / 1000}秒後に再接続を試みます`); + this.reconnectTimer = setTimeout(() => { + this.reconnectTimer = null; + this.connect(); + }, this.reconnectInterval); + } + + private setupCleanup(): void { + const cleanup = () => { + this.stop(); + process.exit(0); + }; + + process.on("SIGINT", cleanup); + process.on("SIGTERM", cleanup); + } + + public stop(): void { + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = null; + } + + if (this.ws) { + this.ws.close(); + this.ws = null; + } + } +} + +// 地名オブジェクトマッピング +async function areaMap(): Promise> { + const res = await fetch( + "https://raw.githubusercontent.com/p2pquake/epsp-specifications/master/epsp-area.csv", + ); + + const text = await res.text(); + + const lines = text.split("\n"); + const map: Record = {}; + + for (const line of lines) { + const cols = line.split(","); + + if (cols.length >= 3 && /^\d+$/.test(cols[0])) { + const id = Number(cols[0]); + const name = cols[2].trim(); + + map[id] = name; + } + } + + return map; +} + +// 地震発生 +async function event(earthquakeInfo: any): Promise { + console.log(JSON.stringify(earthquakeInfo)); + + // 緊急地震速報の場合 + if (earthquakeInfo.code === 554) { + ueuse(` + ==地震情報== + 【緊急地震速報】 + 時刻:${earthquakeInfo.time} + `); + } + + // 地震情報 + else if (earthquakeInfo.code === 551) { + let domesticTsunami; + + if (earthquakeInfo.earthquake.domesticTsunami === "None") { + domesticTsunami = "この地震による国内の津波の心配はありません"; + } else if (earthquakeInfo.earthquake.domesticTsunami === "Unknown") { + domesticTsunami = "この地震による国内の津波情報はありません"; + } else if (earthquakeInfo.earthquake.domesticTsunami === "Checking") { + domesticTsunami = "この地震による国内の津波情報を調査中です"; + } else if (earthquakeInfo.earthquake.domesticTsunami === "NonEffective") { + domesticTsunami = + "この地震による国内の津波影響は若干の海面変動が予想されますが被害の心配はありません"; + } else if (earthquakeInfo.earthquake.domesticTsunami === "Watch") { + domesticTsunami = "この地震により国内で津波注意報が発令しています"; + } else if (earthquakeInfo.earthquake.domesticTsunami === "Warning") { + domesticTsunami = "この地震による国内の津波予報があります"; + } else { + domesticTsunami = "この地震による国内の津波情報はありません"; + } + + let maxScale; + + if (earthquakeInfo.earthquake.maxScale === -1) { + maxScale = "最大震度情報なし"; + } else if (earthquakeInfo.earthquake.maxScale === 10) { + maxScale = "震度1"; + } else if (earthquakeInfo.earthquake.maxScale === 20) { + maxScale = "震度2"; + } else if (earthquakeInfo.earthquake.maxScale === 30) { + maxScale = "震度3"; + } else if (earthquakeInfo.earthquake.maxScale === 40) { + maxScale = "震度4"; + } else if (earthquakeInfo.earthquake.maxScale === 45) { + maxScale = "震度5弱"; + } else if (earthquakeInfo.earthquake.maxScale === 50) { + maxScale = "震度5強"; + } else if (earthquakeInfo.earthquake.maxScale === 55) { + maxScale = "震度6弱"; + } else if (earthquakeInfo.earthquake.maxScale === 60) { + maxScale = "震度6強"; + } else if (earthquakeInfo.earthquake.maxScale === 70) { + maxScale = "震度7"; + } + + ueuse(` + ==地震情報== + 【地域情報更新】 + 時刻:${earthquakeInfo.time} + 最大深度:${maxScale} + 国内の津波:${domesticTsunami} + `); + } + + // 地域情報更新の場合 + else if (earthquakeInfo.code === 555) { + // 対象地域マッピング + const areaMaps: any = await areaMap(); + + const areaNames: Array = Array.from( + new Set( + earthquakeInfo.areas.map((a: any) => { + areaMaps[a.id].filter(Boolean); + }), + ), + ); + + const result = areaNames.join("・"); + + ueuse(` + ==地震情報== + 【地域情報更新】 + 時刻:${earthquakeInfo.time} + 対象地域:${result} + `); + } +} + +async function ueuse(text: string) { + const res = await fetch(`https://${process.env.SERVER}/api/ueuse/create`, { + method: "POST", + body: JSON.stringify({ + token: process.env.TOKEN, + text: text, + }), + }); + + const resData = await res.json(); + + console.log(JSON.stringify(resData)); +} + +export default function earthquake(): void { + console.log("地震情報サーバーに接続します"); + const client = new P2PEarthquakeClient(); + client.start(); +} diff --git a/tsconfig.json b/tsconfig.json index 8965340..8eb024e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,113 +1,22 @@ { "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "libReplacement": true, /* Enable lib replacement. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "ES2022" /* Specify what module code is generated. */, - // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist" /* Specify an output folder for all emitted files. */, - // "removeComments": true, /* Disable emitting comments. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ - // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - - /* Type Checking */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "target": "es2022", + "module": "ES2022", + "moduleResolution": "bundler", + "outDir": "./dist", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "baseUrl": "./", + "typeRoots": [ + "./node_modules/@types", + "./types" + ], + "paths": { + "ws": ["./node_modules/ws/index.js"], + "@types/ws": ["./node_modules/@types/ws/index.d.ts"] + } } } diff --git a/types/ws.d.ts b/types/ws.d.ts new file mode 100644 index 0000000..d5e8776 --- /dev/null +++ b/types/ws.d.ts @@ -0,0 +1,68 @@ +declare module 'ws' { + import { EventEmitter } from 'events'; + import { IncomingMessage } from 'http'; + import { Socket } from 'net'; + + export type Data = string | Buffer | ArrayBuffer | Buffer[]; + + export interface WebSocketEventMap { + close: CloseEvent; + error: ErrorEvent; + message: MessageEvent; + open: Event; + } + + export default class WebSocket extends EventEmitter { + static readonly CONNECTING: 0; + static readonly OPEN: 1; + static readonly CLOSING: 2; + static readonly CLOSED: 3; + + readonly CONNECTING: 0; + readonly OPEN: 1; + readonly CLOSING: 2; + readonly CLOSED: 3; + + readonly readyState: 0 | 1 | 2 | 3; + readonly url: string; + readonly protocol: string; + + constructor(address: string | URL, protocols?: string | string[]); + + close(code?: number, reason?: string): void; + send(data: Data): void; + ping(data?: Data): void; + pong(data?: Data): void; + terminate(): void; + + on(event: 'close', listener: (code: number, reason: Buffer) => void): this; + on(event: 'error', listener: (error: Error) => void): this; + on(event: 'message', listener: (data: Data) => void): this; + on(event: 'open', listener: () => void): this; + on(event: string | symbol, listener: (...args: any[]) => void): this; + + addEventListener(type: 'close', listener: (event: CloseEvent) => void): void; + addEventListener(type: 'error', listener: (event: ErrorEvent) => void): void; + addEventListener(type: 'message', listener: (event: MessageEvent) => void): void; + addEventListener(type: 'open', listener: (event: Event) => void): void; + } + + export interface CloseEvent { + code: number; + reason: string; + wasClean: boolean; + } + + export interface ErrorEvent { + error: Error; + message: string; + type: string; + } + + export namespace WebSocket { + export const CONNECTING: 0; + export const OPEN: 1; + export const CLOSING: 2; + export const CLOSED: 3; + } +}