This commit is contained in:
Last2014 2025-07-06 22:11:59 +09:00
parent 05194ad7b8
commit 22ec582e0a
11 changed files with 200 additions and 9 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@
/config.ts /config.ts
log* log*
/iolog.json

View File

@ -1,5 +1,5 @@
# # ##### ####### ### ##### ####### # # # # # # ###### # # # # ###### ##### ### ###### ####### # # # # # # ###### # #
## # # # # # ## # # # # # # # # # # # ## # # # # # # # # # # # # # # # # #
# ## # # # # # # ###### # # # # # # # # ## # # # ## # # # # # # ####### # # # # # # # # ## # #
# ## # # # # ## # # # # # # # # # # # ## # # # # # # # # # # # # # # #
# # ##### # ### ##### ####### ###### # # ###### ###### ####### # # ###### # ### ###### ####### ###### # # ###### ###### ######

View File

@ -22,6 +22,20 @@ const config: configTypes = {
splitCount: 4, // 返信の分割数 splitCount: 4, // 返信の分割数
}, },
// 緊急時設定
emergency: {
function: true, // 緊急時のコンソール表示
mail: {
function: true, // 緊急時のメール送信
host: "smtp.example.com", // SMTPサーバー
port: 465, // SMTPポート
user: "mailUser@example.com", // BOTメール送信元
password: "mailPassword", // SMTPパスワード
secure: false, // SMTPsecure設定
to: "admin@noticeuwuzu.example.com" // 緊急時メール送信先(配列可)
}
},
apiToken: "TOKEN_EXAMPLE", // BOTアカウントのAPIトークン apiToken: "TOKEN_EXAMPLE", // BOTアカウントのAPIトークン
uwuzuServer: "uwuzu.example.com", // uwuzuのサーバー uwuzuServer: "uwuzu.example.com", // uwuzuのサーバー
}; };

View File

@ -13,6 +13,10 @@ asciiArt();
// フォローバック機能読み込み // フォローバック機能読み込み
import followBack from "./scripts/followBack.js"; import followBack from "./scripts/followBack.js";
// 正常終了確認読み込み
import successExit from "./scripts/successExit.js";
successExit();
// 地震情報観測開始 // 地震情報観測開始
earthquakeNotice(); earthquakeNotice();

View File

