From d7a5d8a43e3d276c5a7d454de38b11bb0c717919 Mon Sep 17 00:00:00 2001 From: Last2014 Date: Thu, 31 Jul 2025 22:02:54 +0900 Subject: [PATCH] =?UTF-8?q?v6.5(LTS)=E3=82=92=E3=83=AA=E3=83=AA=E3=83=BC?= =?UTF-8?q?=E3=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- checks/api.ts | 4 +- main.ts | 8 +- package.json | 4 +- scripts/earthquakeNotice.ts | 255 +++++++++++++++++++++++++----------- scripts/follow/follow.ts | 38 +++--- scripts/follow/unfollow.ts | 31 +++-- tsconfig.json | 9 +- types/types.d.ts | 4 +- 8 files changed, 233 insertions(+), 120 deletions(-) diff --git a/checks/api.ts b/checks/api.ts index cd0d437..8e5e2b9 100644 --- a/checks/api.ts +++ b/checks/api.ts @@ -3,7 +3,7 @@ import config from "../config.js"; export default async function APICheck() { try { - const req = await fetch(`https://${config.uwuzu.host}/api/me`, { + const req = await fetch(`https://${config.uwuzu.host}/api/me/`, { method: "POST", body: JSON.stringify({ token: config.uwuzu.apiToken, @@ -12,7 +12,7 @@ export default async function APICheck() { const res = await req.json(); - if (!res.userid) { + if (res.error_code !== undefined) { console.log(styleText("red", "APIトークンあるいはuwuzuサーバーホストが無効です")); process.exit(); } diff --git a/main.ts b/main.ts index 44d0530..9aca990 100644 --- a/main.ts +++ b/main.ts @@ -26,16 +26,16 @@ successExit(); // 地震情報観測開始 earthquakeNotice(); -// 時報・フォローバック(毎時) +/*// 時報・フォローバック(毎時) cron.schedule("0 * * * *", () => { timeNotice(); follows(); }); // 天気お知らせ(毎日7:01) -cron.schedule("1 7 * * *", () => { +cron.schedule("10 0 7 * * *", () => { weatherNotice(); -}); +});*/ -// コンソールで表示 +// 起動表示 console.log("BOTサーバーが起動しました"); diff --git a/package.json b/package.json index 818cf23..24e49f3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "notice-uwuzu", - "version": "v6.0.3@uwuzu1.5.4", - "description": "uwuzu Notice Bot", + "version": "v6.5@uwuzu1.5.4", + "description": "Notice Bot for uwuzu", "main": "dist/main.js", "scripts": { "start": "node .", diff --git a/scripts/earthquakeNotice.ts b/scripts/earthquakeNotice.ts index 1a7d4ce..48fcb34 100644 --- a/scripts/earthquakeNotice.ts +++ b/scripts/earthquakeNotice.ts @@ -2,6 +2,7 @@ import WebSocket from "ws"; import sendMail from "../src/mailer.js"; import config from "../config.js"; +import { max } from "date-fns/fp"; class P2PEarthquakeClient { private ws: WebSocket | null = null; @@ -37,8 +38,8 @@ class P2PEarthquakeClient { try { const message = JSON.parse(data.toString()); this.handleMessage(message); - } catch (error) { - console.error("メッセージのパースに失敗:", error); + } catch (err) { + console.error(`メッセージのパースでエラーが発生: ${err}`); } }); @@ -63,23 +64,18 @@ class P2PEarthquakeClient { 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; - default: - console.log(`未対応の情報を受信しました(コード: ${message.code})`); - break; - } - } + const supportCode: Array = [ + 551, + 552, + 556, + ]; - private executeEventFunc(earthquakeInfo: any): void { - event(earthquakeInfo); + if (supportCode.indexOf(message.code) !== -1) { + event(message); + } else { + console.log(`未対応の情報を受信しました(コード: ${message.code})`); + console.log(`受信メッセージ:${message}`); + } } private scheduleReconnect(): void { @@ -87,6 +83,7 @@ class P2PEarthquakeClient { console.log("地震情報サーバーから切断されました"); console.log(`${this.reconnectInterval / 1000}秒後に再接続を試みます`); + this.reconnectTimer = setTimeout(() => { this.reconnectTimer = null; this.connect(); @@ -96,7 +93,7 @@ class P2PEarthquakeClient { private setupCleanup(): void { const cleanup = () => { this.stop(); - process.exit(0); + process.exit(); }; process.on("SIGINT", cleanup); @@ -116,7 +113,7 @@ class P2PEarthquakeClient { } } -// 地名オブジェクトマッピング +// 地名マッピング async function areaMap(): Promise> { const res = await fetch(config.earthquake.areasCsvUrl); @@ -141,12 +138,13 @@ async function areaMap(): Promise> { // 情報受信 async function event(earthquakeInfo: any): Promise { - console.log(`受信データ:${JSON.stringify(earthquakeInfo)}`); - + console.log(`受信メッセージ:${earthquakeInfo}`); // ----処理---- // 緊急地震速報の場合 - if (earthquakeInfo.code === 554) { + if (earthquakeInfo.code === 556) { + console.log("緊急地震速報を受信しました"); + // 地震詳細 let descriptionEarthquake: string = ""; @@ -223,28 +221,7 @@ async function event(earthquakeInfo: any): Promise { // 地震情報 else if (earthquakeInfo.code === 551) { - // 国内津波 - let domesticTsunami; - - if (earthquakeInfo.earthquake.domesticTsunami === undefined) { - domesticTsunami = "この地震による国内の津波情報はありません"; - } else 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 = "この地震による国内の津波予報があります"; - } - - // 最大震度 - let maxScale: string = "最大深度:"; + console.log("地震発生情報を受信しました"); if ( earthquakeInfo.earthquake.maxScale !== undefined && @@ -254,29 +231,46 @@ async function event(earthquakeInfo: any): Promise { return; } + // 国内津波 + let domesticTsunami; + + const TsunamiMessages = { + "None": "この地震による国内の津波の心配はありません", + "Unknown": "この地震による国内の津波情報はありません", + "Checking": "この地震による国内の津波情報を調査中です", + "NonEffective": "この地震による国内の津波影響は若干の海面変動が予想されますが被害の心配はありません", + "Watch": "この地震により国内で津波注意報が発令しています", + "Warning": "この地震による国内の津波予報があります", + } as { [key: string]: string }; + + if (earthquakeInfo.earthquake.domesticTsunami === undefined) { + domesticTsunami = "この地震による国内の津波情報はありません"; + } else { + domesticTsunami = TsunamiMessages[earthquakeInfo.earthquake.domesticTsunami]; + } + + // 最大震度 + let maxScale: string = "最大深度:"; + + const maxScales = { + 10: "震度1", + 20: "震度2", + 30: "震度3", + 40: "震度4", + 45: "震度5弱", + 50: "震度5強", + 55: "震度6弱", + 60: "震度6強", + 70: "震度7", + } as { [key: number]: string }; + if ( - earthquakeInfo.earthquake.maxScale == -1 && + earthquakeInfo.earthquake.maxScale == -1 || earthquakeInfo.earthquake.maxScale === undefined ) { - 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"; + maxScale = "最大震度:不明"; + } else { + maxScale = `最大震度:${maxScales[earthquakeInfo.earthquake.maxScale]}`; } // 警告 @@ -312,7 +306,7 @@ async function event(earthquakeInfo: any): Promise { } // 対象地域 - let areas: string = ""; + let areas; if (earthquakeInfo.points !== undefined) { const areaNames: Array = Array.from( @@ -324,7 +318,7 @@ async function event(earthquakeInfo: any): Promise { } // 詳細 - let description: string = ""; + let description; if ( earthquakeInfo.comments.freeFormComment !== "" && @@ -334,29 +328,33 @@ async function event(earthquakeInfo: any): Promise { } // 深さ - let depth: string = ""; + let depth; if ( earthquakeInfo.earthquake.hypocenter.depth !== null || - earthquakeInfo.earthquake.hypocenter.depth !== undefined || - earthquakeInfo.earthquake.hypocenter.depth != -1 + earthquakeInfo.earthquake.hypocenter.depth !== undefined ) { if (earthquakeInfo.earthquake.hypocenter.depth === 0) { depth = "深さ:ごく浅い"; + } else if (earthquakeInfo.earthquake.hypocenter.depth === -1) { + depth = "深さ:不明"; } else { depth = `深さ:${String(earthquakeInfo.earthquake.hypocenter.depth)}km`; } } // マグニチュード - let magnitude: string = ""; + let magnitude; if( earthquakeInfo.earthquake.hypocenter.magnitude !== null || - earthquakeInfo.earthquake.hypocenter.magnitude !== undefined || - earthquakeInfo.earthquake.hypocenter.magnitude != -1 + earthquakeInfo.earthquake.hypocenter.magnitude !== undefined ) { - magnitude = `マグニチュード:M${String(earthquakeInfo.earthquake.hypocenter.magnitude)}`; + if (earthquakeInfo.earthquake.hypocenter.magnitude === -1) { + depth = "マグニチュード:不明"; + } else { + magnitude = `マグニチュード:M${String(earthquakeInfo.earthquake.hypocenter.magnitude)}`; + } } ueuse(` @@ -370,11 +368,116 @@ async function event(earthquakeInfo: any): Promise { ${areas} 国内の津波:${domesticTsunami} `); + } else if (earthquakeInfo.code === 552) { + console.log("津波予報情報を受信しました"); + + // 予報取り消し + if (earthquakeInfo.cancelled) { + ueuse(` + ==地震情報== + 【津波予報】 + ※津波予報が取り消されました※ + 時刻:${earthquakeInfo.time} + `); + + return; + } + + let result: string = ` + ==地震情報== + 【津波予報】 + 時刻:${earthquakeInfo.time} + \n + `; + + for (let i = 0; i < earthquakeInfo.areas.length; i++) { + const data = earthquakeInfo.areas[i]; + + // 種類 + const gradeMessages = { + "MajorWarning": "大津波警報", + "Warning": "津波警報", + "Watch": "津波注意報", + "Unknown": "不明", + } as { [key: string]: string }; + + let grade; + + if (data.grade === undefined) { + grade = "予報種類:不明"; + } else { + grade = `予報種類:${gradeMessages[data.grade]}`; + } + + // 直後襲来 + let immediate; + + if (data.immediate === undefined) { + immediate = "津波の襲来が直後かの情報がありません"; + } else if (data.immediate) { + immediate = "### 津波が直ちに襲来します"; + } else if (!data.immediate) { + immediate = "津波は直ちには襲来しません"; + } + + // 第1波 + let firstHeight; + + if (data.firstHeight === undefined) { + firstHeight = "第1波の情報がありません"; + } else if ( + data.firstHeight.arrivalTime === undefined && + data.firstHeight.condition === undefined + ) { + firstHeight = "第1波の情報がありません"; + } else { + let arrivalTime; + + if (data.arrivalTime === undefined) { + arrivalTime = "不明"; + } else { + arrivalTime = data.arrivalTime; + } + + let condition; + + if (data.condition === undefined) { + condition = "不明"; + } else { + condition = data.condition; + } + + firstHeight = ` + 第1波到達予想時刻:${arrivalTime} + 第1波の状態:${condition} + ` + } + + // 予想高さ + let maxHeight; + + if (data.maxHeight.description === undefined) { + maxHeight = "津波の高さ(予想):不明"; + } else { + maxHeight = `津波の高さ(予想):${data.maxHeight.description}`; + } + + result = ` + 【${data.name}】 + ${grade} + ${immediate} + ${firstHeight} + ${maxHeight}\n\n + `; + } + + ueuse(result); } } async function ueuse(text: string) { - const res = await fetch(`https://${config.uwuzu.host}/api/ueuse/create`, { + console.log(text); + /*const res = await fetch(`https://${config.uwuzu.host}/api/ueuse/create/`, { method: "POST", body: JSON.stringify({ token: config.uwuzu.apiToken, @@ -384,7 +487,7 @@ async function ueuse(text: string) { const resData = await res.json(); - console.log(`地震情報投稿:${JSON.stringify(resData)}`); + console.log(`地震情報投稿:${JSON.stringify(resData)}`);*/ } export default function earthquakeNotice(): void { diff --git a/scripts/follow/follow.ts b/scripts/follow/follow.ts index 4b214e0..49fd1a6 100644 --- a/scripts/follow/follow.ts +++ b/scripts/follow/follow.ts @@ -7,36 +7,38 @@ export default async function followBack() { // フォロワーを取得 const resMe = await fetch( - `https://${config.uwuzu.host}/api/me?token=${config.uwuzu.apiToken}`, + `https://${config.uwuzu.host}/api/me/`, { - method: "GET", - // uwuzu v1.5.4で/api/meのPOSTが死んでいるため簡易的にGET + method: "POST", + body: JSON.stringify({ + token: config.uwuzu.apiToken, + }), }, ); const meData: types.meApi = await resMe.json(); - console.log(`BOTプロフィール:${JSON.stringify(meData)}`); - - const followers: Array = meData.follower; + const followers = meData.follower; // フォロー for (let i = 0; i < followers.length; i++) { const followerItem = followers[i]; - const resFollow = await fetch( - `https://${config.uwuzu.host}/api/users/follow`, - { - method: "POST", - body: JSON.stringify({ - token: config.uwuzu.apiToken, - userid: followerItem, - }), - }, - ); + setTimeout(async () => { + const resFollow = await fetch( + `https://${config.uwuzu.host}/api/users/follow/`, + { + method: "POST", + body: JSON.stringify({ + token: config.uwuzu.apiToken, + userid: followerItem, + }), + }, + ); - const followData: types.followApi = await resFollow.json(); + const followData: types.followApi = await resFollow.json(); - console.log(`フォロー:${JSON.stringify(followData)}`); + console.log(`フォロー:${JSON.stringify(followData)}`); + }, 100); } } diff --git a/scripts/follow/unfollow.ts b/scripts/follow/unfollow.ts index 1cf7eae..78817f6 100644 --- a/scripts/follow/unfollow.ts +++ b/scripts/follow/unfollow.ts @@ -3,25 +3,32 @@ import { meApi } from "types/types.js"; export default async function unFollowBack() { const profile: meApi = await - (await fetch(`https://${config.uwuzu.host}/api/me`, { + (await fetch(`https://${config.uwuzu.host}/api/me/`, { method: "POST", body: JSON.stringify({ token: config.uwuzu.apiToken, }) })).json(); - profile.followee.forEach(async (followUser: string) => { + for (let i = 0; i < profile.followee.length; i++) { + const followUser = profile.followee[i]; + if ( - profile.follower[followUser] === undefined || - profile.follower[followUser] === null + profile.follower.indexOf(followUser) === -1 ) { - await fetch(`https://${config.uwuzu.host}/api/users/unfollow`, { - method: "POST", - body: JSON.stringify({ - token: config.uwuzu.apiToken, - userId: followUser, - }), - }) + setTimeout(async () => { + const req = await fetch(`https://${config.uwuzu.host}/api/users/unfollow/`, { + method: "POST", + body: JSON.stringify({ + token: config.uwuzu.apiToken, + userId: followUser, + }), + }); + + const res = await req.text(); + + console.log(`フォロー解除: ${res}`) + }, 100); } - }); + } } diff --git a/tsconfig.json b/tsconfig.json index 8eb024e..ca0c613 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { - "target": "es2022", - "module": "ES2022", + "target": "ES2024", + "module": "ESNext", "moduleResolution": "bundler", "outDir": "./dist", "esModuleInterop": true, @@ -17,6 +17,7 @@ "paths": { "ws": ["./node_modules/ws/index.js"], "@types/ws": ["./node_modules/@types/ws/index.d.ts"] - } - } + }, + "removeComments": true, + }, } diff --git a/types/types.d.ts b/types/types.d.ts index 05dae07..1d82b53 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -12,9 +12,9 @@ export interface meApi { user_icon: string; user_header: string; registered_date: string; - followee: Array; + followee: Array; followee_cnt: number; - follower: Array; + follower: Array; follower_cnt: number; ueuse_cnt: number; isBot: Boolean;