Compare commits

...

16 Commits
v6.5 ... main

Author SHA1 Message Date
Last2014 db719b8312 v8.1.2 2025-08-08 21:34:30 +09:00
Last2014 2966eee889 v8.1.1 2025-08-08 21:31:43 +09:00
Last2014 db5e174dd4 v8.1 2025-08-08 21:29:19 +09:00
Last2014 2b53b8cd3d v8.0.2 2025-08-07 08:56:49 +09:00
Last2014 d20f630e1b v8.0.1 2025-08-07 08:47:11 +09:00
Last2014 644ba912b1 v8.0 2025-08-06 21:27:29 +09:00
Last2014 1ff4b2c429 Merge branch 'main' of https://gitea.last2014.com/last2014/noticeUwuzu 2025-08-05 20:01:36 +09:00
Last2014 0a3bb241a6 v7.5 2025-08-05 19:58:09 +09:00
Last2014 f14999be13 デバッグ用コード削除 2025-08-04 23:45:52 +09:00
Last2014 6ec9831ed4 v7.3@uwuzu1.5.4をリリース 2025-08-04 20:08:23 +09:00
Last2014 718e97ed45 v7.2@uwuzu1.5.4をリリース 2025-08-02 19:39:50 +09:00
Last2014 f6f7030d8c APIパス修正・無駄import削除 2025-08-01 22:03:36 +09:00
Last2014 a2576f961b APIパスの問題・デバッグ用コメントアウトを修正 2025-08-01 19:53:41 +09:00
Last2014 844a9cd058 APIの500問題を修正 2025-08-01 19:39:21 +09:00
Last2014 2a30fdf1e9 v7.0.1(LTS)をリリース 2025-08-01 19:09:18 +09:00
Last2014 dc67845bc8 v7.0(unconfirmed)をリリース 2025-08-01 18:57:15 +09:00
46 changed files with 1306 additions and 201 deletions

View File

@ -4,7 +4,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,

View File

@ -1,21 +1,44 @@
# uwuzuお知らせBOT # noticeUwuzu
# uwuzuお知らせBOTについて # Overview
uwuzuで動作するお知らせBOTです。 Automatic notification bot for uwuzu
# 設定 # Functions
examples/config.tsをプロジェクトルートへ移動し各設定を更新してください。
# サーバー起動 - Time notification
- Earthquake information notification
- Earthquake Early Warning
- Earthquake occurs
- Tsunami forecast
- Weather notification
- Commands that users can use freely
- About BOT
- Command Help
- Report to the operator
- Follow back
- Unfollow
- Weather Repost
- Startup requirements check
- Check package existence
- Required package version check
- Check uwuzu API
- Check the configuration file
- Version change notification
- ASCII art at startup
- Confirm normal completion
``` # Config
An example configuration file is available in `examples/config.ts`.
# Start the server
```bash
npm install npm install
npm run build npm run build
npm run start npm run start
``` ```
Recommended Node.js version: v22.16.0
※Node.js・npmがインストールされている必要があります。 # License
[Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0)
# ライセンス
Apache 2.0 License

View File

@ -3,11 +3,12 @@ import config from "../config.js";
export default async function APICheck() { export default async function APICheck() {
try { try {
const req = await fetch(`https://${config.uwuzu.host}/api/me/`, { const req = await fetch(`${config.uwuzu.host}/api/me/`, {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: config.uwuzu.apiToken, token: config.uwuzu.apiToken,
}) }),
cache: "no-store",
}); });
const res = await req.json(); const res = await req.json();

12
checks/legal.ts Normal file
View File

@ -0,0 +1,12 @@
import config from "../config.js";
import { styleText } from "util";
export default function LegalCheck() {
if (
config.legal.terms.length <= 50 ||
config.legal.terms.length <= 50
) {
console.log(styleText("red", "利用規約とプライバシーポリシーは50文字以上にしてください。"));
process.exit();
}
}

View File

@ -3,11 +3,16 @@ import PackagesCheck from "./packages.js";
import ConfigCheck from "./config.js"; import ConfigCheck from "./config.js";
import APICheck from "./api.js"; import APICheck from "./api.js";
import VersionCheck from "./version.js"; import VersionCheck from "./version.js";
import LegalCheck from "./legal.js";
import config from "../config.js";
export default async function Check() { export default async function Check() {
PackagesIsExist(); PackagesIsExist();
PackagesCheck(); PackagesCheck();
ConfigCheck(); ConfigCheck();
if (config.debug === undefined) {
LegalCheck()
}
await APICheck(); await APICheck();
await VersionCheck(); await VersionCheck();
} }

View File

