diff --git a/README.md b/README.md index dbb3f74..af5fb62 100644 --- a/README.md +++ b/README.md @@ -3,18 +3,19 @@ uwuzu v1.6.7以上で利用可能です。 ## 機能 -- 時報 +- 時報 毎時0分に「h:00になりました。」と投稿します。 -- 天気予報 - [天気予報 API(livedoor 天気互換)](https://weather.tsukumijima.net/)を利用して、毎日7:00に天気予報を投稿します。 -- 地震情報 +- 天気予報 + [天気予報 API(livedoor 天気互換)](https://weather.tsukumijima.net/)を利用して、毎日7:00及び18:00に天気予報を投稿します。 + 18:00は明日の天気予報を投稿します。 +- 地震情報 [P2P地震情報](https://www.p2pquake.net/)のWebSocket APIを利用して、以下の情報を受信した際に投稿します。 - 地震発生 - 緊急地震速報(警報) - 津波予報 -- 新年迎春 +- 新年迎春 毎年1/1 0:00に投稿します。 -- デバッグモード +- デバッグモード 設定で有効化することで、以下の機能が利用できます。 開発以外では使用しないでください。 - NODE_TLS_REJECT_UNAUTHORIZED=1 diff --git a/package.json b/package.json index dfccf3e..ce11172 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@types/node": "^25.5.2", "@types/ws": "^8.18.1", "better-uwuzu-sdk": "git+https://gitea.last2014.com/last2014/better-uwuzu-sdk.git#1.1.7", + "cron-parser": "^5.5.0", "date-fns": "^4.1.0", "fs": "0.0.1-security", "i18next": "^26.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97ef052..903e42a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: better-uwuzu-sdk: specifier: git+https://gitea.last2014.com/last2014/better-uwuzu-sdk.git#1.1.7 version: git+https://gitea.last2014.com/last2014/better-uwuzu-sdk.git#8017146b1a2d6264d051b54afccdd46571b5fd00 + cron-parser: + specifier: ^5.5.0 + version: 5.5.0 date-fns: specifier: ^4.1.0 version: 4.1.0 @@ -298,6 +301,10 @@ packages: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} + cron-parser@5.5.0: + resolution: {integrity: sha512-oML4lKUXxizYswqmxuOCpgFS8BNUJpIu6k/2HVHyaL8Ynnf3wdf9tkns0yRdJLSIjkJ+b0DXHMZEHGpMwjnPww==} + engines: {node: '>=18'} + date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} @@ -475,6 +482,10 @@ packages: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} + luxon@3.7.2: + resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} + engines: {node: '>=12'} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -882,6 +893,10 @@ snapshots: commander@9.5.0: {} + cron-parser@5.5.0: + dependencies: + luxon: 3.7.2 + date-fns@4.1.0: {} decompress-response@6.0.0: @@ -1058,6 +1073,8 @@ snapshots: dependencies: which-typed-array: 1.1.20 + luxon@3.7.2: {} + math-intrinsics@1.1.0: {} merge2@1.4.1: {} diff --git a/src/feature/command/index.ts b/src/feature/command/index.ts index eb7705c..e319177 100644 --- a/src/feature/command/index.ts +++ b/src/feature/command/index.ts @@ -9,8 +9,13 @@ import unfollowCommand from "@/feature/command/unfollow"; import miqCommand from "@/feature/command/miq"; import initI18n from "@/lib/i18n"; import config from "@/lib/config"; +import CronExpressionParser from "cron-parser"; await initI18n(); + +const next = BigInt(CronExpressionParser.parse(`*/${config.command.interval} * * * *`).next().getTime() * 1_000_000); +while (process.hrtime.bigint() > next) {} + console.log("コマンドの処理を行います"); try { diff --git a/src/feature/hnyNotice.ts b/src/feature/hnyNotice.ts index 8868313..0d74eba 100644 --- a/src/feature/hnyNotice.ts +++ b/src/feature/hnyNotice.ts @@ -1,25 +1,26 @@ import { createUeuse } from "@/lib/client"; import initI18n from "@/lib/i18n"; +import CronExpressionParser from "cron-parser"; import { format } from "date-fns"; import i18next from "i18next"; -import { parentPort } from "node:worker_threads"; await initI18n(); -parentPort?.on("message", async () => { - console.log("新年迎春の投稿を行います"); +const next = BigInt(CronExpressionParser.parse("0 0 1 1 *").next().getTime() * 1_000_000); +while (process.hrtime.bigint() > next) {} - try { - await createUeuse({ - text: i18next.t("hnyNotice", { year: String(new Date().getFullYear()) }), - }, "新年迎春"); +console.log("新年迎春の投稿を行います"); - console.log("新年迎春投稿時刻:", format(new Date(), "yyyy/M/d H:mm:ss:SSS")); - process.exit(0); - } catch (err: any) { - console.error("message" in err - ? err.message - : err); - process.exit(1); - } -}); \ No newline at end of file +try { + await createUeuse({ + text: i18next.t("hnyNotice", { year: String(new Date().getFullYear()) }), + }, "新年迎春"); + + console.log("新年迎春投稿時刻:", format(new Date(), "yyyy/M/d H:mm:ss:SSS")); + process.exit(0); +} catch (err: any) { + console.error("message" in err + ? err.message + : err); + process.exit(1); +} \ No newline at end of file diff --git a/src/feature/timeNotice.ts b/src/feature/timeNotice.ts index 4ad0bfe..1d76c9b 100644 --- a/src/feature/timeNotice.ts +++ b/src/feature/timeNotice.ts @@ -1,9 +1,14 @@ import { createUeuse } from "@/lib/client"; import initI18n from "@/lib/i18n"; +import CronExpressionParser from "cron-parser"; import { format } from "date-fns"; import i18next from "i18next"; await initI18n(); + +const next = BigInt(CronExpressionParser.parse("0 * * * *").next().getTime() * 1_000_000); +while (process.hrtime.bigint() > next) {} + console.log("時報の投稿を行います"); try { diff --git a/src/feature/weatherNotice.ts b/src/feature/weatherNotice.ts index d64eec1..e9c18b3 100644 --- a/src/feature/weatherNotice.ts +++ b/src/feature/weatherNotice.ts @@ -2,6 +2,7 @@ import client from "@/lib/client"; import config from "@/lib/config"; import initI18n from "@/lib/i18n"; import Memory from "@/lib/memory"; +import CronExpressionParser from "cron-parser"; import i18next from "i18next"; import { readFileSync } from "node:fs"; import { EOL } from "node:os"; @@ -63,6 +64,13 @@ if ( workerData.startsWith("scheduledWeatherNotice") ) { await initI18n(); + + const cronStr = workerData.endsWith("Tomorrow") + ? "0 18 * * *" + : "0 7 * * *" + const next = BigInt(CronExpressionParser.parse(cronStr).next().getTime() * 1_000_000); + while (process.hrtime.bigint() > next) {} + console.log("天気予報の投稿を行います"); try { diff --git a/src/index.ts b/src/index.ts index 4d0846d..a2d3bb0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,34 +31,34 @@ try { } try { - schedule("0 * * * *", async () => { + schedule("56 59 * * * *", async () => { new Worker(`${import.meta.dirname}/feature/timeNotice.js`); }); - schedule("0 7 * * *", async () => { + schedule("56 59 6 * * *", async () => { new Worker(`${import.meta.dirname}/feature/weatherNotice.js`, { workerData: "scheduledWeatherNotice", }); }); - schedule("0 18 * * *", async () => { + schedule("56 59 17 * * *", async () => { new Worker(`${import.meta.dirname}/feature/weatherNotice.js`, { workerData: "scheduledWeatherNoticeTomorrow", }); }); - schedule(`*/${config.command.interval} * * * *`, async () => { + const interval = config.command.interval; + const targetMinutes = []; + for (let i = interval - 1; i < 60; i += interval) { + targetMinutes.push(i); + } + const minutesStr = targetMinutes.join(","); + schedule(`56 ${minutesStr} * * * *`, async () => { new Worker(`${import.meta.dirname}/feature/command/index.js`); }); - - let hnyWorker: Worker | undefined = undefined; - - schedule("57 59 23 31 12 *", () => { - hnyWorker = new Worker(`${import.meta.dirname}/feature/hnyNotice.js`); - }); - - schedule("0 0 0 1 1 *", () => { - hnyWorker?.postMessage(""); + + schedule("56 59 23 31 12 *", () => { + new Worker(`${import.meta.dirname}/feature/hnyNotice.js`); }); } catch (err: any) { console.error("message" in err