blockmine 1.0.9 → 1.1.8
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/backend/cli.js +5 -9
- package/backend/nodemon.json +7 -11
- package/backend/package.json +4 -4
- package/backend/src/api/routes/bots.js +7 -3
- package/backend/src/api/routes/servers.js +1 -0
- package/backend/src/core/BotManager.js +233 -29
- package/backend/src/core/BotProcess.js +65 -16
- package/backend/src/core/PluginManager.js +41 -5
- package/backend/src/core/UserService.js +1 -1
- package/backend/src/core/commands/dev.js +41 -0
- package/backend/src/core/commands/ping.js +1 -1
- package/backend/src/server.js +45 -8
- package/frontend/dist/assets/index-B6uPFKxW.css +1 -0
- package/frontend/dist/assets/{index-DjU4Tk7J.js → index-BO23cI3z.js} +339 -339
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/backend/src/core/system/CommandHandler.js +0 -98
- package/frontend/dist/assets/index-B83SHIXE.css +0 -1
package/backend/cli.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
2
|
const fs = require('fs');
|
|
5
3
|
const os = require('os');
|
|
6
4
|
const path = require('path');
|
|
@@ -8,12 +6,6 @@ const { execSync } = require('child_process');
|
|
|
8
6
|
const { startServer } = require('./src/server.js');
|
|
9
7
|
|
|
10
8
|
const DATA_DIR = path.join(os.homedir(), '.blockmine');
|
|
11
|
-
if (!fs.existsSync(DATA_DIR)) {
|
|
12
|
-
console.log(`[BlockMine] Создание папки для данных: ${DATA_DIR}`);
|
|
13
|
-
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
process.env.DATABASE_URL = `file:${path.join(DATA_DIR, 'blockmine.db')}`;
|
|
17
9
|
const prismaSchemaPath = path.join(__dirname, 'prisma', 'schema.prisma');
|
|
18
10
|
|
|
19
11
|
function runCommand(command) {
|
|
@@ -29,13 +21,17 @@ function runCommand(command) {
|
|
|
29
21
|
|
|
30
22
|
async function main() {
|
|
31
23
|
console.log('Запуск панели управления BlockMine...');
|
|
32
|
-
console.log(`[BlockMine] Хранилище данных: ${DATA_DIR}`);
|
|
33
24
|
|
|
34
25
|
const dbPath = path.join(DATA_DIR, 'blockmine.db');
|
|
35
26
|
|
|
36
27
|
if (!fs.existsSync(dbPath)) {
|
|
37
28
|
console.log('База данных не найдена. Создаем и применяем все миграции...');
|
|
38
29
|
runCommand(`npx prisma migrate deploy --schema=${prismaSchemaPath}`);
|
|
30
|
+
|
|
31
|
+
console.log('Заполнение базы данных начальными данными (серверами)...');
|
|
32
|
+
const seedScriptPath = path.join(__dirname, 'prisma', 'seed.js');
|
|
33
|
+
runCommand(`node ${seedScriptPath}`);
|
|
34
|
+
|
|
39
35
|
console.log('Первоначальная настройка базы данных завершена.');
|
|
40
36
|
} else {
|
|
41
37
|
console.log('Проверка и применение обновлений базы данных...');
|
package/backend/nodemon.json
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"prisma/dev.db-journal"
|
|
10
|
-
],
|
|
11
|
-
"exec": "node src/server.js"
|
|
12
|
-
}
|
|
2
|
+
"watch": ["src/"],
|
|
3
|
+
"ext": "js,mjs,cjs,json",
|
|
4
|
+
"ignore": ["storage/", "prisma/dev.db", "prisma/dev.db-journal"],
|
|
5
|
+
"exec": "node src/server.js",
|
|
6
|
+
"signal": "SIGINT",
|
|
7
|
+
"delay": 1500
|
|
8
|
+
}
|
package/backend/package.json
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "src/server.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
"start": "node src/server.js",
|
|
8
|
+
"dev": "nodemon",
|
|
9
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
10
|
+
},
|
|
11
11
|
"prisma": {
|
|
12
12
|
"seed": "node prisma/seed.js"
|
|
13
13
|
},
|
|
@@ -22,11 +22,12 @@ async function setupDefaultPermissionsForBot(botId, prismaClient = prisma) {
|
|
|
22
22
|
{ name: "admin.*", description: "Все права администратора" },
|
|
23
23
|
{ name: "admin.cooldown.bypass", description: "Обход кулдауна для админ-команд" },
|
|
24
24
|
{ name: "user.*", description: "Все права обычного пользователя" },
|
|
25
|
+
{ name: "user.say", description: "Доступ к простым командам" },
|
|
25
26
|
{ name: "user.cooldown.bypass", description: "Обход кулдауна для юзер-команд" },
|
|
26
27
|
],
|
|
27
28
|
groupPermissions: {
|
|
28
|
-
"User": ["user
|
|
29
|
-
"Admin": ["admin.*", "admin.cooldown.bypass", "user.cooldown.bypass"]
|
|
29
|
+
"User": ["user.say"],
|
|
30
|
+
"Admin": ["admin.*", "admin.cooldown.bypass", "user.cooldown.bypass", "user.*"]
|
|
30
31
|
},
|
|
31
32
|
};
|
|
32
33
|
|
|
@@ -55,7 +56,10 @@ router.get('/', async (req, res) => {
|
|
|
55
56
|
try {
|
|
56
57
|
const bots = await prisma.bot.findMany({ include: { server: true }, orderBy: { createdAt: 'asc' } });
|
|
57
58
|
res.json(bots);
|
|
58
|
-
} catch (error) {
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error("[API /api/bots] Ошибка получения списка ботов:", error);
|
|
61
|
+
res.status(500).json({ error: 'Не удалось получить список ботов' });
|
|
62
|
+
}
|
|
59
63
|
});
|
|
60
64
|
|
|
61
65
|
router.get('/state', (req, res) => {
|
|
@@ -8,6 +8,7 @@ router.get('/', async (req, res) => {
|
|
|
8
8
|
const servers = await prisma.server.findMany({ orderBy: { name: 'asc' } });
|
|
9
9
|
res.json(servers);
|
|
10
10
|
} catch (error) {
|
|
11
|
+
console.error("[API /api/servers] Ошибка получения списка серверов:", error);
|
|
11
12
|
res.status(500).json({ error: 'Не удалось получить список серверов' });
|
|
12
13
|
}
|
|
13
14
|
});
|
|
@@ -6,11 +6,34 @@ const { PrismaClient } = require('@prisma/client');
|
|
|
6
6
|
const pidusage = require('pidusage');
|
|
7
7
|
const DependencyService = require('./DependencyService');
|
|
8
8
|
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const { v4: uuidv4 } = require('uuid');
|
|
12
|
+
const crypto = require('crypto');
|
|
13
|
+
|
|
9
14
|
const RealPermissionManager = require('./PermissionManager');
|
|
10
15
|
const RealUserService = require('./UserService');
|
|
11
16
|
|
|
12
17
|
const prisma = new PrismaClient();
|
|
13
18
|
const cooldowns = new Map();
|
|
19
|
+
const warningCache = new Map();
|
|
20
|
+
const WARNING_COOLDOWN = 10 * 1000;
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
const TELEMETRY_ENABLED = 'true';
|
|
24
|
+
const STATS_SERVER_URL = 'http://185.65.200.184:3000';
|
|
25
|
+
let instanceId = null;
|
|
26
|
+
const DATA_DIR = path.join(os.homedir(), '.blockmine');
|
|
27
|
+
|
|
28
|
+
if (TELEMETRY_ENABLED && STATS_SERVER_URL) {
|
|
29
|
+
const idPath = path.join(DATA_DIR, '.instance_id');
|
|
30
|
+
try {
|
|
31
|
+
instanceId = fs.readFileSync(idPath, 'utf-8');
|
|
32
|
+
} catch (e) {
|
|
33
|
+
instanceId = uuidv4();
|
|
34
|
+
fs.writeFileSync(idPath, instanceId, 'utf-8');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
14
37
|
|
|
15
38
|
class BotManager {
|
|
16
39
|
constructor() {
|
|
@@ -19,6 +42,129 @@ class BotManager {
|
|
|
19
42
|
this.resourceUsage = new Map();
|
|
20
43
|
|
|
21
44
|
setInterval(() => this.updateAllResourceUsage(), 5000);
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if (TELEMETRY_ENABLED && STATS_SERVER_URL) {
|
|
48
|
+
setTimeout(() => this.sendHeartbeat(), 10000);
|
|
49
|
+
setInterval(() => this.sendHeartbeat(), 5 * 60 * 1000);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
_sendThrottledWarning(botId, username, warningType, message, typeChat = 'private') {
|
|
55
|
+
const cacheKey = `${botId}:${username}:${warningType}`;
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const lastWarning = warningCache.get(cacheKey);
|
|
58
|
+
|
|
59
|
+
if (!lastWarning || (now - lastWarning > WARNING_COOLDOWN)) {
|
|
60
|
+
this.sendMessageToBot(botId, message, typeChat, username);
|
|
61
|
+
warningCache.set(cacheKey, now);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
async sendHeartbeat() {
|
|
67
|
+
if (!instanceId) return;
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const runningBots = [];
|
|
71
|
+
for (const botProcess of this.bots.values()) {
|
|
72
|
+
if (botProcess.botConfig) {
|
|
73
|
+
runningBots.push({
|
|
74
|
+
username: botProcess.botConfig.username,
|
|
75
|
+
serverHost: botProcess.botConfig.server.host,
|
|
76
|
+
serverPort: botProcess.botConfig.server.port
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (runningBots.length === 0) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log(`[Telemetry] Подготовка к отправке heartbeat для ${runningBots.length} ботов...`);
|
|
86
|
+
|
|
87
|
+
const challengeRes = await fetch(`${STATS_SERVER_URL}/api/challenge?uuid=${instanceId}`);
|
|
88
|
+
if (!challengeRes.ok) {
|
|
89
|
+
console.error(`[Telemetry] Сервер статистики вернул ошибку при получении challenge: ${challengeRes.statusText}`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const { challenge, difficulty, prefix } = await challengeRes.json();
|
|
93
|
+
|
|
94
|
+
let nonce = 0;
|
|
95
|
+
let hash = '';
|
|
96
|
+
do {
|
|
97
|
+
nonce++;
|
|
98
|
+
hash = crypto.createHash('sha256').update(prefix + challenge + nonce).digest('hex');
|
|
99
|
+
} while (!hash.startsWith('0'.repeat(difficulty)));
|
|
100
|
+
|
|
101
|
+
const packageJson = require('../../../package.json');
|
|
102
|
+
await fetch(`${STATS_SERVER_URL}/api/heartbeat`, {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
headers: { 'Content-Type': 'application/json' },
|
|
105
|
+
body: JSON.stringify({
|
|
106
|
+
instanceUuid: instanceId,
|
|
107
|
+
appVersion: packageJson.version,
|
|
108
|
+
bots: runningBots,
|
|
109
|
+
nonce: nonce
|
|
110
|
+
})
|
|
111
|
+
});
|
|
112
|
+
console.log(`[Telemetry] Heartbeat успешно отправлен.`);
|
|
113
|
+
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error(`[Telemetry] Не удалось отправить heartbeat: ${error.message}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
async _syncSystemPermissions(botId) {
|
|
121
|
+
const systemPermissions = [
|
|
122
|
+
{ name: "admin.*", description: "Все права администратора" },
|
|
123
|
+
{ name: "admin.cooldown.bypass", description: "Обход кулдауна для админ-команд" },
|
|
124
|
+
{ name: "user.*", description: "Все права обычного пользователя" },
|
|
125
|
+
{ name: "user.say", description: "Доступ к простым командам" },
|
|
126
|
+
{ name: "user.cooldown.bypass", description: "Обход кулдауна для юзер-команд" },
|
|
127
|
+
];
|
|
128
|
+
const systemGroups = ["User", "Admin"];
|
|
129
|
+
const systemGroupPermissions = {
|
|
130
|
+
"User": ["user.say"],
|
|
131
|
+
"Admin": ["admin.*", "admin.cooldown.bypass", "user.cooldown.bypass", "user.*"]
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
console.log(`[Permission Sync] Синхронизация системных прав для бота ID ${botId}...`);
|
|
135
|
+
|
|
136
|
+
for (const perm of systemPermissions) {
|
|
137
|
+
await prisma.permission.upsert({
|
|
138
|
+
where: { botId_name: { botId, name: perm.name } },
|
|
139
|
+
update: { description: perm.description },
|
|
140
|
+
create: { ...perm, botId, owner: 'system' }
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (const groupName of systemGroups) {
|
|
145
|
+
await prisma.group.upsert({
|
|
146
|
+
where: { botId_name: { botId, name: groupName } },
|
|
147
|
+
update: {},
|
|
148
|
+
create: { name: groupName, botId, owner: 'system' }
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
for (const [groupName, permNames] of Object.entries(systemGroupPermissions)) {
|
|
153
|
+
const group = await prisma.group.findUnique({ where: { botId_name: { botId, name: groupName } } });
|
|
154
|
+
if (group) {
|
|
155
|
+
for (const permName of permNames) {
|
|
156
|
+
const permission = await prisma.permission.findUnique({ where: { botId_name: { botId, name: permName } } });
|
|
157
|
+
if (permission) {
|
|
158
|
+
await prisma.groupPermission.upsert({
|
|
159
|
+
where: { groupId_permissionId: { groupId: group.id, permissionId: permission.id } },
|
|
160
|
+
update: {},
|
|
161
|
+
create: { groupId: group.id, permissionId: permission.id }
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
console.log(`[Permission Sync] Синхронизация для бота ID ${botId} завершена.`);
|
|
22
168
|
}
|
|
23
169
|
|
|
24
170
|
async updateAllResourceUsage() {
|
|
@@ -102,6 +248,9 @@ class BotManager {
|
|
|
102
248
|
return { success: false, message: 'Бот уже запущен или запускается.' };
|
|
103
249
|
}
|
|
104
250
|
}
|
|
251
|
+
|
|
252
|
+
await this._syncSystemPermissions(botConfig.id);
|
|
253
|
+
|
|
105
254
|
this.logCache.set(botConfig.id, []);
|
|
106
255
|
this.emitStatusUpdate(botConfig.id, 'starting', '');
|
|
107
256
|
|
|
@@ -111,27 +260,20 @@ class BotManager {
|
|
|
111
260
|
|
|
112
261
|
const enabledPlugins = allPluginsForBot.filter(p => p.isEnabled);
|
|
113
262
|
|
|
114
|
-
const { sortedPlugins, pluginInfo } = DependencyService.resolveDependencies(enabledPlugins, allPluginsForBot);
|
|
263
|
+
const { sortedPlugins, pluginInfo, hasCriticalIssues } = DependencyService.resolveDependencies(enabledPlugins, allPluginsForBot);
|
|
115
264
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
this.appendLog(botConfig.id, '[DependencyManager] Обнаружены проблемы с зависимостями:');
|
|
265
|
+
if (hasCriticalIssues) {
|
|
266
|
+
this.appendLog(botConfig.id, '[DependencyManager] Обнаружены критические проблемы с зависимостями:');
|
|
119
267
|
for (const plugin of Object.values(pluginInfo)) {
|
|
120
268
|
if (plugin.issues.length > 0) {
|
|
121
269
|
this.appendLog(botConfig.id, ` - Плагин "${plugin.name}":`);
|
|
122
270
|
for (const issue of plugin.issues) {
|
|
123
271
|
const logMessage = ` - [${issue.type.toUpperCase()}] ${issue.message}`;
|
|
124
272
|
this.appendLog(botConfig.id, logMessage);
|
|
125
|
-
if (['missing_dependency', 'version_mismatch', 'circular_dependency'].includes(issue.type)) {
|
|
126
|
-
canStart = false;
|
|
127
|
-
}
|
|
128
273
|
}
|
|
129
274
|
}
|
|
130
275
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (!canStart) {
|
|
134
|
-
this.appendLog(botConfig.id, '[DependencyManager] [FATAL] Запуск отменен из-за критических ошибок в зависимостях.');
|
|
276
|
+
this.appendLog(botConfig.id, '[DependencyManager] [FATAL] Запуск отменен.');
|
|
135
277
|
this.emitStatusUpdate(botConfig.id, 'stopped', 'Ошибка зависимостей плагинов.');
|
|
136
278
|
return { success: false, message: 'Критические ошибки в зависимостях плагинов.' };
|
|
137
279
|
}
|
|
@@ -141,6 +283,8 @@ class BotManager {
|
|
|
141
283
|
const botProcessPath = path.resolve(__dirname, 'BotProcess.js');
|
|
142
284
|
const child = fork(botProcessPath, [], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] });
|
|
143
285
|
|
|
286
|
+
child.botConfig = botConfig;
|
|
287
|
+
|
|
144
288
|
child.on('error', (err) => {
|
|
145
289
|
this.appendLog(botConfig.id, `[PROCESS FATAL] КРИТИЧЕСКАЯ ОШИБКА ПРОЦЕССА: ${err.stack}`);
|
|
146
290
|
});
|
|
@@ -156,6 +300,8 @@ class BotManager {
|
|
|
156
300
|
this.emitStatusUpdate(botConfig.id, message.status);
|
|
157
301
|
} else if (message.type === 'validate_and_run_command') {
|
|
158
302
|
await this.handleCommandValidation(botConfig, message);
|
|
303
|
+
} else if (message.type === 'register_command') {
|
|
304
|
+
await this.handleCommandRegistration(botConfig.id, message.commandConfig);
|
|
159
305
|
}
|
|
160
306
|
});
|
|
161
307
|
|
|
@@ -180,41 +326,58 @@ class BotManager {
|
|
|
180
326
|
try {
|
|
181
327
|
const user = await RealUserService.getUser(username, botConfig.id, botConfig);
|
|
182
328
|
|
|
183
|
-
if (user.isBlacklisted)
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
329
|
+
if (user.isBlacklisted) return;
|
|
186
330
|
|
|
187
|
-
const dbCommand = await prisma.command.
|
|
331
|
+
const dbCommand = await prisma.command.findFirst({
|
|
332
|
+
where: {
|
|
333
|
+
botId: botConfig.id,
|
|
334
|
+
OR: [ { name: commandName }, { aliases: { contains: `"${commandName}"` } } ]
|
|
335
|
+
}
|
|
336
|
+
});
|
|
188
337
|
|
|
189
|
-
if (!dbCommand || (!dbCommand.isEnabled && !user.isOwner))
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
338
|
+
if (!dbCommand || (!dbCommand.isEnabled && !user.isOwner)) return;
|
|
192
339
|
|
|
193
340
|
const allowedTypes = JSON.parse(dbCommand.allowedChatTypes || '[]');
|
|
194
341
|
if (!allowedTypes.includes(typeChat) && !user.isOwner) {
|
|
195
|
-
|
|
196
|
-
|
|
342
|
+
this._sendThrottledWarning(
|
|
343
|
+
botConfig.id,
|
|
344
|
+
username,
|
|
345
|
+
`wrong_chat:${dbCommand.name}:${typeChat}`,
|
|
346
|
+
`Команду ${dbCommand.name} нельзя использовать в этом типе чата.`,
|
|
347
|
+
typeChat
|
|
348
|
+
);
|
|
197
349
|
return;
|
|
198
350
|
}
|
|
199
351
|
|
|
200
352
|
const permission = dbCommand.permissionId ? await prisma.permission.findUnique({ where: { id: dbCommand.permissionId } }) : null;
|
|
201
353
|
if (permission && !user.hasPermission(permission.name)) {
|
|
202
|
-
|
|
203
|
-
|
|
354
|
+
this._sendThrottledWarning(
|
|
355
|
+
botConfig.id,
|
|
356
|
+
username,
|
|
357
|
+
`no_permission:${dbCommand.name}`,
|
|
358
|
+
`У вас нет прав для выполнения команды ${dbCommand.name}.`,
|
|
359
|
+
typeChat
|
|
360
|
+
);
|
|
204
361
|
return;
|
|
205
362
|
}
|
|
206
363
|
|
|
207
364
|
const domain = (permission?.name || '').split('.')[0] || 'user';
|
|
208
365
|
const bypassCooldownPermission = `${domain}.cooldown.bypass`;
|
|
209
366
|
if (dbCommand.cooldown > 0 && !user.isOwner && !user.hasPermission(bypassCooldownPermission)) {
|
|
210
|
-
const cooldownKey = `${botConfig.id}:${
|
|
367
|
+
const cooldownKey = `${botConfig.id}:${dbCommand.name}:${user.id}`;
|
|
211
368
|
const now = Date.now();
|
|
212
369
|
const lastUsed = cooldowns.get(cooldownKey);
|
|
213
370
|
|
|
214
371
|
if (lastUsed && (now - lastUsed < dbCommand.cooldown * 1000)) {
|
|
215
372
|
const timeLeft = Math.ceil((dbCommand.cooldown * 1000 - (now - lastUsed)) / 1000);
|
|
216
|
-
|
|
217
|
-
this.
|
|
373
|
+
|
|
374
|
+
this._sendThrottledWarning(
|
|
375
|
+
botConfig.id,
|
|
376
|
+
username,
|
|
377
|
+
`cooldown:${dbCommand.name}`,
|
|
378
|
+
`Команду ${dbCommand.name} можно будет использовать через ${timeLeft} сек.`,
|
|
379
|
+
typeChat
|
|
380
|
+
);
|
|
218
381
|
return;
|
|
219
382
|
}
|
|
220
383
|
cooldowns.set(cooldownKey, now);
|
|
@@ -222,13 +385,55 @@ class BotManager {
|
|
|
222
385
|
|
|
223
386
|
const child = this.bots.get(botConfig.id);
|
|
224
387
|
if (child) {
|
|
225
|
-
child.send({ type: 'execute_handler', commandName, username, args, typeChat });
|
|
388
|
+
child.send({ type: 'execute_handler', commandName: dbCommand.name, username, args, typeChat });
|
|
226
389
|
}
|
|
227
390
|
|
|
228
391
|
} catch (error) {
|
|
229
392
|
console.error(`[BotManager] Ошибка валидации команды ${commandName}:`, error);
|
|
230
|
-
|
|
231
|
-
|
|
393
|
+
this.sendMessageToBot(botConfig.id, `Произошла внутренняя ошибка при выполнении команды.`, 'private', username);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async handleCommandRegistration(botId, commandConfig) {
|
|
398
|
+
try {
|
|
399
|
+
let permissionId = null;
|
|
400
|
+
if (commandConfig.permissions) {
|
|
401
|
+
let permission = await prisma.permission.findUnique({
|
|
402
|
+
where: { botId_name: { botId, name: commandConfig.permissions } }
|
|
403
|
+
});
|
|
404
|
+
if (!permission) {
|
|
405
|
+
permission = await prisma.permission.create({
|
|
406
|
+
data: {
|
|
407
|
+
botId,
|
|
408
|
+
name: commandConfig.permissions,
|
|
409
|
+
description: `Автоматически создано для команды ${commandConfig.name}`,
|
|
410
|
+
owner: commandConfig.owner,
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
permissionId = permission.id;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const data = {
|
|
418
|
+
description: commandConfig.description,
|
|
419
|
+
aliases: JSON.stringify(commandConfig.aliases || []),
|
|
420
|
+
owner: commandConfig.owner,
|
|
421
|
+
permissionId: permissionId,
|
|
422
|
+
allowedChatTypes: JSON.stringify(commandConfig.allowedChatTypes || []),
|
|
423
|
+
cooldown: commandConfig.cooldown || 0,
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
await prisma.command.upsert({
|
|
427
|
+
where: { botId_name: { botId, name: commandConfig.name } },
|
|
428
|
+
update: data,
|
|
429
|
+
create: {
|
|
430
|
+
botId,
|
|
431
|
+
name: commandConfig.name,
|
|
432
|
+
...data
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
} catch (error) {
|
|
436
|
+
console.error(`[BotManager] Ошибка при регистрации команды '${commandConfig.name}':`, error);
|
|
232
437
|
}
|
|
233
438
|
}
|
|
234
439
|
|
|
@@ -244,7 +449,6 @@ class BotManager {
|
|
|
244
449
|
sendMessageToBot(botId, message, chatType = 'command', username = null) {
|
|
245
450
|
const child = this.bots.get(botId);
|
|
246
451
|
if (child) {
|
|
247
|
-
// Отправляем объект с полной информацией
|
|
248
452
|
child.send({ type: 'chat', payload: { message, chatType, username } });
|
|
249
453
|
return { success: true };
|
|
250
454
|
}
|
|
@@ -21,10 +21,7 @@ function sendLog(content) {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
* Анализирует входящее сообщение и отправляет запрос на валидацию команды в главный процесс.
|
|
26
|
-
*/
|
|
27
|
-
async function handleIncomingChat(type, username, message) {
|
|
24
|
+
function handleIncomingCommand(type, username, message) {
|
|
28
25
|
if (!message.startsWith(bot.config.prefix || '@')) return;
|
|
29
26
|
|
|
30
27
|
const rawMessage = message.slice((bot.config.prefix || '@').length).trim();
|
|
@@ -38,13 +35,47 @@ async function handleIncomingChat(type, username, message) {
|
|
|
38
35
|
if (!commandInstance) return;
|
|
39
36
|
|
|
40
37
|
try {
|
|
41
|
-
const
|
|
38
|
+
const processedArgs = {};
|
|
39
|
+
const parsedArgs = parseArguments(restOfMessage);
|
|
40
|
+
let currentArgIndex = 0;
|
|
41
|
+
|
|
42
|
+
for (const argDef of commandInstance.args) {
|
|
43
|
+
if (argDef.type === 'greedy_string') {
|
|
44
|
+
if (currentArgIndex < parsedArgs.length) {
|
|
45
|
+
processedArgs[argDef.name] = parsedArgs.slice(currentArgIndex).join(' ');
|
|
46
|
+
currentArgIndex = parsedArgs.length;
|
|
47
|
+
}
|
|
48
|
+
} else if (currentArgIndex < parsedArgs.length) {
|
|
49
|
+
let value = parsedArgs[currentArgIndex];
|
|
50
|
+
if (argDef.type === 'number') {
|
|
51
|
+
const numValue = parseFloat(value);
|
|
52
|
+
if (isNaN(numValue)) {
|
|
53
|
+
bot.api.sendMessage(type, `Ошибка: Неверный тип аргумента "${argDef.description}". Ожидалось число.`, username);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
value = numValue;
|
|
57
|
+
}
|
|
58
|
+
processedArgs[argDef.name] = value;
|
|
59
|
+
currentArgIndex++;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (processedArgs[argDef.name] === undefined) {
|
|
63
|
+
if (argDef.required) {
|
|
64
|
+
bot.api.sendMessage(type, `Ошибка: Необходимо указать: ${argDef.description}`, username);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (argDef.default !== undefined) {
|
|
68
|
+
processedArgs[argDef.name] = argDef.default;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
42
73
|
if (process.send) {
|
|
43
74
|
process.send({
|
|
44
75
|
type: 'validate_and_run_command',
|
|
45
76
|
commandName: commandInstance.name,
|
|
46
77
|
username,
|
|
47
|
-
args,
|
|
78
|
+
args: processedArgs,
|
|
48
79
|
typeChat: type
|
|
49
80
|
});
|
|
50
81
|
}
|
|
@@ -140,6 +171,24 @@ process.on('message', async (message) => {
|
|
|
140
171
|
};
|
|
141
172
|
|
|
142
173
|
bot.commands = await loadCommands();
|
|
174
|
+
|
|
175
|
+
if (process.send) {
|
|
176
|
+
for (const cmd of bot.commands.values()) {
|
|
177
|
+
process.send({
|
|
178
|
+
type: 'register_command',
|
|
179
|
+
commandConfig: {
|
|
180
|
+
name: cmd.name,
|
|
181
|
+
description: cmd.description,
|
|
182
|
+
aliases: cmd.aliases,
|
|
183
|
+
owner: cmd.owner,
|
|
184
|
+
permissions: cmd.permissions,
|
|
185
|
+
cooldown: cmd.cooldown,
|
|
186
|
+
allowedChatTypes: cmd.allowedChatTypes,
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
143
192
|
await initializePlugins(bot, config.plugins);
|
|
144
193
|
sendLog('[System] Все системы инициализированы.');
|
|
145
194
|
|
|
@@ -147,12 +196,13 @@ process.on('message', async (message) => {
|
|
|
147
196
|
let messageHandledByCustomParser = false;
|
|
148
197
|
|
|
149
198
|
bot.events.on('chat:message', (data) => {
|
|
150
|
-
|
|
199
|
+
const { type, username, message } = data;
|
|
200
|
+
if (username && message) {
|
|
151
201
|
messageHandledByCustomParser = true;
|
|
152
|
-
|
|
202
|
+
handleIncomingCommand(type, username, message);
|
|
153
203
|
}
|
|
154
204
|
});
|
|
155
|
-
|
|
205
|
+
|
|
156
206
|
bot.on('message', (jsonMsg) => {
|
|
157
207
|
const ansiMessage = jsonMsg.toAnsi();
|
|
158
208
|
if (ansiMessage.trim()) {
|
|
@@ -168,8 +218,7 @@ process.on('message', async (message) => {
|
|
|
168
218
|
if (messageHandledByCustomParser) {
|
|
169
219
|
return;
|
|
170
220
|
}
|
|
171
|
-
|
|
172
|
-
handleIncomingChat('chat', username, message);
|
|
221
|
+
handleIncomingCommand('chat', username, message);
|
|
173
222
|
});
|
|
174
223
|
|
|
175
224
|
bot.on('login', () => {
|
|
@@ -205,11 +254,11 @@ process.on('message', async (message) => {
|
|
|
205
254
|
if (bot) bot.quit();
|
|
206
255
|
else process.exit(0);
|
|
207
256
|
} else if (message.type === 'chat') {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
} else if (message.type === 'execute_handler') {
|
|
257
|
+
if (bot && bot.entity) {
|
|
258
|
+
const { message: msg, chatType, username } = message.payload;
|
|
259
|
+
bot.messageQueue.enqueue(chatType, msg, username);
|
|
260
|
+
}
|
|
261
|
+
} else if (message.type === 'execute_handler') {
|
|
213
262
|
const { commandName, username, args, typeChat } = message;
|
|
214
263
|
const commandInstance = bot.commands.get(commandName);
|
|
215
264
|
if (commandInstance) {
|