Compare commits

...

11 Commits

Author SHA1 Message Date
last2014 97af8da4dd v25.8.6 2025-08-25 16:30:46 +09:00
last2014 a83054f5ce Git LFS 2025-08-25 06:15:14 +09:00
last2014 bbf2d66b57 v25.8.5@uwuzu1.6.4 2025-08-25 06:11:36 +09:00
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
46 changed files with 794 additions and 535 deletions
BIN
View File
Binary file not shown.
+1
View File
@@ -19,6 +19,7 @@ Automatic notification bot for uwuzu
- Follow back - Follow back
- Unfollow - Unfollow
- Weather Repost - Weather Repost
- Make it a quote
- Startup requirements check - Startup requirements check
- Check package existence - Check package existence
- Required package version check - Required package version check
+3 -2
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
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();
}
}
+5
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();
} }
+1 -1
View File
@@ -26,7 +26,7 @@ export default async function VersionCheck() {
const releaseUrl = `${packageJson.repository.url}/releases/tag/${packageJson.tag}`; const releaseUrl = `${packageJson.repository.url}/releases/tag/${packageJson.tag}`;
await fetch(`https://${config.uwuzu.host}/api/ueuse/create`, { 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,
+15 -6
View File
@@ -21,13 +21,14 @@ const config: configTypes = {
weather: { weather: {
splitCount: 4, // 返信の分割数 splitCount: 4, // 返信の分割数
}, },
// Make it a quote設定
miq: true, // 有効/無効
// 緊急時設定 // 緊急時設定
emergency: { emergency: {
function: true, // 緊急時のコンソール表示 isEnabled: true, // 緊急時のコンソール表示
report: false, // reportコマンド
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メール送信元
@@ -36,6 +37,11 @@ const config: configTypes = {
to: "admin@noticeuwuzu.example.com", // 緊急時メール送信先(配列可) to: "admin@noticeuwuzu.example.com", // 緊急時メール送信先(配列可)
}, },
}, },
// /report設定
report: {
isEnabled: true, // 有効/無効
message: "", // 報告者へのメッセージ
},
// 規約等 // 規約等
legal: { legal: {
terms: ` terms: `
@@ -46,13 +52,16 @@ const config: configTypes = {
// 管理者情報設定 // 管理者情報設定
admin: { admin: {
name: "あどみん", // BOT管理者名 name: "あどみん", // BOT管理者名
showMail: true, // メールアドレスを公開するか(emergency.mail.toが使用されます) showMail: false, // メールアドレスを公開するか(false:非公開/文字列:メールアドレス)
panel: { // 管理パネル
isEnabled: true, // 有効/無効
port: 74919, // 配信ポート
},
}, },
// uwuzuサーバー設定 // uwuzuサーバー設定
uwuzu: { uwuzu: {
apiToken: "TOKEN_EXAMPLE", // APIトークン apiToken: "TOKEN_EXAMPLE", // APIトークン
clientToken: "TOKEN_EXAMPLE", // クライアントトークン(任意) host: "https://uwuzu.example.com", // サーバーホスト
host: "uwuzu.example.com", // サーバーホスト(HTTPSである必要があります)
}, },
}; };
+20 -5
View File
@@ -11,16 +11,14 @@ 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 birthdayNotice from "./scripts/birthdayNotice.js"; import EventDays from "./scripts/eventday.js";
import Commands from "./scripts/commands/main.js"; import Commands from "./scripts/commands/main.js";
import BirthdayDataSet from "./scripts/birthdayDataSet.js";
// その他機能 // その他機能
import asciiArt from "./scripts/asciiart.js"; import asciiArt from "./scripts/asciiart.js";
asciiArt(); asciiArt();
import successExit from "./scripts/successExit.js"; import successExit from "./scripts/successExit.js";
successExit(); successExit();
BirthdayDataSet();
// 地震情報観測開始 // 地震情報観測開始
earthquakeNotice(); earthquakeNotice();
@@ -31,17 +29,34 @@ cron.schedule("0 * * * *", () => {
}); });
// コマンド(10分/1回) // コマンド(10分/1回)
cron.schedule('*/10 * * * *', () => { cron.schedule("*/10 * * * *", () => {
Commands(); Commands();
}); });
// 祝日などお知らせ(毎日0:00)
cron.schedule("0 0 * * *", () => {
EventDays();
});
// 天気お知らせ(毎日7:00) // 天気お知らせ(毎日7:00)
import { setTimeout } from "timers";
cron.schedule("0 7 * * *", () => { cron.schedule("0 7 * * *", () => {
setTimeout(() => { setTimeout(() => {
weatherNotice(); weatherNotice();
birthdayNotice();
}, 100); }, 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"], "デバッグモードで起動中"));
}
+93
View File
@@ -0,0 +1,93 @@
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
BIN
View File
Binary file not shown.
LFS Vendored
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+54
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
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
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
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
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
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
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
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
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
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;
-14
View File
@@ -1,14 +0,0 @@
import { existsSync, writeFileSync } from "fs";
const initialData = {} as
{ [key: string]: string | undefined };
export default function BirthdayDataSet() {
if (!existsSync("data/birthdays.json")) {
writeFileSync(
"data/birthdays.json",
JSON.stringify(initialData),
"utf-8",
);
}
}
-56
View File
@@ -1,56 +0,0 @@
import { readFileSync } from "fs";
import { isSameDay, format, differenceInYears } from "date-fns/fp";
import config from "../config.js";
export default async function birthdayNotice() {
// 読み込み
const birthdays: { [key: string]: string | undefined } =
JSON.parse(readFileSync("data/birthdays.json", "utf-8"));
// 配列化
const birthdaysIndex: string[] = Object.entries(birthdays)
.map(([key, value]) => value)
.filter(value => value !== undefined) as string[];
// 初期値
const resultInitial: string = `
【今日誕生日の人】\n`;
let result = resultInitial;
for (let i = 0; i < Object.keys(birthdays).length; i++) {
const birthday = format(birthdaysIndex[i], "yyyy/MM/dd")
if (isSameDay(birthday, new Date())) {
const age = differenceInYears(new Date(), birthday);
const req = await fetch(`https://${config.uwuzu.host}/api/users/`, {
method: "POST",
body: JSON.stringify({
token: config.uwuzu.apiToken,
userid: Object.keys(birthdays)[i],
}),
});
const res = await req.json();
result+= `${res.username}さん(${age}歳)\n`
}
}
if (result === resultInitial) {
return;
}
const req = await fetch(`https://${config.uwuzu.host}/api/ueuse/create`, {
method: "POST",
body: JSON.stringify({
token: config.uwuzu.apiToken,
text: result,
}),
});
const res = await req.json();
console.log("誕生日お知らせ:", res);
}
-88
View File
@@ -1,88 +0,0 @@
import { ueuse } from "types/types.js";
import { readFileSync, writeFileSync, existsSync } from "fs";
import { parse, isValid } from 'date-fns/fp';
import { Reply } from "./main.js";
function normalizedString(Str: string) {
return Str.replace(/[\u3000-\u303F]+/g, ' ')
.replace(/[\uFF01-\uFF0F]+/g, '0-9')
.replace(/[\u3001-\u3002]+/g, '/');
}
function isValidDateString(dateString: string) {
const normalizedStr = normalizedString(dateString);
const regex = /^\d{4}\/\d{2}\/\d{2}$/;
if (!regex.test(normalizedStr)) {
return false;
}
const parseString = parse(new Date(), 'yyyy/MM/dd');
return isValid(parseString(normalizedStr));
}
export default function Birthday(data: ueuse) {
// 読み込み
const birthdays: { [key: string]: string | undefined } =
JSON.parse(readFileSync("data/birthdays.json", "utf-8"));
if (
(data.abi === "none" ||
data.abi === "") &&
birthdays[data.account.userid] === undefined
) {
Reply(`
追記に誕生日を入力してください
(このユーズはもう利用できません。他のユーズで\`/birthday\`をまたご利用ください。)
`, data.uniqid);
return;
}
if (
data.abi === "delete" &&
birthdays[data.account.userid] !== undefined
) {
birthdays[data.account.userid] = undefined;
writeFileSync(
"data/birthdays.json",
JSON.stringify(birthdays),
"utf-8",
);
Reply(`
誕生日のデータを削除しました
`, data.uniqid);
return;
}
if (
data.abi === "delete" &&
birthdays[data.account.userid] === undefined
) {
Reply(`
誕生日のデータが存在しないため削除できません
`, data.uniqid);
return;
}
if (!isValidDateString(data.abi)) {
Reply(`
誕生日の形式が違います。
yyyy/MM/ddの形式で入力してください。
スラッシュ・数字は全角での使用が可能です。
(このユーズはもう利用できません。他のユーズで\`/birthday\`をまたご利用ください。)
`, data.uniqid);
return;
}
birthdays[data.account.userid] = normalizedString(data.abi);
writeFileSync(
"data/birthdays.json",
JSON.stringify(birthdays),
"utf-8",
);
Reply(`
${data.account.username}さんの誕生日を${normalizedString(data.abi)}に設定しました
`, data.uniqid);
}
+2 -2
View File
@@ -1,9 +1,9 @@
import { ueuse } from "types/types.js"; import { ueuse } from "types/types";
import config from "../../config.js"; import config from "../../config.js";
import { Reply } from "./main.js"; import { Reply } from "./main.js";
export default async function Follow(data: ueuse) { export default async function Follow(data: ueuse) {
const followReq = await fetch(`https://${config.uwuzu.host}/api/users/follow`, { const followReq = await fetch(`${config.uwuzu.host}/api/users/follow`, {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: config.uwuzu.apiToken, token: config.uwuzu.apiToken,
Binary file not shown.
+8 -13
View File
@@ -1,4 +1,4 @@
import { ueuse } from "types/types.js"; import { ueuse } from "types/types";
import { readFileSync } from "fs"; import { readFileSync } from "fs";
import { Reply } from "./main.js"; import { Reply } from "./main.js";
import config from "../../config.js"; import config from "../../config.js";
@@ -15,23 +15,15 @@ export default async function Info(data: ueuse) {
let adminMail; let adminMail;
if ( if (config.admin.showMail === false) {
config.admin.showMail &&
config.emergency.mail.to !== undefined
) {
adminMail = config.emergency.mail.to;
} else if (
config.admin.showMail &&
config.emergency.mail.to === undefined
) {
adminMail = "未設定";
} else {
adminMail = "非公開"; adminMail = "非公開";
} else {
adminMail = config.admin.showMail;
} }
let isReport; let isReport;
if (config.emergency.report) { if (config.report.isEnabled) {
isReport = "有効"; isReport = "有効";
} else { } else {
isReport = "無効"; isReport = "無効";
@@ -58,6 +50,9 @@ export default async function Info(data: ueuse) {
運営者へ報告が必要ですか? 運営者へ報告が必要ですか?
\`/report\`をご利用ください。 \`/report\`をご利用ください。
利用規約をお探しですか?
\`/legal terms\`をご利用ください。
プライバシーポリシーをお探しですか? プライバシーポリシーをお探しですか?
\`/legal privacy\`をご利用ください。 \`/legal privacy\`をご利用ください。
+1 -1
View File
@@ -1,4 +1,4 @@
import { ueuse } from "types/types.js"; import { ueuse } from "types/types";
import { Reply } from "../main.js"; import { Reply } from "../main.js";
import config from "../../../config.js"; import config from "../../../config.js";
+1 -1
View File
@@ -1,4 +1,4 @@
import { ueuse } from "types/types.js"; import { ueuse } from "types/types";
import { Reply } from "../main.js"; import { Reply } from "../main.js";
import config from "../../../config.js"; import config from "../../../config.js";
Binary file not shown.
BIN
View File
Binary file not shown.
+14 -5
View File
@@ -1,4 +1,4 @@
import { ueuse } from "types/types.js"; import { ueuse } from "types/types";
import { Reply } from "./main.js"; import { Reply } from "./main.js";
import config from "../../config.js"; import config from "../../config.js";
import sendMail from "../../src/mailer.js"; import sendMail from "../../src/mailer.js";
@@ -15,7 +15,15 @@ export default async function Report(data: ueuse) {
return; return;
} }
if (!config.emergency.mail.function) { if (!config.emergency.isEnabled) {
console.log("報告(重要通知オフ)", await Reply(`
BOTの運営者によって重要通知が無効化されています。
そのため報告機能はご利用いただけません。
`, data.uniqid));
return;
}
if (!config.emergency.mail.isEnabled) {
console.log("報告(メールオフ)", await Reply(` console.log("報告(メールオフ)", await Reply(`
BOTの運営者によってメール送信機能が無効化されています。 BOTの運営者によってメール送信機能が無効化されています。
そのため報告機能はご利用いただけません。 そのため報告機能はご利用いただけません。
@@ -23,7 +31,7 @@ export default async function Report(data: ueuse) {
return; return;
} }
if (!config.emergency.report) { if (!config.report.isEnabled) {
console.log("報告(機能オフ)", await Reply(` console.log("報告(機能オフ)", await Reply(`
BOTの運営者によって報告機能が無効化されています。 BOTの運営者によって報告機能が無効化されています。
そのため報告機能はご利用いただけません。 そのため報告機能はご利用いただけません。
@@ -40,7 +48,7 @@ export default async function Report(data: ueuse) {
【報告】 【報告】
BOT管理者さん、noticeUwuzu自動送信メールです。 BOT管理者さん、noticeUwuzu自動送信メールです。
@${data.account.userid}@${config.uwuzu.host}から/reportコマンドを利用した報告がありました。 @${data.account.userid}@${config.uwuzu.host}から/reportコマンドを利用した報告がありました。
報告元ユーズ:https://${config.uwuzu.host}/!${data.uniqid} 報告元ユーズ:${config.uwuzu.host}/!${data.uniqid}
下記が内容となります。 下記が内容となります。
${data.abi} ${data.abi}
`, `,
@@ -49,7 +57,8 @@ export default async function Report(data: ueuse) {
console.log("報告(完了)", await Reply(` console.log("報告(完了)", await Reply(`
報告が完了しました。 報告が完了しました。
運営者は報告者、ユーズのURL、内容を確認できます。 運営者は報告者、ユーズのURL、内容を確認できます。
場合によっては運営者、BOTからブロックされる可能性があります。 --運営者からのメッセージ--
${config.report.message}
`, data.uniqid)); `, data.uniqid));
return; return;
+2 -2
View File
@@ -1,9 +1,9 @@
import { ueuse } from "types/types.js"; import { ueuse } from "types/types";
import config from "../../config.js"; import config from "../../config.js";
import { Reply } from "./main.js"; import { Reply } from "./main.js";
export default async function UnFollow(data: ueuse) { export default async function UnFollow(data: ueuse) {
const unfollowReq = await fetch(`https://${config.uwuzu.host}/api/users/unfollow`, { const unfollowReq = await fetch(`${config.uwuzu.host}/api/users/unfollow`, {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: config.uwuzu.apiToken, token: config.uwuzu.apiToken,
+1 -1
View File
@@ -1,5 +1,5 @@
import { weatherReply } from "../weatherNotice.js"; import { weatherReply } from "../weatherNotice.js";
import { ueuse } from "types/types.js"; import { ueuse } from "types/types";
export default function Weather(data: ueuse) { export default function Weather(data: ueuse) {
weatherReply(data.uniqid); weatherReply(data.uniqid);
+3 -3
View File
@@ -276,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("----------------");
@@ -284,7 +284,7 @@ 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強以上の地震を受信しました",
@@ -485,7 +485,7 @@ async function event(earthquakeInfo: any): Promise<void> {
} }
async function ueuse(text: string) { async function ueuse(text: string) {
const res = await fetch(`https://${config.uwuzu.host}/api/ueuse/create`, { const res = 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,
+30
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
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;
+2 -2
View File
@@ -15,7 +15,7 @@ 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;
@@ -23,7 +23,7 @@ export default function successExit() {
if (isAfter(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: "【警告】前回終了が不適切な可能性",
+1 -1
View File
@@ -28,7 +28,7 @@ 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({
+3 -3
View File
@@ -1,6 +1,6 @@
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";
@@ -9,7 +9,7 @@ export async function weatherNotice() {
// 仮投稿 // 仮投稿
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({
@@ -107,7 +107,7 @@ export async function weatherReply(uniqid: string) {
// 分割投稿 // 分割投稿
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({
+13 -1
View File
@@ -11,6 +11,10 @@ export interface EmailMessage {
} }
async function createTransporter() { async function createTransporter() {
if (
config.emergency.isEnabled &&
config.emergency.mail.isEnabled
) {
const transporter = nodemailer.createTransport({ const transporter = nodemailer.createTransport({
host: config.emergency.mail.host, host: config.emergency.mail.host,
port: config.emergency.mail.port, port: config.emergency.mail.port,
@@ -32,10 +36,15 @@ async function createTransporter() {
return transporter; return transporter;
} }
}
export default async function sendMail(message: EmailMessage): Promise<void> { export default async function sendMail(message: EmailMessage): Promise<void> {
if (
config.emergency.isEnabled &&
config.emergency.mail.isEnabled
) {
try { try {
const transporter = await createTransporter(); const transporter: any = await createTransporter();
await transporter.sendMail({ await transporter.sendMail({
from: config.emergency.mail.user, from: config.emergency.mail.user,
@@ -49,4 +58,7 @@ export default async function sendMail(message: EmailMessage): Promise<void> {
console.error("メール送信に失敗しました:", error); console.error("メール送信に失敗しました:", error);
throw error; throw error;
} }
} else {
return;
}
} }
+1 -1
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,
+34 -9
View File
@@ -18,8 +18,8 @@ interface timeTypes {
stopTimes: stopsTypes; stopTimes: stopsTypes;
} }
interface emergencyMailTypes { interface emergencyMailFullTypes {
function: boolean; isEnabled: true;
host: string; host: string;
port: number; port: number;
user: string; user: string;
@@ -28,10 +28,23 @@ interface emergencyMailTypes {
to: string | string[]; to: string | string[];
} }
interface emergencyTypes { interface emergencyMailMinTypes {
function: boolean; isEnabled: false;
mail: emergencyMailTypes; mail: undefined;
report: boolean; }
interface emergencyFullTypes {
isEnabled: true;
mail: emergencyMailFullTypes | emergemcyMailMinTypes;
}
interface emergencyMinTypes {
isEnabled: false;
}
interface reportTypes {
isEnabled: boolean;
message: string;
} }
interface legalTypes { interface legalTypes {
@@ -39,14 +52,23 @@ interface legalTypes {
privacy: string; privacy: string;
} }
interface PanelFullTypes {
isEnabled: true;
port: number;
}
interface PanelMinTypes {
isEnabled: false;
}
interface adminTypes { interface adminTypes {
name: string; name: string;
showMail: boolean; showMail: string | false;
panel: PanelFullTypes | PanelMinTypes;
} }
interface uwuzuTypes { interface uwuzuTypes {
apiToken: string; apiToken: string;
clientToken?: string;
host: string; host: string;
} }
@@ -54,9 +76,12 @@ export interface configTypes {
time: timeTypes, time: timeTypes,
earthquake: earthquakeTypes; earthquake: earthquakeTypes;
weather: weatherTypes; weather: weatherTypes;
miq: boolean;
emergency: emergencyTypes; emergency: emergencyFullTypes | emergencyMinTypes;
report: reportTypes;
legal: legalTypes; legal: legalTypes;
admin: adminTypes; admin: adminTypes;
uwuzu: uwuzuTypes; uwuzu: uwuzuTypes;
debug?: true;
} }
+14 -5
View File
@@ -25,18 +25,21 @@ export interface meApi {
export interface ueuse { export interface ueuse {
uniqid: string; uniqid: string;
replyid: string;
reuseid: string;
text: string; text: string;
account: { account: {
username: string; username: string;
userid: string; userid: string;
user_icon: string; user_icon: string;
user_header: string; user_header: string;
is_bot: boolean;
}; };
photo1: Base64URLString; photo1: string;
photo2: Base64URLString; photo2: string;
photo3: Base64URLString; photo3: string;
photo4: Base64URLString; photo4: string;
video1: Base64URLString; video1: string;
favorite: Array<string>; favorite: Array<string>;
favorite_cnt: string; favorite_cnt: string;
datetime: string; datetime: string;
@@ -53,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;
}