/[sudobot]/branches/6.x/src/services/KeypressHandlerService.ts
ViewVC logotype

Contents of /branches/6.x/src/services/KeypressHandlerService.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (show annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 5544 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 /**
2 * This file is part of SudoBot.
3 *
4 * Copyright (C) 2021-2023 OSN Developers.
5 *
6 * SudoBot is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * SudoBot is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with SudoBot. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 import { spawnSync } from "node:child_process";
21 import { lstat } from "node:fs/promises";
22 import path from "node:path";
23 import { chdir, cwd } from "node:process";
24 import readline from "node:readline";
25 import Service from "../core/Service";
26 import { LogLevel, logError, logInfo, logWarn, logWithLevel } from "../utils/logger";
27 import { developmentMode } from "../utils/utils";
28
29 export const name = "keypressHandler";
30
31 enum CommandKey {
32 ReloadCommands = "R",
33 ForceReloadCommands = "Shift+R",
34 ReloadConfig = "L",
35 WriteConfig = "Shift+L",
36 Quit = "Q"
37 }
38
39 export default class KeypressHandlerService extends Service {
40 readonly keyHandlers: Record<CommandKey, Function> = {
41 [CommandKey.ReloadCommands]: this.reloadCommands.bind(this),
42 [CommandKey.ForceReloadCommands]: () => this.reloadCommands(true),
43 [CommandKey.Quit]: this.quit.bind(this),
44 [CommandKey.ReloadConfig]: this.reloadConfig.bind(this),
45 [CommandKey.WriteConfig]: this.writeConfig.bind(this)
46 };
47
48 lastCommandUpdate = Date.now();
49
50 onKeyPress = (
51 _: string,
52 key: {
53 ctrl: boolean;
54 meta: boolean;
55 shift: boolean;
56 name: string;
57 }
58 ) => {
59 if (key.name.length !== 1) {
60 return;
61 }
62
63 const modifiers: string[] = [];
64
65 if (key.ctrl) modifiers.push("Ctrl");
66 if (key.meta) modifiers.push("Meta");
67 if (key.shift) modifiers.push("Shift");
68
69 const commandKey = `${modifiers.join("+")}${modifiers.length > 0 ? "+" : ""}${key.name.toUpperCase()}`;
70 const handler = this.keyHandlers[commandKey as CommandKey];
71
72 if (handler) {
73 handler();
74 return;
75 }
76
77 if (key.ctrl && !key.meta && !key.shift && key.name === "c") {
78 this.interrupt();
79 return;
80 }
81
82 logWarn("Unrecognized command key: ", `${commandKey}`);
83 };
84
85 quit() {
86 logWithLevel(LogLevel.EVENT, "Quit");
87 process.exit(0);
88 }
89
90 interrupt() {
91 logWithLevel(LogLevel.EVENT, "SIGINT signal received. Exiting");
92 process.exit(1);
93 }
94
95 async reloadConfig() {
96 await this.client.configManager.load();
97 logWithLevel(LogLevel.EVENT, "Successfully reloaded configuration files");
98 }
99
100 async writeConfig() {
101 await this.client.configManager.write({ guild: true, system: true });
102 logWithLevel(LogLevel.EVENT, "Successfully saved configuration files to disk");
103 }
104
105 async reloadCommands(force = false) {
106 const srcDir = process.env.SOURCE_DIRECTORY_PATH ?? path.resolve(__dirname, "../../src");
107 const buildDir = process.env.BUILD_DIRECTORY_PATH ?? path.resolve(__dirname, "../../build");
108 let built = false,
109 failed = false;
110
111 logWithLevel(LogLevel.EVENT, "Hot reloading commands");
112
113 await this.client.loadCommands(undefined, undefined, async filePath => {
114 if (failed) {
115 return false;
116 }
117
118 if (force) {
119 return true;
120 }
121
122 delete require.cache[require.resolve(filePath)];
123
124 const sourceFile = filePath.replace(buildDir, srcDir).replace(/\.js$/gi, ".ts");
125 const sourceInfo = await lstat(sourceFile);
126
127 if (sourceInfo.mtime.getTime() < this.lastCommandUpdate) {
128 return false;
129 }
130
131 if (!built) {
132 const currentDirectory = cwd();
133 chdir(path.resolve(__dirname, "../.."));
134
135 logInfo("Rebuilding project source files");
136
137 const { status } = spawnSync(`npm run build`, {
138 encoding: "utf-8",
139 shell: true,
140 stdio: "inherit"
141 });
142
143 chdir(currentDirectory);
144
145 if (status !== 0) {
146 failed = true;
147 return false;
148 }
149
150 built = true;
151 }
152
153 return true;
154 });
155
156 this.lastCommandUpdate = Date.now();
157
158 if (failed) {
159 logError("Build failed. Aborting hot reload");
160 } else {
161 logWithLevel(LogLevel.EVENT, "Successfully hot reloaded commands");
162 }
163 }
164
165 override boot() {
166 if (!developmentMode() || !process.stdin.isTTY) {
167 return;
168 }
169
170 readline.emitKeypressEvents(process.stdin);
171 process.stdin.on("keypress", this.onKeyPress);
172 process.stdin.setRawMode(true);
173 }
174
175 override deactivate() {
176 if (!developmentMode() || !process.stdin.isTTY) {
177 return;
178 }
179
180 process.stdin.off("keypress", this.onKeyPress);
181 process.stdin.setRawMode(false);
182 }
183 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26