@ -1,35 +1,41 @@
import * as fs from "fs";
import config from "../config.js"; import config from "../config.js";
import { readFileSync, writeFileSync, existsSync } from "fs";
export default async function VersionCheck() { export default async function VersionCheck() {
const nowVersion: string = JSON.parse(fs.readFileSync("package.json", "utf-8")).version; const packageJson = JSON.parse(readFileSync("package.json", "utf-8"));
// 初期化 // 初期化
if (!fs.existsSync("logs/version.txt")) { if (!existsSync("logs/version.txt")) {
fs.writeFileSync( writeFileSync(
"logs/version.txt", "logs/version.txt",
nowVersion, packageJson.version,
"utf-8", "utf-8",
); );
} }
// 最終起動バージョン取得 // 最終起動バージョン取得
const oldVersion = fs.readFileSync("logs/version.txt", "utf-8"); const oldVersion = readFileSync("logs/version.txt", "utf-8");
if (oldVersion !== nowVersion) { if (oldVersion !== packageJson.version) {
try { try {
fs.writeFileSync( writeFileSync(
"logs/version.txt", "logs/version.txt",
nowVersion, packageJson.version,
"utf-8", "utf-8",
); );
await fetch(`https://${config.uwuzu.host}/api/ueuse/create`, { const releaseUrl = `${packageJson.repository.url}/releases/tag/${packageJson.tag}`;
await fetch(`${config.uwuzu.host}/api/ueuse/create`, {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: config.uwuzu.apiToken, token: config.uwuzu.apiToken,
text: `${nowVersion}にBOTがアップデートされました`, text: `
${packageJson.version}BOTがアップデートされました
${releaseUrl}
`,
}), }),
cache: "no-store",
}); });
} catch (err) { } catch (err) {
console.log("アップデート通知にエラーが発生しました: ", err); console.log("アップデート通知にエラーが発生しました: ", err);

2
data/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -17,15 +17,16 @@ const config: configTypes = {
areasCsvUrl: "https://raw.githubusercontent.com/p2pquake/epsp-specifications/master/epsp-area.csv", // 対象地域CSVファイルのURL areasCsvUrl: "https://raw.githubusercontent.com/p2pquake/epsp-specifications/master/epsp-area.csv", // 対象地域CSVファイルのURL
maxScaleMin: 30, // 地震発生の際の最低震度(10-70) maxScaleMin: 30, // 地震発生の際の最低震度(10-70)
}, },
// 天気お知らせ設定
weather: { weather: {
splitCount: 4, // 返信の分割数 splitCount: 4, // 返信の分割数
}, },
// 緊急時設定 // 緊急時設定
emergency: { emergency: {
function: true, // 緊急時のコンソール表示 isEnabled: true, // 緊急時のコンソール表示
mail: { mail: {
function: true, // 緊急時のメール送信 isEnabled: true, // 緊急時のメール送信
host: "smtp.example.com", // SMTPサーバー host: "smtp.example.com", // SMTPサーバー
port: 465, // SMTPポート port: 465, // SMTPポート
user: "mailUser@example.com", // BOTメール送信元 user: "mailUser@example.com", // BOTメール送信元
@ -34,10 +35,31 @@ const config: configTypes = {
to: "admin@noticeuwuzu.example.com", // 緊急時メール送信先(配列可) to: "admin@noticeuwuzu.example.com", // 緊急時メール送信先(配列可)
}, },
}, },
// /report設定
report: {
isEnabled: true, // 有効/無効
message: "", // 報告者へのメッセージ
},
// 規約等
legal: {
terms: `
`, // 利用規約
privacy: `
`, // プライバシーポリシー
},
// 管理者情報設定
admin: {
name: "あどみん", // BOT管理者名
showMail: false, // メールアドレスを公開するか(false非公開/文字列:メールアドレス)
panel: { // 管理パネル
isEnabled: true, // 有効/無効
port: 74919, // 配信ポート
},
},
// uwuzuサーバー設定
uwuzu: { uwuzu: {
apiToken: "TOKEN_EXAMPLE", apiToken: "TOKEN_EXAMPLE", // APIトークン
clientToken: "TOKEN_EXAMPLE", host: "https://uwuzu.example.com", // サーバーホスト
host: "uwuzu.example.com",
}, },
}; };

47
main.ts
View File

@ -9,33 +9,54 @@ import * as cron from "node-cron";
// 機能読み込み // 機能読み込み
import timeNotice from "./scripts/timeNotice.js"; import timeNotice from "./scripts/timeNotice.js";
import weatherNotice from "./scripts/weatherNotice.js"; import { weatherNotice } from "./scripts/weatherNotice.js";
import earthquakeNotice from "./scripts/earthquakeNotice.js"; import earthquakeNotice from "./scripts/earthquakeNotice.js";
import EventDays from "./scripts/eventday.js";
import Commands from "./scripts/commands/main.js";
// アスキーアート読み込み // その他機能
import asciiArt from "./scripts/asciiart.js"; import asciiArt from "./scripts/asciiart.js";
asciiArt(); asciiArt();
// フォロー機能読み込み
import follows from "./scripts/follow/main.js";
// 正常終了確認読み込み
import successExit from "./scripts/successExit.js"; import successExit from "./scripts/successExit.js";
successExit(); successExit();
// 地震情報観測開始 // 地震情報観測開始
earthquakeNotice(); earthquakeNotice();
/*// () // 時報(1時間/1回)
cron.schedule("0 * * * *", () => { cron.schedule("0 * * * *", () => {
timeNotice(); timeNotice();
follows();
}); });
// 天気お知らせ(毎日7:01) // コマンド(10分/1回)
cron.schedule("10 0 7 * * *", () => { cron.schedule("*/10 * * * *", () => {
weatherNotice(); Commands();
});*/ });
// 祝日などお知らせ(毎日0:00)
cron.schedule("0 0 * * *", () => {
EventDays();
});
// 天気お知らせ(毎日7:00)
import { setTimeout } from "timers";
cron.schedule("0 7 * * *", () => {
setTimeout(() => {
weatherNotice();
}, 100);
});
// 管理パネル
import AdminPanel from "./panel/main.js";
import { styleText } from "util";
(async () => {
await AdminPanel();
})();
// 起動表示 // 起動表示
console.log("BOTサーバーが起動しました"); console.log("BOTサーバーが起動しました");
import config from "./config.js";
if (config.debug !== undefined) {
console.log(styleText(["bgRed", "cyan", "bold"], "デバッグモードで起動中"));
}

View File

