1 |
import Application from "@framework/app/Application"; |
2 |
import EventListener from "@framework/events/EventListener"; |
3 |
import { Logger } from "@framework/log/Logger"; |
4 |
import { Class } from "@framework/types/Utils"; |
5 |
import { sourceFile } from "@framework/utils/utils"; |
6 |
import { Extension } from "@sudobot/extensions/Extension"; |
7 |
import { ChildProcess, fork } from "child_process"; |
8 |
import { Awaitable } from "discord.js"; |
9 |
import "module-alias/register"; |
10 |
import path from "path"; |
11 |
import MessageCreateEventListener from "./events/MessageCreateEventListener"; |
12 |
import { MessageType } from "./types/MessageType"; |
13 |
|
14 |
export default class ArchiverExtension extends Extension { |
15 |
private readonly logger = new Logger(`extension:${this.constructor.name}`, true); |
16 |
private archiverProcess?: ChildProcess; |
17 |
private archiveServerProcess?: ChildProcess; |
18 |
|
19 |
protected override events(): Awaitable<Class<EventListener, [Application]>[]> { |
20 |
return [MessageCreateEventListener]; |
21 |
} |
22 |
|
23 |
public override postConstruct(): void { |
24 |
const archiverProcess = fork(sourceFile(path.resolve(__dirname, "service/ArchiveProcess"))); |
25 |
|
26 |
archiverProcess.on("message", serializable => { |
27 |
if ( |
28 |
!serializable || |
29 |
typeof serializable !== "object" || |
30 |
!("type" in serializable) || |
31 |
typeof serializable.type !== "string" |
32 |
) { |
33 |
return; |
34 |
} |
35 |
|
36 |
const message = serializable as Record<string, unknown>; |
37 |
this.logger.debug("Archiver: ", message); |
38 |
|
39 |
switch (message.type) { |
40 |
case MessageType.Ping: |
41 |
archiverProcess.send({ |
42 |
type: MessageType.Acknowledgement |
43 |
}); |
44 |
break; |
45 |
case MessageType.ServerReady: |
46 |
this.logger.info("Archive server is ready"); |
47 |
break; |
48 |
case MessageType.Message: |
49 |
this.logger.info("Archiver: ", message.payload); |
50 |
break; |
51 |
} |
52 |
}); |
53 |
|
54 |
archiverProcess.on("error", error => { |
55 |
this.logger.error("Archiver: ", error); |
56 |
}); |
57 |
|
58 |
archiverProcess.on("disconnect", () => { |
59 |
this.logger.warn("Archiver process disconnected"); |
60 |
}); |
61 |
|
62 |
archiverProcess.on("spawn", () => { |
63 |
this.logger.info("Archiver process spawned"); |
64 |
}); |
65 |
|
66 |
archiverProcess.on("exit", code => { |
67 |
if (code !== 0) { |
68 |
this.logger.error(`Unexpected archiver process exit with code ${code}`); |
69 |
} else { |
70 |
this.logger.info("Archiver process exited successfully"); |
71 |
} |
72 |
}); |
73 |
|
74 |
process.on("beforeExit", () => { |
75 |
archiverProcess.kill("SIGKILL"); |
76 |
}); |
77 |
|
78 |
setTimeout(() => { |
79 |
archiverProcess.send({ |
80 |
type: MessageType.Ping |
81 |
}); |
82 |
}, 1000); |
83 |
|
84 |
this.archiverProcess = archiverProcess; |
85 |
} |
86 |
|
87 |
public override cleanup() { |
88 |
if (this.archiverProcess) { |
89 |
this.archiverProcess.kill("SIGTERM"); |
90 |
} |
91 |
|
92 |
if (this.archiveServerProcess) { |
93 |
this.archiveServerProcess.kill("SIGTERM"); |
94 |
} |
95 |
} |
96 |
|
97 |
public sendToArchiver(message: Record<string, unknown>) { |
98 |
this.archiverProcess?.send(message); |
99 |
} |
100 |
} |