This commit is contained in:
Last2014 2025-08-08 21:29:19 +09:00
parent 2b53b8cd3d
commit db5e174dd4
29 changed files with 235 additions and 208 deletions

View File

@ -3,7 +3,7 @@ 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,

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

@ -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,

View File

@ -35,6 +35,11 @@ const config: configTypes = {
to: "admin@noticeuwuzu.example.com", // 緊急時メール送信先(配列可) to: "admin@noticeuwuzu.example.com", // 緊急時メール送信先(配列可)
}, },
}, },
// /report設定
report: {
isEnabled: true, // 有効/無効
message: "", // 報告者へのメッセージ
},
// 規約等 // 規約等
legal: { legal: {
terms: ` terms: `
@ -51,16 +56,10 @@ const config: configTypes = {
port: 74919, // 配信ポート port: 74919, // 配信ポート
}, },
}, },
// /report設定
report: {
isEnabled: true, // 有効/無効
message: "", // 報告者へのメッセージ
},
// 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である必要があります)
}, },
}; };

15
main.ts
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,23 +29,30 @@ 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 AdminPanel from "./panel/main.js";
import { styleText } from "util";
(async () => { (async () => {
await AdminPanel(); await AdminPanel();
})(); })();
// 起動表示 // 起動表示
console.log("BOTサーバーが起動しました"); console.log("BOTサーバーが起動しました");
console.log(styleText(["bgRed", "cyan", "bold"], "デバッグモードで起動中"));

View File