@ -1,42 +1,56 @@
{ {
"name": "notice-uwuzu", "name": "notice-uwuzu",
"version": "v6.5(LTS)@uwuzu1.5.4", "version": "v8.1.2@uwuzu1.6.1",
"tag": "v8.1.2",
"description": "Notice Bot for uwuzu", "description": "Notice Bot for uwuzu",
"main": "dist/main.js", "main": "dist/main.js",
"scripts": { "scripts": {
"start": "node .", "start": "node .",
"build": "tsc", "build": "tsc",
"main": "tsc && node .", "main": "tsc && node .",
"dev": "tsx main.ts" "dev": "tsx main.ts",
"clean": "tsc && node dist/scripts/clean/main.js"
},
"repository": {
"type": "git",
"url": "https://gitea.last2014.com/last2014/noticeUwuzu"
}, },
"keywords": [ "keywords": [
"uwuzu", "uwuzu",
"bot", "bot",
"cron", "cron",
"notice", "notice",
"mail",
"weather", "weather",
"time", "time",
"earthquake" "earthquake",
"command",
"commands"
], ],
"author": { "author": {
"name": "Last2014", "name": "Last2014",
"url": "https://last2014.com", "url": "https://last2014.com",
"email": "info@last2014.com" "email": "info@last2014.com"
}, },
"contributors": [],
"license": "Apache-2.0", "license": "Apache-2.0",
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@types/date-fns": "^2.5.3", "@types/date-fns": "^2.5.3",
"@types/dotenv": "^6.1.1", "@types/dotenv": "^6.1.1",
"@types/express": "^5.0.3",
"@types/node": "^24.0.7",
"@types/node-cron": "^3.0.11", "@types/node-cron": "^3.0.11",
"@types/nodemailer": "^6.4.17", "@types/nodemailer": "^6.4.17",
"@types/node": "^24.0.7",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"child_process": "^1.0.2",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"express": "^5.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", "nodemailer": "^7.0.4",
"typescript": "^5.8.3", "timers": "^0.1.1",
"typescript": "^5.9.2",
"ws": "^8.18.3" "ws": "^8.18.3"
}, },
"devDependencies": { "devDependencies": {

54
panel/main.ts Normal file
View File

@ -0,0 +1,54 @@
import express from "express";
import * as os from "os";
import config from "../config.js";
import { NetworkInterfaceDetails } from "types/types";
// バックエンドルーティング
import CommandExecute from "./route/command.js";
import ueusePost from "./route/ueuse.js";
import WeatherUeuse from "./route/weather.js";
import API from "./route/api.js";
import Token from "./route/token.js";
import Debug from "./route/debug.js";
export default async function AdminPanel() {
// 無効
if (!config.admin.panel.isEnabled) {
return;
}
// 管理パネル
const app = express();
const port = config.admin.panel.port;
// ルーティング
app.use(ueusePost);
app.use(CommandExecute);
app.use(WeatherUeuse);
app.use(API);
app.use(Token);
app.use(Debug);
app.use(express.static("panel/public"));
app.listen(port, () => {
console.log(`http://${LocalIP()}:${port} で管理パネルを起動しました`);
});
}
function LocalIP() {
const interfaces = os.networkInterfaces();
for (const name in interfaces) {
const iface: any = interfaces[name];
for (const i of iface) {
const details: NetworkInterfaceDetails = i;
if (details.family === 'IPv4' && details.internal !== true) {
return details.address;
}
}
}
return "localhost";
}

40
panel/public/index.html Normal file
View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>管理パネル</title>
<!-- パッケージ -->
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://code.iconify.design/iconify-icon/3.0.0/iconify-icon.min.js"></script>
<script>
import "iconify-icon";
</script>
</head>
<body class="dark:bg-gray-950 dark:text-white text-center">
<h1 class="text-4xl font-bold">noticeUwuzu管理パネル</h1>
<button id="commandExec" class="border rounded-[10px] p-[5px] m-[10px] mt-[20px] cursor-pointer">
コマンド実行
</button>
<button id="weatherUeuse" class="border rounded-[10px] p-[5px] m-[10px] mt-[20px] cursor-pointer">
天気お知らせ
</button>
<button id="eventdayUeuse" class="border rounded-[10px] p-[5px] m-[10px] mt-[20px] cursor-pointer">
祝日等お知らせ
</button>
<button id="ueuse" class="border rounded-[10px] p-[5px] m-[10px] mt-[20px] cursor-pointer">
ユーズ投稿
</button>
<button id="api" class="border rounded-[10px] p-[5px] m-[10px] mt-[20px] cursor-pointer">
API使用
</button>
<script src="/script.js"></script>
</body>
</html>

106
panel/public/script.js Normal file
View File

@ -0,0 +1,106 @@
document.getElementById("commandExec").addEventListener("click", async () => {
const req = await fetch("/actions/command-execute", {
method: "POST",
});
const res = await req.text();
if (res === "Accepted") {
alert("コマンド実行を受け付けました");
} else {
alert(`コマンド実行の要求にエラーが発生しました:${res}`);
}
});
document.getElementById("weatherUeuse").addEventListener("click", async () => {
const req = await fetch("/actions/weather", {
method: "POST",
});
const res = await req.text();
if (res === "Accepted") {
alert("天気お知らせを受け付けました");
} else {
alert(`天気お知らせの要求にエラーが発生しました:${res}`);
}
});
document.getElementById("eventdayUeuse").addEventListener("click", async () => {
const req = await fetch("/actions/eventday", {
method: "POST",
});
const res = await req.text();
if (res === "Accepted") {
alert("祝日等お知らせを受け付けました");
} else {
alert(`祝日等お知らせの要求にエラーが発生しました:${res}`);
}
});
document.getElementById("ueuse").addEventListener("click", async () => {
const text = prompt("ユーズ内容").toLowerCase();
if (text === "") {
alert("ユーズ内容がありません。");
return;
}
const nsfw = confirm("NSFWにしますか");
const req = await fetch("/actions/ueuse", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
text, text,
nsfw: nsfw,
}),
});
const res = await req.text();
if (res === "Success") {
alert("ユーズ投稿を受け付けました");
} else {
alert(`ユーズ投稿の要求にエラーが発生しました:${res}`);
}
});
document.getElementById("api").addEventListener("click", async () => {
const token = await (await fetch("/actions/token", {
method: "GET",
})).text();
const endpoint = prompt("エンドポイント", "/serverinfo-api").toLowerCase();
if (endpoint === "") {
alert("エンドポイントが設定されていません。");
return;
}
const body = prompt("body(JSON)", `{"token": "${token}"}`).toLowerCase();
if (body === "") {
alert("bodyが設定されていません。");
return;
}
const req = await fetch("/actions/api", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
endpoint: endpoint,
body: JSON.parse(body),
}),
});
const res = await req.text();
alert(res);
});

34
panel/route/api.ts Normal file
View File

@ -0,0 +1,34 @@
import express from "express";
const API = express.Router();
import config from "../../config.js";
API.use(express.json());
API.use(express.urlencoded({ extended: true }));
API.post("/actions/api", async (req, res, next) => {
const endpoint = req.body.endpoint;
const body = req.body.body;
try {
const apiReq = await fetch(`${config.uwuzu.host}/api${endpoint}`, {
method: "POST",
body: JSON.stringify(body),
});
const apiRes = await apiReq.json();
res.status(200)
.send(apiRes);
} catch(err) {
res.status(500)
.send(`Error: ${err}`);
}
});
API.get("/actions/api", (req, res) => {
res.status(501)
.send("POST Only");
});
export default API;

25
panel/route/command.ts Normal file
View File

@ -0,0 +1,25 @@
import express from "express";
const CommandExecute = express.Router();
import Commands from "../../scripts/commands/main.js";
CommandExecute.post("/actions/command-execute", (req, res) => {
try {
(async () => {
await Commands();
})();
res.status(202)
.send("Accepted");
} catch(err) {
res.status(500)
.send(`Error: ${err}`);
}
});
CommandExecute.get("/actions/command-execute", (req, res) => {
res.status(501)
.send("POST Only");
});
export default CommandExecute;

