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"; import { isMainThread, workerData } from "node:worker_threads"; const cityList = [ "016010", "020010", "030010", "040010", "050010", "060010", "070010", "080010", "090010", "100010", "110010", "120010", "130010", "140010", "150010", "160010", "170010", "180010", "190010", "200010", "210010", "220010", "230010", "240010", "250010", "260010", "270000", "280010", "290010", "300010", "310010", "320010", "330010", "340010", "350010", "360010", "370000", "380010", "390010", "400010", "410010", "420010", "430010", "440010", "450010", "460010", "471010", ]; 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 { let provisionalUeuse; let success = false; for (let attempt = 1; attempt <= config.ueuse.maxRetries; attempt++) { provisionalUeuse = await client.request("ueuse/create", { text: i18next.t("weatherProvisional"), }); if (provisionalUeuse.success) { success = true; break; } console.warn(`天気仮投稿に失敗しました (試行 ${attempt}/${config.ueuse.maxRetries}):`, provisionalUeuse.error_code); if (attempt < config.ueuse.maxRetries) { await new Promise(resolve => setTimeout(resolve, 1000)); } } if (!success || !provisionalUeuse?.success) { console.error("天気仮投稿の全試行に失敗したため、終了します。"); process.exit(1); } console.log("天気仮投稿:", provisionalUeuse.uniqid); await weatherReply(provisionalUeuse.uniqid, workerData.endsWith("Tomorrow")); process.exit(0); } catch (err: any) { console.error("message" in err ? err.message : err); process.exit(1); } } export async function weatherReply(uniqid: string, isTomorrow: boolean) { // インデックス const aboutFullLength = 3100; const mem = Memory.memory; const splitCount = Math.round(aboutFullLength / mem.max_length); const total = cityList.length; const chunkSizes = Array(splitCount).fill(0).map((_, i) => Math.floor((total + i) / splitCount) ); // 分割インデックス let start = 0; const ranges: [number, number][] = chunkSizes.map(size => { const range: [number, number] = [start, start + size]; start += size; return range; }); // 配列作成 const weatherResults = Array(splitCount).fill(""); // package.json取得 const packageJson = JSON.parse(readFileSync(`${import.meta.dirname}/../../package.json`, "utf-8")); // 天気取得 for (let chunkIndex = 0; chunkIndex < splitCount; chunkIndex++) { const range = ranges[chunkIndex]; if (!range) continue; const [chunkStart, chunkEnd] = range; for (let i = chunkStart; i < chunkEnd; i++) { const res = await fetch(`https://weather.tsukumijima.net/api/forecast/city/${cityList[i]}`, { headers: { "User-Agent": `noticeUwuzu/${packageJson.version}`, }, }); const data = await res.json(); const itDay = isTomorrow ? data.forecasts[1] : data.forecasts[0]; // 天気 const weather = itDay.telop ?? "取得できませんでした"; const maxTemp = itDay.temperature.max.celsius ? `${itDay.temperature.max.celsius}℃` : "取得できませんでした"; const minTemp = itDay.temperature.min.celsius ? `${itDay.temperature.min.celsius}℃` : "取得できませんでした"; const chanceOfRain = ( itDay.chanceOfRain.T06_12 !== null && itDay.chanceOfRain.T06_12 !== "--%" ) ? itDay.chanceOfRain.T06_12 : "取得できませんでした"; weatherResults[chunkIndex] += `${i18next.t("weatherReply", { city: data.location.city, weather, maxTemp, minTemp, chanceOfRain, })}${EOL.repeat(2)}`; } } // 分割投稿 for (let i = 0; i < splitCount; i++) { let replyUeuse; let success = false; for (let attempt = 1; attempt <= config.ueuse.maxRetries; attempt++) { replyUeuse = await client.request("ueuse/create", { text: weatherResults[i].trim(), replyid: uniqid, }); if (replyUeuse.success) { success = true; break; } console.warn(`天気返信に失敗しました (試行 ${attempt}/${config.ueuse.maxRetries}):`, replyUeuse.error_code); if (attempt < config.ueuse.maxRetries) { await new Promise(resolve => setTimeout(resolve, config.ueuse.retryInterval)); } } if (!success || !replyUeuse?.success) { console.error("天気返信の全試行に失敗したため、終了します。"); process.exit(1); } console.log("天気返信:", replyUeuse.uniqid); } }