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

Annotation of /branches/8.x/src/services/KeypressHandlerService.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (hide annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 5646 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 rakinar2 577 /**
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 { AnyFunction } from "../types/Utils";
27     import { LogLevel, logError, logInfo, logWarn, logWithLevel } from "../utils/Logger";
28     import { developmentMode } from "../utils/utils";
29    
30     export const name = "keypressHandler";
31    
32     enum CommandKey {
33     ReloadCommands = "R",
34     ForceReloadCommands = "Shift+R",
35     ReloadConfig = "L",
36     WriteConfig = "Shift+L",
37     Quit = "Q"
38     }
39    
40     export default class KeypressHandlerService extends Service {
41     readonly keyHandlers: Record<CommandKey, AnyFunction<[], unknown>> = {
42     [CommandKey.ReloadCommands]: this.reloadCommands.bind(this),
43     [CommandKey.ForceReloadCommands]: () => this.reloadCommands(true),
44     [CommandKey.Quit]: this.quit.bind(this),
45     [CommandKey.ReloadConfig]: this.reloadConfig.bind(this),
46     [CommandKey.WriteConfig]: this.writeConfig.bind(this)
47     };
48    
49     lastCommandUpdate = Date.now();
50    
51     onKeyPress = (
52     _: string,
53     key: {
54     ctrl: boolean;
55     meta: boolean;
56     shift: boolean;
57     name: string;
58     }
59     ) => {
60     if (key.name.length !== 1) {
61     return;
62     }
63    
64     const modifiers: string[] = [];
65    
66     if (key.ctrl) modifiers.push("Ctrl");
67     if (key.meta) modifiers.push("Meta");
68     if (key.shift) modifiers.push("Shift");
69    
70     const commandKey = `${modifiers.join("+")}${modifiers.length > 0 ? "+" : ""}${key.name.toUpperCase()}`;
71     const handler = this.keyHandlers[commandKey as CommandKey];
72    
73     if (handler) {
74     handler();
75     return;
76     }
77    
78     if (key.ctrl && !key.meta && !key.shift && key.name === "c") {
79     this.interrupt();
80     return;
81     }
82    
83     logWarn("Unrecognized command key: ", `${commandKey}`);
84     };
85    
86     quit() {
87     logWithLevel(LogLevel.Event, "Quit");
88     process.exit(0);
89     }
90    
91     interrupt() {
92     logWithLevel(LogLevel.Event, "SIGINT signal received. Exiting");
93     process.exit(1);
94     }
95    
96     async reloadConfig() {
97     await this.client.configManager.load();
98     logWithLevel(LogLevel.Event, "Successfully reloaded configuration files");
99     }
100    
101     async writeConfig() {
102     await this.client.configManager.write({ guild: true, system: true });
103     logWithLevel(LogLevel.Event, "Successfully saved configuration files to disk");
104     }
105    
106     async reloadCommands(force = false) {
107     const srcDir = process.env.SOURCE_DIRECTORY_PATH ?? path.resolve(__dirname, "../../src");
108     const buildDir = process.env.BUILD_DIRECTORY_PATH ?? path.resolve(__dirname, "../../build");
109     let built = false,
110     failed = false;
111    
112     logWithLevel(LogLevel.Event, "Hot reloading commands");
113    
114     await this.client.dynamicLoader.loadCommands(undefined, true, async filePath => {
115     if (failed) {
116     return false;
117     }
118    
119     if (force) {
120     return true;
121     }
122    
123     delete require.cache[require.resolve(filePath)];
124    
125     const sourceFile = filePath.replace(buildDir, srcDir).replace(/\.js$/gi, ".ts");
126     const sourceInfo = await lstat(sourceFile);
127    
128     if (sourceInfo.mtime.getTime() < this.lastCommandUpdate) {
129     return false;
130     }
131    
132     if (!built && !__filename.endsWith(".ts")) {
133     const currentDirectory = cwd();
134     chdir(path.resolve(__dirname, "../.."));
135    
136     logInfo("Rebuilding project source files");
137    
138     const { status } = spawnSync("npm run build", {
139     encoding: "utf-8",
140     shell: true,
141     stdio: "inherit"
142     });
143    
144     chdir(currentDirectory);
145    
146     if (status !== 0) {
147     failed = true;
148     return false;
149     }
150    
151     built = true;
152     }
153    
154     return true;
155     });
156    
157     this.lastCommandUpdate = Date.now();
158    
159     if (failed) {
160     logError("Build failed. Aborting hot reload");
161     } else {
162     logWithLevel(LogLevel.Event, "Successfully hot reloaded commands");
163     }
164     }
165    
166     override boot() {
167     if (!developmentMode() || !process.stdin.isTTY) {
168     return;
169     }
170    
171     readline.emitKeypressEvents(process.stdin);
172     process.stdin.on("keypress", this.onKeyPress);
173     process.stdin.setRawMode(true);
174     }
175    
176     override deactivate() {
177     if (!developmentMode() || !process.stdin.isTTY) {
178     return;
179     }
180    
181     process.stdin.off("keypress", this.onKeyPress);
182     process.stdin.setRawMode(false);
183     }
184     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26