24
panel/route/debug.ts Normal file
View File

@ -0,0 +1,24 @@
import express from "express";
const Debug = express.Router();
import config from "../../config.js";
Debug.post("/actions/debug", (req, res, next) => {
res.status(501)
.send("GET Only");
});
Debug.get("/actions/debug", (req, res) => {
let debug;
if (config.debug === undefined) {
debug = false;
} else {
debug = true;
}
res.status(200)
.send(debug);
});
export default Debug;

25
panel/route/eventday.ts Normal file
View File

@ -0,0 +1,25 @@
import express from "express";
const EventdayUeuse = express.Router();
import EventDays from "../../scripts/eventday.js";
EventdayUeuse.post("/actions/eventday", (req, res) => {
try {
(async () => {
await EventDays();
})();
res.status(202)
.send("Accepted");
} catch(err) {
res.status(500)
.send(`Error: ${err}`);
}
});
EventdayUeuse.get("/actions/eventday", (req, res) => {
res.status(501)
.send("POST Only");
});
export default EventdayUeuse;

16
panel/route/token.ts Normal file
View File

@ -0,0 +1,16 @@
import express from "express";
const Token = express.Router();
import config from "../../config.js";
Token.post("/actions/token", (req, res, next) => {
res.status(501)
.send("GET Only");
});
Token.get("/actions/token", (req, res) => {
res.status(200)
.send(config.uwuzu.apiToken);
});
export default Token;

44
panel/route/ueuse.ts Normal file
View File

@ -0,0 +1,44 @@
import express from "express";
const ueusePost = express.Router();
import config from "../../config.js";
ueusePost.use(express.json());
ueusePost.use(express.urlencoded({ extended: true }));
ueusePost.post("/actions/ueuse", async (req, res, next) => {
const text = req.body.text;
const nsfw = req.body.nsfw;
try {
const ueuseReq = await fetch(`${config.uwuzu.host}/api/ueuse/create`, {
method: "POST",
body: JSON.stringify({
token: config.uwuzu.apiToken,
text: `
${text}
noticeUwuzuの管理パネルから投稿されました
`,
nsfw: nsfw,
}),
});
const ueuseRes = await ueuseReq.json();
console.log(`ユーズ(管理パネル)${JSON.stringify(ueuseRes)}`);
res.status(200)
.send("Success");
} catch(err) {
res.status(500)
.send(`Error: ${err}`);
}
});
ueusePost.get("/actions/ueuse", (req, res) => {
res.status(501)
.send("POST Only");
});
export default ueusePost;

25
panel/route/weather.ts Normal file
View File

@ -0,0 +1,25 @@
import express from "express";
const WeatherUeuse = express.Router();
import { weatherNotice } from "../../scripts/weatherNotice.js";
WeatherUeuse.post("/actions/weather", (req, res) => {
try {
(async () => {
await weatherNotice();
})();
res.status(202)
.send("Accepted");
} catch(err) {
res.status(500)
.send(`Error: ${err}`);
}
});
WeatherUeuse.get("/actions/weather", (req, res) => {
res.status(501)
.send("POST Only");
});
export default WeatherUeuse;

16
scripts/clean/logsDel.ts Normal file
View File

@ -0,0 +1,16 @@
import { unlinkSync } from "fs";
export default function logsDelete() {
console.log("ログを削除中します");
try {
unlinkSync("logs/boot.json");
unlinkSync("logs/version.txt");
console.log("ログファイルを削除しました");
console.log("----------------");
} catch (err) {
console.log("ログファイルの削除中にエラーが発生しました:\n", err);
process.exit();
}
}

7
scripts/clean/main.ts Normal file
View File

@ -0,0 +1,7 @@
import logsDelete from "./logsDel.js";
import packageLockJsonDelete from "./packageLockDel.js";
import npmInstall from "./npmInstall.js";
logsDelete();
packageLockJsonDelete();
npmInstall();

View File

@ -0,0 +1,23 @@
import { execSync } from "child_process";
export default function npmInstall() {
// npm install実行
console.log("npm installを実行します");
try {
const npmInstall = execSync("npm install");
console.log("----");
console.log("$ npm install");
console.log(npmInstall.toString());
console.log("----");
console.log("npm installを実行しました");
} catch (err) {
console.log("npm installの実行中にエラーが発生しました:\n", err);
process.exit();
}
console.log("初期化が完了しました");
process.exit();
}

View File

@ -0,0 +1,15 @@
import { unlinkSync } from "fs";
export default function packageLockJsonDelete() {
console.log("package-lock.jsonを削除します");
try {
unlinkSync("package-lock.json");
console.log("package-lock.jsonを削除しました");
console.log("----------------");
} catch (err) {
console.log("package-lock.jsonの削除中にエラーが発生しました:\n", err);
process.exit();
}
}

View File

@ -0,0 +1,25 @@
import { ueuse } from "types/types";
import config from "../../config.js";
import { Reply } from "./main.js";
export default async function Follow(data: ueuse) {
const followReq = await fetch(`${config.uwuzu.host}/api/users/follow`, {
method: "POST",
body: JSON.stringify({
token: config.uwuzu.apiToken,
userid: data.account.userid,
}),
cache: "no-store",
});
const followRes = await followReq.json();
console.log("フォロー: ", followRes);
const notice = await Reply(`
${data.account.username}
`, data.uniqid);
console.log("フォロー通知: ", notice);
}

79
scripts/commands/help.ts Normal file
View File

