Compare commits

..

No commits in common. "50db83cc8cb06dd9dc8cf132604a5e42da06c1b2" and "1c10aeb9e467c0d4200610627ec01a84a4329f98" have entirely different histories.

8 changed files with 120 additions and 233 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(`https://${config.uwuzu.host}/api/me`, {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
token: config.uwuzu.apiToken, token: config.uwuzu.apiToken,
@ -12,7 +12,7 @@ export default async function APICheck() {
const res = await req.json(); const res = await req.json();
if (res.error_code !== undefined) { if (!res.userid) {
console.log(styleText("red", "APIトークンあるいはuwuzuサーバーホストが無効です")); console.log(styleText("red", "APIトークンあるいはuwuzuサーバーホストが無効です"));
process.exit(); process.exit();
} }

View File

@ -26,16 +26,16 @@ successExit();
// 地震情報観測開始 // 地震情報観測開始
earthquakeNotice(); earthquakeNotice();
/*// () // 時報・フォローバック(毎時)
cron.schedule("0 * * * *", () => { cron.schedule("0 * * * *", () => {
timeNotice(); timeNotice();
follows(); follows();
}); });
// 天気お知らせ(毎日7:01) // 天気お知らせ(毎日7:01)
cron.schedule("10 0 7 * * *", () => { cron.schedule("1 7 * * *", () => {
weatherNotice(); weatherNotice();
});*/ });
// 起動表示 // コンソールで表示
console.log("BOTサーバーが起動しました"); console.log("BOTサーバーが起動しました");

View File

@ -1,7 +1,7 @@
{ {
"name": "notice-uwuzu", "name": "notice-uwuzu",
"version": "v6.5(LTS)@uwuzu1.5.4", "version": "v6.0.3@uwuzu1.5.4",
"description": "Notice Bot for uwuzu", "description": "uwuzu Notice Bot",
"main": "dist/main.js", "main": "dist/main.js",
"scripts": { "scripts": {
"start": "node .", "start": "node .",

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;
@ -38,8 +37,8 @@ class P2PEarthquakeClient {
try { try {
const message = JSON.parse(data.toString()); const message = JSON.parse(data.toString());
this.handleMessage(message); this.handleMessage(message);
} catch (err) { } catch (error) {
console.error(`メッセージのパースでエラーが発生: ${err}`); console.error("メッセージのパースに失敗:", error);
} }
}); });
@ -64,26 +63,30 @@ class P2PEarthquakeClient {
private handleMessage(message: any): void { private handleMessage(message: any): void {
console.log("----------------"); console.log("----------------");
const supportCode: Array<number> = [ switch (message.code) {
551, case 551: // 地震情報
552, console.log("地震情報を受信しました");
556, this.executeEventFunc(message);
]; break;
case 554: // 緊急地震速報
if (supportCode.indexOf(message.code) !== -1) { console.log("緊急地震速報を受信しました");
event(message); this.executeEventFunc(message);
} else { break;
default:
console.log(`未対応の情報を受信しました(コード: ${message.code})`); console.log(`未対応の情報を受信しました(コード: ${message.code})`);
console.log(`受信メッセージ:${message}`); break;
} }
} }
private executeEventFunc(earthquakeInfo: any): void {
event(earthquakeInfo);
}
private scheduleReconnect(): void { private scheduleReconnect(): void {
if (this.reconnectTimer) return; if (this.reconnectTimer) return;
console.log("地震情報サーバーから切断されました"); console.log("地震情報サーバーから切断されました");
console.log(`${this.reconnectInterval / 1000}秒後に再接続を試みます`); console.log(`${this.reconnectInterval / 1000}秒後に再接続を試みます`);
this.reconnectTimer = setTimeout(() => { this.reconnectTimer = setTimeout(() => {
this.reconnectTimer = null; this.reconnectTimer = null;
this.connect(); this.connect();
@ -93,7 +96,7 @@ class P2PEarthquakeClient {
private setupCleanup(): void { private setupCleanup(): void {
const cleanup = () => { const cleanup = () => {
this.stop(); this.stop();
process.exit(); process.exit(0);
}; };
process.on("SIGINT", cleanup); process.on("SIGINT", cleanup);
@ -113,7 +116,7 @@ class P2PEarthquakeClient {
} }
} }
// 地名マッピング // 地名オブジェクトマッピング
async function areaMap(): Promise<Record<number, string>> { async function areaMap(): Promise<Record<number, string>> {
const res = await fetch(config.earthquake.areasCsvUrl); const res = await fetch(config.earthquake.areasCsvUrl);
@ -138,13 +141,12 @@ 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(`受信データ:${JSON.stringify(earthquakeInfo)}`);
// ----処理---- // ----処理----
// 緊急地震速報の場合 // 緊急地震速報の場合
if (earthquakeInfo.code === 556) { if (earthquakeInfo.code === 554) {
console.log("緊急地震速報を受信しました");
// 地震詳細 // 地震詳細
let descriptionEarthquake: string = ""; let descriptionEarthquake: string = "";
@ -221,7 +223,28 @@ async function event(earthquakeInfo: any): Promise<void> {
// 地震情報 // 地震情報
else if (earthquakeInfo.code === 551) { else if (earthquakeInfo.code === 551) {
console.log("地震発生情報を受信しました"); // 国内津波
let domesticTsunami;
if (earthquakeInfo.earthquake.domesticTsunami === undefined) {
domesticTsunami = "この地震による国内の津波情報はありません";
} else if (earthquakeInfo.earthquake.domesticTsunami === "None") {
domesticTsunami = "この地震による国内の津波の心配はありません";
} else if (earthquakeInfo.earthquake.domesticTsunami === "Unknown") {
domesticTsunami = "この地震による国内の津波情報はありません";
} else if (earthquakeInfo.earthquake.domesticTsunami === "Checking") {
domesticTsunami = "この地震による国内の津波情報を調査中です";
} else if (earthquakeInfo.earthquake.domesticTsunami === "NonEffective") {
domesticTsunami =
"この地震による国内の津波影響は若干の海面変動が予想されますが被害の心配はありません";
} else if (earthquakeInfo.earthquake.domesticTsunami === "Watch") {
domesticTsunami = "この地震により国内で津波注意報が発令しています";
} else if (earthquakeInfo.earthquake.domesticTsunami === "Warning") {
domesticTsunami = "この地震による国内の津波予報があります";
}
// 最大震度
let maxScale: string = "最大深度:";
if ( if (
earthquakeInfo.earthquake.maxScale !== undefined && earthquakeInfo.earthquake.maxScale !== undefined &&
@ -231,46 +254,29 @@ async function event(earthquakeInfo: any): Promise<void> {
return; return;
} }
// 国内津波
let domesticTsunami;
const TsunamiMessages = {
"None": "この地震による国内の津波の心配はありません",
"Unknown": "この地震による国内の津波情報はありません",
"Checking": "この地震による国内の津波情報を調査中です",
"NonEffective": "この地震による国内の津波影響は若干の海面変動が予想されますが被害の心配はありません",
"Watch": "この地震により国内で津波注意報が発令しています",
"Warning": "この地震による国内の津波予報があります",
} as { [key: string]: string };
if (earthquakeInfo.earthquake.domesticTsunami === undefined) {
domesticTsunami = "この地震による国内の津波情報はありません";
} else {
domesticTsunami = TsunamiMessages[earthquakeInfo.earthquake.domesticTsunami];
}
// 最大震度
let maxScale: string = "最大深度:";
const maxScales = {
10: "震度1",
20: "震度2",
30: "震度3",
40: "震度4",
45: "震度5弱",
50: "震度5強",
55: "震度6弱",
60: "震度6強",
70: "震度7",
} as { [key: number]: string };
if ( if (
earthquakeInfo.earthquake.maxScale == -1 || earthquakeInfo.earthquake.maxScale == -1 &&
earthquakeInfo.earthquake.maxScale === undefined earthquakeInfo.earthquake.maxScale === undefined
) { ) {
maxScale = "最大震度:不明"; maxScale = "最大震度情報なし";
} else { } else if (earthquakeInfo.earthquake.maxScale === 10) {
maxScale = `最大震度:${maxScales[earthquakeInfo.earthquake.maxScale]}`; maxScale += "震度1";
} else if (earthquakeInfo.earthquake.maxScale === 20) {
maxScale += "震度2";
} else if (earthquakeInfo.earthquake.maxScale === 30) {
maxScale += "震度3";
} else if (earthquakeInfo.earthquake.maxScale === 40) {
maxScale += "震度4";
} else if (earthquakeInfo.earthquake.maxScale === 45) {
maxScale += "震度5弱";
} else if (earthquakeInfo.earthquake.maxScale === 50) {
maxScale += "震度5強";
} else if (earthquakeInfo.earthquake.maxScale === 55) {
maxScale += "震度6弱";
} else if (earthquakeInfo.earthquake.maxScale === 60) {
maxScale += "震度6強";
} else if (earthquakeInfo.earthquake.maxScale === 70) {
maxScale += "震度7";
} }
// 警告 // 警告
@ -306,7 +312,7 @@ async function event(earthquakeInfo: any): Promise<void> {
} }
// 対象地域 // 対象地域
let areas; let areas: string = "";
if (earthquakeInfo.points !== undefined) { if (earthquakeInfo.points !== undefined) {
const areaNames: Array<string> = Array.from( const areaNames: Array<string> = Array.from(
@ -318,7 +324,7 @@ async function event(earthquakeInfo: any): Promise<void> {
} }
// 詳細 // 詳細
let description; let description: string = "";
if ( if (
earthquakeInfo.comments.freeFormComment !== "" && earthquakeInfo.comments.freeFormComment !== "" &&
@ -328,34 +334,30 @@ async function event(earthquakeInfo: any): Promise<void> {
} }
// 深さ // 深さ
let depth; let depth: string = "";
if ( if (
earthquakeInfo.earthquake.hypocenter.depth !== null || earthquakeInfo.earthquake.hypocenter.depth !== null ||
earthquakeInfo.earthquake.hypocenter.depth !== undefined earthquakeInfo.earthquake.hypocenter.depth !== undefined ||
earthquakeInfo.earthquake.hypocenter.depth != -1
) { ) {
if (earthquakeInfo.earthquake.hypocenter.depth === 0) { if (earthquakeInfo.earthquake.hypocenter.depth === 0) {
depth = "深さ:ごく浅い"; depth = "深さ:ごく浅い";
} else if (earthquakeInfo.earthquake.hypocenter.depth === -1) {
depth = "深さ:不明";
} else { } else {
depth = `深さ:${String(earthquakeInfo.earthquake.hypocenter.depth)}km`; depth = `深さ:${String(earthquakeInfo.earthquake.hypocenter.depth)}km`;
} }
} }
// マグニチュード // マグニチュード
let magnitude; let magnitude: string = "";
if( if(
earthquakeInfo.earthquake.hypocenter.magnitude !== null || earthquakeInfo.earthquake.hypocenter.magnitude !== null ||
earthquakeInfo.earthquake.hypocenter.magnitude !== undefined earthquakeInfo.earthquake.hypocenter.magnitude !== undefined ||
earthquakeInfo.earthquake.hypocenter.magnitude != -1
) { ) {
if (earthquakeInfo.earthquake.hypocenter.magnitude === -1) {
depth = "マグニチュード:不明";
} else {
magnitude = `マグニチュードM${String(earthquakeInfo.earthquake.hypocenter.magnitude)}`; magnitude = `マグニチュードM${String(earthquakeInfo.earthquake.hypocenter.magnitude)}`;
} }
}
ueuse(` ueuse(`
==== ====
@ -368,116 +370,11 @@ async function event(earthquakeInfo: any): Promise<void> {
${areas} ${areas}
${domesticTsunami} ${domesticTsunami}
`); `);
} else if (earthquakeInfo.code === 552) {
console.log("津波予報情報を受信しました");
// 予報取り消し
if (earthquakeInfo.cancelled) {
ueuse(`
====
${earthquakeInfo.time}
`);
return;
}
let result: string = `
====
${earthquakeInfo.time}
\n
`;
for (let i = 0; i < earthquakeInfo.areas.length; i++) {
const data = earthquakeInfo.areas[i];
// 種類
const gradeMessages = {
"MajorWarning": "大津波警報",
"Warning": "津波警報",
"Watch": "津波注意報",
"Unknown": "不明",
} as { [key: string]: string };
let grade;
if (data.grade === undefined) {
grade = "予報種類:不明";
} else {
grade = `予報種類:${gradeMessages[data.grade]}`;
}
// 直後襲来
let immediate;
if (data.immediate === undefined) {
immediate = "津波の襲来が直後かの情報がありません";
} else if (data.immediate) {
immediate = "### 津波が直ちに襲来します";
} else if (!data.immediate) {
immediate = "津波は直ちには襲来しません";
}
// 第1波
let firstHeight;
if (data.firstHeight === undefined) {
firstHeight = "第1波の情報がありません";
} else if (
data.firstHeight.arrivalTime === undefined &&
data.firstHeight.condition === undefined
) {
firstHeight = "第1波の情報がありません";
} else {
let arrivalTime;
if (data.arrivalTime === undefined) {
arrivalTime = "不明";
} else {
arrivalTime = data.arrivalTime;
}
let condition;
if (data.condition === undefined) {
condition = "不明";
} else {
condition = data.condition;
}
firstHeight = `
1${arrivalTime}
1${condition}
`
}
// 予想高さ
let maxHeight;
if (data.maxHeight.description === undefined) {
maxHeight = "津波の高さ(予想):不明";
} else {
maxHeight = `津波の高さ(予想)${data.maxHeight.description}`;
}
result = `
${data.name}
${grade}
${immediate}
${firstHeight}
${maxHeight}\n\n
`;
}
ueuse(result);
} }
} }
async function ueuse(text: string) { async function ueuse(text: string) {
console.log(text); const res = await fetch(`https://${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,
@ -487,7 +384,7 @@ async function ueuse(text: string) {
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 {

View File

@ -7,26 +7,25 @@ export default async function followBack() {
// フォロワーを取得 // フォロワーを取得
const resMe = await fetch( const resMe = await fetch(
`https://${config.uwuzu.host}/api/me/`, `https://${config.uwuzu.host}/api/me?token=${config.uwuzu.apiToken}`,
{ {
method: "POST", method: "GET",
body: JSON.stringify({ // uwuzu v1.5.4で/api/meのPOSTが死んでいるため簡易的にGET
token: config.uwuzu.apiToken,
}),
}, },
); );
const meData: types.meApi = await resMe.json(); const meData: types.meApi = await resMe.json();
const followers = meData.follower; console.log(`BOTプロフィール${JSON.stringify(meData)}`);
const followers: Array<string> = meData.follower;
// フォロー // フォロー
for (let i = 0; i < followers.length; i++) { for (let i = 0; i < followers.length; i++) {
const followerItem = followers[i]; const followerItem = followers[i];
setTimeout(async () => {
const resFollow = await fetch( const resFollow = await fetch(
`https://${config.uwuzu.host}/api/users/follow/`, `https://${config.uwuzu.host}/api/users/follow`,
{ {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
@ -39,6 +38,5 @@ export default async function followBack() {
const followData: types.followApi = await resFollow.json(); const followData: types.followApi = await resFollow.json();
console.log(`フォロー:${JSON.stringify(followData)}`); console.log(`フォロー:${JSON.stringify(followData)}`);
}, 100);
} }
} }

View File

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

View File

@ -1,7 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2024", "target": "es2022",
"module": "ESNext", "module": "ES2022",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"outDir": "./dist", "outDir": "./dist",
"esModuleInterop": true, "esModuleInterop": true,
@ -17,7 +17,6 @@
"paths": { "paths": {
"ws": ["./node_modules/ws/index.js"], "ws": ["./node_modules/ws/index.js"],
"@types/ws": ["./node_modules/@types/ws/index.d.ts"] "@types/ws": ["./node_modules/@types/ws/index.d.ts"]
}, }
"removeComments": true, }
},
} }

4
types/types.d.ts vendored
View File

@ -12,9 +12,9 @@ export interface meApi {
user_icon: string; user_icon: string;
user_header: string; user_header: string;
registered_date: string; registered_date: string;
followee: Array<string>; followee: Array;
followee_cnt: number; followee_cnt: number;
follower: Array<string>; follower: Array;
follower_cnt: number; follower_cnt: number;
ueuse_cnt: number; ueuse_cnt: number;
isBot: Boolean; isBot: Boolean;