@ -1,7 +1,7 @@
{ {
"name": "notice-uwuzu", "name": "notice-uwuzu",
"version": "v8.0.2@uwuzu1.6.1", "version": "v8.1@uwuzu1.6.1",
"tag": "v8.0.2", "tag": "v8.1",
"description": "Notice Bot for uwuzu", "description": "Notice Bot for uwuzu",
"main": "dist/main.js", "main": "dist/main.js",
"scripts": { "scripts": {
@ -49,6 +49,7 @@
"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",
"timers": "^0.1.1",
"typescript": "^5.9.2", "typescript": "^5.9.2",
"ws": "^8.18.3" "ws": "^8.18.3"
}, },

View File

@ -9,6 +9,7 @@ import ueusePost from "./route/ueuse.js";
import WeatherUeuse from "./route/weather.js"; import WeatherUeuse from "./route/weather.js";
import API from "./route/api.js"; import API from "./route/api.js";
import Token from "./route/token.js"; import Token from "./route/token.js";
import Debug from "./route/debug.js";
export default async function AdminPanel() { export default async function AdminPanel() {
// 無効 // 無効
@ -26,6 +27,7 @@ export default async function AdminPanel() {
app.use(WeatherUeuse); app.use(WeatherUeuse);
app.use(API); app.use(API);
app.use(Token); app.use(Token);
app.use(Debug);
app.use(express.static("panel/public")); app.use(express.static("panel/public"));
app.listen(port, () => { app.listen(port, () => {

View File

@ -23,6 +23,10 @@
天気お知らせ 天気お知らせ
</button> </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 id="ueuse" class="border rounded-[10px] p-[5px] m-[10px] mt-[20px] cursor-pointer">
ユーズ投稿 ユーズ投稿
</button> </button>

View File

@ -26,6 +26,20 @@ document.getElementById("weatherUeuse").addEventListener("click", async () => {
} }
}); });
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 () => { document.getElementById("ueuse").addEventListener("click", async () => {
const text = prompt("ユーズ内容").toLowerCase(); const text = prompt("ユーズ内容").toLowerCase();

View File

@ -11,7 +11,7 @@ API.post("/actions/api", async (req, res, next) => {
const body = req.body.body; const body = req.body.body;
try { try {
const apiReq = await fetch(`https://${config.uwuzu.host}/api${endpoint}`, { const apiReq = await fetch(`${config.uwuzu.host}/api${endpoint}`, {
method: "POST", method: "POST",
body: JSON.stringify(body), body: JSON.stringify(body),
}); });

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;

View File

@ -3,9 +3,6 @@ const Token = express.Router();
import config from "../../config.js"; import config from "../../config.js";
Token.use(express.json());
Token.use(express.urlencoded({ extended: true }));
Token.post("/actions/token", (req, res, next) => { Token.post("/actions/token", (req, res, next) => {
res.status(501) res.status(501)
.send("GET Only"); .send("GET Only");

View File

@ -11,18 +11,22 @@ ueusePost.post("/actions/ueuse", async (req, res, next) => {
const nsfw = req.body.nsfw; const nsfw = req.body.nsfw;
try { try {
const ueuseReq = await fetch(`https://${config.uwuzu.host}/api/ueuse/create`, { const ueuseReq = 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: text, text: `
${text}
noticeUwuzuの管理パネルから投稿されました
`,
nsfw: nsfw, nsfw: nsfw,
}), }),
}); });
const ueuseRes = await ueuseReq.json(); const ueuseRes = await ueuseReq.json();
console.log(`ユーズ(管理パネル)${ueuseRes}`); console.log(`ユーズ(管理パネル)${JSON.stringify(ueuseRes)}`);
res.status(200) res.status(200)
.send("Success"); .send("Success");

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",
);
}
}

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);
}

View File

@ -1,88 +0,0 @@
import { ueuse } from "types/types";
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);
}

View File

@ -3,7 +3,7 @@ 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,

View File

@ -9,7 +9,6 @@ const helpsMin = {
"unfollow": "コマンド送信者をフォロー解除します。", "unfollow": "コマンド送信者をフォロー解除します。",
"weather": "天気を返信します。", "weather": "天気を返信します。",
"report": "運営者に不具合などを報告します。", "report": "運営者に不具合などを報告します。",
"birthday": "誕生日を設定・削除できます。",
"legal terms": "利用規約を返信します。", "legal terms": "利用規約を返信します。",
"legal privacy": "プライバシーポリシーを返信します。", "legal privacy": "プライバシーポリシーを返信します。",
} as { [key: string]: string }; } as { [key: string]: string };
@ -41,12 +40,6 @@ const helpsFull = {
使 使
\`/report\`を使用してそのユーズの追記に内容を入力することで使用できます。 \`/report\`を使用してそのユーズの追記に内容を入力することで使用できます。
`, `,
"birthday": `
設定された誕生日の7:00に祝われます
yyyy/MM/ddの形式で誕生日を入力することで誕生日を設定できます
\`delete\`と入力することで誕生日のデータを削除できます。
`,
"legal terms": ` "legal terms": `
`, `,

View File

@ -11,7 +11,6 @@ import UnFollow from "./unfollow.js";
import Weather from "./weather.js"; import Weather from "./weather.js";
import Help from "./help.js"; import Help from "./help.js";
import Report from "./report.js"; import Report from "./report.js";
import Birthday from "./birthday.js";
import Terms from "./legal/terms.js" import Terms from "./legal/terms.js"
import PrivacyPolicy from "./legal/privacy.js"; import PrivacyPolicy from "./legal/privacy.js";
@ -38,7 +37,7 @@ function cutAfterChar(str: string, char: string) {
} }
export async function Reply(text: string, reply: string) { export async function Reply(text: string, reply: string) {
const req = await fetch(`https://${config.uwuzu.host}/api/ueuse/create`, { const req = 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,
@ -65,7 +64,7 @@ function alreadyAdd(data: string) {
export default async function Commands() { export default async function Commands() {
const mentionsReq = await fetch( const mentionsReq = await fetch(
`https://${config.uwuzu.host}/api/ueuse/mentions`, { `${config.uwuzu.host}/api/ueuse/mentions`, {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: config.uwuzu.apiToken, token: config.uwuzu.apiToken,
@ -84,6 +83,10 @@ export default async function Commands() {
const data = mentions[key]; const data = mentions[key];
// 除外ユーズ // 除外ユーズ
if (data.text === undefined) {
break;
}
if (alreadyCommands.indexOf(data.uniqid) !== -1) { if (alreadyCommands.indexOf(data.uniqid) !== -1) {
break; break;
} }
@ -130,9 +133,6 @@ export default async function Commands() {
case "weather": case "weather":
Weather(data); Weather(data);
break; break;
case "birthday":
Birthday(data);
break;
default: default:
const reply = await Reply(` const reply = await Reply(`

View File

@ -48,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}
`, `,

View File

@ -3,7 +3,7 @@ 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,

View File

@ -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
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

@ -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({

View File

@ -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({

14
types/config.d.ts vendored
View File

@ -42,6 +42,11 @@ interface emergencyMinTypes {
isEnabled: false; isEnabled: false;
} }
interface reportTypes {
isEnabled: boolean;
message: string;
}
interface legalTypes { interface legalTypes {
terms: string; terms: string;
privacy: string; privacy: string;
@ -62,14 +67,8 @@ interface adminTypes {
panel: PanelFullTypes | PanelMinTypes; panel: PanelFullTypes | PanelMinTypes;
} }
interface reportTypes {
isEnabled: boolean;
message: string;
}
interface uwuzuTypes { interface uwuzuTypes {
apiToken: string; apiToken: string;
clientToken?: string;
host: string; host: string;
} }
@ -79,8 +78,9 @@ export interface configTypes {
weather: weatherTypes; weather: weatherTypes;
emergency: emergencyFullTypes | emergencyMinTypes; emergency: emergencyFullTypes | emergencyMinTypes;
report: reportTypes;
legal: legalTypes; legal: legalTypes;
admin: adminTypes; admin: adminTypes;
report: reportTypes;
uwuzu: uwuzuTypes; uwuzu: uwuzuTypes;
debug?: true;
} }