diff --git a/.gitignore b/.gitignore index e53ee7c..f360113 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ /memory.json /config/** !/config/example.yaml -/src/feature/earthquake/generateImage/data.json +/src/feature/earthquake/generateImage/data*.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c340c3..8f88293 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +# 2026.5.1 +- Feat: 地震の震度分布画像生成 +- Feat: 震度分布画に全タイルが埋まる機能 +- Feat: 震度分布画像の震源がタイル単位で中央になる機能 +- Feat: 震度分布画像で欠けているタイルが描画される機能 +- Feat: 震度分布画像のアセットの位置が中央基準になる機能 +- Chg: 全てのスケジュールが正確に動作するように +- Chg: 毎日18:00に明日の天気予報を投稿するように +- Chg: 震度分布画像のズームレベルを8から9へ +- Chg: 震度分布画像の生成条件を震源と位置が存在するへ変更 +- Chg: 震度分布画像のアセットを並列に取得 +- Chg: 震度分布画像の震源のアセットに縁取りを追加 +- Chg: 震度分布画像の震度ではないアセットを拡大 +- Chg: ユーズの分割の案内文を太字に +- Fix: 地震情報の画像生成での並び替えで、震度の位置が震源の位置として扱われる問題 +- Fix: 毎日7:00に天気予報の返信をできない問題 +- Del: 最大震度が不明な場合に投稿するかどうかのconfigを削除 + # 2026.5.0 - Feat(dev): 地震情報の画像生成機能は未完成のため、ユーズには適用されません - Chg: 地震情報でのタイトルを変更 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/config/example.yaml b/config/example.yaml index 5059566..b31e541 100644 --- a/config/example.yaml +++ b/config/example.yaml @@ -12,8 +12,6 @@ earthquake: # 例: 30を指定すると、最大震度が震度3以上の地震発生情報のみを投稿します。 # 10(震度1), 20(震度2), 30(震度3), 40(震度4), 45(震度5弱), 50(震度5強), 55(震度6弱), 60(震度6強), 70(震度7)が有効です。 requireMaxScale: 30 - # 最大震度が不明な地震発生情報を投稿するかどうか boolean - canUnknownMaxScale: true # 過去のデバッグ用データを使用して地震情報を配信するかどうか boolean # デバッグ用途のみで使用してください。 useHistoryData: false diff --git a/package.json b/package.json index dfccf3e..cf46869 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "notice-uwuzu", - "version": "2026.5.0", + "version": "2026.5.1", "type": "module", "main": "dist/index.js", "scripts": { @@ -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/earthquake/generateImage/assets/epicenter.png b/src/feature/earthquake/generateImage/assets/epicenter.png index 515e605..ca8d7e8 100644 Binary files a/src/feature/earthquake/generateImage/assets/epicenter.png and b/src/feature/earthquake/generateImage/assets/epicenter.png differ diff --git a/src/feature/earthquake/generateImage/assets/information.png b/src/feature/earthquake/generateImage/assets/information.png index 7f98fe0..1931737 100644 Binary files a/src/feature/earthquake/generateImage/assets/information.png and b/src/feature/earthquake/generateImage/assets/information.png differ diff --git a/src/feature/earthquake/generateImage/index.ts b/src/feature/earthquake/generateImage/index.ts index d5ace75..f065cc1 100644 --- a/src/feature/earthquake/generateImage/index.ts +++ b/src/feature/earthquake/generateImage/index.ts @@ -153,12 +153,12 @@ function getEdge(arr: T, property: keyof T[number]) { export default async function generateImage(message: any) { if ( - message.earthquake.hypocenter === undefined && + message.earthquake.hypocenter === undefined || message.points === undefined ) return "input_lack"; - const ZOOM_LEVEL = 8; + const ZOOM_LEVEL = 9; // タイル・地点取得 const tiles: { @@ -278,23 +278,120 @@ export default async function generateImage(message: any) { scale: number; }))[] = []; - // タイルのXYの各最大、最小を取得 - const tileXCount = getEdge(tiles, "tileX"); - const tileYCount = getEdge(tiles, "tileY"); + // タイルの幅、最大最小 + let tileXCount = getEdge(tiles, "tileX"); + let tileYCount = getEdge(tiles, "tileY"); + let xSize = tileXCount.most - tileXCount.least + 1; + let ySize = tileYCount.most - tileYCount.least + 1; - // タイルサイズ決定 + // タイルサイズ仮計算 let tileSize: number; - const xSize = tileXCount.most - tileXCount.least + 1; - const ySize = tileYCount.most - tileYCount.least + 1; + xSize = tileXCount.most - tileXCount.least + 1; + ySize = tileYCount.most - tileYCount.least + 1; + if (xSize > ySize) { tileSize = Math.round(WIDTH / xSize); } else { tileSize = Math.round(HEIGHT / ySize); } + // 全画面 + if (WIDTH - xSize * tileSize > 10) { + const requireTilesX = Math.ceil(WIDTH / tileSize); + const halfTilesX = Math.ceil(requireTilesX / 2); + + tileXCount = { + least: tileXCount.least - halfTilesX, + most: tileXCount.most + halfTilesX, + } + } + + if (HEIGHT - ySize * tileSize > 10) { + const requireTilesY = Math.ceil(HEIGHT / tileSize); + const halfTilesY = Math.ceil(requireTilesY / 2); + + tileYCount = { + least: tileYCount.least - halfTilesY, + most: tileYCount.most + halfTilesY, + } + } + + // 震源を中心とする + const epicenterPosition = positions.filter(position => position.type === "epicenter")[0]; + if (!epicenterPosition) + return "input_lack"; + + const epicenterTile = tiles[epicenterPosition.tileIndex]; + if (!epicenterTile) + return "input_lack"; + + const leftPx = (epicenterTile.tileX - tileXCount.least) * tileSize + epicenterPosition.innerX; + const rightPx = xSize * tileSize - leftPx; + const topPx = (epicenterTile.tileY - tileYCount.least) * tileSize + epicenterPosition.innerY; + const bottomPx = ySize * tileSize - topPx; + + if (leftPx > rightPx) { + const leftTiles = epicenterTile.tileX - tileXCount.least; + tileXCount = { + ...tileXCount, + most: epicenterTile.tileX + leftTiles, + } + } else { + const rightTiles = tileXCount.most - epicenterTile.tileX; + tileXCount = { + ...tileXCount, + least: epicenterTile.tileX - rightTiles, + } + } + + if (topPx > bottomPx) { + const topTiles = epicenterTile.tileY - tileYCount.least; + tileYCount = { + ...tileYCount, + most: epicenterTile.tileY + topTiles, + } + } else { + const bottomTiles = tileYCount.most - epicenterTile.tileY; + tileYCount = { + ...tileYCount, + least: epicenterTile.tileY - bottomTiles, + } + } + + // タイルサイズ再計算 + xSize = tileXCount.most - tileXCount.least + 1; + ySize = tileYCount.most - tileYCount.least + 1; + + if (xSize > ySize) { + tileSize = Math.round(WIDTH / xSize); + } else { + tileSize = Math.round(HEIGHT / ySize); + } + + // 欠けているタイルを取得 + for (let xIndex = 0; xIndex < xSize; xIndex++) { + const itX = xIndex + tileXCount.least; + + for (let yIndex = 0; yIndex < ySize; yIndex++) { + const itY = yIndex + tileYCount.least; + + const itTile = tiles.filter(tile => tile.tileX === itX && tile.tileY === itY)[0]; + if (itTile) + continue; + + tiles.push({ + tileX: itX, + tileY: itY, + }); + } + } + // アセット - const assets: Record> = {} + const assets: Record; + size: number; + }> = {} const assetsList = [ "scales/10", "scales/20", @@ -309,15 +406,20 @@ export default async function generateImage(message: any) { "epicenter", ]; - assetsList.forEach(name => { - const asset = readFileSync(`${import.meta.dirname}/assets/${name}.png`); - + await Promise.all(assetsList.map(async name => { const key = name.split("/").at(-1); if (!key) return; - assets[key] = asset; - }); + const asset = readFileSync(`${import.meta.dirname}/assets/${name}.png`); + const assetMeta = await sharp(asset).metadata(); + const assetSize = assetMeta.width; + + assets[key] = { + buffer: asset, + size: assetSize, + } + })); // 各画像処理 await Promise.all(tiles.map(async (tile, index) => { @@ -364,16 +466,16 @@ export default async function generateImage(message: any) { } } else { about = { - type: "epicenter", + type: "scale", scale: position.scale, } } compounds.push({ ...about, - input: asset, - top: Math.round(top + position.innerY * tileResizedSize), - left: Math.round(left + position.innerX * tileResizedSize), + input: asset.buffer, + top: Math.round(top + position.innerY * tileResizedSize - asset.size / 2), + left: Math.round(left + position.innerX * tileResizedSize - asset.size / 2), }); }); })); @@ -424,6 +526,4 @@ export default async function generateImage(message: any) { const buffer = await result.png().toBuffer(); return buffer; -} - -writeFileSync("result.png", await generateImage(JSON.parse(readFileSync(`${import.meta.dirname}/data.json`, "utf8")))); \ No newline at end of file +} \ No newline at end of file diff --git a/src/feature/earthquake/index.ts b/src/feature/earthquake/index.ts index 06055b8..649b0ee 100644 --- a/src/feature/earthquake/index.ts +++ b/src/feature/earthquake/index.ts @@ -7,6 +7,7 @@ import i18next from "i18next"; import { readFileSync } from "node:fs"; import { EOL } from "node:os"; import { WebSocket } from "ws"; +import generateImage from "@/feature/earthquake/generateImage"; await initI18n(); @@ -104,15 +105,6 @@ const processMessage = async (message: any) => { { console.log("地震発生情報を受信しました"); - if ( - (message.earthquake.maxScale === -1 || - message.earthquake.maxScale === undefined) && - !config.earthquake.canUnknownMaxScale - ) { - console.log("最大震度が不明であり、最大震度が不明な場合の投稿が許可されていないため、スキップします"); - break; - } - if ( message.earthquake.maxScale !== -1 && message.earthquake.maxScale < config.earthquake.requireMaxScale @@ -177,33 +169,61 @@ const processMessage = async (message: any) => { .join(EOL.repeat(2)) .trim(); - await createUeuse({ - text: i18next.t("earthquakeNotice", { - type: typeMessage[message.issue.type] ?? "地震情報", - occuredTime: format(new Date(message.earthquake.time), "yyyy年M月d日 H:mm"), - maxScale: scaleMessages[String(message.earthquake.maxScale)], - epicenter: message.earthquake.hypocenter.name === "" - ? "不明" - : message.earthquake.hypocenter.name, - magnitude: message.earthquake.hypocenter.magnitude === -1 - ? "不明" - : `M${message.earthquake.hypocenter.magnitude.toFixed(1)}`, - depth: message.earthquake.hypocenter.depth === 0 - ? "ごく浅い" - : (message.earthquake.hypocenter.depth === -1 - ? "不明" - : `${message.earthquake.hypocenter.depth}km`), - domesticTsunami: domesticTsunamiMessages[(message.earthquake.domesticTsunami ?? "Unknown")], - foreignTsunami: foreignTsunamiMessages[(message.earthquake.foreignTsunami ?? "Unknown")], - points: pointsMsg === "" - ? "" - : EOL.repeat(2) + pointsMsg, - source: message.issue.source ?? "不明", - comment: message.comments.freeFormComment === "" - ? "" - : EOL + message.comments.freeFormComment + EOL, - }), - }, "地震発生情報"); + let earthquakeUniqid: string | null = null; + + await Promise.allSettled([ + (async () => { + const ueuses = await createUeuse({ + text: i18next.t("earthquakeNotice", { + type: typeMessage[message.issue.type] ?? "地震情報", + occuredTime: format(new Date(message.earthquake.time), "yyyy年M月d日 H:mm"), + maxScale: scaleMessages[String(message.earthquake.maxScale)], + epicenter: message.earthquake.hypocenter.name === "" + ? "不明" + : message.earthquake.hypocenter.name, + magnitude: message.earthquake.hypocenter.magnitude === -1 + ? "不明" + : `M${message.earthquake.hypocenter.magnitude.toFixed(1)}`, + depth: message.earthquake.hypocenter.depth === 0 + ? "ごく浅い" + : (message.earthquake.hypocenter.depth === -1 + ? "不明" + : `${message.earthquake.hypocenter.depth}km`), + domesticTsunami: domesticTsunamiMessages[(message.earthquake.domesticTsunami ?? "Unknown")], + foreignTsunami: foreignTsunamiMessages[(message.earthquake.foreignTsunami ?? "Unknown")], + points: pointsMsg === "" + ? "" + : EOL.repeat(2) + pointsMsg, + source: message.issue.source ?? "不明", + comment: message.comments.freeFormComment === "" + ? "" + : EOL + message.comments.freeFormComment + EOL, + }), + }, "地震発生情報"); + + earthquakeUniqid = ueuses[0]?.uniqid ?? null; + })(), + (async () => { + const result = await generateImage(message); + + if (typeof result === "string") { + console.warn("情報が不足しているため、地震の画像生成ができませんでした"); + return; + } + + while (typeof (earthquakeUniqid as string | null) !== "string") {} + + await createUeuse({ + text: "この地震の震度分布画像を生成しました。", + media: { + photo: [ + result.toString("base64"), + ] + }, + reuseid: (earthquakeUniqid as unknown as string), + }, "震度分布画像"); + })(), + ]); } break; case 552: 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 2d69206..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"; @@ -57,8 +58,19 @@ const cityList = [ "471010", ]; -if (!isMainThread && workerData === "scheduledWeatherNotice") { +if ( + !isMainThread && + typeof workerData === "string" && + 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 { @@ -88,7 +100,7 @@ if (!isMainThread && workerData === "scheduledWeatherNotice") { console.log("天気仮投稿:", provisionalUeuse.uniqid); - weatherReply(provisionalUeuse.uniqid); + await weatherReply(provisionalUeuse.uniqid, workerData.endsWith("Tomorrow")); process.exit(0); } catch (err: any) { console.error("message" in err @@ -98,7 +110,7 @@ if (!isMainThread && workerData === "scheduledWeatherNotice") { } } -export async function weatherReply(uniqid: string) { +export async function weatherReply(uniqid: string, isTomorrow: boolean) { // インデックス const aboutFullLength = 3100; const mem = Memory.memory; @@ -137,21 +149,23 @@ export async function weatherReply(uniqid: string) { }); const data = await res.json(); - const today = data.forecasts[0]; + const itDay = isTomorrow + ? data.forecasts[1] + : data.forecasts[0]; // 天気 - const weather = today.telop ?? "取得できませんでした"; - const maxTemp = today.temperature.max.celsius - ? `${today.temperature.max.celsius}℃` + const weather = itDay.telop ?? "取得できませんでした"; + const maxTemp = itDay.temperature.max.celsius + ? `${itDay.temperature.max.celsius}℃` : "取得できませんでした"; - const minTemp = today.temperature.min.celsius - ? `${today.temperature.min.celsius}℃` + const minTemp = itDay.temperature.min.celsius + ? `${itDay.temperature.min.celsius}℃` : "取得できませんでした"; const chanceOfRain = ( - today.chanceOfRain.T06_12 !== null && - today.chanceOfRain.T06_12 !== "--%" + itDay.chanceOfRain.T06_12 !== null && + itDay.chanceOfRain.T06_12 !== "--%" ) - ? today.chanceOfRain.T06_12 + ? itDay.chanceOfRain.T06_12 : "取得できませんでした"; weatherResults[chunkIndex] += `${i18next.t("weatherReply", { diff --git a/src/index.ts b/src/index.ts index 3a626dd..a2d3bb0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,28 +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(`*/${config.command.interval} * * * *`, async () => { + schedule("56 59 17 * * *", async () => { + new Worker(`${import.meta.dirname}/feature/weatherNotice.js`, { + workerData: "scheduledWeatherNoticeTomorrow", + }); + }); + + 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 diff --git a/src/lib/client.ts b/src/lib/client.ts index a038d99..8fc050b 100644 --- a/src/lib/client.ts +++ b/src/lib/client.ts @@ -15,12 +15,15 @@ export default client; export const createUeuse = async (data: ApiMap["ueuse/create"]["body"], title: string) => { const mem = Memory.memory; - const excessedMessage = "👉返信に続きがあります。"; + const excessedMessage = "**👉返信に続きがあります。**"; let lines = data.text.split(EOL); let firstUniqid = ""; let count = 0; + type ExtractSuccess = T extends { success: true } ? T : never; + const results: ExtractSuccess[] = []; + while (lines.length > 0) { count++; let currentText = ""; @@ -76,6 +79,7 @@ export const createUeuse = async (data: ApiMap["ueuse/create"]["body"], title: s if (response.success) { success = true; postedUniqid = response.uniqid; + results.push(response); break; } @@ -99,4 +103,6 @@ export const createUeuse = async (data: ApiMap["ueuse/create"]["body"], title: s lines.shift(); } } + + return results; } \ No newline at end of file diff --git a/src/lib/config.ts b/src/lib/config.ts index 960c708..6b936d0 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -20,7 +20,6 @@ const schema = z.object({ z.literal(60), z.literal(70), ]), - canUnknownMaxScale: z.boolean(), useHistoryData: z.boolean(), reconnectInterval: z.number().positive(), }),