Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c3ab3a8456 | |||
| 678f4aa1b1 | |||
| a96c3f0e4f | |||
| 53ba26f417 | |||
| 6b68b6e1b5 | |||
| e3451323f3 | |||
| 8be3684a78 | |||
| 600626d071 | |||
| 5c0c34bbb4 | |||
| 88bd2e88a5 | |||
| 7c7dfdcf3c | |||
| dd70fe5494 | |||
| 73091bb92f | |||
| 6c69ce80ef | |||
| 7e3c4840d3 | |||
| 007efd85a8 | |||
| 2689dfd777 | |||
| 909258f421 | |||
| de3c970712 | |||
| c5bd22600a | |||
| c4d2990118 |
@@ -1,2 +0,0 @@
|
|||||||
TOKEN=
|
|
||||||
SERVER=
|
|
||||||
+4
-2
@@ -1,5 +1,7 @@
|
|||||||
/dist/
|
/dist/
|
||||||
/.env
|
/.env*
|
||||||
/.env.local
|
|
||||||
/node_modules/
|
/node_modules/
|
||||||
/package-lock.json
|
/package-lock.json
|
||||||
|
/config.ts
|
||||||
|
|
||||||
|
log*
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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分割を推奨)
|
||||||
|
|
||||||
|
apiToken:BOTアカウントのAPIキー
|
||||||
|
uwuzuServer:使用するuwuzuサーバーのホスト名(uwuzu.netなど)
|
||||||
|
|
||||||
# サーバー起動
|
# サーバー起動
|
||||||
|
|
||||||
@@ -17,3 +26,6 @@ npm run start
|
|||||||
```
|
```
|
||||||
|
|
||||||
※Node.js・npmがインストールされている必要があります。
|
※Node.js・npmがインストールされている必要があります。
|
||||||
|
|
||||||
|
# ライセンス
|
||||||
|
Apache 2.0 License
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import type { configTypes } from "types/config";
|
||||||
|
|
||||||
|
// READMEの設定項目を参照
|
||||||
|
const config: configTypes = {
|
||||||
|
// 時報設定
|
||||||
|
time: {
|
||||||
|
// 時報休止期間
|
||||||
|
stopTimes: {
|
||||||
|
start: 23, // 開始
|
||||||
|
stop: 6, // 停止
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 地震速報設定
|
||||||
|
earthquake: {
|
||||||
|
reconnectTimes: 5000, // 再接続時間(ミリ秒)
|
||||||
|
websocketUrl: "wss://api.p2pquake.net/v2/ws", // WebSocketのURL
|
||||||
|
areasCsvUrl: "https://raw.githubusercontent.com/p2pquake/epsp-specifications/master/epsp-area.csv", // 対象地域CSVファイルのURL
|
||||||
|
maxScaleMin: 30, // 地震発生の際の最低震度(10-70)
|
||||||
|
rateLimit: 30, // 地域情報更新のレート制限(分)
|
||||||
|
},
|
||||||
|
weather: {
|
||||||
|
splitCount: 4, // 返信の分割数
|
||||||
|
},
|
||||||
|
|
||||||
|
apiToken: "TOKEN_EXAMPLE", // BOTアカウントのAPIトークン
|
||||||
|
uwuzuServer: "uwuzu.example.com", // uwuzuのサーバー
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@@ -1,19 +1,29 @@
|
|||||||
|
// 定期実行読み込み
|
||||||
import * as cron from "node-cron";
|
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 followBack from "./scripts/followBack.js";
|
import followBack from "./scripts/followBack.js";
|
||||||
|
|
||||||
|
// 地震情報観測開始
|
||||||
|
earthquakeNotice();
|
||||||
|
|
||||||
// 時報・フォローバック(毎時)
|
// 時報・フォローバック(毎時)
|
||||||
cron.schedule("0 * * * *", () => {
|
cron.schedule("0 * * * *", () => {
|
||||||
timeNotice();
|
timeNotice();
|
||||||
followBack();
|
followBack();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 天気お知らせ(毎日7:00)
|
// 天気お知らせ(毎日7:01)
|
||||||
cron.schedule("0 7 * * *", () => {
|
cron.schedule("1 7 * * *", () => {
|
||||||
weatherNotice();
|
setTimeout(() => {
|
||||||
|
weatherNotice();
|
||||||
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("サーバーが起動しました");
|
// コンソールで表示
|
||||||
|
console.log("BOTサーバーが起動しました");
|
||||||
|
|||||||
+11
-6
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "noticeuwuzu",
|
"name": "noticeuwuzu",
|
||||||
"version": "v2.0@uwuzu1.5.4",
|
"version": "v4.2@uwuzu1.5.4",
|
||||||
"description": "uwuzu Notice Bot",
|
"description": "uwuzu Notice Bot",
|
||||||
"main": "dist/main.js",
|
"main": "dist/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -12,25 +12,30 @@
|
|||||||
"uwuzu",
|
"uwuzu",
|
||||||
"bot",
|
"bot",
|
||||||
"cron",
|
"cron",
|
||||||
|
"notice",
|
||||||
|
"weather",
|
||||||
"time",
|
"time",
|
||||||
"timenotice",
|
"earthquake"
|
||||||
"notice"
|
|
||||||
],
|
],
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Last2014",
|
"name": "Last2014",
|
||||||
"url": "https://last2014.com",
|
"url": "https://last2014.com",
|
||||||
"email": "info@last2014.com"
|
"email": "info@last2014.com"
|
||||||
},
|
},
|
||||||
"license": "ISC",
|
"license": "Apache-2.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/date-fns": "^2.5.3",
|
"@types/date-fns": "^2.5.3",
|
||||||
"@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",
|
||||||
|
"ws": "^8.18.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.0.7",
|
||||||
|
"@types/ws": "^8.18.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,386 @@
|
|||||||
|
import WebSocket from "ws";
|
||||||
|
import { differenceInMinutes, subMinutes } from "date-fns";
|
||||||
|
|
||||||
|
import config from "../config.js";
|
||||||
|
|
||||||
|
let rateLimit: Date | null = null;
|
||||||
|
|
||||||
|
class P2PEarthquakeClient {
|
||||||
|
private ws: WebSocket | null = null;
|
||||||
|
private reconnectInterval: number = config.earthquake.reconnectTimes;
|
||||||
|
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(config.earthquake.websocketUrl);
|
||||||
|
|
||||||
|
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 {
|
||||||
|
console.log("----------------");
|
||||||
|
|
||||||
|
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<Record<number, string>> {
|
||||||
|
const res = await fetch(config.earthquake.areasCsvUrl);
|
||||||
|
|
||||||
|
const text = await res.text();
|
||||||
|
|
||||||
|
const lines = text.split("\n");
|
||||||
|
const map: Record<number, string> = {};
|
||||||
|
|
||||||
|
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<void> {
|
||||||
|
console.log(`受信データ:${JSON.stringify(earthquakeInfo)}`);
|
||||||
|
|
||||||
|
// ----処理----
|
||||||
|
|
||||||
|
// 緊急地震速報の場合
|
||||||
|
if (earthquakeInfo.code === 554) {
|
||||||
|
// 地震詳細
|
||||||
|
let descriptionEarthquake: string = "";
|
||||||
|
|
||||||
|
if (earthquakeInfo.earthquake.description !== "") {
|
||||||
|
descriptionEarthquake = `この地震について:${earthquakeInfo.earthquake.description}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 発令詳細
|
||||||
|
let description: string = "";
|
||||||
|
|
||||||
|
if (earthquakeInfo.comments.freeFormComment !== "") {
|
||||||
|
description = `この発令について:${earthquakeInfo.comments.freeFormComment}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// テスト・訓練
|
||||||
|
let test: string = "";
|
||||||
|
|
||||||
|
if (earthquakeInfo.test) {
|
||||||
|
test = "これはテスト、あるいは訓練です";
|
||||||
|
} else if (earthquakeInfo.test === false) {
|
||||||
|
test = "これはテスト・訓練ではありません";
|
||||||
|
} else {
|
||||||
|
test = "この情報にテスト・訓練かの情報はありません";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 対象地域
|
||||||
|
let areas: string = "";
|
||||||
|
|
||||||
|
if (earthquakeInfo.areas !== null) {
|
||||||
|
const areaNames: Array<string> = Array.from(
|
||||||
|
new Set(
|
||||||
|
earthquakeInfo.areas.map((point: any) => point.name).filter(Boolean),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
areas = `対象地域:${areaNames.join("・")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 速報取り消し
|
||||||
|
let cancelled: string = "";
|
||||||
|
|
||||||
|
if (earthquakeInfo.cancelled) {
|
||||||
|
cancelled = "※以下の緊急地震速報が取り消されました※";
|
||||||
|
}
|
||||||
|
|
||||||
|
// マグニチュード
|
||||||
|
let magnitude: string = "マグニチュード:";
|
||||||
|
|
||||||
|
if (
|
||||||
|
earthquakeInfo.earthquake.hypocenter.magnitude !== -1 ||
|
||||||
|
earthquakeInfo.earthquake.hypocenter.magnitude === null
|
||||||
|
) {
|
||||||
|
magnitude += "マグニチュードの情報はありません";
|
||||||
|
} else {
|
||||||
|
magnitude += `M${String(earthquakeInfo.earthquake.hypocenter.magnitude)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
ueuse(`
|
||||||
|
==地震情報==
|
||||||
|
【緊急地震速報】
|
||||||
|
${cancelled}
|
||||||
|
時刻:${earthquakeInfo.time}
|
||||||
|
${descriptionEarthquake}
|
||||||
|
${description}
|
||||||
|
${test}
|
||||||
|
${areas}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 地震情報
|
||||||
|
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: string = "最大深度:";
|
||||||
|
|
||||||
|
if (earthquakeInfo.earthquake.maxScale < config.earthquake.maxScaleMin) {
|
||||||
|
console.log("最低震度に満たしていないため投稿されませんでした");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
earthquakeInfo.earthquake.maxScale === -1 ||
|
||||||
|
earthquakeInfo.earthquake.maxScale === null
|
||||||
|
) {
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 対象地域
|
||||||
|
let areas: string = "";
|
||||||
|
|
||||||
|
if (earthquakeInfo.points !== null) {
|
||||||
|
const areaNames: Array<string> = Array.from(
|
||||||
|
new Set(
|
||||||
|
earthquakeInfo.points.map((point: any) => point.addr).filter(Boolean),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
areas = `対象地域:${areaNames.join("・")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 詳細
|
||||||
|
let description: string = "";
|
||||||
|
|
||||||
|
if (earthquakeInfo.comments.freeFormComment !== "") {
|
||||||
|
description = `この地震について:${earthquakeInfo.comments.freeFormComment}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 深さ
|
||||||
|
let depth: string = "";
|
||||||
|
|
||||||
|
if (
|
||||||
|
earthquakeInfo.earthquake.hypocenter.depth !== null ||
|
||||||
|
earthquakeInfo.earthquake.hypocenter.depth !== -1
|
||||||
|
) {
|
||||||
|
if (earthquakeInfo.earthquake.hypocenter.depth === 0) {
|
||||||
|
depth = "深さ:ごく浅い";
|
||||||
|
} else {
|
||||||
|
depth = `深さ:${earthquakeInfo.hypocenter.depth}km`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// マグニチュード
|
||||||
|
let magnitude: string = "";
|
||||||
|
|
||||||
|
if(
|
||||||
|
earthquakeInfo.earthquake.hypocenter.magnitude !== null ||
|
||||||
|
earthquakeInfo.earthquake.hypocenter.magnitude !== -1
|
||||||
|
) {
|
||||||
|
magnitude = `マグニチュード:${earthquakeInfo.earthquake.hypocenter.magnitude}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
ueuse(`
|
||||||
|
==地震情報==
|
||||||
|
【地震発生】
|
||||||
|
時刻:${earthquakeInfo.time}
|
||||||
|
${description}
|
||||||
|
${magnitude}
|
||||||
|
${depth}
|
||||||
|
${maxScale}
|
||||||
|
${areas}
|
||||||
|
国内の津波:${domesticTsunami}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 地域情報更新の場合
|
||||||
|
else if (earthquakeInfo.code === 555) {
|
||||||
|
if (rateLimit === null) {
|
||||||
|
rateLimit = subMinutes(new Date(), 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 対象地域マッピング
|
||||||
|
const areaMaps: any = await areaMap();
|
||||||
|
|
||||||
|
const areaNames: Array<string> = Array.from(
|
||||||
|
new Set(
|
||||||
|
earthquakeInfo.areas
|
||||||
|
.map((i: any) => {
|
||||||
|
return areaMaps[i.id];
|
||||||
|
})
|
||||||
|
.filter(Boolean),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const areas = areaNames.join("・");
|
||||||
|
|
||||||
|
if (Math.abs(differenceInMinutes(rateLimit, new Date())) >= config.earthquake.rateLimit) {
|
||||||
|
ueuse(`
|
||||||
|
==地震情報==
|
||||||
|
【地域情報更新】
|
||||||
|
時刻:${earthquakeInfo.time}
|
||||||
|
対象地域:${areas}
|
||||||
|
`);
|
||||||
|
|
||||||
|
rateLimit = new Date();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ueuse(text: string) {
|
||||||
|
const res = await fetch(`https://${config.uwuzuServer}/api/ueuse/create`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
token: config.apiToken,
|
||||||
|
text: text,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const resData = await res.json();
|
||||||
|
|
||||||
|
console.log(JSON.stringify(resData));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function earthquakeNotice(): void {
|
||||||
|
console.log("地震情報サーバーに接続します");
|
||||||
|
const client = new P2PEarthquakeClient();
|
||||||
|
client.start();
|
||||||
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
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() {
|
||||||
// uwuzu.netで/api/meのPOSTが死んでいるため簡易的にGET
|
// フォロワーを取得
|
||||||
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
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -19,15 +18,16 @@ export default async function followBack() {
|
|||||||
|
|
||||||
const followers: Array<string> = meData.follower;
|
const followers: Array<string> = meData.follower;
|
||||||
|
|
||||||
|
// フォロー
|
||||||
for (let i = 0; i < followers.length; i++) {
|
for (let i = 0; i < followers.length; i++) {
|
||||||
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,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|||||||
+35
-17
@@ -1,25 +1,43 @@
|
|||||||
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";
|
||||||
|
import config from "../config.js";
|
||||||
|
|
||||||
|
|
||||||
|
// 停止時間
|
||||||
|
// 時刻取得
|
||||||
|
const start = config.time.stopTimes.start;
|
||||||
|
const stop = config.time.stopTimes.stop;
|
||||||
|
|
||||||
|
// 現在の時間を取得
|
||||||
|
const nowHour = new Date().getHours();
|
||||||
|
|
||||||
|
// 停止時刻内かどうかの判定
|
||||||
|
let inRange: boolean = false;
|
||||||
|
|
||||||
|
if (start < stop) {
|
||||||
|
inRange = nowHour >= start && nowHour < stop;
|
||||||
|
} else {
|
||||||
|
inRange = nowHour >= start || nowHour < stop;
|
||||||
|
}
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
export default async function timeNotice() {
|
export default async function timeNotice() {
|
||||||
const now = format(new Date(), "HH:mm");
|
if (inRange === false) {
|
||||||
|
// 投稿
|
||||||
|
const resUeuse = await fetch(
|
||||||
|
`https://${config.uwuzuServer}/api/ueuse/create`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
token: config.apiToken,
|
||||||
|
text: `${format(new Date(), "HH:mm")}になりました`,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const resUeuse = await fetch(
|
const ueuseData: types.ueuseCreateApi = await resUeuse.json();
|
||||||
`https://${process.env.SERVER}/api/ueuse/create`,
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({
|
|
||||||
token: process.env.TOKEN,
|
|
||||||
text: `${now}になりました`,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const ueuseData: types.ueuseCreateApi = await resUeuse.json();
|
console.log(JSON.stringify(ueuseData));
|
||||||
|
}
|
||||||
console.log(JSON.stringify(ueuseData));
|
|
||||||
}
|
}
|
||||||
|
|||||||
+100
-31
@@ -1,44 +1,20 @@
|
|||||||
import * as dotenv from "dotenv";
|
|
||||||
import { cityList } from "../src/weatherId.js";
|
import { cityList } from "../src/weatherId.js";
|
||||||
|
|
||||||
import type * as types from "../types";
|
import type * as types from "types/types.js";
|
||||||
|
|
||||||
dotenv.config();
|
import config from "../config.js";
|
||||||
|
|
||||||
export default async function weatherNotice() {
|
export default async function weatherNotice() {
|
||||||
let weatherResults: string = "";
|
// 仮投稿
|
||||||
|
|
||||||
for (const [cityId, cityName] of Object.entries(cityList)) {
|
|
||||||
const res = await fetch(
|
|
||||||
`https://weather.tsukumijima.net/api/forecast/city/${cityId}`,
|
|
||||||
);
|
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
const today = data.forecasts[0];
|
|
||||||
const weather = today.telop ?? "取得できませんでした";
|
|
||||||
const maxTemp = today.temperature.max?.celsius ?? "取得できませんでした";
|
|
||||||
const minTemp = today.temperature.min?.celsius ?? "取得できませんでした";
|
|
||||||
const chanceOfRain = data.chanceOfRain?.["T06_12"] ?? "取得できませんでした";
|
|
||||||
|
|
||||||
weatherResults = weatherResults +
|
|
||||||
`【${cityName}】\n
|
|
||||||
天気:${weather}\n
|
|
||||||
最高気温:${maxTemp}℃\n
|
|
||||||
最低気温:${minTemp}℃\n
|
|
||||||
降水確率:${chanceOfRain}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 返信用ユーズ
|
|
||||||
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: `
|
||||||
# 本日の天気\n
|
本日の天気
|
||||||
${weatherResults}
|
※タイムラインが埋まるため返信に記載しています
|
||||||
`,
|
`,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -47,4 +23,97 @@ export default async function weatherNotice() {
|
|||||||
const ueuseData: types.ueuseCreateApi = await resUeuse.json();
|
const ueuseData: types.ueuseCreateApi = await resUeuse.json();
|
||||||
|
|
||||||
console.log(JSON.stringify(ueuseData));
|
console.log(JSON.stringify(ueuseData));
|
||||||
|
|
||||||
|
|
||||||
|
// インデックス
|
||||||
|
const splitCount = config.weather.splitCount;
|
||||||
|
const total = cityList.length;
|
||||||
|
const chunkSizes = Array(splitCount).fill(0).map((_, i) =>
|
||||||
|
Math.floor((total + i) / splitCount)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 分割インデックス
|
||||||
|
let start = 0;
|
||||||
|
const ranges = chunkSizes.map(size => {
|
||||||
|
const range = [start, start + size];
|
||||||
|
start += size;
|
||||||
|
return range;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 配列作成
|
||||||
|
const weatherResults = Array(splitCount).fill("");
|
||||||
|
|
||||||
|
// 天気取得
|
||||||
|
for (let chunkIndex = 0; chunkIndex < splitCount; chunkIndex++) {
|
||||||
|
const [chunkStart, chunkEnd] = ranges[chunkIndex];
|
||||||
|
|
||||||
|
for (let i = chunkStart; i < chunkEnd; i++) {
|
||||||
|
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 ?? "取得できませんでした";
|
||||||
|
|
||||||
|
// 最高気温
|
||||||
|
let maxTemp: string;
|
||||||
|
|
||||||
|
if (today.temperature.max.celsius !== null) {
|
||||||
|
maxTemp = `${today.temperature.max.celsius}℃`;
|
||||||
|
} else {
|
||||||
|
maxTemp = "取得できませんでした";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最低気温
|
||||||
|
let minTemp: string;
|
||||||
|
|
||||||
|
if (today.temperature.min.celsius !== null) {
|
||||||
|
minTemp = `${today.temperature.min.celsius}℃`;
|
||||||
|
} else {
|
||||||
|
minTemp = "取得できませんでした";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 降水確率
|
||||||
|
let chanceOfRain: string;
|
||||||
|
|
||||||
|
if (
|
||||||
|
today.chanceOfRain.T06_12 !== null ||
|
||||||
|
today.chanceOfRain.T06_12 !== "--%"
|
||||||
|
) {
|
||||||
|
chanceOfRain = today.chanceOfRain.T06_12;
|
||||||
|
} else {
|
||||||
|
chanceOfRain = "取得できませんでした";
|
||||||
|
}
|
||||||
|
|
||||||
|
weatherResults[chunkIndex] += `
|
||||||
|
【${data.location.city}】
|
||||||
|
天気:${weather}
|
||||||
|
最高気温:${maxTemp}
|
||||||
|
最低気温:${minTemp}
|
||||||
|
降水確率:${chanceOfRain}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分割投稿
|
||||||
|
for (let i = 0; i < splitCount; i++) {
|
||||||
|
const resReply = await fetch(
|
||||||
|
`https://${config.uwuzuServer}/api/ueuse/create`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
token: config.apiToken,
|
||||||
|
text: weatherResults[i],
|
||||||
|
replyid: ueuseData.uniqid
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const replyData: types.ueuseCreateApi = await resReply.json();
|
||||||
|
|
||||||
|
console.log(JSON.stringify(replyData));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-49
@@ -1,49 +1,49 @@
|
|||||||
export const cityList: { [cityId: string]: string } = {
|
export const cityList: Array<String> = [
|
||||||
"016010": "札幌",
|
"016010",
|
||||||
"020010": "青森",
|
"020010",
|
||||||
"030010": "盛岡",
|
"030010",
|
||||||
"040010": "仙台",
|
"040010",
|
||||||
"050010": "秋田",
|
"050010",
|
||||||
"060010": "山形",
|
"060010",
|
||||||
"070010": "福島",
|
"070010",
|
||||||
"080010": "水戸",
|
"080010",
|
||||||
"090010": "宇都宮",
|
"090010",
|
||||||
"100010": "前橋",
|
"100010",
|
||||||
"110010": "さいたま",
|
"110010",
|
||||||
"120010": "千葉",
|
"120010",
|
||||||
"130010": "東京",
|
"130010",
|
||||||
"140010": "横浜",
|
"140010",
|
||||||
"150010": "新潟",
|
"150010",
|
||||||
"160010": "富山",
|
"160010",
|
||||||
"170010": "金沢",
|
"170010",
|
||||||
"180010": "福井",
|
"180010",
|
||||||
"190010": "甲府",
|
"190010",
|
||||||
"200010": "長野",
|
"200010",
|
||||||
"210010": "岐阜",
|
"210010",
|
||||||
"220010": "静岡",
|
"220010",
|
||||||
"230010": "名古屋",
|
"230010",
|
||||||
"240010": "津",
|
"240010",
|
||||||
"250010": "大津",
|
"250010",
|
||||||
"260010": "京都",
|
"260010",
|
||||||
"270000": "大阪",
|
"270000",
|
||||||
"280010": "神戸",
|
"280010",
|
||||||
"290010": "奈良",
|
"290010",
|
||||||
"300010": "和歌山",
|
"300010",
|
||||||
"310010": "鳥取",
|
"310010",
|
||||||
"320010": "松江",
|
"320010",
|
||||||
"330010": "岡山",
|
"330010",
|
||||||
"340010": "広島",
|
"340010",
|
||||||
"350010": "山口",
|
"350010",
|
||||||
"360010": "徳島",
|
"360010",
|
||||||
"370000": "高松",
|
"370000",
|
||||||
"380010": "松山",
|
"380010",
|
||||||
"390010": "高知",
|
"390010",
|
||||||
"400010": "福岡",
|
"400010",
|
||||||
"410010": "佐賀",
|
"410010",
|
||||||
"420010": "長崎",
|
"420010",
|
||||||
"430010": "熊本",
|
"430010",
|
||||||
"440010": "大分",
|
"440010",
|
||||||
"450010": "宮崎",
|
"450010",
|
||||||
"460010": "鹿児島",
|
"460010",
|
||||||
"471010": "那覇",
|
"471010",
|
||||||
};
|
];
|
||||||
|
|||||||
+18
-109
@@ -1,113 +1,22 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
"target": "es2022",
|
||||||
|
"module": "ES2022",
|
||||||
/* Projects */
|
"moduleResolution": "bundler",
|
||||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
"outDir": "./dist",
|
||||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
"esModuleInterop": true,
|
||||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
"allowSyntheticDefaultImports": true,
|
||||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
"forceConsistentCasingInFileNames": true,
|
||||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
"strict": true,
|
||||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
"skipLibCheck": true,
|
||||||
|
"baseUrl": "./",
|
||||||
/* Language and Environment */
|
"typeRoots": [
|
||||||
"target": "es2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
"./node_modules/@types",
|
||||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
"./types"
|
||||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
],
|
||||||
// "libReplacement": true, /* Enable lib replacement. */
|
"paths": {
|
||||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
"ws": ["./node_modules/ws/index.js"],
|
||||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
"@types/ws": ["./node_modules/@types/ws/index.d.ts"]
|
||||||
// "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 '<reference>'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. */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+29
@@ -0,0 +1,29 @@
|
|||||||
|
interface earthquakeTypes {
|
||||||
|
reconnectTimes: number;
|
||||||
|
websocketUrl: string;
|
||||||
|
areasCsvUrl: string;
|
||||||
|
maxScaleMin: number;
|
||||||
|
rateLimit: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface weatherTypes {
|
||||||
|
splitCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface stopsTypes {
|
||||||
|
start: number;
|
||||||
|
stop: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface timeTypes {
|
||||||
|
stopTimes: stopsTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface configTypes {
|
||||||
|
time: timeTypes,
|
||||||
|
earthquake: earthquakeTypes;
|
||||||
|
weather: weatherTypes;
|
||||||
|
|
||||||
|
apiToken: string;
|
||||||
|
uwuzuServer: string;
|
||||||
|
}
|
||||||
Vendored
Vendored
+68
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user