diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 0000000..005717e --- /dev/null +++ b/data/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/examples/config.ts b/examples/config.ts index 570a2bc..889e1cb 100644 --- a/examples/config.ts +++ b/examples/config.ts @@ -17,6 +17,7 @@ const config: configTypes = { areasCsvUrl: "https://raw.githubusercontent.com/p2pquake/epsp-specifications/master/epsp-area.csv", // 対象地域CSVファイルのURL maxScaleMin: 30, // 地震発生の際の最低震度(10-70) }, + // 天気お知らせ設定 weather: { splitCount: 4, // 返信の分割数 }, @@ -35,10 +36,23 @@ const config: configTypes = { to: "admin@noticeuwuzu.example.com", // 緊急時メール送信先(配列可) }, }, + // 規約等 + legal: { + terms: ` + `, // 利用規約 + privacy: ` + `, // プライバシーポリシー + }, + // 管理者情報設定 + admin: { + name: "あどみん", // BOT管理者名 + showMail: true, // メールアドレスを公開するか(emergency.mail.toが使用されます) + }, + // uwuzuサーバー設定 uwuzu: { - apiToken: "TOKEN_EXAMPLE", - clientToken: "TOKEN_EXAMPLE", - host: "uwuzu.example.com", + apiToken: "TOKEN_EXAMPLE", // APIトークン + clientToken: "TOKEN_EXAMPLE", // クライアントトークン(任意) + host: "uwuzu.example.com", // サーバーホスト(HTTPSである必要があります) }, }; diff --git a/main.ts b/main.ts index 13d06e2..75c9466 100644 --- a/main.ts +++ b/main.ts @@ -11,13 +11,16 @@ import * as cron from "node-cron"; import timeNotice from "./scripts/timeNotice.js"; import { weatherNotice } from "./scripts/weatherNotice.js"; import earthquakeNotice from "./scripts/earthquakeNotice.js"; +import birthdayNotice from "./scripts/birthdayNotice.js"; import Commands from "./scripts/commands/main.js"; +import BirthdayDataSet from "./scripts/birthdayDataSet.js"; // その他機能 import asciiArt from "./scripts/asciiart.js"; asciiArt(); import successExit from "./scripts/successExit.js"; successExit(); +BirthdayDataSet(); // 地震情報観測開始 earthquakeNotice(); @@ -27,6 +30,8 @@ cron.schedule("0 * * * *", () => { timeNotice(); }); +birthdayNotice(); + // コマンド(10分/1回) cron.schedule('*/10 * * * *', () => { Commands(); @@ -34,7 +39,10 @@ cron.schedule('*/10 * * * *', () => { // 天気お知らせ(毎日7:00) cron.schedule("0 7 * * *", () => { - weatherNotice(); + setTimeout(() => { + weatherNotice(); + birthdayNotice(); + }, 100); }); // 起動表示 diff --git a/package.json b/package.json index 7f3a399..c9411cc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "notice-uwuzu", - "version": "v7.2@uwuzu1.5.4", - "tag": "v7.2", + "version": "v7.3@uwuzu1.5.4", + "tag": "v7.3", "description": "Notice Bot for uwuzu", "main": "dist/main.js", "scripts": { @@ -20,15 +20,19 @@ "bot", "cron", "notice", + "mail", "weather", "time", - "earthquake" + "earthquake", + "command", + "commands" ], "author": { "name": "Last2014", "url": "https://last2014.com", "email": "info@last2014.com" }, + "contributors": [], "license": "Apache-2.0", "type": "module", "dependencies": { diff --git a/scripts/birthdayDataSet.ts b/scripts/birthdayDataSet.ts new file mode 100644 index 0000000..5f083a4 --- /dev/null +++ b/scripts/birthdayDataSet.ts @@ -0,0 +1,14 @@ +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", + ); + } +} diff --git a/scripts/birthdayNotice.ts b/scripts/birthdayNotice.ts new file mode 100644 index 0000000..3a4d765 --- /dev/null +++ b/scripts/birthdayNotice.ts @@ -0,0 +1,56 @@ +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); +} diff --git a/scripts/commands/birthday.ts b/scripts/commands/birthday.ts new file mode 100644 index 0000000..5989fa6 --- /dev/null +++ b/scripts/commands/birthday.ts @@ -0,0 +1,88 @@ +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); +} diff --git a/scripts/commands/help.ts b/scripts/commands/help.ts index 86860ef..8f0d43c 100644 --- a/scripts/commands/help.ts +++ b/scripts/commands/help.ts @@ -2,27 +2,81 @@ import { ueuse } from "types/types.js"; import { readFileSync } from "fs"; import { Reply } from "./main.js"; -const helps = { +const helpsMin = { "info": "このBOTについての概要を返信するコマンドです。", - "help": "このコマンドです。コマンドの概要を返信します。", + "help": "コマンドの概要を返信します。追記に\`/\`抜きのコマンド名を入力することでそのコマンドの詳細(フル)を返信します。", "follow": "コマンド送信者をフォローします。", "unfollow": "コマンド送信者をフォロー解除します。", - "weather": "天気を返信します。7:00に投稿されるものとは異なり再取得します。", + "weather": "天気を返信します。", + "report": "運営者に不具合などを報告します。", + "birthday": "誕生日を設定・削除できます。", + "legal privacy": "プライバシーポリシーを返信します。", +} as { [key: string]: string }; + +const helpsFull = { + "info": ` + このBOTについての概要を返信するコマンドです。 + バージョン、開発者などが確認できます。 + `, + "help": ` + このコマンドです。コマンドの概要を返信します。 + 追記に\`/\`抜きのコマンド名を入力することでそのコマンドの詳細(フル)を返信します。 + `, + "follow": ` + コマンドを送信したユーザーをフォローします。 + 既にフォローされているユーザーも使用できます。 + `, + "unfollow": ` + コマンドを送信したユーザーをフォロー解除します。 + 既にフォローされていないユーザーも使用できます。 + `, + "weather": ` + 天気を返信します。 + 毎日7:00の天気を再投稿するわけではなく、 + 再取得して返信します。 + `, + "report": ` + 不具合などを運営者にメールで報告できます。 + 運営者によって有効化されていないと使用できません。 + \`/report\`を使用してそのユーズの追記に内容を入力することで使用できます。 + `, + "birthday": ` + 誕生日を設定できます。 + 設定された誕生日の7:00に祝われます。 + 追記にyyyy/MM/ddの形式で誕生日を入力することで誕生日を設定できます。 + また、追記に\`delete\`と入力することで誕生日のデータを削除できます。 + `, + "legal privacy": ` + プライバシーポリシーを返信します。 + `, } as { [key: string]: string }; export default async function Help(data: ueuse) { const packageJson = JSON.parse(readFileSync("package.json", "utf-8")); - const helpMsg = - Object.entries(helps) - .map(([command, message]) => - `\`/${command}\`:${message}` - ).join('\n'); + if ( + data.abi === "none" || + data.abi === "" + ) { + const helpMsg = + Object.entries(helpsMin) + .map(([command, message]) => + `\`/${command}\`:${message}` + ).join('\n'); - const ueuse = await Reply(` - ${helpMsg} - 機能を見る:${packageJson.repository.url}/wiki - `, data.uniqid); + const ueuse = await Reply(` + ${helpMsg} + BOTの概要は\`/info\`をご利用ください。 + Wikiを見る:${packageJson.repository.url}/wiki + `, data.uniqid); - console.log("ヘルプ:", ueuse); + console.log("ヘルプ:", ueuse); + } else { + const ueuse = await Reply(` + ${helpsFull[data.abi]} + 機能を見る:${packageJson.repository.url}/wiki + `, data.uniqid); + + console.log("ヘルプ:", ueuse); + } } diff --git a/scripts/commands/info.ts b/scripts/commands/info.ts index 03e86b4..8e13016 100644 --- a/scripts/commands/info.ts +++ b/scripts/commands/info.ts @@ -1,6 +1,7 @@ import { ueuse } from "types/types.js"; 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")); @@ -12,13 +13,56 @@ export default async function Info(data: ueuse) { editor = `\nEdited by ${packageJson.author.name}`; } + let adminMail; + + if ( + 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 = "非公開"; + } + + let isReport; + + if (config.emergency.report) { + 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 privacy\`をご利用ください。 + + + 【クレジット】 Created by Last2014${editor} `, data.uniqid); diff --git a/scripts/commands/legal/privacy.ts b/scripts/commands/legal/privacy.ts new file mode 100644 index 0000000..a6d7d48 --- /dev/null +++ b/scripts/commands/legal/privacy.ts @@ -0,0 +1,7 @@ +import { ueuse } from "types/types.js"; +import { Reply } from "../main.js"; +import config from "../../../config.js"; + +export default function PrivacyPolicy(data: ueuse) { + Reply(config.legal.privacy, data.uniqid); +} diff --git a/scripts/commands/legal/terms.ts b/scripts/commands/legal/terms.ts new file mode 100644 index 0000000..1406d28 --- /dev/null +++ b/scripts/commands/legal/terms.ts @@ -0,0 +1,7 @@ +import { ueuse } from "types/types.js"; +import { Reply } from "../main.js"; +import config from "../../../config.js"; + +export default function Terms(data: ueuse) { + Reply(config.legal.terms, data.uniqid); +} diff --git a/scripts/commands/main.ts b/scripts/commands/main.ts index 3387dd3..11ba9d6 100644 --- a/scripts/commands/main.ts +++ b/scripts/commands/main.ts @@ -11,6 +11,9 @@ import UnFollow from "./unfollow.js"; import Weather from "./weather.js"; import Help from "./help.js"; import Report from "./report.js"; +import Birthday from "./birthday.js"; +import Terms from "./legal/terms.js" +import PrivacyPolicy from "./legal/privacy.js"; // 初期化 if (!fs.existsSync("logs/alreadyCommands.json")) { @@ -97,37 +100,39 @@ export default async function Commands() { const commandName = cutAfterChar(data.text, "/"); + alreadyAdd(data.uniqid); + switch (commandName) { case "": - alreadyAdd(data.uniqid); break; case "info": - alreadyAdd(data.uniqid); Info(data); break; case "help": - alreadyAdd(data.uniqid); Help(data); break; + case "legal terms": + Terms(data); + break; + case "legal privacy": + PrivacyPolicy(data); + break; case "report": - alreadyAdd(data.uniqid); Report(data); break; case "follow": - alreadyAdd(data.uniqid); Follow(data); break; case "unfollow": - alreadyAdd(data.uniqid); UnFollow(data); break; case "weather": - alreadyAdd(data.uniqid); Weather(data); break; + case "birthday": + Birthday(data); + break; default: - alreadyAdd(data.uniqid); - const reply = await Reply(` 不明なコマンドです。 コマンド実行を除外する場合は1文字目に\`!\`を入れてください。 diff --git a/scripts/commands/report.ts b/scripts/commands/report.ts index 38794f9..5463ffe 100644 --- a/scripts/commands/report.ts +++ b/scripts/commands/report.ts @@ -1,6 +1,6 @@ import { ueuse } from "types/types.js"; import { Reply } from "./main.js"; -import config from "config.js"; +import config from "../../config.js"; import sendMail from "../../src/mailer.js"; export default async function Report(data: ueuse) { @@ -15,10 +15,6 @@ export default async function Report(data: ueuse) { return; } - if (data.abi === "ignore") { - return; - } - if (!config.emergency.mail.function) { console.log("報告(メールオフ):", await Reply(` BOTの運営者によってメール送信機能が無効化されています。 diff --git a/scripts/successExit.ts b/scripts/successExit.ts index eacd277..b85f575 100644 --- a/scripts/successExit.ts +++ b/scripts/successExit.ts @@ -1,5 +1,5 @@ import * as fs from "fs"; -import { isBefore } from "date-fns/fp"; +import { isAfter } from "date-fns"; import config from "../config.js"; import sendMail from "../src/mailer.js"; @@ -20,7 +20,7 @@ export default function successExit() { const start = iolog.start; const stop = iolog.stop; - if (isBefore(start, stop)) { + if (isAfter(start, stop)) { console.log("前回の終了が適切でない可能性があります"); if (config.emergency.mail.function) { diff --git a/types/config.d.ts b/types/config.d.ts index 806eccd..9c6de31 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -19,19 +19,29 @@ interface timeTypes { } interface emergencyMailTypes { - function: Boolean; - host: string | undefined; + function: boolean; + host: string; port: number; user: string; password: string; - secure: Boolean; - to: string; + secure: boolean; + to: string | string[]; } interface emergencyTypes { - function: Boolean; + function: boolean; mail: emergencyMailTypes; - report: Boolean; + report: boolean; +} + +interface legalTypes { + terms: string; + privacy: string; +} + +interface adminTypes { + name: string; + showMail: boolean; } interface uwuzuTypes { @@ -46,5 +56,7 @@ export interface configTypes { weather: weatherTypes; emergency: emergencyTypes; + legal: legalTypes; + admin: adminTypes; uwuzu: uwuzuTypes; }