Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: handle terminate signal #975

Merged
merged 5 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/@types/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2021-2023 mtripg6666tdr
*
* This file is part of mtripg6666tdr/Discord-SimpleMusicBot.
* (npm package name: 'discord-music-bot' / repository url: <https://github.com/mtripg6666tdr/Discord-SimpleMusicBot> )
*
* mtripg6666tdr/Discord-SimpleMusicBot is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* mtripg6666tdr/Discord-SimpleMusicBot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with mtripg6666tdr/Discord-SimpleMusicBot.
* If not, see <https://www.gnu.org/licenses/>.
*/

// eslint-disable-next-line eslint-comments/disable-enable-pair
/* eslint-disable no-var */
import type { Worker } from "worker_threads";

declare global {
var workerThread:Worker;
}
File renamed without changes.
7 changes: 5 additions & 2 deletions src/AudioSource/youtube/spawner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { type exportableYouTube, YouTube } from "..";
import Util from "../../Util";

const worker = isMainThread ? new Worker(path.join(__dirname, "./worker.js")).on("error", console.error) : null;
global.workerThread = worker;

export type WithId<T> = T & {id:string};
export type spawnerJobMessage = spawnerGetInfoMessage | spawnerSearchMessage;
Expand Down Expand Up @@ -60,12 +61,14 @@ export type workerLoggingMessage = {
};

type jobCallback = (callback:workerMessage & {id: string}) => void;
const jobQueue = worker && new Map<string, {
type jobQueueContent = {
callback: jobCallback,
start: number,
}>();
};
const jobQueue = worker && new Map<string, jobQueueContent>();

if(worker){
worker.unref();
worker.on("message", (message:WithId<workerMessage>) => {
if(message.type === "log"){
Util.logger.log(message.data, message.level);
Expand Down
2 changes: 1 addition & 1 deletion src/AudioSource/youtube/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export function createRefreshableYTLiveStream(info:ytdl.videoInfo, options:ytdl.
stream.updatePlaylist(await refresher());
Util.logger.log("playlist updated");
}
}, 60 * 60 * 1000);
}, 60 * 60 * 1000).unref();
});
stream.once("close", () => {
clearInterval(timeout);
Expand Down
2 changes: 2 additions & 0 deletions src/AudioSource/youtube/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import * as ytsr from "ytsr";
import { YouTube } from ".";
import Util from "../../Util";

parentPort.unref();

function postMessage(message:workerMessage|WithId<workerMessage>){
parentPort.postMessage(message);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Commands/bulkdelete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export default class BulkDelete extends BaseCommand {
await reply.edit(messages.length + "件見つかりました。削除を実行します。");
await options.client.deleteMessages(message.channel.id, messages.map(msg => msg.id), `${message.member.username}#${message.member.discriminator}により${count}件のメッセージの削除が要求されたため。`);
await reply.edit(":sparkles:完了!(このメッセージは自動的に消去されます)");
setTimeout(() => reply.delete().catch(() => {}), 10 * 1000);
setTimeout(() => reply.delete().catch(() => {}), 10 * 1000).unref();
}
catch(er){
Util.logger.log(er, "error");
Expand Down
2 changes: 1 addition & 1 deletion src/Commands/effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default class Effect extends BaseCommand {
embeds: [embed.toEris()],
components: [messageActions]
});
setTimeout(() => reply.edit({components: []}), 5 * 60 * 1000);
setTimeout(() => reply.edit({components: []}), 5 * 60 * 1000).unref();
}
catch(e){
Util.logger.log(e, "error");
Expand Down
4 changes: 2 additions & 2 deletions src/Component/PlayManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ export class PlayManager extends ServerManagerBase {
this.onStreamFailed();
})
;
});
}).unref();
}else{
this._errorReportChannel?.createMessage(":tired_face:曲の再生に失敗しました...。" + (this._errorCount + 1 >= this.retryLimit ? "スキップします。" : "再試行します。"));
this.onStreamFailed();
Expand Down Expand Up @@ -560,7 +560,7 @@ export class PlayManager extends ServerManagerBase {
;
}
this.disconnect();
}, 10 * 60 * 1000);
}, 10 * 60 * 1000).unref();
this._finishTimeout = true;
const playHandler = () => {
clearTimeout(timer);
Expand Down
4 changes: 4 additions & 0 deletions src/Component/backupper/httpBased.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,10 @@ export class HttpBackupper extends Backupper {
;
});
}

