n1cat-discord-script-manager
Version:
A Discord.js plugin for dynamic script management and execution
215 lines (190 loc) • 5.43 kB
JavaScript
const fs = require("fs");
const path = require("path");
const { promisify } = require("util");
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);
const Logger = require("../utils/logger");
const {
installDependency,
extractMissingModule,
} = require("../utils/moduleLoader");
// 用於追蹤函式調用的輔助函數
function getCallerInfo() {
const stack = new Error().stack;
const callerLine = stack.split("\n")[3]; // 跳過 Error 和 getCallerInfo 的堆疊
const match = callerLine.match(/at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/);
if (match) {
const [, functionName, file, line, column] = match;
return {
function: functionName,
file: file.split("/").pop(), // 只取檔案名稱
line,
column,
};
}
return {
function: "unknown",
file: "unknown",
line: "unknown",
column: "unknown",
};
}
// 日誌輸出函數
function log(message, isError = false) {
const caller = getCallerInfo();
const debugMode = this.options?.debug ?? false;
// 如果是錯誤或 debug 模式開啟,則輸出詳細日誌
if (isError || debugMode) {
console.log(`[${caller.file}:${caller.line}] ${message}`);
}
}
async function checkDependencies(scriptPath) {
const logger = Logger.createContextLogger({
module: "ScriptLoader",
debug: this.options?.debug || false,
});
try {
const content = await fs.promises.readFile(scriptPath, "utf8");
const requireMatches = content.match(/require\(['"](.+?)['"]\)/g) || [];
const missingModules = [];
for (const match of requireMatches) {
const moduleName = match.match(/require\(['"](.+?)['"]\)/)[1];
try {
require.resolve(moduleName);
} catch (error) {
if (error.code === "MODULE_NOT_FOUND") {
missingModules.push(moduleName);
}
}
}
return missingModules;
} catch (error) {
logger.error(`檢查依賴時出錯: ${error.message}`);
return [];
}
}
async function installDependency(dependency) {
const logger = Logger.createContextLogger({
module: "ScriptLoader",
debug: this.options?.debug || false,
});
try {
logger.log(`正在安裝缺少的依賴: ${dependency}`);
const { exec } = require("child_process");
await new Promise((resolve, reject) => {
exec(`npm install ${dependency}`, (error) => {
if (error) reject(error);
else resolve();
});
});
return true;
} catch (error) {
logger.error(`安裝依賴失敗: ${error.message}`);
return false;
}
}
async function loadScript(scriptPath, options = {}) {
const logger = Logger.createContextLogger({
module: "ScriptLoader",
debug: options.debug || false,
});
try {
// 檢查依賴
const missingModules = await checkDependencies(scriptPath);
for (const dependency of missingModules) {
const success = await installDependency(dependency);
if (!success) {
throw new Error(`無法安裝依賴: ${dependency}`);
}
}
// 清除快取
if (require.cache[scriptPath]) {
delete require.cache[scriptPath];
}
// 載入腳本
const script = require(scriptPath);
logger.log(`✅ 成功安裝並載入依賴: ${dependency}`);
return {
success: true,
script,
error: null,
};
} catch (error) {
logger.error(`模組載入失敗: ${error.message}`);
return {
success: false,
script: null,
error: error.message,
};
}
}
async function loadScripts(SCRIPT_FOLDER, options = {}) {
const logger = Logger.createContextLogger({
module: "ScriptLoader",
debug: options.debug || false,
});
try {
logger.log("\n=== Loading Scripts ===");
logger.log(`Loading scripts from folder: ${SCRIPT_FOLDER}`);
const files = await readdir(SCRIPT_FOLDER);
const scriptFiles = files.filter((file) => file.endsWith(".js"));
if (scriptFiles.length === 0) {
logger.log("No scripts found in folder");
return {
total: 0,
loaded: 0,
errors: 0,
scripts: [],
};
}
let loaded = 0;
let errors = 0;
const scripts = [];
for (const file of scriptFiles) {
logger.log(`\n載入腳本: ${file}`);
const scriptPath = path.join(SCRIPT_FOLDER, file);
try {
const result = await loadScript(scriptPath, options);
if (result.success) {
scripts.push({
name: file,
path: scriptPath,
module: result.script,
});
loaded++;
logger.log(`✅ ${file}`);
} else {
errors++;
logger.error(`載入腳本失敗 ${file}: ${result.error}`);
}
} catch (error) {
errors++;
logger.error(`載入腳本失敗 ${file}: ${error.message}`);
}
}
logger.log("\n=== Script Loading Summary ===");
logger.log(`Total scripts found: ${scriptFiles.length}`);
logger.log(`Successfully loaded: ${loaded}`);
logger.log("========================\n");
return {
total: scriptFiles.length,
loaded,
errors,
scripts,
};
} catch (error) {
logger.error(`載入腳本時出錯: ${error.message}`);
return {
total: 0,
loaded: 0,
errors: 1,
scripts: [],
};
}
}
module.exports = {
loadScripts,
loadScript,
checkDependencies,
installDependency,
};