@ -0,0 +1,79 @@
import { ueuse } from "types/types";
import { readFileSync } from "fs";
import { Reply } from "./main.js";
const helpsMin = {
"info": "このBOTについての概要を返信するコマンドです。",
"help": "コマンドの概要を返信します。追記に\`/\`抜きのコマンド名を入力することでそのコマンドの詳細(フル)を返信します。",
"follow": "コマンド送信者をフォローします。",
"unfollow": "コマンド送信者をフォロー解除します。",
"weather": "天気を返信します。",
"report": "運営者に不具合などを報告します。",
"legal terms": "利用規約を返信します。",
"legal privacy": "プライバシーポリシーを返信します。",
} as { [key: string]: string };
const helpsFull = {
"info": `
BOTについての概要を返信するコマンドです
`,
"help": `
\`/\`抜きのコマンド名を入力することでそのコマンドの詳細(フル)を返信します。
`,
"follow": `
使
`,
"unfollow": `
使
`,
"weather": `
毎日7:00の天気を再投稿するわけではなく
`,
"report": `
使
\`/report\`を使用してそのユーズの追記に内容を入力することで使用できます。
`,
"legal terms": `
`,
"legal privacy": `
`,
} as { [key: string]: string };
export default async function Help(data: ueuse) {
const packageJson = JSON.parse(readFileSync("package.json", "utf-8"));
if (
data.abi === "none" ||
data.abi === ""
) {
const helpMsg =
Object.entries(helpsMin)
.map(([command, message]) =>
`\`/${command}\`${message}`
).join('\n');
const ueuse = await Reply(`
${helpMsg}
BOTの概要は\`/info\`をご利用ください。
Wikiを見る${packageJson.repository.url}/wiki
`, data.uniqid);
console.log("ヘルプ:", ueuse);
} else {
const ueuse = await Reply(`
${helpsFull[data.abi]}
${packageJson.repository.url}/wiki
`, data.uniqid);
console.log("ヘルプ:", ueuse);
}
}

65
scripts/commands/info.ts Normal file
View File

@ -0,0 +1,65 @@
import { ueuse } from "types/types";
import { readFileSync } from "fs";
import { Reply } from "./main.js";
import config from "../../config.js";
export default async function Info(data: ueuse) {
const packageJson = JSON.parse(readFileSync("package.json", "utf-8"));
const releaseUrl = `${packageJson.repository.url}/releases/tag/${packageJson.tag}`;
let editor = "";
if (packageJson.author.name !== "Last2014") {
editor = `\nEdited by ${packageJson.author.name}`;
}
let adminMail;
if (config.admin.showMail === false) {
adminMail = "非公開";
} else {
adminMail = config.admin.showMail;
}
let isReport;
if (config.report.isEnabled) {
isReport = "有効";
} else {
isReport = "無効";
}
const ueuse = await Reply(`
BOTについて
BOTはオープンソースソフトウェアであるnoticeUwuzuを利用して運営されています
noticeUwuzuはApache License 2.0
使
${packageJson.version}
${releaseUrl}
${config.admin.name}
${adminMail}
(\`/report\`)${isReport}
\`/help\`をご利用ください。
\`/report\`をご利用ください。
\`/legal terms\`をご利用ください。
\`/legal privacy\`をご利用ください。
Created by Last2014${editor}
`, data.uniqid);
console.log("概要:", ueuse);
}

View File

@ -0,0 +1,7 @@
import { ueuse } from "types/types";
import { Reply } from "../main.js";
import config from "../../../config.js";
export default function PrivacyPolicy(data: ueuse) {
Reply(config.legal.privacy, data.uniqid);
}

View File

@ -0,0 +1,7 @@
import { ueuse } from "types/types";
import { Reply } from "../main.js";
import config from "../../../config.js";
export default function Terms(data: ueuse) {
Reply(config.legal.terms, data.uniqid);
}

147
scripts/commands/main.ts Normal file
View File

@ -0,0 +1,147 @@
import * as fs from "fs";
import config from "../../config.js";
import type { ueuse } from "types/types";
const initialFile: Array<string> = [];
// コマンド読み込み
import Info from "./info.js";
import Follow from "./follow.js";
import UnFollow from "./unfollow.js";
import Weather from "./weather.js";
import Help from "./help.js";
import Report from "./report.js";
import Terms from "./legal/terms.js"
import PrivacyPolicy from "./legal/privacy.js";
// 初期化
if (!fs.existsSync("data/alreadyCommands.json")) {
fs.writeFileSync(
"data/alreadyCommands.json",
JSON.stringify(initialFile),
"utf-8",
);
}
// 対応済みユーズ一覧
const alreadyCommands: Array<string> = JSON.parse(fs.readFileSync("data/alreadyCommands.json", "utf-8"));
function cutAfterChar(str: string, char: string) {
const index = str.indexOf(char);
if (index === -1) {
return "";
}
return str.substring(index + 1);
}
export async function Reply(text: string, reply: string) {
const req = await fetch(`${config.uwuzu.host}/api/ueuse/create`, {
method: "POST",
body: JSON.stringify({
token: config.uwuzu.apiToken,
text: text,
replyid: reply,
}),
cache: "no-store",
});
const res = await req.json();
return res;
}
function alreadyAdd(data: string) {
alreadyCommands[alreadyCommands.length] = data;
fs.writeFileSync(
"data/alreadyCommands.json",
JSON.stringify(alreadyCommands),
"utf-8",
);
}
export default async function Commands() {
const mentionsReq = await fetch(
`${config.uwuzu.host}/api/ueuse/mentions`, {
method: "POST",
body: JSON.stringify({
token: config.uwuzu.apiToken,
}),
cache: "no-store",
}
);
const mentions: { [key: string]: ueuse } = await mentionsReq.json();
console.log("----------------");
console.log("コマンド処理");
for (const key in mentions) {
if (mentions.hasOwnProperty(key)) {
const data = mentions[key];
// 除外ユーズ
if (data.text === undefined) {
break;
}
if (alreadyCommands.indexOf(data.uniqid) !== -1) {
break;
}
if (
data.text.charAt(0) === "!" ||
data.text.charAt(0) === "" ||
data.abi === "ignore"
) {
break;
}
// コマンド処理
console.log("--------");
const commandName = cutAfterChar(data.text, "/");
alreadyAdd(data.uniqid);
switch (commandName) {
case "":
break;
case "info":
Info(data);
break;
case "help":
Help(data);
break;
case "legal terms":
Terms(data);
break;
case "legal privacy":
PrivacyPolicy(data);
break;
case "report":
Report(data);
break;
case "follow":
Follow(data);
break;
case "unfollow":
UnFollow(data);
break;
case "weather":
Weather(data);
break;
default:
const reply = await Reply(`
1\`!\`を入れてください。
`, data.uniqid);
console.log("未対応コマンド: ", reply);
break;
}
}
}
}

View File

