Small Improvements for the new plugin.json code
This commit is contained in:
@@ -28,6 +28,7 @@ const fs = require("fs")
|
|||||||
const ts = require("typescript")
|
const ts = require("typescript")
|
||||||
const {createHash,Hash} = require("crypto")
|
const {createHash,Hash} = require("crypto")
|
||||||
const nodepath = require('path')
|
const nodepath = require('path')
|
||||||
|
const ansis = require("ansis")
|
||||||
|
|
||||||
/** ## What is this?
|
/** ## What is this?
|
||||||
* This is a function which compares `./src/` with a hash stored in `./dist/hash.txt`.
|
* This is a function which compares `./src/` with a hash stored in `./dist/hash.txt`.
|
||||||
@@ -84,48 +85,47 @@ function saveNewCompilationHash(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!process.argv.includes("--no-compile")){
|
if (!process.argv.includes("--no-compile")){
|
||||||
const pluginDependencies = new Set()
|
const requiredDependencies = new Set()
|
||||||
if (fs.existsSync("./plugins")){
|
if (fs.existsSync("./plugins")){
|
||||||
console.log("OT: Reading plugin.json files...")
|
console.log("OT: Reading plugin.json files...")
|
||||||
const plugins = fs.readdirSync("./plugins")
|
for (const pluginDir of fs.readdirSync("./plugins")){
|
||||||
for (const pluginDir of plugins){
|
|
||||||
if (pluginDir === ".DS_Store") continue
|
if (pluginDir === ".DS_Store") continue
|
||||||
const pluginPath = nodepath.join("./plugins", pluginDir)
|
const pluginPath = nodepath.join("./plugins", pluginDir)
|
||||||
if (!fs.statSync(pluginPath).isDirectory()) continue
|
if (!fs.statSync(pluginPath).isDirectory()) continue
|
||||||
|
|
||||||
const pluginJsonPath = nodepath.join(pluginPath, "plugin.json")
|
const pluginJsonPath = nodepath.join(pluginPath, "plugin.json")
|
||||||
if (fs.existsSync(pluginJsonPath)){
|
if (fs.existsSync(pluginJsonPath)){
|
||||||
try {
|
try{
|
||||||
const pluginData = JSON.parse(fs.readFileSync(pluginJsonPath).toString())
|
const pluginData = JSON.parse(fs.readFileSync(pluginJsonPath).toString())
|
||||||
if (pluginData.npmDependencies && Array.isArray(pluginData.npmDependencies)){
|
if (pluginData.npmDependencies && Array.isArray(pluginData.npmDependencies)){
|
||||||
pluginData.npmDependencies.forEach(dep => {
|
pluginData.npmDependencies.forEach((dep) => {
|
||||||
if (typeof dep === "string" && dep.trim()) {
|
if (typeof dep === "string" && dep.trim()){
|
||||||
pluginDependencies.add(dep.trim())
|
requiredDependencies.add(dep.trim())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
}catch(err){
|
||||||
// skip invalid plugin.json files, will be caught later
|
// skip invalid plugin.json files, will be caught later
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pluginDependencies.size > 0){
|
if (requiredDependencies.size > 0){
|
||||||
console.log("OT: Checking plugin npm dependencies...")
|
console.log("OT: Checking plugin npm dependencies...")
|
||||||
|
/**@type {string[]} */
|
||||||
const missingDeps = []
|
const missingDeps = []
|
||||||
for (const dep of pluginDependencies){
|
for (const dep of requiredDependencies){
|
||||||
try {
|
try{
|
||||||
require.resolve(dep)
|
require.resolve(dep)
|
||||||
} catch {
|
}catch(err){
|
||||||
missingDeps.push(dep)
|
missingDeps.push(dep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (missingDeps.length > 0){
|
if (missingDeps.length > 0){
|
||||||
console.log("OT: Warning - Missing npm dependencies required by plugins:")
|
console.log(ansis.red("OT: ❌ Fatal Error --> Missing npm dependencies required by plugins:\n\n")+ansis.cyan(missingDeps.map((dep) => " - "+dep).join("\n")+"\n"))
|
||||||
missingDeps.forEach(dep => console.log(` - ${dep}`))
|
console.log("OT: Please install missing dependencies using the following command:\n> "+ansis.bold.green("npm install " + missingDeps.join(" "))+"\n")
|
||||||
console.log("OT: Please install missing dependencies with: npm install " + missingDeps.join(" "))
|
process.exit(1)
|
||||||
console.log("OT: Continuing compilation anyway...")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"id":"example-plugin",
|
"id":"example-plugin",
|
||||||
"version":"1.0.0",
|
"version":"1.0.0",
|
||||||
"startFile":"index.ts",
|
"startFile":"index.ts",
|
||||||
|
"supportedVersions":["OTv4.0.x","OTv4.1.x"],
|
||||||
|
|
||||||
"enabled":false,
|
"enabled":false,
|
||||||
"priority":0,
|
"priority":0,
|
||||||
@@ -14,9 +15,7 @@
|
|||||||
|
|
||||||
"details":{
|
"details":{
|
||||||
"author":"DJj123dj",
|
"author":"DJj123dj",
|
||||||
"authors":["DJj123dj"],
|
|
||||||
"contributors":[],
|
"contributors":[],
|
||||||
"versions":["OTv4.0.x","OTv4.1.x"],
|
|
||||||
"shortDescription":"A simple template for an Open Ticket v4 plugin!",
|
"shortDescription":"A simple template for an Open Ticket v4 plugin!",
|
||||||
"longDescription":"A simple example of an Open Ticket v4 plugin!",
|
"longDescription":"A simple example of an Open Ticket v4 plugin!",
|
||||||
"imageUrl":"",
|
"imageUrl":"",
|
||||||
|
|||||||
@@ -55,6 +55,11 @@ export interface ODPluginData {
|
|||||||
version:string,
|
version:string,
|
||||||
/**The location of the start file of the plugin relative to the rootDir of the plugin */
|
/**The location of the start file of the plugin relative to the rootDir of the plugin */
|
||||||
startFile:string,
|
startFile:string,
|
||||||
|
/**A list of compatible versions. (e.g. `["OTv4.0.x", "OMv1.x.x"]`) (optional, will be required in future version)
|
||||||
|
* - `OT` --> Open Ticket support
|
||||||
|
* - `OM` --> Open Moderation support
|
||||||
|
*/
|
||||||
|
supportedVersions?:string[],
|
||||||
|
|
||||||
/**Is this plugin enabled? */
|
/**Is this plugin enabled? */
|
||||||
enabled:boolean,
|
enabled:boolean,
|
||||||
@@ -78,14 +83,10 @@ export interface ODPluginData {
|
|||||||
* Additional details in the `plugin.json` file from a plugin.
|
* Additional details in the `plugin.json` file from a plugin.
|
||||||
*/
|
*/
|
||||||
export interface ODPluginDetails {
|
export interface ODPluginDetails {
|
||||||
/**The author of the plugin. (string for backwards compatibility, or string[] for multiple authors) */
|
/**The main author of the plugin. Additional contributors can be specified in `contributors`. */
|
||||||
author:string|string[],
|
author:string,
|
||||||
/**A list of authors of the plugin. (new format, optional if author is provided) */
|
/**A list of plugin contributors. (optional, will be required in future version) */
|
||||||
authors?:string[],
|
|
||||||
/**A list of contributors to the plugin. (optional) */
|
|
||||||
contributors?:string[],
|
contributors?:string[],
|
||||||
/**A list of compatible versions. (e.g. ["OTv4.0.x", "OTv4.1.x", "ODv1.0.0"]) */
|
|
||||||
versions?:string[],
|
|
||||||
/**A short description of this plugin. */
|
/**A short description of this plugin. */
|
||||||
shortDescription:string,
|
shortDescription:string,
|
||||||
/**A large description of this plugin. */
|
/**A large description of this plugin. */
|
||||||
@@ -127,7 +128,7 @@ export class ODPlugin extends ODManagerData {
|
|||||||
/**Did this plugin crash? (A reason is available in the `crashReason`) */
|
/**Did this plugin crash? (A reason is available in the `crashReason`) */
|
||||||
crashed: boolean
|
crashed: boolean
|
||||||
/**The reason which caused this plugin to crash. */
|
/**The reason which caused this plugin to crash. */
|
||||||
crashReason: null|"incompatible.plugin"|"missing.plugin"|"missing.dependency"|"executed" = null
|
crashReason: null|"incompatible.plugin"|"missing.plugin"|"missing.dependency"|"incompatible.version"|"executed" = null
|
||||||
|
|
||||||
constructor(dir:string, jsondata:ODPluginData){
|
constructor(dir:string, jsondata:ODPluginData){
|
||||||
super(jsondata.id)
|
super(jsondata.id)
|
||||||
@@ -218,24 +219,9 @@ export class ODPlugin extends ODManagerData {
|
|||||||
|
|
||||||
return incompatible
|
return incompatible
|
||||||
}
|
}
|
||||||
|
/**Get a list of all authors & contributors of this plugin. */
|
||||||
getAuthors(): string[] {
|
getAuthors(): string[] {
|
||||||
if (Array.isArray(this.details.author)) {
|
return [this.details.author,...(this.details.contributors ?? [])]
|
||||||
return this.details.author
|
|
||||||
} else if (this.details.authors && Array.isArray(this.details.authors)) {
|
|
||||||
return this.details.authors
|
|
||||||
} else if (typeof this.details.author === "string") {
|
|
||||||
return [this.details.author]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
getContributors(): string[] {
|
|
||||||
return this.details.contributors || []
|
|
||||||
}
|
|
||||||
|
|
||||||
getCompatibleVersions(): string[] {
|
|
||||||
return this.details.versions || []
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const loadAllPlugins = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
const plugins = fs.readdirSync("./plugins")
|
const plugins = fs.readdirSync("./plugins")
|
||||||
|
const pluginVersionRegex = /^(OT|OM)v(\d+)\.(\d+|x)\.(\d+|x)$/
|
||||||
|
|
||||||
//check & validate
|
//check & validate
|
||||||
plugins.forEach((p) => {
|
plugins.forEach((p) => {
|
||||||
@@ -38,6 +39,20 @@ export const loadAllPlugins = async () => {
|
|||||||
if (typeof rawplugindata.version != "string") throw new ODPluginError("Failed to load plugin.json/version")
|
if (typeof rawplugindata.version != "string") throw new ODPluginError("Failed to load plugin.json/version")
|
||||||
if (typeof rawplugindata.startFile != "string") throw new ODPluginError("Failed to load plugin.json/startFile")
|
if (typeof rawplugindata.startFile != "string") throw new ODPluginError("Failed to load plugin.json/startFile")
|
||||||
|
|
||||||
|
//only check "supportedVersions" if it exists (should be array)
|
||||||
|
if (rawplugindata.supportedVersions){
|
||||||
|
if (!Array.isArray(rawplugindata.supportedVersions)) throw new ODPluginError("Failed to load plugin.json/supportedVersions (must be array)")
|
||||||
|
for (const version of rawplugindata.supportedVersions){
|
||||||
|
if (typeof version !== "string"){
|
||||||
|
throw new ODPluginError("Failed to load plugin.json/supportedVersions (all items must be strings)")
|
||||||
|
}
|
||||||
|
//only OT (Open Ticket) & OM (Open Moderation) are supported at the moment
|
||||||
|
if (!pluginVersionRegex.test(version)){
|
||||||
|
throw new ODPluginError(`Failed to load plugin.json/supportedVersions (invalid format: "${version}", expected format like "OTv4.0.x" or "OMv1.0.0")`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof rawplugindata.enabled != "boolean") throw new ODPluginError("Failed to load plugin.json/enabled")
|
if (typeof rawplugindata.enabled != "boolean") throw new ODPluginError("Failed to load plugin.json/enabled")
|
||||||
if (typeof rawplugindata.priority != "number") throw new ODPluginError("Failed to load plugin.json/priority")
|
if (typeof rawplugindata.priority != "number") throw new ODPluginError("Failed to load plugin.json/priority")
|
||||||
if (!Array.isArray(rawplugindata.events)) throw new ODPluginError("Failed to load plugin.json/events")
|
if (!Array.isArray(rawplugindata.events)) throw new ODPluginError("Failed to load plugin.json/events")
|
||||||
@@ -47,40 +62,10 @@ export const loadAllPlugins = async () => {
|
|||||||
if (!Array.isArray(rawplugindata.incompatiblePlugins)) throw new ODPluginError("Failed to load plugin.json/incompatiblePlugins")
|
if (!Array.isArray(rawplugindata.incompatiblePlugins)) throw new ODPluginError("Failed to load plugin.json/incompatiblePlugins")
|
||||||
|
|
||||||
if (typeof rawplugindata.details != "object") throw new ODPluginError("Failed to load plugin.json/details")
|
if (typeof rawplugindata.details != "object") throw new ODPluginError("Failed to load plugin.json/details")
|
||||||
|
if (typeof rawplugindata.details.author != "string") throw new ODPluginError("Failed to load plugin.json/details/author (must be string or array)")
|
||||||
|
|
||||||
// author can be string (old) or array (new), convert to array for consistency
|
//only check "contributors" if it exists (should be array)
|
||||||
if (typeof rawplugindata.details.author != "string" && !Array.isArray(rawplugindata.details.author)) {
|
if (rawplugindata.details.contributors && !Array.isArray(rawplugindata.details.contributors)) throw new ODPluginError("Failed to load plugin.json/details/contributors (must be array)")
|
||||||
throw new ODPluginError("Failed to load plugin.json/details/author (must be string or array)")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof rawplugindata.details.author == "string") {
|
|
||||||
rawplugindata.details.authors = [rawplugindata.details.author]
|
|
||||||
} else if (Array.isArray(rawplugindata.details.author)) {
|
|
||||||
rawplugindata.details.authors = rawplugindata.details.author
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawplugindata.details.authors && !Array.isArray(rawplugindata.details.authors)) {
|
|
||||||
throw new ODPluginError("Failed to load plugin.json/details/authors (must be array)")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawplugindata.details.contributors && !Array.isArray(rawplugindata.details.contributors)) {
|
|
||||||
throw new ODPluginError("Failed to load plugin.json/details/contributors (must be array)")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawplugindata.details.versions) {
|
|
||||||
if (!Array.isArray(rawplugindata.details.versions)) {
|
|
||||||
throw new ODPluginError("Failed to load plugin.json/details/versions (must be array)")
|
|
||||||
}
|
|
||||||
for (const version of rawplugindata.details.versions) {
|
|
||||||
if (typeof version != "string") {
|
|
||||||
throw new ODPluginError("Failed to load plugin.json/details/versions (all items must be strings)")
|
|
||||||
}
|
|
||||||
const versionPattern = /^(OT|OD|OM|OU)v\d+\.\d+(\.\d+|\.x)$/
|
|
||||||
if (!versionPattern.test(version)) {
|
|
||||||
throw new ODPluginError(`Failed to load plugin.json/details/versions (invalid format: "${version}", expected format like "OTv4.0.x" or "ODv1.0.0")`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof rawplugindata.details.shortDescription != "string") throw new ODPluginError("Failed to load plugin.json/details/shortDescription")
|
if (typeof rawplugindata.details.shortDescription != "string") throw new ODPluginError("Failed to load plugin.json/details/shortDescription")
|
||||||
if (typeof rawplugindata.details.longDescription != "string") throw new ODPluginError("Failed to load plugin.json/details/longDescription")
|
if (typeof rawplugindata.details.longDescription != "string") throw new ODPluginError("Failed to load plugin.json/details/longDescription")
|
||||||
@@ -134,41 +119,31 @@ export const loadAllPlugins = async () => {
|
|||||||
plugin.pluginsIncompatible(opendiscord.plugins).forEach((incompatible) => incompatibilities.push({from,to:incompatible}))
|
plugin.pluginsIncompatible(opendiscord.plugins).forEach((incompatible) => incompatibilities.push({from,to:incompatible}))
|
||||||
plugin.pluginsInstalled(opendiscord.plugins).forEach((missing) => missingPlugins.push({id:from,missing}))
|
plugin.pluginsInstalled(opendiscord.plugins).forEach((missing) => missingPlugins.push({id:from,missing}))
|
||||||
|
|
||||||
// check if plugin versions are compatible
|
//check if plugins are compatible with version of bot
|
||||||
if (plugin.data.details.versions && plugin.data.details.versions.length > 0) {
|
if (plugin.data.supportedVersions && plugin.data.supportedVersions.length > 0){
|
||||||
const currentVersion = opendiscord.versions.get("opendiscord:version")
|
const currentVersion = opendiscord.versions.get("opendiscord:version")
|
||||||
let isCompatible = false
|
let isCompatible = false
|
||||||
|
|
||||||
for (const versionStr of plugin.data.details.versions) {
|
for (const versionStr of plugin.data.supportedVersions){
|
||||||
const match = versionStr.match(/^(OT|OD|OM|OU)v(\d+)\.(\d+)(?:\.(\d+|x))$/)
|
const match = versionStr.match(pluginVersionRegex)
|
||||||
if (!match) continue
|
if (!match) continue
|
||||||
|
|
||||||
const projectPrefix = match[1]
|
const projectPrefix = match[1]
|
||||||
const primary = parseInt(match[2])
|
const primary = parseInt(match[2])
|
||||||
const secondary = parseInt(match[3])
|
const secondary = (match[3] === "x") ? null : parseInt(match[3])
|
||||||
const tertiary = match[4]
|
const tertiary = (match[4] === "x") ? null : parseInt(match[4])
|
||||||
|
|
||||||
if (projectPrefix !== "OT") continue
|
if (projectPrefix !== "OT") continue
|
||||||
|
else if (primary !== currentVersion.primary) continue
|
||||||
if (tertiary === "x") {
|
else if (typeof secondary === "number" && secondary !== currentVersion.secondary) continue
|
||||||
if (currentVersion.primary === primary && currentVersion.secondary === secondary) {
|
else if (typeof tertiary === "number" && tertiary !== currentVersion.tertiary) continue
|
||||||
isCompatible = true
|
else{
|
||||||
break
|
isCompatible = true
|
||||||
}
|
break
|
||||||
} else {
|
|
||||||
const requiredVersion = api.ODVersion.fromString("temp", `v${primary}.${secondary}.${parseInt(tertiary)}`)
|
|
||||||
if (currentVersion.primary === requiredVersion.primary &&
|
|
||||||
currentVersion.secondary === requiredVersion.secondary &&
|
|
||||||
currentVersion.tertiary === requiredVersion.tertiary) {
|
|
||||||
isCompatible = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isCompatible) {
|
if (!isCompatible) versionIncompatibilities.push({id:from})
|
||||||
versionIncompatibilities.push({id:from})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -224,16 +199,17 @@ export const loadAllPlugins = async () => {
|
|||||||
initPluginError = true
|
initPluginError = true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//handle all bot version incompatibilities
|
||||||
versionIncompatibilities.forEach((match) => {
|
versionIncompatibilities.forEach((match) => {
|
||||||
const plugin = opendiscord.plugins.get(match.id)
|
const plugin = opendiscord.plugins.get(match.id)
|
||||||
if (plugin && !plugin.crashed){
|
if (plugin && !plugin.crashed){
|
||||||
plugin.crashed = true
|
plugin.crashed = true
|
||||||
plugin.crashReason = "missing.dependency"
|
plugin.crashReason = "incompatible.version"
|
||||||
}
|
}
|
||||||
|
|
||||||
const versions = plugin?.data.details.versions?.join(", ") ?? "unknown"
|
const versions = plugin?.data.supportedVersions?.join(", ") ?? "<unknown-version>"
|
||||||
const currentVersion = opendiscord.versions.get("opendiscord:version").toString()
|
const currentVersion = opendiscord.versions.get("opendiscord:version").toString()
|
||||||
opendiscord.log(`Plugin version incompatibility: plugin requires "${versions}" but current version is "${currentVersion}", canceling plugin execution...`,"plugin",[
|
opendiscord.log(`Plugin version incompatibility: plugin requires "${versions}" but current bot version is "${currentVersion}", canceling plugin execution...`,"plugin",[
|
||||||
{key:"path",value:"./plugins/"+match.id}
|
{key:"path",value:"./plugins/"+match.id}
|
||||||
])
|
])
|
||||||
initPluginError = true
|
initPluginError = true
|
||||||
@@ -264,20 +240,19 @@ export const loadAllPlugins = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const plugin of sortedPlugins){
|
for (const plugin of sortedPlugins){
|
||||||
const authors = (Array.isArray(plugin.details.author) ? plugin.details.author :
|
const authors = [plugin.details.author,...(plugin.details.contributors ?? [])].join(", ")
|
||||||
(plugin.details.authors || [plugin.details.author as string])).join(", ")
|
|
||||||
|
|
||||||
if (plugin.enabled){
|
if (plugin.enabled){
|
||||||
opendiscord.debug.debug("Plugin \""+plugin.id.value+"\" loaded",[
|
opendiscord.debug.debug("Plugin \""+plugin.id.value+"\" loaded",[
|
||||||
{key:"status",value:(plugin.crashed ? "crashed" : "success")},
|
{key:"status",value:(plugin.crashed ? "crashed" : "success")},
|
||||||
{key:"crashReason",value:(plugin.crashed ? (plugin.crashReason ?? "/") : "/")},
|
{key:"crashReason",value:(plugin.crashed ? (plugin.crashReason ?? "/") : "/")},
|
||||||
{key:"author",value:authors},
|
{key:"authors",value:authors},
|
||||||
{key:"version",value:plugin.version.toString()},
|
{key:"version",value:plugin.version.toString()},
|
||||||
{key:"priority",value:plugin.priority.toString()}
|
{key:"priority",value:plugin.priority.toString()}
|
||||||
])
|
])
|
||||||
}else{
|
}else{
|
||||||
opendiscord.debug.debug("Plugin \""+plugin.id.value+"\" disabled",[
|
opendiscord.debug.debug("Plugin \""+plugin.id.value+"\" disabled",[
|
||||||
{key:"author",value:authors},
|
{key:"authors",value:authors},
|
||||||
{key:"version",value:plugin.version.toString()},
|
{key:"version",value:plugin.version.toString()},
|
||||||
{key:"priority",value:plugin.priority.toString()}
|
{key:"priority",value:plugin.priority.toString()}
|
||||||
])
|
])
|
||||||
|
|||||||
Reference in New Issue
Block a user