xincbot
Version:
A flexible QQ bot framework based on NapCat and node-napcat-ts
706 lines • 30.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PluginManager = void 0;
exports.definePlugin = definePlugin;
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const node_napcat_ts_1 = require("node-napcat-ts");
const config_1 = require("./config");
const logger_1 = require("./logger");
class PluginManager {
plugins = new Map();
cleanupFns = new Map();
eventHandlers = new Map(); // 新增:记录插件的事件处理器
eventBus;
dataDir;
configDir;
startTime = Date.now();
constructor(ws, dataDir, configDir) {
this.eventBus = ws;
this.dataDir = dataDir;
this.configDir = configDir;
}
async loadPlugin(plugin) {
// 类型检查
if (!plugin || typeof plugin !== 'object') {
throw new Error('Invalid plugin: plugin must be an object');
}
if (!plugin.name || typeof plugin.name !== 'string') {
throw new Error('Invalid plugin: plugin.name must be a string');
}
if (plugin.setup && typeof plugin.setup !== 'function') {
throw new Error('Invalid plugin: plugin.setup must be a function');
}
// 检查是否已加载
if (this.plugins.has(plugin.name)) {
throw new Error(`Plugin ${plugin.name} is already loaded`);
}
const ctx = {
eventBus: this.eventBus,
handle: (event, handler) => {
// 记录事件处理器
if (!this.eventHandlers.has(plugin.name)) {
this.eventHandlers.set(plugin.name, new Set());
}
this.eventHandlers.get(plugin.name).add(handler);
this.eventBus.on(event, handler);
},
off: (event, handler) => {
this.eventBus.off(event, handler);
},
getDataPath: () => {
const dir = path_1.default.join(this.dataDir, plugin.name);
if (!fs_1.default.existsSync(dir)) {
fs_1.default.mkdirSync(dir, { recursive: true });
}
return dir;
},
getConfigPath: () => {
const dir = path_1.default.join(this.configDir, plugin.name);
if (!fs_1.default.existsSync(dir)) {
fs_1.default.mkdirSync(dir, { recursive: true });
}
return dir;
},
getText: (e) => {
// 处理消息数组
if (Array.isArray(e.message)) {
const text = e.message
.filter((msg) => msg.type === 'text')
.map((msg) => msg.data.text)
.join('');
return text.trim();
}
return '';
},
getAvatarLink: (qq) => {
return `https://thirdqq.qlogo.cn/headimg_dl?dst_uin=${qq}&spec=0`;
},
getGroupLink: (group_id) => {
return `https://p.qlogo.cn/gh/${group_id}/${group_id}/0`;
},
getQuoteMsg: async (e) => {
try {
// 检查是否存在引用消息
const reply = e.message?.find((msg) => msg.type === 'reply');
if (!reply)
return null;
// 获取引用消息ID
const messageId = reply.data.id;
if (!messageId)
return null;
// 获取消息详情
try {
const result = await this.eventBus.get_msg({
message_id: parseInt(messageId)
});
return result;
}
catch (error) {
console.error('Failed to get message by ID:', error);
return null;
}
}
catch (error) {
console.error('Failed to get quote message:', error);
return null;
}
},
getImageURL: (e) => {
const imageMsg = e.message.find((msg) => msg.type === 'image');
return imageMsg ? imageMsg.data.url : null;
},
getQuoteImageURL: async (e) => {
const quoteMsg = await ctx.getQuoteMsg(e);
if (!quoteMsg)
return null;
return ctx.getImageURL(quoteMsg);
},
getMentionedImageURL: async (e) => {
const directImageUrl = ctx.getImageURL(e);
if (directImageUrl)
return directImageUrl;
const quoteImageUrl = await ctx.getQuoteImageURL(e);
return quoteImageUrl;
},
getTaggedUserID: (e) => {
const atMessages = e.message.filter((msg) => msg.type === 'at');
if (atMessages.length === 0)
return null;
return atMessages[atMessages.length - 1].data.qq;
},
uploadFileToDir: async (group_id, file, name) => {
try {
await this.eventBus.upload_group_file({
group_id: Number(group_id),
file,
name: name || path_1.default.basename(file),
folder_id: "/"
});
}
catch (error) {
throw new Error(`Failed to upload file: ${error instanceof Error ? error.message : String(error)}`);
}
},
isRoot: (e) => {
const config = config_1.configManager.getConfig();
const rootList = config.root || [];
return rootList.includes(String(e.user_id));
},
isAdmin: (e) => {
const config = config_1.configManager.getConfig();
const adminList = config.admin || [];
// 主人也是管理员
return ctx.isRoot(e) || adminList.includes(String(e.user_id));
},
isGroupOwner: (e) => {
return e.message_type === 'group' && e.sender.role === 'owner';
},
isGroupAdmin: (e) => {
return e.message_type === 'group' &&
(e.sender.role === 'owner' || e.sender.role === 'admin');
},
getLoadedPlugins: () => {
return Array.from(this.plugins.values());
},
reloadPlugin: async (name) => {
await this.unloadPlugin(name);
const pluginPath = path_1.default.join(process.cwd(), 'plugins', name);
const plugin = (await Promise.resolve(`${path_1.default.join(pluginPath, 'index.ts')}`).then(s => __importStar(require(s)))).default;
await this.loadPlugin(plugin);
},
disablePlugin: async (name) => {
if (!this.plugins.has(name)) {
throw new Error(`Plugin ${name} not found`);
}
await this.unloadPlugin(name);
},
enablePlugin: async (name) => {
const pluginPath = path_1.default.join(process.cwd(), 'plugins', name, 'index.ts');
const devPluginPath = path_1.default.join(process.cwd(), 'plugins', name, 'index.ts');
// 检查插件文件是否存在
if (!fs_1.default.existsSync(devPluginPath)) {
throw new Error(`找不到插件 ${name}`);
}
try {
// 尝试导入编译后的文件
const pluginModule = await Promise.resolve(`${pluginPath}`).then(s => __importStar(require(s)));
const plugin = pluginModule.default;
if (!plugin || !plugin.name) {
throw new Error(`插件 ${name} 格式不正确`);
}
await this.loadPlugin(plugin);
// 更新配置文件
const config = config_1.configManager.getConfig();
config.plugins = Array.from(new Set([...(config.plugins || []), name]));
config_1.configManager.saveConfig();
}
catch (importError) {
// 如果导入失败,尝试使用 require
try {
const plugin = require(devPluginPath).default;
if (!plugin || !plugin.name) {
throw new Error(`插件 ${name} 格式不正确`);
}
await this.loadPlugin(plugin);
// 更新配置文件
const config = config_1.configManager.getConfig();
config.plugins = Array.from(new Set([...(config.plugins || []), name]));
config_1.configManager.saveConfig();
}
catch (requireError) {
throw new Error(`无法加载插件: ${requireError.message}`);
}
}
},
getStatus: async () => {
const uptime = this.formatUptime(Date.now() - this.startTime);
const memory = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
const pluginCount = this.plugins.size;
// 获取活跃群组数
const groups = await this.eventBus.get_group_list();
const groupCount = groups.length;
return { uptime, memory, pluginCount, groupCount };
},
shutdown: async () => {
// 卸载所有插件
for (const name of this.plugins.keys()) {
await this.unloadPlugin(name);
}
// 断开连接
this.eventBus.disconnect();
// 退出进程
process.exit(0);
},
recallMsg: async (message_id) => {
try {
await this.eventBus.delete_msg({
message_id
});
}
catch (error) {
throw new Error(`Failed to recall message: ${error instanceof Error ? error.message : String(error)}`);
}
},
respond: async (e, message) => {
try {
const formattedMessage = message.map((msg) => typeof msg === 'string' ? node_napcat_ts_1.Structs.text(msg) : msg);
let result;
if (e.message_type === 'group') {
result = await this.eventBus.send_group_msg({
group_id: e.group_id,
message: formattedMessage
});
// 添加日志
const replyText = formattedMessage.map((msg) => msg.type === 'text' ? msg.data.text : `[${msg.type}]`).join('');
logger_1.logger.info(`succeed to send: [Group(${e.group_id})] ${replyText}`);
}
else {
result = await this.eventBus.send_private_msg({
user_id: e.user_id,
message: formattedMessage
});
// 添加日志
const replyText = formattedMessage.map((msg) => msg.type === 'text' ? msg.data.text : `[${msg.type}]`).join('');
logger_1.logger.info(`succeed to send: [Private(${e.user_id})] ${replyText}`);
}
return result;
}
catch (error) {
throw new Error(`Failed to send message: ${error instanceof Error ? error.message : String(error)}`);
}
},
sendLike: async (user_id, times = 50) => {
try {
// 确保次数在1-50之间
const count = Math.max(1, Math.min(50, times));
await this.eventBus.send_like({
user_id: Number(user_id),
times: count
});
}
catch (error) {
throw new Error(`Failed to send like: ${error instanceof Error ? error.message : String(error)}`);
}
},
kick: async (group_id, user_id, reject_add_request = false) => {
try {
await this.eventBus.set_group_kick({
group_id: Number(group_id),
user_id: Number(user_id),
reject_add_request
});
}
catch (error) {
throw new Error(`Failed to kick member: ${error instanceof Error ? error.message : String(error)}`);
}
},
mute: async (group_id, user_id, duration = 1800) => {
try {
await this.eventBus.set_group_ban({
group_id: Number(group_id),
user_id: Number(user_id),
duration: Math.max(0, duration) // 确保时长不为负数
});
}
catch (error) {
throw new Error(`Failed to mute member: ${error instanceof Error ? error.message : String(error)}`);
}
},
muteGroup: async (group_id, enable = true) => {
try {
await this.eventBus.set_group_whole_ban({
group_id: Number(group_id),
enable
});
}
catch (error) {
throw new Error(`Failed to set group mute: ${error instanceof Error ? error.message : String(error)}`);
}
},
setAdmin: async (group_id, user_id, enable = true) => {
try {
await this.eventBus.set_group_admin({
group_id: Number(group_id),
user_id: Number(user_id),
enable
});
}
catch (error) {
throw new Error(`Failed to set admin: ${error instanceof Error ? error.message : String(error)}`);
}
},
setGroupCard: async (group_id, user_id, card) => {
try {
await this.eventBus.set_group_card({
group_id: Number(group_id),
user_id: Number(user_id),
card
});
}
catch (error) {
throw new Error(`Failed to set group card: ${error instanceof Error ? error.message : String(error)}`);
}
},
setGroupName: async (group_id, group_name) => {
try {
await this.eventBus.set_group_name({
group_id: Number(group_id),
group_name
});
}
catch (error) {
throw new Error(`Failed to set group name: ${error instanceof Error ? error.message : String(error)}`);
}
},
quitGroup: async (group_id) => {
try {
await this.eventBus.set_group_leave({
group_id: Number(group_id)
});
}
catch (error) {
throw new Error(`Failed to quit group: ${error instanceof Error ? error.message : String(error)}`);
}
},
setTitle: async (group_id, user_id, special_title) => {
try {
await this.eventBus.set_group_special_title({
group_id: Number(group_id),
user_id: Number(user_id),
special_title
});
}
catch (error) {
throw new Error(`Failed to set group title: ${error instanceof Error ? error.message : String(error)}`);
}
},
getGroupInfo: async (group_id) => {
try {
const info = await this.eventBus.get_group_info({
group_id: Number(group_id)
});
return {
group_id: info.group_id,
group_name: info.group_name,
member_count: info.member_count,
max_member_count: info.max_member_count
};
}
catch (error) {
throw new Error(`Failed to get group info: ${error instanceof Error ? error.message : String(error)}`);
}
},
getGroupList: async () => {
try {
const groups = await this.eventBus.get_group_list();
return groups.map(group => ({
group_id: group.group_id,
group_name: group.group_name,
member_count: group.member_count,
max_member_count: group.max_member_count
}));
}
catch (error) {
throw new Error(`Failed to get group list: ${error instanceof Error ? error.message : String(error)}`);
}
},
getGroupMemberInfo: async (group_id, user_id, no_cache) => {
try {
const info = await this.eventBus.get_group_member_info({
group_id: Number(group_id),
user_id: Number(user_id),
no_cache
});
return {
group_id: info.group_id,
user_id: info.user_id,
nickname: info.nickname,
card: info.card,
role: info.role,
title: info.title,
join_time: info.join_time,
last_sent_time: info.last_sent_time
};
}
catch (error) {
throw new Error(`Failed to get member info: ${error instanceof Error ? error.message : String(error)}`);
}
},
getCookie: async (domain) => {
try {
const info = await this.eventBus.get_cookies({
domain
});
return {
cookies: info.cookies,
bkn: info.bkn
};
}
catch (error) {
throw new Error(`Failed to get cookies: ${error instanceof Error ? error.message : String(error)}`);
}
},
getCsrfToken: async () => {
try {
const info = await this.eventBus.get_csrf_token();
return info.token;
}
catch (error) {
throw new Error(`Failed to get CSRF token: ${error instanceof Error ? error.message : String(error)}`);
}
},
getCredentials: async () => {
try {
const info = await this.eventBus.get_credentials();
return {
cookies: info.cookies,
token: info.token
};
}
catch (error) {
throw new Error(`Failed to get credentials: ${error instanceof Error ? error.message : String(error)}`);
}
},
getVersionInfo: async () => {
try {
const info = await this.eventBus.get_version_info();
return {
app_name: info.app_name,
app_version: info.app_version,
protocol_version: info.protocol_version
};
}
catch (error) {
throw new Error(`Failed to get version info: ${error instanceof Error ? error.message : String(error)}`);
}
},
getAiRoleList: async (group_id) => {
try {
const result = await this.eventBus.get_ai_characters({
group_id: Number(group_id)
});
return result;
}
catch (error) {
throw new Error(`Failed to get AI characters: ${error instanceof Error ? error.message : String(error)}`);
}
},
sendGroupAiRecord: async (group_id, character, text) => {
try {
const result = await this.eventBus.send_group_ai_record({
group_id: Number(group_id),
character,
text
});
return result;
}
catch (error) {
throw new Error(`Failed to send AI voice record: ${error instanceof Error ? error.message : String(error)}`);
}
},
wsSend: async (action, params) => {
try {
const json = {
action: action,
params: params,
echo: `${action}_${Date.now()}`
};
return await this.eventBus.send(action, params);
}
catch (error) {
throw new Error(`Failed to send WebSocket message: ${error instanceof Error ? error.message : String(error)}`);
}
},
getPlugins: () => {
// 获取插件目录下的所有插件
const pluginsDir = path_1.default.join(process.cwd(), 'plugins');
const allPlugins = fs_1.default.readdirSync(pluginsDir)
.filter(name => fs_1.default.statSync(path_1.default.join(pluginsDir, name)).isDirectory())
.map(name => {
try {
// 尝试获取插件信息
const pluginPath = path_1.default.join(pluginsDir, name, 'index.ts');
if (fs_1.default.existsSync(pluginPath)) {
// 检查是否已加载
const loadedPlugin = this.plugins.get(name);
if (loadedPlugin) {
return {
...loadedPlugin,
enabled: true
};
}
// 如果未加载,返回基本信息
return {
name,
enabled: false
};
}
return null;
}
catch (error) {
return null;
}
})
.filter((p) => p !== null);
return allPlugins;
}
};
const cleanup = await plugin.setup?.(ctx);
if (typeof cleanup === 'function') {
this.cleanupFns.set(plugin.name, cleanup);
}
this.plugins.set(plugin.name, plugin);
}
async unloadPlugin(name) {
try {
const handlers = this.eventHandlers.get(name);
if (handlers) {
for (const handler of handlers) {
this.eventBus.off('message', handler);
// 使用正确的事件类型
const events = ['notice', 'request', 'meta_event'];
for (const event of events) {
this.eventBus.off(event, handler);
}
}
this.eventHandlers.delete(name);
}
// 2. 执行清理函数
const cleanup = this.cleanupFns.get(name);
if (cleanup) {
await Promise.resolve(cleanup());
this.cleanupFns.delete(name);
}
// 3. 从内存中移除插件
this.plugins.delete(name);
logger_1.logger.info(`Plugin ${name} unloaded successfully`);
}
catch (error) {
throw new Error(`Failed to unload plugin: ${error.message}`);
}
}
formatUptime(ms) {
const seconds = Math.floor(ms / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
return `${days}天${hours % 24}时${minutes % 60}分${seconds % 60}秒`;
}
getLoadedPlugins() {
return Array.from(this.plugins.values());
}
findPlugin(name) {
// 直接匹配
let plugin = this.plugins.get(name);
// 不区分大小写匹配
if (!plugin) {
for (const [key, value] of this.plugins.entries()) {
if (key.toLowerCase() === name.toLowerCase()) {
plugin = value;
break;
}
}
}
return plugin;
}
async reloadPlugin(name) {
try {
// 1. 先禁用
await this.disablePlugin(name);
// 2. 再启用
await this.enablePlugin(name);
logger_1.logger.info(`Plugin ${name} reloaded successfully`);
}
catch (error) {
throw new Error(`重载插件失败: ${error.message}`);
}
}
async enablePlugin(name) {
try {
// 检查插件是否已启用
if (this.plugins.has(name)) {
throw new Error(`插件 ${name} 已经启用`);
}
// 统一使用正确的插件路径
const pluginPath = path_1.default.join(process.cwd(), 'plugins', name, 'index.ts');
if (!fs_1.default.existsSync(pluginPath)) {
throw new Error(`找不到插件文件: ${pluginPath}`);
}
// 导入插件
const plugin = (await Promise.resolve(`${`${pluginPath}?t=${Date.now()}`}`).then(s => __importStar(require(s)))).default;
await this.loadPlugin(plugin);
// 更新配置
const config = config_1.configManager.getConfig();
if (!config.plugins.includes(name)) {
config.plugins.push(name);
config_1.configManager.saveConfig();
}
logger_1.logger.info(`Plugin ${name} enabled successfully`);
}
catch (error) {
throw new Error(`启用插件失败: ${error.message}`);
}
}
async disablePlugin(name) {
try {
// 1. 检查插件是否存在
const plugin = this.plugins.get(name);
if (!plugin) {
throw new Error(`找不到插件 ${name}`);
}
// 2. 卸载插件
await this.unloadPlugin(name);
// 3. 更新配置文件
const config = config_1.configManager.getConfig();
config.plugins = config.plugins.filter(p => p !== name);
config_1.configManager.saveConfig();
logger_1.logger.info(`Plugin ${name} disabled successfully`);
}
catch (error) {
throw new Error(`禁用插件失败: ${error.message}`);
}
}
}
exports.PluginManager = PluginManager;
function definePlugin(plugin) {
console.log(`定义插件: ${plugin.name}`);
return plugin;
}
//# sourceMappingURL=plugin.js.map