@ -0,0 +1,74 @@
import { ueuse } from "types/types";
import { Reply } from "./main.js";
import config from "../../config.js";
import sendMail from "../../src/mailer.js";
export default async function Report(data: ueuse) {
if (
data.abi === "none" ||
data.abi === ""
) {
console.log("報告(内容なし)", await Reply(`
(\`/report\`をまたご利用ください。)
`, data.uniqid));
return;
}
if (!config.emergency.isEnabled) {
console.log("報告(重要通知オフ)", await Reply(`
BOTの運営者によって重要通知が無効化されています
`, data.uniqid));
return;
}
if (!config.emergency.mail.isEnabled) {
console.log("報告(メールオフ)", await Reply(`
BOTの運営者によってメール送信機能が無効化されています
`, data.uniqid));
return;
}
if (!config.report.isEnabled) {
console.log("報告(機能オフ)", await Reply(`
BOTの運営者によって報告機能が無効化されています
`, data.uniqid));
return;
}
try {
sendMail({
to: config.emergency.mail.to,
subject: "【報告】BOT利用者からの報告",
text: `
noticeUwuzu自動送信によるメールです
BOT管理者さんnoticeUwuzu自動送信メールです
@${data.account.userid}@${config.uwuzu.host}/reportコマンドを利用した報告がありました
${config.uwuzu.host}/!${data.uniqid}
${data.abi}
`,
});
console.log("報告(完了)", await Reply(`
URL
----
${config.report.message}
`, data.uniqid));
return;
} catch (err) {
console.log("/reportエラー", err);
console.log("報告(エラー)", await Reply(`
`, data.uniqid));
return;
}
}

View File

@ -0,0 +1,25 @@
import { ueuse } from "types/types";
import config from "../../config.js";
import { Reply } from "./main.js";
export default async function UnFollow(data: ueuse) {
const unfollowReq = await fetch(`${config.uwuzu.host}/api/users/unfollow`, {
method: "POST",
body: JSON.stringify({
token: config.uwuzu.apiToken,
userid: data.account.userid,
}),
cache: "no-store",
});
const unfollowRes = await unfollowReq.json();
console.log("フォロー解除: ", unfollowRes);
const notice = await Reply(`
${data.account.username}
`, data.uniqid);
console.log("フォロー解除通知: ", notice);
}

View File

@ -0,0 +1,6 @@
import { weatherReply } from "../weatherNotice.js";
import { ueuse } from "types/types";
export default function Weather(data: ueuse) {
weatherReply(data.uniqid);
}

View File

