@open-discord-bots/framework 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/api/modules/plugin.d.ts +11 -2
- package/dist/api/modules/plugin.js +4 -0
- package/dist/cli/cli.d.ts +35 -0
- package/dist/cli/cli.js +157 -0
- package/dist/cli/editConfig.d.ts +35 -0
- package/dist/cli/editConfig.js +977 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -1
- package/dist/startup/compilation.d.ts +2 -0
- package/dist/startup/compilation.js +179 -0
- package/dist/startup/pluginLauncher.js +66 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export * as api from "./api/api";
|
|
2
2
|
export * as utilities from "./api/utils";
|
|
3
|
+
export * as cli from "./cli/cli";
|
|
3
4
|
export { loadDumpCommand } from "./startup/dump";
|
|
4
5
|
export { loadAllPlugins } from "./startup/pluginLauncher";
|
|
6
|
+
export { frameworkStartup } from "./startup/compilation";
|
package/dist/index.js
CHANGED
|
@@ -33,10 +33,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.loadAllPlugins = exports.loadDumpCommand = exports.utilities = exports.api = void 0;
|
|
36
|
+
exports.frameworkStartup = exports.loadAllPlugins = exports.loadDumpCommand = exports.cli = exports.utilities = exports.api = void 0;
|
|
37
37
|
exports.api = __importStar(require("./api/api"));
|
|
38
38
|
exports.utilities = __importStar(require("./api/utils"));
|
|
39
|
+
exports.cli = __importStar(require("./cli/cli"));
|
|
39
40
|
var dump_1 = require("./startup/dump");
|
|
40
41
|
Object.defineProperty(exports, "loadDumpCommand", { enumerable: true, get: function () { return dump_1.loadDumpCommand; } });
|
|
41
42
|
var pluginLauncher_1 = require("./startup/pluginLauncher");
|
|
42
43
|
Object.defineProperty(exports, "loadAllPlugins", { enumerable: true, get: function () { return pluginLauncher_1.loadAllPlugins; } });
|
|
44
|
+
var compilation_1 = require("./startup/compilation");
|
|
45
|
+
Object.defineProperty(exports, "frameworkStartup", { enumerable: true, get: function () { return compilation_1.frameworkStartup; } });
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.frameworkStartup = frameworkStartup;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
9
|
+
const crypto_1 = require("crypto");
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const ansis_1 = __importDefault(require("ansis"));
|
|
12
|
+
/** ## What is this?
|
|
13
|
+
* This is a function which compares `./src/` with a hash stored in `./dist/hash.txt`.
|
|
14
|
+
* The hash is based on the modified date & file metadata of all files in `./src/`.
|
|
15
|
+
*
|
|
16
|
+
* If the hash is different, the bot will automatically re-compile.
|
|
17
|
+
* This will help you save CPU resources because the bot shouldn't re-compile when nothing has been changed :)
|
|
18
|
+
*/
|
|
19
|
+
function computeSourceHash(dir, upperHash) {
|
|
20
|
+
const hash = upperHash ? upperHash : (0, crypto_1.createHash)("sha256");
|
|
21
|
+
const info = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
22
|
+
for (const file of info) {
|
|
23
|
+
const fullPath = path_1.default.join(dir, file.name);
|
|
24
|
+
if (file.isFile() && [".js", ".ts", ".jsx", ".tsx"].some((ext) => file.name.endsWith(ext))) {
|
|
25
|
+
const statInfo = fs_1.default.statSync(fullPath);
|
|
26
|
+
//compute hash using file metadata
|
|
27
|
+
const fileInfo = `${fullPath}:${statInfo.size}:${statInfo.mtimeMs}`;
|
|
28
|
+
hash.update(fileInfo);
|
|
29
|
+
}
|
|
30
|
+
else if (file.isDirectory()) {
|
|
31
|
+
//recursively compute all folders
|
|
32
|
+
computeSourceHash(fullPath, hash);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//return when not being called recursively
|
|
36
|
+
if (!upperHash) {
|
|
37
|
+
return hash.digest("hex");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function requiresCompilation(project) {
|
|
41
|
+
const logTitle = (project == "openticket") ? "OT" : "OM";
|
|
42
|
+
//check hashes when not using "--compile-only" flag
|
|
43
|
+
if (process.argv.includes("--compile-only"))
|
|
44
|
+
return true;
|
|
45
|
+
console.log(logTitle + ": Comparing prebuilds with source...");
|
|
46
|
+
const sourceHash = computeSourceHash("./src/");
|
|
47
|
+
const pluginHash = computeSourceHash("./plugins/");
|
|
48
|
+
const hash = sourceHash + ":" + pluginHash;
|
|
49
|
+
if (fs_1.default.existsSync("./dist/hash.txt")) {
|
|
50
|
+
const distHash = fs_1.default.readFileSync("./dist/hash.txt").toString();
|
|
51
|
+
if (distHash === hash)
|
|
52
|
+
return false;
|
|
53
|
+
else
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
else
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
function saveNewCompilationHash() {
|
|
60
|
+
const sourceHash = computeSourceHash("./src/");
|
|
61
|
+
const pluginHash = computeSourceHash("./plugins/");
|
|
62
|
+
const hash = sourceHash + ":" + pluginHash;
|
|
63
|
+
fs_1.default.writeFileSync("./dist/hash.txt", hash);
|
|
64
|
+
}
|
|
65
|
+
function frameworkStartup(startupFlags, project, startCallback) {
|
|
66
|
+
const logTitle = (project == "openticket") ? "OT" : "OM";
|
|
67
|
+
//push additional startup flags (for pterodactyl panels)
|
|
68
|
+
process.argv.push(...startupFlags);
|
|
69
|
+
//check directory structure
|
|
70
|
+
const requiredStructures = [
|
|
71
|
+
"index.js",
|
|
72
|
+
"./package.json",
|
|
73
|
+
"./README.md",
|
|
74
|
+
"./LICENSE.md",
|
|
75
|
+
"./tsconfig.json",
|
|
76
|
+
"./src/",
|
|
77
|
+
"./src/index.ts",
|
|
78
|
+
"./languages/",
|
|
79
|
+
"./config/",
|
|
80
|
+
"./plugins/",
|
|
81
|
+
"./.github/",
|
|
82
|
+
"./.github/FUNDING.yml",
|
|
83
|
+
"./.github/SECURITY.yml"
|
|
84
|
+
];
|
|
85
|
+
for (const path of requiredStructures) {
|
|
86
|
+
if (!fs_1.default.existsSync(path))
|
|
87
|
+
throw new Error(logTitle + ": Project uses invalid structure for Open Discord! (" + path + ")");
|
|
88
|
+
}
|
|
89
|
+
//start compilation
|
|
90
|
+
if (!process.argv.includes("--no-compile")) {
|
|
91
|
+
const requiredDependencies = new Set();
|
|
92
|
+
if (fs_1.default.existsSync("./plugins")) {
|
|
93
|
+
console.log(logTitle + ": Reading plugin.json files...");
|
|
94
|
+
for (const pluginDir of fs_1.default.readdirSync("./plugins")) {
|
|
95
|
+
if (pluginDir === ".DS_Store")
|
|
96
|
+
continue;
|
|
97
|
+
const pluginPath = path_1.default.join("./plugins", pluginDir);
|
|
98
|
+
if (!fs_1.default.statSync(pluginPath).isDirectory())
|
|
99
|
+
continue;
|
|
100
|
+
const pluginJsonPath = path_1.default.join(pluginPath, "plugin.json");
|
|
101
|
+
if (fs_1.default.existsSync(pluginJsonPath)) {
|
|
102
|
+
try {
|
|
103
|
+
const pluginData = JSON.parse(fs_1.default.readFileSync(pluginJsonPath).toString());
|
|
104
|
+
if (pluginData.npmDependencies && Array.isArray(pluginData.npmDependencies)) {
|
|
105
|
+
pluginData.npmDependencies.forEach((dep) => {
|
|
106
|
+
if (typeof dep === "string" && dep.trim()) {
|
|
107
|
+
requiredDependencies.add(dep.trim());
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
// skip invalid plugin.json files, will be caught later
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (requiredDependencies.size > 0) {
|
|
118
|
+
console.log(logTitle + ": Checking plugin npm dependencies...");
|
|
119
|
+
const missingDeps = [];
|
|
120
|
+
for (const dep of requiredDependencies) {
|
|
121
|
+
try {
|
|
122
|
+
require.resolve(dep);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
missingDeps.push(dep);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (missingDeps.length > 0) {
|
|
129
|
+
console.log(ansis_1.default.red(logTitle + ": ❌ Fatal Error --> Missing npm dependencies required by plugins:\n\n") + ansis_1.default.cyan(missingDeps.map((dep) => " - " + dep).join("\n") + "\n"));
|
|
130
|
+
console.log(logTitle + ": Please install missing dependencies using the following command:\n> " + ansis_1.default.bold.green("npm install " + missingDeps.join(" ")) + "\n");
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (requiresCompilation(project)) {
|
|
136
|
+
console.log(logTitle + ": Compilation Required...");
|
|
137
|
+
//REMOVE EXISTING BUILDS
|
|
138
|
+
console.log(logTitle + ": Removing Prebuilds...");
|
|
139
|
+
fs_1.default.rmSync("./dist", { recursive: true, force: true });
|
|
140
|
+
//COMPILE TYPESCRIPT
|
|
141
|
+
console.log(logTitle + ": Compiling Typescript...");
|
|
142
|
+
const configPath = path_1.default.resolve('./tsconfig.json');
|
|
143
|
+
const configFile = typescript_1.default.readConfigFile(configPath, typescript_1.default.sys.readFile);
|
|
144
|
+
//check for tsconfig errors
|
|
145
|
+
if (configFile.error) {
|
|
146
|
+
const message = typescript_1.default.formatDiagnosticsWithColorAndContext([configFile.error], typescript_1.default.createCompilerHost({}));
|
|
147
|
+
console.error(message);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
//parse tsconfig file
|
|
151
|
+
const parsedConfig = typescript_1.default.parseJsonConfigFileContent(configFile.config, typescript_1.default.sys, path_1.default.dirname(configPath));
|
|
152
|
+
//create program/compiler
|
|
153
|
+
const program = typescript_1.default.createProgram({
|
|
154
|
+
rootNames: parsedConfig.fileNames,
|
|
155
|
+
options: parsedConfig.options
|
|
156
|
+
});
|
|
157
|
+
//emit all compiled files
|
|
158
|
+
const emitResult = program.emit();
|
|
159
|
+
//print emit errors/warnings (type errors)
|
|
160
|
+
const allDiagnostics = typescript_1.default.getPreEmitDiagnostics(program).concat(emitResult.diagnostics);
|
|
161
|
+
const formattedDiagnostics = typescript_1.default.formatDiagnosticsWithColorAndContext(allDiagnostics, typescript_1.default.createCompilerHost(parsedConfig.options));
|
|
162
|
+
console.log(formattedDiagnostics);
|
|
163
|
+
if (emitResult.emitSkipped || allDiagnostics.find((d) => d.category == typescript_1.default.DiagnosticCategory.Error || d.category == typescript_1.default.DiagnosticCategory.Warning)) {
|
|
164
|
+
console.log(logTitle + ": Compilation Failed!");
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else
|
|
169
|
+
console.log(logTitle + ": No Compilation Required...");
|
|
170
|
+
//save new compilation hash
|
|
171
|
+
saveNewCompilationHash();
|
|
172
|
+
}
|
|
173
|
+
//START BOT
|
|
174
|
+
console.log(logTitle + ": Compilation Succeeded!");
|
|
175
|
+
if (process.argv.includes("--compile-only"))
|
|
176
|
+
process.exit(0); //exit when no startup is required!
|
|
177
|
+
console.log(logTitle + ": Starting Bot!");
|
|
178
|
+
startCallback();
|
|
179
|
+
}
|
|
@@ -15,6 +15,7 @@ const loadAllPlugins = async (opendiscord) => {
|
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
const plugins = fs_1.default.readdirSync("./plugins");
|
|
18
|
+
const pluginVersionRegex = /^(OT|OM)v(\d+)\.(\d+|x)\.(\d+|x)$/;
|
|
18
19
|
//check & validate
|
|
19
20
|
for (const p of plugins) {
|
|
20
21
|
//prechecks
|
|
@@ -44,6 +45,20 @@ const loadAllPlugins = async (opendiscord) => {
|
|
|
44
45
|
throw new index_1.api.ODPluginError("Failed to load plugin.json/version");
|
|
45
46
|
if (typeof rawplugindata.startFile != "string")
|
|
46
47
|
throw new index_1.api.ODPluginError("Failed to load plugin.json/startFile");
|
|
48
|
+
//only check "supportedVersions" if it exists (should be array)
|
|
49
|
+
if (rawplugindata.supportedVersions) {
|
|
50
|
+
if (!Array.isArray(rawplugindata.supportedVersions))
|
|
51
|
+
throw new index_1.api.ODPluginError("Failed to load plugin.json/supportedVersions (must be array)");
|
|
52
|
+
for (const version of rawplugindata.supportedVersions) {
|
|
53
|
+
if (typeof version !== "string") {
|
|
54
|
+
throw new index_1.api.ODPluginError("Failed to load plugin.json/supportedVersions (all items must be strings)");
|
|
55
|
+
}
|
|
56
|
+
//only OT (Open Ticket) & OM (Open Moderation) are supported at the moment
|
|
57
|
+
if (!pluginVersionRegex.test(version)) {
|
|
58
|
+
throw new index_1.api.ODPluginError(`Failed to load plugin.json/supportedVersions (invalid format: "${version}", expected format like "OTv4.0.x" or "OMv1.0.0")`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
47
62
|
if (typeof rawplugindata.enabled != "boolean")
|
|
48
63
|
throw new index_1.api.ODPluginError("Failed to load plugin.json/enabled");
|
|
49
64
|
if (typeof rawplugindata.priority != "number")
|
|
@@ -60,6 +75,9 @@ const loadAllPlugins = async (opendiscord) => {
|
|
|
60
75
|
throw new index_1.api.ODPluginError("Failed to load plugin.json/details");
|
|
61
76
|
if (typeof rawplugindata.details.author != "string")
|
|
62
77
|
throw new index_1.api.ODPluginError("Failed to load plugin.json/details/author");
|
|
78
|
+
//only check "contributors" if it exists (should be array)
|
|
79
|
+
if (rawplugindata.details.contributors && !Array.isArray(rawplugindata.details.contributors))
|
|
80
|
+
throw new index_1.api.ODPluginError("Failed to load plugin.json/details/contributors (must be array)");
|
|
63
81
|
if (typeof rawplugindata.details.shortDescription != "string")
|
|
64
82
|
throw new index_1.api.ODPluginError("Failed to load plugin.json/details/shortDescription");
|
|
65
83
|
if (typeof rawplugindata.details.longDescription != "string")
|
|
@@ -105,12 +123,43 @@ const loadAllPlugins = async (opendiscord) => {
|
|
|
105
123
|
const incompatibilities = [];
|
|
106
124
|
const missingDependencies = [];
|
|
107
125
|
const missingPlugins = [];
|
|
126
|
+
const versionIncompatibilities = [];
|
|
108
127
|
//go through all plugins for errors
|
|
109
128
|
sortedPlugins.filter((plugin) => plugin.enabled).forEach((plugin) => {
|
|
110
129
|
const from = plugin.id.value;
|
|
111
130
|
plugin.dependenciesInstalled().forEach((missing) => missingDependencies.push({ id: from, missing }));
|
|
112
131
|
plugin.pluginsIncompatible(opendiscord.plugins).forEach((incompatible) => incompatibilities.push({ from, to: incompatible }));
|
|
113
132
|
plugin.pluginsInstalled(opendiscord.plugins).forEach((missing) => missingPlugins.push({ id: from, missing }));
|
|
133
|
+
//check if plugins are compatible with version of bot
|
|
134
|
+
if (plugin.data.supportedVersions && plugin.data.supportedVersions.length > 0) {
|
|
135
|
+
const currentVersion = opendiscord.versions.get("opendiscord:version");
|
|
136
|
+
if (!currentVersion)
|
|
137
|
+
throw new index_1.api.ODSystemError("Unable to get project version: opendiscord.versions.get('opendiscord:version')!");
|
|
138
|
+
let isCompatible = false;
|
|
139
|
+
for (const versionStr of plugin.data.supportedVersions) {
|
|
140
|
+
const match = versionStr.match(pluginVersionRegex);
|
|
141
|
+
if (!match)
|
|
142
|
+
continue;
|
|
143
|
+
const projectPrefix = match[1];
|
|
144
|
+
const primary = parseInt(match[2]);
|
|
145
|
+
const secondary = (match[3] === "x") ? null : parseInt(match[3]);
|
|
146
|
+
const tertiary = (match[4] === "x") ? null : parseInt(match[4]);
|
|
147
|
+
if (projectPrefix !== "OT")
|
|
148
|
+
continue;
|
|
149
|
+
else if (primary !== currentVersion.primary)
|
|
150
|
+
continue;
|
|
151
|
+
else if (typeof secondary === "number" && secondary !== currentVersion.secondary)
|
|
152
|
+
continue;
|
|
153
|
+
else if (typeof tertiary === "number" && tertiary !== currentVersion.tertiary)
|
|
154
|
+
continue;
|
|
155
|
+
else {
|
|
156
|
+
isCompatible = true;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (!isCompatible)
|
|
161
|
+
versionIncompatibilities.push({ id: from });
|
|
162
|
+
}
|
|
114
163
|
});
|
|
115
164
|
//handle all incompatibilities
|
|
116
165
|
const alreadyLoggedCompatPlugins = [];
|
|
@@ -159,6 +208,20 @@ const loadAllPlugins = async (opendiscord) => {
|
|
|
159
208
|
]);
|
|
160
209
|
initPluginError = true;
|
|
161
210
|
});
|
|
211
|
+
//handle all bot version incompatibilities
|
|
212
|
+
versionIncompatibilities.forEach((match) => {
|
|
213
|
+
const plugin = opendiscord.plugins.get(match.id);
|
|
214
|
+
if (plugin && !plugin.crashed) {
|
|
215
|
+
plugin.crashed = true;
|
|
216
|
+
plugin.crashReason = "incompatible.version";
|
|
217
|
+
}
|
|
218
|
+
const versions = plugin?.data.supportedVersions?.join(", ") ?? "<unknown-version>";
|
|
219
|
+
const currentVersion = opendiscord.versions.get("opendiscord:version")?.toString() ?? "<OD:UNKNOWN_VERION>";
|
|
220
|
+
opendiscord.log(`Plugin version incompatibility: plugin requires "${versions}" but current bot version is "${currentVersion}", canceling plugin execution...`, "plugin", [
|
|
221
|
+
{ key: "path", value: "./plugins/" + match.id }
|
|
222
|
+
]);
|
|
223
|
+
initPluginError = true;
|
|
224
|
+
});
|
|
162
225
|
//exit on error (when soft mode disabled)
|
|
163
226
|
if (!opendiscord.sharedFuses.getFuse("softPluginLoading") && initPluginError) {
|
|
164
227
|
console.log("");
|
|
@@ -181,18 +244,19 @@ const loadAllPlugins = async (opendiscord) => {
|
|
|
181
244
|
}
|
|
182
245
|
}
|
|
183
246
|
for (const plugin of sortedPlugins) {
|
|
247
|
+
const authors = [plugin.details.author, ...(plugin.details.contributors ?? [])].join(", ");
|
|
184
248
|
if (plugin.enabled) {
|
|
185
249
|
opendiscord.debug.debug("Plugin \"" + plugin.id.value + "\" loaded", [
|
|
186
250
|
{ key: "status", value: (plugin.crashed ? "crashed" : "success") },
|
|
187
251
|
{ key: "crashReason", value: (plugin.crashed ? (plugin.crashReason ?? "/") : "/") },
|
|
188
|
-
{ key: "
|
|
252
|
+
{ key: "authors", value: authors },
|
|
189
253
|
{ key: "version", value: plugin.version.toString() },
|
|
190
254
|
{ key: "priority", value: plugin.priority.toString() }
|
|
191
255
|
]);
|
|
192
256
|
}
|
|
193
257
|
else {
|
|
194
258
|
opendiscord.debug.debug("Plugin \"" + plugin.id.value + "\" disabled", [
|
|
195
|
-
{ key: "
|
|
259
|
+
{ key: "authors", value: authors },
|
|
196
260
|
{ key: "version", value: plugin.version.toString() },
|
|
197
261
|
{ key: "priority", value: plugin.priority.toString() }
|
|
198
262
|
]);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-discord-bots/framework",
|
|
3
3
|
"author": "DJj123dj",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.4",
|
|
5
5
|
"description": "The core framework of the popular open-source discord bots: Open Ticket & Open Moderation.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|