Compare commits

...

2 Commits

14 changed files with 176 additions and 299 deletions

View File

@ -1,2 +0,0 @@
TOKEN=
SERVER=

6
.gitignore vendored
View File

@ -1,5 +1,7 @@
/dist/ /dist/
/.env /.env*
/.env.local
/node_modules/ /node_modules/
/package-lock.json /package-lock.json
/config.ts
log*

13
LICENSE Normal file
View File

@ -0,0 +1,13 @@
Copyright 2025 Last2014
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -4,9 +4,18 @@
uwuzuで動作するお知らせBOTです。 uwuzuで動作するお知らせBOTです。
# .env形式 # 設定
examples/config.tsをプロジェクトルートへ移動し各設定を更新してください。
`.env.example`をご覧ください。 ## 設定項目
earthquake.reconnectTimes地震情報のWebSocketが切断されたときに自動再接続する時間(ミリ秒)
earthquake.websocketUrl地震情報のWebSocket接続先URL
earthquake.areasCsvUrl地域情報のデータベース(CSV)ファイルのURL
weather.splitCount天気お知らせの返信の分割数(4分割を推奨)
apiTokenBOTアカウントのAPIキー
uwuzuServer使用するuwuzuサーバーのホスト名(uwuzu.netなど)
# サーバー起動 # サーバー起動
@ -17,3 +26,6 @@ npm run start
``` ```
※Node.js・npmがインストールされている必要があります。 ※Node.js・npmがインストールされている必要があります。
# ライセンス
Apache 2.0 License

17
examples/config.ts Normal file
View File

@ -0,0 +1,17 @@
import type { configTypes } from "types/config";
const config: configTypes = {
earthquake: {
reconnectTimes: 5000,
websocketUrl: "wss://api.p2pquake.net/v2/ws",
areasCsvUrl: "https://raw.githubusercontent.com/p2pquake/epsp-specifications/master/epsp-area.csv",
},
weather: {
splitCount: 4,
},
apiToken: "TOKEN_EXAMPLE",
uwuzuServer: "uwuzu.example.com",
};
export default config;

View File

@ -1,166 +0,0 @@
地震情報サーバーに接続します
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]
📊 震度情報が更新されました

View File