@ -2,7 +2,6 @@ import WebSocket from "ws";
import sendMail from "../src/mailer.js"; import sendMail from "../src/mailer.js";
import config from "../config.js"; import config from "../config.js";
import { max } from "date-fns/fp";
class P2PEarthquakeClient { class P2PEarthquakeClient {
private ws: WebSocket | null = null; private ws: WebSocket | null = null;
@ -74,7 +73,7 @@ class P2PEarthquakeClient {
event(message); event(message);
} else { } else {
console.log(`未対応の情報を受信しました(コード: ${message.code})`); console.log(`未対応の情報を受信しました(コード: ${message.code})`);
console.log(`受信メッセージ:${message}`); console.log("受信メッセージ:", message);
} }
} }
@ -138,7 +137,7 @@ async function areaMap(): Promise<Record<number, string>> {
// 情報受信 // 情報受信
async function event(earthquakeInfo: any): Promise<void> { async function event(earthquakeInfo: any): Promise<void> {
console.log(`受信メッセージ:${earthquakeInfo}`); console.log("受信メッセージ:", earthquakeInfo);
// ----処理---- // ----処理----
// 緊急地震速報の場合 // 緊急地震速報の場合
@ -277,7 +276,7 @@ async function event(earthquakeInfo: any): Promise<void> {
if ( if (
earthquakeInfo.earthquake.maxScale !== undefined && earthquakeInfo.earthquake.maxScale !== undefined &&
earthquakeInfo.earthquake.maxScale >= 60 && earthquakeInfo.earthquake.maxScale >= 60 &&
config.emergency.function config.emergency.isEnabled
) { ) {
console.log("----------------"); console.log("----------------");
@ -285,17 +284,17 @@ async function event(earthquakeInfo: any): Promise<void> {
console.log("サーバーがダウンする可能性があります"); console.log("サーバーがダウンする可能性があります");
// メール送信 // メール送信
if (config.emergency.function) { if (config.emergency.isEnabled) {
sendMail({ sendMail({
to: config.emergency.mail.to, to: config.emergency.mail.to,
subject: "【警告】震度6強以上の地震を受信しました", subject: "【警告】震度6強以上の地震を受信しました",
text: ` text: `
noticeUwuzu自動送信によるメールです noticeUwuzu自動送信によるメールです
BOT管理者さんnoticeUwuzu自動送信メールです BOT管理者さんnoticeUwuzu自動送信メールです
6 6
` `
}); });
@ -315,6 +314,8 @@ async function event(earthquakeInfo: any): Promise<void> {
), ),
); );
areas = `対象地域:${areaNames.join("・")}`; areas = `対象地域:${areaNames.join("・")}`;
} else {
areas = "対象地域:不明";
} }
// 詳細 // 詳細
@ -325,6 +326,8 @@ async function event(earthquakeInfo: any): Promise<void> {
earthquakeInfo.comments.freeFormComment !== undefined earthquakeInfo.comments.freeFormComment !== undefined
) { ) {
description = `この地震について:${earthquakeInfo.comments.freeFormComment}`; description = `この地震について:${earthquakeInfo.comments.freeFormComment}`;
} else {
description = "";
} }
// 深さ // 深さ
@ -341,6 +344,8 @@ async function event(earthquakeInfo: any): Promise<void> {
} else { } else {
depth = `深さ:${String(earthquakeInfo.earthquake.hypocenter.depth)}km`; depth = `深さ:${String(earthquakeInfo.earthquake.hypocenter.depth)}km`;
} }
} else {
depth = "深さ:不明";
} }
// マグニチュード // マグニチュード
@ -351,10 +356,12 @@ async function event(earthquakeInfo: any): Promise<void> {
earthquakeInfo.earthquake.hypocenter.magnitude !== undefined earthquakeInfo.earthquake.hypocenter.magnitude !== undefined
) { ) {
if (earthquakeInfo.earthquake.hypocenter.magnitude === -1) { if (earthquakeInfo.earthquake.hypocenter.magnitude === -1) {
depth = "マグニチュード:不明"; magnitude = "マグニチュード:不明";
} else { } else {
magnitude = `マグニチュードM${String(earthquakeInfo.earthquake.hypocenter.magnitude)}`; magnitude = `マグニチュードM${String(earthquakeInfo.earthquake.hypocenter.magnitude)}`;
} }
} else {
magnitude = "マグニチュード:不明";
} }
ueuse(` ueuse(`
@ -418,6 +425,8 @@ async function event(earthquakeInfo: any): Promise<void> {
immediate = "### 津波が直ちに襲来します"; immediate = "### 津波が直ちに襲来します";
} else if (!data.immediate) { } else if (!data.immediate) {
immediate = "津波は直ちには襲来しません"; immediate = "津波は直ちには襲来しません";
} else {
immediate = "津波の襲来が直後かの情報がありません";
} }
// 第1波 // 第1波
@ -476,18 +485,18 @@ async function event(earthquakeInfo: any): Promise<void> {
} }
async function ueuse(text: string) { async function ueuse(text: string) {
console.log(text); const res = await fetch(`${config.uwuzu.host}/api/ueuse/create`, {
/*const res = await fetch(`https://${config.uwuzu.host}/api/ueuse/create/`, {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: config.uwuzu.apiToken, token: config.uwuzu.apiToken,
text: text, text: text,
}), }),
cache: "no-store",
}); });
const resData = await res.json(); const resData = await res.json();
console.log(`地震情報投稿:${JSON.stringify(resData)}`);*/ console.log(`地震情報投稿:${JSON.stringify(resData)}`);
} }
export default function earthquakeNotice(): void { export default function earthquakeNotice(): void {

30
scripts/eventday.ts Normal file
View File

@ -0,0 +1,30 @@
import { format } from "date-fns";
import eventdays from "./eventdayData.js";
import config from "../config.js";
export default async function EventDays() {
const now = format(new Date(), "MM/dd");
for (let i = 0; i < Object.keys(eventdays).length; i++) {
const day = Object.keys(eventdays)[i];
const value = Object.values(eventdays)[i];
const name = value.name;
const message = value.message;
if (day === now) {
const req = await fetch(`${config.uwuzu.host}/api/ueuse/create`, {
method: "POST",
body: JSON.stringify({
token: config.uwuzu.apiToken,
text:
`今日は${name}です
${message}`,
}),
});
const res = await req.json();
console.log("祝日等ユーズ:", res);
}
}
}

70
scripts/eventdayData.ts Normal file
View File

@ -0,0 +1,70 @@
interface eventdaysValue {
name: string;
message: string;
}
const eventdays = {
"01/01": {
name: "元日",
message: "はい年越した瞬間地球にいなかった~",
},
"02/11": {
name: "建国記念日",
message: "建国記念日とかいう強制休暇",
},
"04/29": {
name: "昭和の日",
message: "平成の日は???",
},
"05/03": {
name: "憲法記念日",
message: "憲法決めた日とか祝日じゃなくていいだろ",
},
"05/04": {
name: "みどりの日",
message:
`なんだよみどりの日って
`,
},
"05/05": {
name: "こどもの日",
message: "こどもの日あるならおとなの日もあっていいだろ",
},
"07/07": {
name: "七夕",
message:
`祭りでも行っとけ
Last2014は家でサーバーいじってるから`,
},
"08/11": {
name: "山の日",
message:
`空の日と山の日も作れよ
3`,
},
"11/03": {
name: "文化の日",
message: "ネットミームできるたびに休みになればいいのになぁ...",
},
"11/23": {
name: "勤労感謝の日",
message: "学生は神!!",
},
"12/24": {
name: "クリスマスイブ",
message:
`リア充爆破します
by `,
},
"12/25": {
name: "クリスマス",
message: `リア充爆破します(2回目)
by `,
},
"12/31": {
name: "大晦日",
message: "大掃除!!大掃除!!",
},
} as { [key: string]: eventdaysValue };
export default eventdays;

View File

@ -1,44 +0,0 @@
import type * as types from "types/types";
import config from "../../config.js";
export default async function followBack() {
console.log("----------------");
// フォロワーを取得
const resMe = await fetch(
`https://${config.uwuzu.host}/api/me/`,
{
method: "POST",
body: JSON.stringify({
token: config.uwuzu.apiToken,
}),
},
);
const meData: types.meApi = await resMe.json();
const followers = meData.follower;
// フォロー
for (let i = 0; i < followers.length; i++) {
const followerItem = followers[i];
setTimeout(async () => {
const resFollow = await fetch(
`https://${config.uwuzu.host}/api/users/follow/`,
{
method: "POST",
body: JSON.stringify({
token: config.uwuzu.apiToken,
userid: followerItem,
}),
},
);
const followData: types.followApi = await resFollow.json();
console.log(`フォロー:${JSON.stringify(followData)}`);
}, 100);
}
}

View File

@ -1,7 +0,0 @@
import followBack from "./follow.js";
import unFollowBack from "./unfollow.js";
export default function follows() {
unFollowBack();
followBack();
}

View File

@ -1,34 +0,0 @@
import config from "../../config.js";
import { meApi } from "types/types.js";
export default async function unFollowBack() {
const profile: meApi = await
(await fetch(`https://${config.uwuzu.host}/api/me/`, {
method: "POST",
body: JSON.stringify({
token: config.uwuzu.apiToken,
})
})).json();
for (let i = 0; i < profile.followee.length; i++) {
const followUser = profile.followee[i];
if (
profile.follower.indexOf(followUser) === -1
) {
setTimeout(async () => {
const req = await fetch(`https://${config.uwuzu.host}/api/users/unfollow/`, {
method: "POST",
body: JSON.stringify({
token: config.uwuzu.apiToken,
userId: followUser,
}),
});
const res = await req.text();
console.log(`フォロー解除: ${res}`)
}, 100);
}
}
}

View File

@ -1,5 +1,5 @@
import * as fs from "fs"; import * as fs from "fs";
import { isBefore } from "date-fns/fp"; import { isAfter } from "date-fns";
import config from "../config.js"; import config from "../config.js";
import sendMail from "../src/mailer.js"; import sendMail from "../src/mailer.js";
@ -15,15 +15,15 @@ export default function successExit() {
const iolog = JSON.parse(fs.readFileSync("logs/boot.json", "utf-8")); const iolog = JSON.parse(fs.readFileSync("logs/boot.json", "utf-8"));
if (config.emergency.function) { if (config.emergency.isEnabled) {
// 前回の終了確認 // 前回の終了確認
const start = iolog.start; const start = iolog.start;
const stop = iolog.stop; const stop = iolog.stop;
if (isBefore(start, stop)) { if (isAfter(start, stop)) {
console.log("前回の終了が適切でない可能性があります"); console.log("前回の終了が適切でない可能性があります");
if (config.emergency.mail.function) { if (config.emergency.mail.isEnabled) {
sendMail({ sendMail({
to: config.emergency.mail.to, to: config.emergency.mail.to,
subject: "【警告】前回終了が不適切な可能性", subject: "【警告】前回終了が不適切な可能性",

View File

@ -28,13 +28,14 @@ export default async function timeNotice() {
} else { } else {
// 投稿 // 投稿
const resUeuse = await fetch( const resUeuse = await fetch(
`https://${config.uwuzu.host}/api/ueuse/create`, `${config.uwuzu.host}/api/ueuse/create`,
{ {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: config.uwuzu.apiToken, token: config.uwuzu.apiToken,
text: `${format(new Date(), "HH:mm")}になりました`, text: `${format(new Date(), "HH:mm")}になりました`,
}), }),
cache: "no-store",
}, },
); );

View File

@ -1,15 +1,15 @@
import { cityList } from "../src/weatherId.js"; import { cityList } from "../src/weatherId.js";
import type * as types from "types/types.js"; import type * as types from "types/types";
import config from "../config.js"; import config from "../config.js";
export default async function weatherNotice() { export async function weatherNotice() {
console.log("----------------"); console.log("----------------");
// 仮投稿 // 仮投稿
const resUeuse = await fetch( const resUeuse = await fetch(
`https://${config.uwuzu.host}/api/ueuse/create`, `${config.uwuzu.host}/api/ueuse/create`,
{ {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
@ -19,6 +19,7 @@ export default async function weatherNotice() {
`, `,
}), }),
cache: "no-store",
}, },
); );
@ -26,7 +27,10 @@ export default async function weatherNotice() {
console.log(`天気仮投稿:${JSON.stringify(ueuseData)}`); console.log(`天気仮投稿:${JSON.stringify(ueuseData)}`);
weatherReply(ueuseData.uniqid);
}
export async function weatherReply(uniqid: string) {
// インデックス // インデックス
const splitCount = config.weather.splitCount; const splitCount = config.weather.splitCount;
const total = cityList.length; const total = cityList.length;
@ -103,19 +107,20 @@ export default async function weatherNotice() {
// 分割投稿 // 分割投稿
for (let i = 0; i < splitCount; i++) { for (let i = 0; i < splitCount; i++) {
const resReply = await fetch( const resReply = await fetch(
`https://${config.uwuzu.host}/api/ueuse/create`, `${config.uwuzu.host}/api/ueuse/create`,
{ {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: config.uwuzu.apiToken, token: config.uwuzu.apiToken,
text: weatherResults[i], text: weatherResults[i],
replyid: ueuseData.uniqid replyid: uniqid,
}), }),
cache: "no-store",
}, },
); );
const replyData: types.ueuseCreateApi = await resReply.json(); const replyData: types.ueuseCreateApi = await resReply.json();
console.log(`天気投稿${JSON.stringify(replyData)}`); console.log(`天気返信${JSON.stringify(replyData)}`);
} }
} }

View File

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

View File

@ -2,7 +2,7 @@
"compilerOptions": { "compilerOptions": {
"target": "ES2024", "target": "ES2024",
"module": "ESNext", "module": "ESNext",
"moduleResolution": "bundler", "moduleResolution": "node",
"outDir": "./dist", "outDir": "./dist",
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,

57
types/config.d.ts vendored
View File

@ -18,24 +18,57 @@ interface timeTypes {
stopTimes: stopsTypes; stopTimes: stopsTypes;
} }
interface emergencyMailTypes { interface emergencyMailFullTypes {
function: Boolean; isEnabled: true;
host: string | undefined; host: string;
port: number; port: number;
user: string; user: string;
password: string; password: string;
secure: Boolean; secure: boolean;
to: string; to: string | string[];
} }
interface emergencyTypes { interface emergencyMailMinTypes {
function: Boolean; isEnabled: false;
mail: emergencyMailTypes; mail: undefined;
}
interface emergencyFullTypes {
isEnabled: true;
mail: emergencyMailFullTypes | emergemcyMailMinTypes;
}
interface emergencyMinTypes {
isEnabled: false;
}
interface reportTypes {
isEnabled: boolean;
message: string;
}
interface legalTypes {
terms: string;
privacy: string;
}
interface PanelFullTypes {
isEnabled: true;
port: number;
}
interface PanelMinTypes {
isEnabled: false;
}
interface adminTypes {
name: string;
showMail: string | false;
panel: PanelFullTypes | PanelMinTypes;
} }
interface uwuzuTypes { interface uwuzuTypes {
apiToken: string; apiToken: string;
clientToken?: string;
host: string; host: string;
} }
@ -44,6 +77,10 @@ export interface configTypes {
earthquake: earthquakeTypes; earthquake: earthquakeTypes;
weather: weatherTypes; weather: weatherTypes;
emergency: emergencyTypes; emergency: emergencyFullTypes | emergencyMinTypes;
report: reportTypes;
legal: legalTypes;
admin: adminTypes;
uwuzu: uwuzuTypes; uwuzu: uwuzuTypes;
debug?: true;
} }

31
types/types.d.ts vendored
View File

@ -23,6 +23,31 @@ export interface meApi {
language: String; language: String;
} }
export interface ueuse {
uniqid: string;
relpyid: string;
reuseid: string;
text: string;
account: {
username: string;
userid: string;
user_icon: string;
user_header: string;
is_bot: boolean;
};
photo1: string;
photo2: string;
photo3: string;
photo4: string;
video1: string;
favorite: Array<string>;
favorite_cnt: string;
datetime: string;
abi: string;
abidatetime: string;
nsfw: boolean;
}
export interface ueuseCreateApi { export interface ueuseCreateApi {
uniqid: string; uniqid: string;
userid: string; userid: string;
@ -31,3 +56,9 @@ export interface ueuseCreateApi {
export interface followApi { export interface followApi {
userid: string; userid: string;
} }
export interface NetworkInterfaceDetails {
family: string;
internal: boolean;
address: string;
}