Compare commits

..

8 Commits

11 changed files with 152 additions and 34 deletions
+19
View File
@@ -1,3 +1,22 @@
# 2026.4.0-alpha.2
- Feat: 地震情報のid除外
- Feat: 新年迎春
- Fix: for文でスキップではなくループの終了を行っていた問題
- Feat: 返信済みかどうかをuniqidのみで判断する機能
- Chg: 時報の時刻フォーマットをHH:mmからH:mmに変更
- Feat: 緊急地震速報(警報)解除
# 2026.4.0-alpha.1
- Del: tsxの開発環境を削除
- Chg: typescriptを5.9.3にダウングレード
- Feat: 緊急地震速報の最大予測震度の上限に99(〜程度以上)を実装
- Chg: 緊急地震速報の最大予測震度で上限と下限が一致する場合に"から〜"を投稿内容に含めないように
- Feat: worker_threadsによる並列処理
- Del: 各地震情報の情報源の信頼できるかどうかの内容を削除
- Chg: 津波予報情報の各時刻情報を実際に取得できる値まで削減(例: 2:03:00 > 2:03)
- Fix: フォロー・フォロー解除に失敗した際にreturnできていない問題
- Feat: デバッグ用とで過去の地震情報を読み込み10秒おきに投稿する機能
# 2026.4.0-alpha.0 # 2026.4.0-alpha.0
- Breaking: noticeUwuzuを0から作り直しました - Breaking: noticeUwuzuを0から作り直しました
- Breaking: 25.12.0-alpha.1までの方針を全て廃止しました - Breaking: 25.12.0-alpha.1までの方針を全て廃止しました
+2
View File
@@ -12,6 +12,8 @@ uwuzu v1.6.3以上で利用可能です。
- 地震発生 - 地震発生
- 緊急地震速報(警報) - 緊急地震速報(警報)
- 津波予報 - 津波予報
- 新年迎春
毎年1/1 0:00に投稿します。
- デバッグモード - デバッグモード
設定で有効化することで、以下の機能が利用できます。 設定で有効化することで、以下の機能が利用できます。
開発以外では使用しないでください。 開発以外では使用しないでください。
+6
View File
@@ -50,6 +50,12 @@ eewNotice: |
📍震源地: {{ epicenter }} 📍震源地: {{ epicenter }}
💪マグニチュード: {{ magnitude }} 💪マグニチュード: {{ magnitude }}
🪨深さ: {{ depth }}{{ areas }} 🪨深さ: {{ depth }}{{ areas }}
eewCancelNotice: |
### ==緊急地震速報(警報)**解除**==
{{ isTest }}
⏰発表時刻: {{ announceTime }}
hnyNotice: |
あけましておめでとうございます。今年は、{{ year }}年です。
commandNotFound: | commandNotFound: |
コマンドが本文から参照できませんでした。 コマンドが本文から参照できませんでした。
Botでは、このアカウントに対してのメンション部分を取り除きます。 Botでは、このアカウントに対してのメンション部分を取り除きます。
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "notice-uwuzu", "name": "notice-uwuzu",
"version": "26.4.0-alpha.0", "version": "26.4.0-alpha.2",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {
+1 -1
View File
@@ -47,7 +47,7 @@ export default async function helpCommand(ueuse: ueuseModule, args: string[]) {
for (let i = 0; i < helps.length; i++) { for (let i = 0; i < helps.length; i++) {
const help = helps[i]; const help = helps[i];
if (!help) if (!help)
break; continue;
summarys += `${i18next.t(`help${help.charAt(0).toUpperCase()}${help.slice(1)}`)}${EOL}`; summarys += `${i18next.t(`help${help.charAt(0).toUpperCase()}${help.slice(1)}`)}${EOL}`;
} }
+28 -19
View File
@@ -17,16 +17,22 @@ try {
{ {
const response = await client.request("me/notification/", { const response = await client.request("me/notification/", {
page: 1,
limit: 20, limit: 20,
}); });
if (response.success) { if (response.success) {
for (const notification of response.data) { for (const [index, notification] of response.data.entries()) {
if (notification.category !== "reply") if (notification.category !== "reply")
break; continue;
if (!notification.valueid) { if (!notification.valueid) {
console.warn("返信通知にvalueidが存在しないため、スキップします"); console.warn("返信通知にvalueidが存在しないため、スキップします");
continue;
}
const mem = Memory.memory;
if (mem.lastReadReply === notification.valueid) {
break; break;
} }
@@ -36,7 +42,12 @@ try {
if (!ueuseResponse.success || !ueuseResponse.data[0]) { if (!ueuseResponse.success || !ueuseResponse.data[0]) {
console.warn("返信通知からユーズを参照できないため、スキップします"); console.warn("返信通知からユーズを参照できないため、スキップします");
break; continue;
}
if (index === 0) {
const mem = Memory.memory;
mem.lastReadReply = ueuseResponse.data[0].uniqid;
} }
ueuses.push(ueuseResponse.data[0]); ueuses.push(ueuseResponse.data[0]);
@@ -48,11 +59,24 @@ try {
{ {
const response = await client.request("ueuse/mentions", { const response = await client.request("ueuse/mentions", {
page: 1,
limit: 20, limit: 20,
}); });
if (response.success) { if (response.success) {
ueuses.push(...response.data); for (const [index, mention] of response.data.entries()) {
const mem = Memory.memory;
if (mem.lastReadMention === mention.uniqid) {
break;
}
if (index === 0) {
const mem = Memory.memory;
mem.lastReadMention = mention.uniqid;
}
ueuses.push(mention);
}
} else { } else {
console.warn("メンションの取得に失敗しましたが、続行します"); console.warn("メンションの取得に失敗しましたが、続行します");
} }
@@ -61,13 +85,6 @@ try {
ueuses = [...new Set(ueuses)]; ueuses = [...new Set(ueuses)];
for (const ueuse of ueuses) { for (const ueuse of ueuses) {
const repliedUeuse = (Memory.memory["repliedUeuse"] as string[]);
if (repliedUeuse.includes(ueuse.uniqid)) {
console.log("既に応答しているため、スキップします");
break;
}
const mem = Memory.memory; const mem = Memory.memory;
let text = ueuse.text; let text = ueuse.text;
text = text.replace(`@${mem.userid}`, ""); text = text.replace(`@${mem.userid}`, "");
@@ -119,14 +136,6 @@ try {
console.warn("ユーズの作成に失敗しました:", response.error_code); console.warn("ユーズの作成に失敗しました:", response.error_code);
break; break;
} }
{
const repliedUeuse = (Memory.memory["repliedUeuse"] as string[]);
repliedUeuse.push(ueuse.uniqid);
const mem = Memory.memory;
mem["repliedUeuse"] = repliedUeuse;
Memory.memory = mem;
}
} }
process.exit(0); process.exit(0);
+36 -2
View File
@@ -1,6 +1,7 @@
import client from "@/lib/client"; import client from "@/lib/client";
import config from "@/lib/config"; import config from "@/lib/config";
import initI18n from "@/lib/i18n"; import initI18n from "@/lib/i18n";
import Memory from "@/lib/memory";
import { format } from "date-fns"; import { format } from "date-fns";
import i18next from "i18next"; import i18next from "i18next";
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
@@ -39,11 +40,23 @@ if (config.earthquake?.useHistoryData) {
? JSON.parse(event.data) ? JSON.parse(event.data)
: event.data; : event.data;
} catch (err) { } catch (err) {
console.error(`メッセージのパースでエラーが発生: ${err}`); console.error(`地震情報メッセージのパースでエラーが発生: ${err}`);
return;
}
const id = message.id ?? message._id;
const mem = Memory.memory;
if (mem.processedInfo.includes(id) && !config.debug) {
console.log("重複した地震情報: ", message.id);
return; return;
} }
processMessage(message); processMessage(message);
if (!config.debug) {
mem.processedInfo.concat([id]);
Memory.memory = mem;
}
}); });
} }
@@ -65,6 +78,9 @@ const processMessage = async (message: any) => {
} }
switch (message.code) { switch (message.code) {
case 555:
console.log("ピアの地域分布情報を受信しました");
break;
case 551: case 551:
{ {
console.log("地震発生情報を受信しました"); console.log("地震発生情報を受信しました");
@@ -95,7 +111,7 @@ const processMessage = async (message: any) => {
for (const point of message.points) { for (const point of message.points) {
const scaleMsg = scaleMessages[String(point.scale)]; const scaleMsg = scaleMessages[String(point.scale)];
if (!scaleMsg) if (!scaleMsg)
break; continue;
points[scaleMsg]?.push(point); points[scaleMsg]?.push(point);
} }
@@ -221,6 +237,24 @@ const processMessage = async (message: any) => {
{ {
console.log("緊急地震速報(警報)を受信しました"); console.log("緊急地震速報(警報)を受信しました");
if (message.cancelled) {
const response = await client.request("ueuse/create", {
text: i18next.t("eewCancelNotice", {
isTest: message.test
? "⚒️これは**テストです。**"
: "🚨これは**テストではありません。**",
announceTime: format(new Date(message.issue.time), "yyyy年M月d日 H:mm:ss"),
}),
});
if (!response.success) {
console.warn("ユーズの作成に失敗しました:", response.error_code);
break;
}
console.log("緊急地震速報(警報)解除情報を投稿:", response.uniqid);
}
const kindMessages: Record<string, string> = { const kindMessages: Record<string, string> = {
"10": "⏳主要動は、**未到達と予測**されています。", "10": "⏳主要動は、**未到達と予測**されています。",
"11": "🫨主要動が、**既に到達していると予測**されています。", "11": "🫨主要動が、**既に到達していると予測**されています。",
+30
View File
@@ -0,0 +1,30 @@
import client from "@/lib/client";
import initI18n from "@/lib/i18n";
import { format } from "date-fns";
import i18next from "i18next";
import { parentPort } from "node:worker_threads";
await initI18n();
parentPort?.on("message", async () => {
console.log("新年迎春の投稿を行います");
try {
const response = await client.request("ueuse/create", {
text: i18next.t("hnyNotice", { year: String(new Date().getFullYear()) }),
});
if (!response.success) {
console.warn("新年迎春投稿に失敗しました:", response.error_code);
process.exit(1);
}
console.log("新年迎春投稿:", `${response.uniqid} (${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);
}
});
+1 -1
View File
@@ -8,7 +8,7 @@ console.log("時報の投稿を行います");
try { try {
const response = await client.request("ueuse/create", { const response = await client.request("ueuse/create", {
text: i18next.t("timeNotice", { time: format(new Date(), "HH:mm") }), text: i18next.t("timeNotice", { time: format(new Date(), "H:mm") }),
}); });
if (!response.success) { if (!response.success) {
+22 -6
View File
@@ -30,14 +30,30 @@ try {
process.exit(1); process.exit(1);
} }
schedule("0 * * * *", async () => { try {
schedule("0 * * * *", async () => {
new Worker(`${import.meta.dirname}/feature/timeNotice.js`); new Worker(`${import.meta.dirname}/feature/timeNotice.js`);
}); });
schedule("0 7 * * *", async () => { schedule("0 7 * * *", async () => {
new Worker(`${import.meta.dirname}/feature/weatherNotice.js`); new Worker(`${import.meta.dirname}/feature/weatherNotice.js`);
}); });
schedule(`*/${config.command.interval} * * * *`, async () => { schedule(`*/${config.command.interval} * * * *`, async () => {
new Worker(`${import.meta.dirname}/feature/command/index.js`); 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("");
});
} catch (err: any) {
console.error("message" in err
? err.message
: err);
}
+3 -1
View File
@@ -9,8 +9,10 @@ class MemoryClass {
constructor() { constructor() {
if (!existsSync(path)) { if (!existsSync(path)) {
writeFileSync(path, JSON.stringify({ writeFileSync(path, JSON.stringify({
repliedUeuse: [], processedInfo: [],
permissions: {}, permissions: {},
lastReadMention: "",
lastReadReply: "",
userid: "", userid: "",
})); }));
} }