@ -4,7 +4,7 @@ import * as cron from "node-cron";
// 機能読み込み // 機能読み込み
import timeNotice from "./scripts/timeNotice.js"; import timeNotice from "./scripts/timeNotice.js";
import weatherNotice from "./scripts/weatherNotice.js"; import weatherNotice from "./scripts/weatherNotice.js";
import earthquakeNotice from "./scripts/earthquakeNotice.js" import earthquakeNotice from "./scripts/earthquakeNotice.js";
// フォローバック機能読み込み // フォローバック機能読み込み
import followBack from "./scripts/followBack.js"; import followBack from "./scripts/followBack.js";
@ -22,7 +22,7 @@ cron.schedule("0 * * * *", () => {
cron.schedule("1 7 * * *", () => { cron.schedule("1 7 * * *", () => {
setTimeout(() => { setTimeout(() => {
weatherNotice(); weatherNotice();
}, 100) }, 100);
}); });
// コンソールで表示 // コンソールで表示

View File

@ -1,6 +1,6 @@
{ {
"name": "noticeuwuzu", "name": "noticeuwuzu",
"version": "v3.5@uwuzu1.5.4", "version": "v3.5.1@uwuzu1.5.4",
"description": "uwuzu Notice Bot", "description": "uwuzu Notice Bot",
"main": "dist/main.js", "main": "dist/main.js",
"scripts": { "scripts": {
@ -28,7 +28,6 @@
"@types/dotenv": "^6.1.1", "@types/dotenv": "^6.1.1",
"@types/node-cron": "^3.0.11", "@types/node-cron": "^3.0.11",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"dotenv": "^17.0.0",
"node-cron": "^4.1.1", "node-cron": "^4.1.1",
"tsx": "^4.20.3", "tsx": "^4.20.3",
"typescript": "^5.8.3", "typescript": "^5.8.3",

View File

@ -1,11 +1,10 @@
import WebSocket from "ws"; import WebSocket from "ws";
import * as dotenv from "dotenv";
dotenv.config(); import config from "../config.js";
class P2PEarthquakeClient { class P2PEarthquakeClient {
private ws: WebSocket | null = null; private ws: WebSocket | null = null;
private reconnectInterval: number = 5000; private reconnectInterval: number = config.earthquake.reconnectTimes;
private reconnectTimer: NodeJS.Timeout | null = null; private reconnectTimer: NodeJS.Timeout | null = null;
private isConnecting: boolean = false; private isConnecting: boolean = false;
@ -21,7 +20,7 @@ class P2PEarthquakeClient {
console.log("P2P地震情報に接続中"); console.log("P2P地震情報に接続中");
try { try {
this.ws = new WebSocket("wss://api.p2pquake.net/v2/ws"); this.ws = new WebSocket(config.earthquake.websocketUrl);
this.ws.on("open", () => { this.ws.on("open", () => {
console.log("P2P地震情報に接続しました"); console.log("P2P地震情報に接続しました");
@ -119,9 +118,7 @@ class P2PEarthquakeClient {
// 地名オブジェクトマッピング // 地名オブジェクトマッピング
async function areaMap(): Promise<Record<number, string>> { async function areaMap(): Promise<Record<number, string>> {
const res = await fetch( const res = await fetch(config.earthquake.areasCsvUrl);
"https://raw.githubusercontent.com/p2pquake/epsp-specifications/master/epsp-area.csv",
);
const text = await res.text(); const text = await res.text();
@ -244,42 +241,39 @@ async function event(earthquakeInfo: any): Promise<void> {
} }
// 最大震度 // 最大震度
let maxScale; let maxScale: string = "最大深度:";
if (earthquakeInfo.earthquake.maxScale === -1) { if (
earthquakeInfo.earthquake.maxScale === -1 ||
earthquakeInfo.earthquake.maxScale === null
) {
maxScale = "最大震度情報なし"; maxScale = "最大震度情報なし";
} else if (earthquakeInfo.earthquake.maxScale === 10) { } else if (earthquakeInfo.earthquake.maxScale === 10) {
maxScale = "震度1"; maxScale += "震度1";
} else if (earthquakeInfo.earthquake.maxScale === 20) { } else if (earthquakeInfo.earthquake.maxScale === 20) {
maxScale = "震度2"; maxScale += "震度2";
} else if (earthquakeInfo.earthquake.maxScale === 30) { } else if (earthquakeInfo.earthquake.maxScale === 30) {
maxScale = "震度3"; maxScale += "震度3";
} else if (earthquakeInfo.earthquake.maxScale === 40) { } else if (earthquakeInfo.earthquake.maxScale === 40) {
maxScale = "震度4"; maxScale += "震度4";
} else if (earthquakeInfo.earthquake.maxScale === 45) { } else if (earthquakeInfo.earthquake.maxScale === 45) {
maxScale = "震度5弱"; maxScale += "震度5弱";
} else if (earthquakeInfo.earthquake.maxScale === 50) { } else if (earthquakeInfo.earthquake.maxScale === 50) {
maxScale = "震度5強"; maxScale += "震度5強";
} else if (earthquakeInfo.earthquake.maxScale === 55) { } else if (earthquakeInfo.earthquake.maxScale === 55) {
maxScale = "震度6弱"; maxScale += "震度6弱";
} else if (earthquakeInfo.earthquake.maxScale === 60) { } else if (earthquakeInfo.earthquake.maxScale === 60) {
maxScale = "震度6強"; maxScale += "震度6強";
} else if (earthquakeInfo.earthquake.maxScale === 70) { } else if (earthquakeInfo.earthquake.maxScale === 70) {
maxScale = "震度7"; maxScale += "震度7";
} }
// 対象地域 // 対象地域
let areas: string = ""; let areas: string = "";
if (earthquakeInfo.points !== null) { if (earthquakeInfo.points !== null) {
const areaMaps: any = await areaMap();
const areaNames: Array<string> = Array.from( const areaNames: Array<string> = Array.from(
new Set( new Set(earthquakeInfo.points.map((i: any) => i.addr).filter(Boolean)),
earthquakeInfo.points.map((i: any) => {
areaMaps[i.addr].filter(Boolean);
}),
),
); );
areas = `対象地域:${areaNames.join("・")}`; areas = `対象地域:${areaNames.join("・")}`;
@ -297,7 +291,7 @@ async function event(earthquakeInfo: any): Promise<void> {
${earthquakeInfo.time} ${earthquakeInfo.time}
${description} ${description}
${maxScale} ${maxScale}
${areas} ${areas}
${domesticTsunami} ${domesticTsunami}
`); `);
@ -328,10 +322,10 @@ async function event(earthquakeInfo: any): Promise<void> {
} }
async function ueuse(text: string) { async function ueuse(text: string) {
const res = await fetch(`https://${process.env.SERVER}/api/ueuse/create`, { const res = await fetch(`https://${config.uwuzuServer}/api/ueuse/create`, {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: process.env.TOKEN, token: config.apiToken,
text: text, text: text,
}), }),
}); });

View File

@ -1,13 +1,11 @@
import * as dotenv from "dotenv"; import type * as types from "types/types";
import type * as types from "../types"; import config from "../config.js";
dotenv.config();
export default async function followBack() { export default async function followBack() {
// フォロワーを取得 // フォロワーを取得
const resMe = await fetch( const resMe = await fetch(
`https://${process.env.SERVER}/api/me?token=${process.env.TOKEN}`, `https://${config.uwuzuServer}/api/me?token=${config.apiToken}`,
{ {
method: "GET", method: "GET",
// uwuzu.netで/api/meのPOSTが死んでいるため簡易的にGET // uwuzu.netで/api/meのPOSTが死んでいるため簡易的にGET
@ -25,11 +23,11 @@ export default async function followBack() {
const followerItem = followers[i]; const followerItem = followers[i];
const resFollow = await fetch( const resFollow = await fetch(
`https://${process.env.SERVER}/api/users/follow`, `https://${config.uwuzuServer}/api/users/follow`,
{ {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: process.env.TOKEN, token: config.apiToken,
userid: followerItem, userid: followerItem,
}), }),
}, },

View File

@ -1,9 +1,8 @@
import * as dotenv from "dotenv";
import { format } from "date-fns"; import { format } from "date-fns";
import type * as types from "../types"; import type * as types from "types/types";
dotenv.config(); import config from "../config.js";
export default async function timeNotice() { export default async function timeNotice() {
// 現在時刻を取得 // 現在時刻を取得
@ -11,11 +10,11 @@ export default async function timeNotice() {
// 投稿 // 投稿
const resUeuse = await fetch( const resUeuse = await fetch(
`https://${process.env.SERVER}/api/ueuse/create`, `https://${config.uwuzuServer}/api/ueuse/create`,
{ {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: process.env.TOKEN, token: config.apiToken,
text: `${now}になりました`, text: `${now}になりました`,
}), }),
}, },

View File

@ -1,17 +1,17 @@
import * as dotenv from "dotenv";
import { cityList } from "../src/weatherId.js"; import { cityList } from "../src/weatherId.js";
import type * as types from "../types";
dotenv.config(); import type * as types from "types/types.js";
import config from "../config.js";
export default async function weatherNotice() { export default async function weatherNotice() {
// 仮投稿 // 仮投稿
const resUeuse = await fetch( const resUeuse = await fetch(
`https://${process.env.SERVER}/api/ueuse/create`, `https://${config.uwuzuServer}/api/ueuse/create`,
{ {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: process.env.TOKEN, token: config.apiToken,
text: ` text: `
@ -24,102 +24,96 @@ export default async function weatherNotice() {
console.log(JSON.stringify(ueuseData)); console.log(JSON.stringify(ueuseData));
// 3分割処理
// インデックス
const splitCount = config.weather.splitCount;
const total = cityList.length; const total = cityList.length;
const firstLength = Math.trunc(total / 3); const chunkSizes = Array(splitCount).fill(0).map((_, i) =>
const secondLength = Math.trunc((total - firstLength) / 2); Math.floor((total + i) / splitCount)
const thirdLength = total - firstLength - secondLength; );
// インデックス作成 // 分割インデックス
const firstStart = 0; let start = 0;
const firstEnd = firstStart + firstLength; const ranges = chunkSizes.map(size => {
const secondStart = firstEnd; const range = [start, start + size];
const secondEnd = secondStart + secondLength; start += size;
const thirdStart = secondEnd; return range;
const thirdEnd = total; });
let weatherResults = ["", "", ""]; // 配列作成
const weatherResults = Array(splitCount).fill("");
// 投稿1 // 天気取得
for (let i = firstStart; i < firstEnd; i++) { for (let chunkIndex = 0; chunkIndex < splitCount; chunkIndex++) {
const [chunkStart, chunkEnd] = ranges[chunkIndex];
for (let i = chunkStart; i < chunkEnd; i++) {
const res = await fetch( const res = await fetch(
`https://weather.tsukumijima.net/api/forecast/city/${cityList[i]}`, `https://weather.tsukumijima.net/api/forecast/city/${cityList[i]}`,
); );
const data = await res.json(); const data = await res.json();
const today = data.forecasts[0]; const today = data.forecasts[0];
// 天気
const weather = today.telop ?? "取得できませんでした"; const weather = today.telop ?? "取得できませんでした";
const maxTemp = today.temperature.max?.celsius ?? "取得できませんでした";
const minTemp = today.temperature.min?.celsius ?? "取得できませんでした";
const chanceOfRain = data.chanceOfRain?.["T06_12"] ?? "取得できませんでした";
weatherResults[0] += ` // 最高気温
${data.location.city}\n let maxTemp: string;
${weather}\n
${maxTemp}\n if (today.temperature.max.celsius !== null) {
${minTemp}\n maxTemp = `${today.temperature.max.celsius}`;
${chanceOfRain}\n } else {
`; maxTemp = "取得できませんでした";
} }
// 投稿2 // 最低気温
for (let i = secondStart; i < secondEnd; i++) { let minTemp: string;
const res = await fetch(
`https://weather.tsukumijima.net/api/forecast/city/${cityList[i]}`,
);
const data = await res.json();
const today = data.forecasts[0];
const weather = today.telop ?? "取得できませんでした"; if (today.temperature.min.celsius !== null) {
const maxTemp = today.temperature.max?.celsius ?? "取得できませんでした"; minTemp = `${today.temperature.min.celsius}`;
const minTemp = today.temperature.min?.celsius ?? "取得できませんでした"; } else {
const chanceOfRain = data.chanceOfRain?.["T06_12"] ?? "取得できませんでした"; minTemp = "取得できませんでした";
weatherResults[1] += `
${data.location.city}\n
${weather}\n
${maxTemp}\n
${minTemp}\n
${chanceOfRain}\n
`;
} }
// 投稿3 // 降水確率
for (let i = thirdStart; i < thirdEnd; i++) { let chanceOfRain: string;
const res = await fetch(
`https://weather.tsukumijima.net/api/forecast/city/${cityList[i]}`,
);
const data = await res.json();
const today = data.forecasts[0];
const weather = today.telop ?? "取得できませんでした"; if (
const maxTemp = today.temperature.max?.celsius ?? "取得できませんでした"; today.chanceOfRain.T06_12 !== null ||
const minTemp = today.temperature.min?.celsius ?? "取得できませんでした"; today.chanceOfRain.T06_12 !== "--%"
const chanceOfRain = data.chanceOfRain?.["T06_12"] ?? "取得できませんでした"; ) {
chanceOfRain = `${today.chanceOfRain.T06_12}%`;
weatherResults[2] += ` } else {
${data.location.city}\n chanceOfRain = "取得できませんでした";
${weather}\n
${maxTemp}\n
${minTemp}\n
${chanceOfRain}\n
`;
} }
// 3分割投稿 weatherResults[chunkIndex] += `
for (let i = 0; i < 3; i++) { ${data.location.city}
${weather}
${maxTemp}
${minTemp}
${chanceOfRain}
`;
}
}
// 分割投稿
for (let i = 0; i < splitCount; i++) {
const resReply = await fetch( const resReply = await fetch(
`https://${process.env.SERVER}/api/ueuse/create`, `https://${config.uwuzuServer}/api/ueuse/create`,
{ {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: process.env.TOKEN, token: config.apiToken,
text: weatherResults[i], text: weatherResults[i],
replyid: ueuseData.uniqid replyid: ueuseData.uniqid
}), }),
}, },
); );
const replyData: types.ueuseCreateApi = await resReply.json(); const replyData: types.ueuseCreateApi = await resReply.json();
console.log(JSON.stringify(replyData)); console.log(JSON.stringify(replyData));
} }
} }

17
types/config.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
interface earthquakeTypes {
reconnectTimes: number;
websocketUrl: string;
areasCsvUrl: string;
}
interface weatherTypes {
splitCount: number;
}
export interface configTypes {
earthquake: earthquakeTypes;
weather: weatherTypes;
apiToken: string;
uwuzuServer: string;
}

View File