@ -1,11 +1,12 @@
{ {
"name": "noticeuwuzu", "name": "noticeuwuzu",
"version": "v4.3.3@uwuzu1.5.4", "version": "v5.0@uwuzu1.5.4",
"description": "uwuzu Notice Bot", "description": "uwuzu Notice Bot",
"main": "dist/main.js", "main": "dist/main.js",
"scripts": { "scripts": {
"start": "node .", "start": "node .",
"build": "tsc", "build": "tsc",
"main": "tsc && node .",
"dev": "tsx main.ts" "dev": "tsx main.ts"
}, },
"keywords": [ "keywords": [
@ -28,9 +29,11 @@
"@types/date-fns": "^2.5.3", "@types/date-fns": "^2.5.3",
"@types/dotenv": "^6.1.1", "@types/dotenv": "^6.1.1",
"@types/node-cron": "^3.0.11", "@types/node-cron": "^3.0.11",
"@types/nodemailer": "^6.4.17",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"fs": "^0.0.1-security", "fs": "^0.0.1-security",
"node-cron": "^4.1.1", "node-cron": "^4.1.1",
"nodemailer": "^7.0.4",
"tsx": "^4.20.3", "tsx": "^4.20.3",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"ws": "^8.18.3" "ws": "^8.18.3"

View File

@ -1,5 +1,6 @@
import WebSocket from "ws"; import WebSocket from "ws";
import { differenceInMinutes, subMinutes } from "date-fns"; import { differenceInMinutes, subMinutes } from "date-fns";
import sendMail from "../src/mailer.js";
import config from "../config.js"; import config from "../config.js";
@ -276,6 +277,38 @@ async function event(earthquakeInfo: any): Promise<void> {
maxScale += "震度7"; maxScale += "震度7";
} }
// 警告
if (
earthquakeInfo.earthquake.maxScale >= 60 ||
config.emergency.function
) {
console.log("----------------");
console.log("震度6強以上の地震を受信しました");
console.log("サーバーがダウンする可能性があります");
// メール送信
if (config.emergency.function) {
sendMail({
from: "noticeUwuzu自動送信",
to: config.emergency.mail.to,
subject: "【警告】震度6強以上の地震を受信しました",
html: `
noticeUwuzu自動送信によるメールです
BOT管理者さんnoticeUwuzu自動送信メールです
6
`
});
console.log("管理者へ警告メールを送信しました");
}
console.log("----------------");
}
// 対象地域 // 対象地域
let areas: string = ""; let areas: string = "";
@ -300,6 +333,7 @@ async function event(earthquakeInfo: any): Promise<void> {
if ( if (
earthquakeInfo.earthquake.hypocenter.depth !== null || earthquakeInfo.earthquake.hypocenter.depth !== null ||
earthquakeInfo.earthquake.hypocenter.depth !== undefined ||
earthquakeInfo.earthquake.hypocenter.depth !== -1 earthquakeInfo.earthquake.hypocenter.depth !== -1
) { ) {
if (earthquakeInfo.earthquake.hypocenter.depth === 0) { if (earthquakeInfo.earthquake.hypocenter.depth === 0) {
@ -314,6 +348,7 @@ async function event(earthquakeInfo: any): Promise<void> {
if( if(
earthquakeInfo.earthquake.hypocenter.magnitude !== null || earthquakeInfo.earthquake.hypocenter.magnitude !== null ||
earthquakeInfo.earthquake.hypocenter.magnitude !== undefined ||
earthquakeInfo.earthquake.hypocenter.magnitude !== -1 earthquakeInfo.earthquake.hypocenter.magnitude !== -1
) { ) {
magnitude = `マグニチュード:${earthquakeInfo.earthquake.hypocenter.magnitude}`; magnitude = `マグニチュード:${earthquakeInfo.earthquake.hypocenter.magnitude}`;

View File

@ -16,7 +16,7 @@ export default async function followBack() {
const meData: types.meApi = await resMe.json(); const meData: types.meApi = await resMe.json();
console.log(`BOTプロフィール${meData}`); console.log(`BOTプロフィール${JSON.stringify(meData)}`);
const followers: Array<string> = meData.follower; const followers: Array<string> = meData.follower;

64
scripts/successExit.ts Normal file
View File

@ -0,0 +1,64 @@
import * as fs from "fs";
import { format, isAfter } from "date-fns";
import { parse } from "date-fns/fp";
import config from "../config.js";
import sendMail from "../src/mailer.js";
const formatParse = parse(new Date(), "yyyy-MM-dd HH:mm:ss.SSS")
// 初期化
if (fs.existsSync("iolog.json") === false) {
fs.writeFileSync("iolog.json", JSON.stringify({
start: format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS"),
stop: "",
}), "utf-8");
}
export default function successExit() {
const iolog = JSON.parse(fs.readFileSync("iolog.json", "utf-8"));
if (config.emergency.function) {
// 前回の終了確認
const start = formatParse(iolog.start);
const stop = formatParse(iolog.stop);
if (isAfter(start, stop)) {
console.log("前回の終了が適切でない可能性があります");
if (config.emergency.mail.function) {
sendMail({
from: "noticeUwuzu自動送信",
to: config.emergency.mail.to,
subject: "【警告】前回終了が不適切な可能性",
html: `
noticeUwuzu自動送信によるメールです
BOT管理者さんnoticeUwuzu自動送信メールです
BOTの前回終了で不適切なデータを検出しました
OSからのシャットダウンを使用してください
BOTのプログラムが破損していないかご確認ください
`
});
}
console.log("----------------");
}
}
// 起動時に起動時刻を保存
iolog.start = format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
fs.writeFileSync("iolog.json", JSON.stringify(iolog), "utf-8");
// 終了時に終了時刻を保存
process.on("exit", () => {
const iolog = JSON.parse(fs.readFileSync("iolog.json", "utf-8"));
iolog.stop = format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
fs.writeFileSync("iolog.json", JSON.stringify(iolog), "utf-8");
});
}
successExit();

View File

@ -4,8 +4,6 @@ import type * as types from "types/types";
import config from "../config.js"; import config from "../config.js";
export default async function timeNotice() { export default async function timeNotice() {
console.log("----------------");
// 停止時間 // 停止時間
// 時刻取得 // 時刻取得
const start = config.time.stopTimes.start; const start = config.time.stopTimes.start;
@ -24,6 +22,7 @@ export default async function timeNotice() {
} }
if (inRange) { if (inRange) {
console.log("----------------");
console.log("時報休止期間のため投稿されませんでした"); console.log("時報休止期間のため投稿されませんでした");
return; return;
} else { } else {
@ -41,6 +40,7 @@ export default async function timeNotice() {
const ueuseData: types.ueuseCreateApi = await resUeuse.json(); const ueuseData: types.ueuseCreateApi = await resUeuse.json();
console.log("----------------");
console.log(`時報投稿:${JSON.stringify(ueuseData)}`); console.log(`時報投稿:${JSON.stringify(ueuseData)}`);
} }
} }

53
src/mailer.ts Normal file
View File

@ -0,0 +1,53 @@
import config from "../config";
import * as nodemailer from "nodemailer";
import type SMTPTransport from "nodemailer/lib/smtp-transport";
export interface EmailMessage {
from: string;
to: string | string[];
subject: string;
text?: string;
html?: string;
}
async function createTransporter() {
const transporter = nodemailer.createTransport({
host: config.emergency.mail.host,
port: config.emergency.mail.port,
secure: config.emergency.mail.secure,
auth: {
user: config.emergency.mail.user,
pass: config.emergency.mail.password,
},
} as SMTPTransport.Options);
// 接続テスト
try {
await transporter.verify();
console.log("SMTPサーバーに接続できました");
} catch (error) {
console.error("SMTP接続テストに失敗:", error);
throw error;
}
return transporter;
}
export default async function sendMail(message: EmailMessage): Promise<void> {
try {
const transporter = await createTransporter();
await transporter.sendMail({
from: message.from,
to: Array.isArray(message.to) ? message.to.join(",") : message.to,
subject: message.subject,
text: message.text,
html: message.html,
});
console.log("メール送信成功");
} catch (error) {
console.error("メール送信に失敗しました:", error);
throw error;
}
}

17
types/config.d.ts vendored
View File

@ -19,11 +19,28 @@ interface timeTypes {
stopTimes: stopsTypes; stopTimes: stopsTypes;
} }
interface emergencyMailTypes {
function: Boolean;
host: string | undefined;
port: number;
user: string;
password: string;
secure: Boolean;
to: string;
}
interface emergencyTypes {
function: Boolean;
mail: emergencyMailTypes;
}
export interface configTypes { export interface configTypes {
time: timeTypes, time: timeTypes,
earthquake: earthquakeTypes; earthquake: earthquakeTypes;
weather: weatherTypes; weather: weatherTypes;
emergency: emergencyTypes;
apiToken: string; apiToken: string;
uwuzuServer: string; uwuzuServer: string;
} }