destroy(){
/* empty */
}
}

type getResult = {
Expand Down
4 changes: 4 additions & 0 deletions src/Component/backupper/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,8 @@ export abstract class Backupper extends LogEmitter {
* バックアップ済みのキューのデータを取得します
*/
abstract getQueueDataFromBackup(guildids:string[]):Promise<Map<string, YmxFormat>>;
/**
* サーバーとの接続を破棄します
*/
abstract destroy():void|Promise<void>;
}
5 changes: 5 additions & 0 deletions src/Component/backupper/mongodb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,9 @@ export class MongoBackupper extends Backupper {
return null;
}
}

async destroy(){
await this.client.close();
this.collections = null;
}
}
2 changes: 1 addition & 1 deletion src/Component/streams/normalizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class Normalizer extends Readable {
this.pauseOrigin();
}
});
});
}).unref();
this.origin.once("end", () => this.push(null));
this.origin.on("error", er => this.destroy(er));

Expand Down
2 changes: 1 addition & 1 deletion src/Structure/GuildDataContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ export class GuildDataContainer extends LogEmitter {
*/
async playFromURL(message:CommandMessage, rawArg:string, first:boolean = true, cancellable:boolean = false){
const t = Util.time.timer.start("MusicBot#PlayFromURL");
setTimeout(() => message.suppressEmbeds(true).catch(e => this.Log(Util.general.StringifyObject(e), "warn")), 4000);
setTimeout(() => message.suppressEmbeds(true).catch(e => this.Log(Util.general.StringifyObject(e), "warn")), 4000).unref();
if(rawArg.match(/^https?:\/\/(www\.|canary\.|ptb\.)?discord(app)?\.com\/channels\/[0-9]+\/[0-9]+\/[0-9]+$/)){
// Discordメッセへのリンクならば
const smsg = await message.reply("🔍メッセージを取得しています...");
Expand Down
4 changes: 2 additions & 2 deletions src/Util/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export function waitForEnteringState(predicate:()=>boolean, timeout:number = 10
resolve(Date.now() - startTime);
}
}
}, timeStep);
}, timeStep).unref();
});
}

Expand All @@ -156,7 +156,7 @@ export function waitForEnteringState(predicate:()=>boolean, timeout:number = 10
* @param time 待機時間(ミリ秒単位)
*/
export function wait(time:number){
return new Promise<void>(resolve => setTimeout(resolve, time));
return new Promise<void>(resolve => setTimeout(resolve, time).unref());
}

const UUID_TEMPLATE = "10000000-1000-4000-8000-100000000000";
Expand Down
20 changes: 12 additions & 8 deletions src/Util/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type LoggerType = (content:string, level?:LogLevels)=>void;

class LogStore {
private readonly loggingStream = null as fs.WriteStream;
private destroyed = false;

constructor(){
if(config.debug && isMainThread){
Expand All @@ -36,26 +37,20 @@ class LogStore {
fs.mkdirSync(path.join(__dirname, dirPath));
}
this.loggingStream = fs.createWriteStream(path.join(__dirname, `${dirPath}/log-${Date.now()}.log`));
const onExit = () => {
if(!this.loggingStream.destroyed){
this.loggingStream.write(Buffer.from(`INFO ${new Date().toISOString()} [Logger] detect process exiting, closing stream...`));
this.loggingStream.destroy();
}
};
process.on("exit", onExit);
process.on("SIGINT", onExit);
}
}

log:boolean = true;
maxLength = 30;

private readonly _data:string[] = [];
get data():Readonly<LogStore["_data"]>{
return this._data;
}

// eslint-disable-next-line @typescript-eslint/no-shadow
addLog(level:LogLevels, log:string){
if(this.destroyed) return;
if(level !== "debug"){
this._data.push(`${level[0].toUpperCase()}:${log}`);
if(this.data.length > this.maxLength){
Expand All @@ -78,6 +73,15 @@ class LogStore {
}\r\n`));
}
}

destroy(){
if(this.destroyed) return;
this.destroyed = true;
if(!this.loggingStream.destroyed){
this.loggingStream.write(Buffer.from(`INFO ${new Date().toISOString()} [Logger] detect process exiting, closing stream...`));
this.loggingStream.destroy();
}
}
}

export const logStore = new LogStore();
Expand Down
47 changes: 35 additions & 12 deletions src/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ export class MusicBot extends MusicBotBase {
.on("voiceChannelLeave", this.onVoiceChannelLeave.bind(this))
.on("voiceChannelSwitch", this.onVoiceChannelSwitch.bind(this))
.on("error", this.onError.bind(this))
.on("debug", this.onDebug.bind(this))
.on("warn", this.onWarn.bind(this))
;
}

Expand Down Expand Up @@ -147,8 +149,8 @@ export class MusicBot extends MusicBotBase {
// Set main tick
setTimeout(() => {
this.maintenanceTick();
setInterval(this.maintenanceTick.bind(this), 1 * 60 * 1000);
}, 10 * 1000);
setInterval(this.maintenanceTick.bind(this), 1 * 60 * 1000).unref();
}, 10 * 1000).unref();
this.Log("Interval jobs set up successfully");

// Command instance preparing
Expand Down Expand Up @@ -499,7 +501,7 @@ export class MusicBot extends MusicBotBase {
this._client.createMessage(server.boundTextChannel, ":postbox: 長時間使用しなかったため、終了します").catch(e => this.Log(e, "error"));
server.player.disconnect();
}
}, 10 * 60 * 1000);
}, 10 * 60 * 1000).unref();
const playHandler = () => clearTimeout(timer);
server.player.once("playCalled", playHandler);
server.player.once("disconnect", playHandler);
Expand All @@ -519,15 +521,23 @@ export class MusicBot extends MusicBotBase {

private async onError(er:Error){
Util.logger.log(er, "error");
this.Log("Attempt reconnecting after waiting for a while...");
await Util.general.wait(3000);
this.client.connect()
.then(() => Util.logger.log("Reconnected!"))
.catch(_er => {
this.Log(_er);
Util.logger.log("Reconnect attempt failed");
})
;
if(er.message?.startsWith("Invalid token")){
this.Log("Invalid token detected. Please ensure that you set the correct token. You can also re-generate new token for your bot.");
process.exit(1);
}else{
this.Log("Attempt reconnecting after waiting for a while...");
this._client.disconnect({
reconnect: "auto",
});
}
}

private onDebug(message:string, id?:number){
this.Log(`${message} (ID: ${id || "NaN"})`, "debug");
}

private onWarn(message:string, id?:number){
this.Log(`${message} (ID: ${id || "NaN"})`, "warn");
}

/**
Expand All @@ -541,6 +551,19 @@ export class MusicBot extends MusicBotBase {
if(debugLogStoreLength) Util.logger.logStore.maxLength = debugLogStoreLength;
}

async stop(){
this.Log("Shutting down the bot...");
this._client.removeAllListeners();
this._client.on("error", () => {});
if(this._backupper){
this.Log("Shutting down the db...");
await this._backupper.destroy();
}
this._client.disconnect({
reconnect: false,
});
}

/**
* コマンドを実行する際にランナーに渡す引数を生成します
* @param options コマンドのパース済み引数
Expand Down
Loading