blockmine 1.22.0 → 1.23.0
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/.claude/settings.json +5 -1
- package/.claude/settings.local.json +10 -1
- package/CHANGELOG.md +27 -3
- package/CLAUDE.md +284 -0
- package/README.md +302 -152
- package/backend/package-lock.json +681 -9
- package/backend/package.json +8 -0
- package/backend/prisma/migrations/20251116111851_add_execution_trace/migration.sql +22 -0
- package/backend/prisma/migrations/20251120154914_add_panel_api_keys/migration.sql +21 -0
- package/backend/prisma/migrations/20251121110241_add_proxy_table/migration.sql +45 -0
- package/backend/prisma/migrations/migration_lock.toml +2 -2
- package/backend/prisma/schema.prisma +70 -1
- package/backend/src/__tests__/services/BotLifecycleService.test.js +9 -4
- package/backend/src/ai/plugin-assistant-system-prompt.md +788 -0
- package/backend/src/api/middleware/auth.js +27 -0
- package/backend/src/api/middleware/botAccess.js +7 -3
- package/backend/src/api/middleware/panelApiAuth.js +135 -0
- package/backend/src/api/routes/aiAssistant.js +995 -0
- package/backend/src/api/routes/auth.js +669 -633
- package/backend/src/api/routes/botCommands.js +107 -0
- package/backend/src/api/routes/botGroups.js +165 -0
- package/backend/src/api/routes/botHistory.js +108 -0
- package/backend/src/api/routes/botPermissions.js +99 -0
- package/backend/src/api/routes/botStatus.js +36 -0
- package/backend/src/api/routes/botUsers.js +162 -0
- package/backend/src/api/routes/bots.js +2451 -2402
- package/backend/src/api/routes/eventGraphs.js +4 -1
- package/backend/src/api/routes/logs.js +13 -3
- package/backend/src/api/routes/panel.js +66 -66
- package/backend/src/api/routes/panelApiKeys.js +179 -0
- package/backend/src/api/routes/pluginIde.js +1715 -135
- package/backend/src/api/routes/plugins.js +376 -219
- package/backend/src/api/routes/proxies.js +130 -0
- package/backend/src/api/routes/search.js +4 -0
- package/backend/src/api/routes/servers.js +20 -3
- package/backend/src/api/routes/settings.js +5 -0
- package/backend/src/api/routes/system.js +174 -174
- package/backend/src/api/routes/traces.js +131 -0
- package/backend/src/config/debug.config.js +36 -0
- package/backend/src/core/BotHistoryStore.js +180 -0
- package/backend/src/core/BotManager.js +14 -4
- package/backend/src/core/BotProcess.js +1517 -1092
- package/backend/src/core/EventGraphManager.js +37 -123
- package/backend/src/core/GraphExecutionEngine.js +977 -321
- package/backend/src/core/MessageQueue.js +12 -6
- package/backend/src/core/PluginLoader.js +99 -5
- package/backend/src/core/PluginManager.js +74 -13
- package/backend/src/core/TaskScheduler.js +1 -1
- package/backend/src/core/commands/whois.js +1 -1
- package/backend/src/core/node-registries/actions.js +70 -0
- package/backend/src/core/node-registries/arrays.js +18 -0
- package/backend/src/core/node-registries/data.js +1 -1
- package/backend/src/core/node-registries/events.js +14 -0
- package/backend/src/core/node-registries/logic.js +17 -0
- package/backend/src/core/node-registries/strings.js +34 -0
- package/backend/src/core/node-registries/type.js +25 -0
- package/backend/src/core/nodes/actions/bot_look_at.js +1 -1
- package/backend/src/core/nodes/actions/create_command.js +189 -0
- package/backend/src/core/nodes/actions/delete_command.js +92 -0
- package/backend/src/core/nodes/actions/update_command.js +133 -0
- package/backend/src/core/nodes/arrays/join.js +28 -0
- package/backend/src/core/nodes/data/cast.js +2 -1
- package/backend/src/core/nodes/logic/not.js +22 -0
- package/backend/src/core/nodes/strings/starts_with.js +1 -1
- package/backend/src/core/nodes/strings/to_lower.js +22 -0
- package/backend/src/core/nodes/strings/to_upper.js +22 -0
- package/backend/src/core/nodes/type/to_string.js +32 -0
- package/backend/src/core/services/BotLifecycleService.js +255 -16
- package/backend/src/core/services/CommandExecutionService.js +430 -351
- package/backend/src/core/services/DebugSessionManager.js +347 -0
- package/backend/src/core/services/GraphCollaborationManager.js +501 -0
- package/backend/src/core/services/MinecraftBotManager.js +259 -0
- package/backend/src/core/services/MinecraftViewerService.js +216 -0
- package/backend/src/core/services/TraceCollectorService.js +545 -0
- package/backend/src/core/system/RuntimeCommandRegistry.js +116 -0
- package/backend/src/core/system/Transport.js +0 -4
- package/backend/src/core/validation/nodeSchemas.js +6 -6
- package/backend/src/real-time/botApi/handlers/graphHandlers.js +2 -2
- package/backend/src/real-time/botApi/handlers/graphWebSocketHandlers.js +1 -1
- package/backend/src/real-time/botApi/utils.js +11 -0
- package/backend/src/real-time/panelNamespace.js +387 -0
- package/backend/src/real-time/presence.js +7 -2
- package/backend/src/real-time/socketHandler.js +395 -4
- package/backend/src/server.js +18 -0
- package/frontend/dist/assets/index-B1serztM.js +11210 -0
- package/frontend/dist/assets/index-t6K1u4OV.css +32 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/package-lock.json +9437 -0
- package/frontend/package.json +8 -0
- package/package.json +2 -2
- package/screen/console.png +0 -0
- package/screen/dashboard.png +0 -0
- package/screen/graph_collabe.png +0 -0
- package/screen/graph_live_debug.png +0 -0
- package/screen/management_command.png +0 -0
- package/screen/node_debug_trace.png +0 -0
- package/screen/plugin_/320/276/320/261/320/267/320/276/321/200.png +0 -0
- package/screen/websocket.png +0 -0
- package/screen//320/275/320/260/321/201/321/202/321/200/320/276/320/271/320/272/320/270_/320/276/321/202/320/264/320/265/320/273/321/214/320/275/321/213/321/205_/320/272/320/276/320/274/320/260/320/275/320/264_/320/272/320/260/320/266/320/264/321/203_/320/272/320/276/320/274/320/260/320/275/320/273/320/264/321/203_/320/274/320/276/320/266/320/275/320/276_/320/275/320/260/321/201/321/202/321/200/320/260/320/270/320/262/320/260/321/202/321/214.png +0 -0
- package/screen//320/277/320/273/320/260/320/275/320/270/321/200/320/276/320/262/321/211/320/270/320/272_/320/274/320/276/320/266/320/275/320/276_/320/267/320/260/320/264/320/260/320/262/320/260/321/202/321/214_/320/264/320/265/320/271/321/201/321/202/320/262/320/270/321/217_/320/277/320/276_/320/262/321/200/320/265/320/274/320/265/320/275/320/270.png +0 -0
- package/frontend/dist/assets/index-CfTo92bP.css +0 -1
- package/frontend/dist/assets/index-CiFD5X9Z.js +0 -8344
|
@@ -1,351 +1,430 @@
|
|
|
1
|
-
const { v4: uuidv4 } = require('uuid');
|
|
2
|
-
const UserService = require('../UserService');
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
|
|
1
|
+
const { v4: uuidv4 } = require('uuid');
|
|
2
|
+
const UserService = require('../UserService');
|
|
3
|
+
const { getRuntimeCommandRegistry } = require('../system/RuntimeCommandRegistry');
|
|
4
|
+
const botHistoryStore = require('../BotHistoryStore');
|
|
5
|
+
|
|
6
|
+
// Кулдауны и предупреждения - глобальные для всех инстансов
|
|
7
|
+
const cooldowns = new Map();
|
|
8
|
+
const warningCache = new Map();
|
|
9
|
+
const WARNING_COOLDOWN = 10 * 1000;
|
|
10
|
+
|
|
11
|
+
class CommandExecutionService {
|
|
12
|
+
constructor({
|
|
13
|
+
botProcessManager,
|
|
14
|
+
cacheManager,
|
|
15
|
+
eventGraphManager,
|
|
16
|
+
commandRepository,
|
|
17
|
+
permissionRepository,
|
|
18
|
+
groupRepository,
|
|
19
|
+
logger
|
|
20
|
+
}) {
|
|
21
|
+
this.processManager = botProcessManager;
|
|
22
|
+
this.cache = cacheManager;
|
|
23
|
+
this.eventGraphManager = eventGraphManager;
|
|
24
|
+
this.commandRepository = commandRepository;
|
|
25
|
+
this.permissionRepository = permissionRepository;
|
|
26
|
+
this.groupRepository = groupRepository;
|
|
27
|
+
this.logger = logger;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async handleCommandValidation(botConfig, message) {
|
|
31
|
+
const { commandName, username, args, typeChat } = message;
|
|
32
|
+
const botId = botConfig.id;
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
// Получаем конфигурацию из кеша или загружаем из БД
|
|
36
|
+
const botConfigCache = await this.cache.getOrLoadBotConfig(botId);
|
|
37
|
+
|
|
38
|
+
const user = await UserService.getUser(username, botId, botConfig);
|
|
39
|
+
|
|
40
|
+
const child = this.processManager.getProcess(botId);
|
|
41
|
+
if (!child) return;
|
|
42
|
+
|
|
43
|
+
if (user.isBlacklisted) {
|
|
44
|
+
child.send({
|
|
45
|
+
type: 'handle_blacklist',
|
|
46
|
+
commandName,
|
|
47
|
+
username,
|
|
48
|
+
typeChat
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const mainCommandName = botConfigCache.commandAliases.get(commandName) || commandName;
|
|
54
|
+
let dbCommand = botConfigCache.commands.get(mainCommandName);
|
|
55
|
+
|
|
56
|
+
// Если команда не найдена в БД, проверяем runtime registry (временные команды)
|
|
57
|
+
if (!dbCommand) {
|
|
58
|
+
const runtimeRegistry = getRuntimeCommandRegistry();
|
|
59
|
+
const tempCommand = runtimeRegistry.get(botId, mainCommandName);
|
|
60
|
+
|
|
61
|
+
if (tempCommand) {
|
|
62
|
+
// Преобразуем временную команду в формат dbCommand
|
|
63
|
+
dbCommand = {
|
|
64
|
+
name: tempCommand.name,
|
|
65
|
+
isEnabled: true,
|
|
66
|
+
allowedChatTypes: JSON.stringify(tempCommand.allowedChatTypes || ['chat', 'private']),
|
|
67
|
+
permissionId: tempCommand.permissionId || null,
|
|
68
|
+
cooldown: tempCommand.cooldown || 0,
|
|
69
|
+
isTemporary: true
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!dbCommand || (!dbCommand.isEnabled && !user.isOwner)) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const allowedTypes = JSON.parse(dbCommand.allowedChatTypes || '[]');
|
|
79
|
+
if (!allowedTypes.includes(typeChat) && !user.isOwner) {
|
|
80
|
+
// Тип чата не разрешен для обычного пользователя - просто молча игнорируем
|
|
81
|
+
// Никаких сообщений об ошибке не отправляем
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Проверяем required аргументы ПОСЛЕ проверки типа чата
|
|
86
|
+
// чтобы обычные пользователи не видели ошибки в неразрешенных чатах
|
|
87
|
+
if (message.commandArgs) {
|
|
88
|
+
for (const argDef of message.commandArgs) {
|
|
89
|
+
if (argDef.required && (args[argDef.name] === undefined || args[argDef.name] === null)) {
|
|
90
|
+
const usage = message.commandArgs.map(arg => {
|
|
91
|
+
return arg.required ? `<${arg.description || arg.name}>` : `[${arg.description || arg.name}]`;
|
|
92
|
+
}).join(' ');
|
|
93
|
+
|
|
94
|
+
child.send({
|
|
95
|
+
type: 'send_message',
|
|
96
|
+
typeChat,
|
|
97
|
+
message: `Ошибка: Необходимо указать: ${argDef.description || argDef.name}`,
|
|
98
|
+
username
|
|
99
|
+
});
|
|
100
|
+
child.send({
|
|
101
|
+
type: 'send_message',
|
|
102
|
+
typeChat,
|
|
103
|
+
message: `Использование: ${botConfig.prefix || '@'}${dbCommand.name} ${usage}`,
|
|
104
|
+
username
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const permission = dbCommand.permissionId ? botConfigCache.permissionsById.get(dbCommand.permissionId) : null;
|
|
112
|
+
if (permission && !user.hasPermission(permission.name) && !user.isOwner) {
|
|
113
|
+
child.send({
|
|
114
|
+
type: 'handle_permission_error',
|
|
115
|
+
commandName: dbCommand.name,
|
|
116
|
+
username,
|
|
117
|
+
typeChat
|
|
118
|
+
});
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const domain = (permission?.name || '').split('.')[0] || 'user';
|
|
123
|
+
const bypassCooldownPermission = `${domain}.cooldown.bypass`;
|
|
124
|
+
|
|
125
|
+
if (dbCommand.cooldown > 0 && !user.isOwner && !user.hasPermission(bypassCooldownPermission)) {
|
|
126
|
+
const cooldownKey = `${botId}:${dbCommand.name}:${user.id}`;
|
|
127
|
+
const now = Date.now();
|
|
128
|
+
const lastUsed = cooldowns.get(cooldownKey);
|
|
129
|
+
|
|
130
|
+
if (lastUsed && (now - lastUsed < dbCommand.cooldown * 1000)) {
|
|
131
|
+
const timeLeft = Math.ceil((dbCommand.cooldown * 1000 - (now - lastUsed)) / 1000);
|
|
132
|
+
child.send({
|
|
133
|
+
type: 'handle_cooldown',
|
|
134
|
+
commandName: dbCommand.name,
|
|
135
|
+
username,
|
|
136
|
+
typeChat,
|
|
137
|
+
timeLeft
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
cooldowns.set(cooldownKey, now);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (this.eventGraphManager) {
|
|
145
|
+
this.eventGraphManager.handleEvent(botId, 'command', {
|
|
146
|
+
commandName: dbCommand.name,
|
|
147
|
+
user: { username },
|
|
148
|
+
args,
|
|
149
|
+
typeChat
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
child.send({ type: 'execute_handler', commandName: dbCommand.name, username, args, typeChat });
|
|
154
|
+
|
|
155
|
+
} catch (error) {
|
|
156
|
+
this.logger.error({ botId, command: commandName, username, error }, 'Ошибка валидации команды');
|
|
157
|
+
this.sendMessageToBot(botId, `Произошла внутренняя ошибка при выполнении команды.`, 'private', username);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async validateAndExecuteCommandForApi(botId, username, commandName, args) {
|
|
162
|
+
const botConfig = this.processManager.getProcess(botId)?.botConfig;
|
|
163
|
+
if (!botConfig) {
|
|
164
|
+
throw new Error('Bot configuration not found.');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const typeChat = 'websocket';
|
|
168
|
+
|
|
169
|
+
let botConfigCache = this.cache.getBotConfig(botId);
|
|
170
|
+
if (!botConfigCache) {
|
|
171
|
+
throw new Error('Bot configuration cache not loaded.');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const user = await UserService.getUser(username, botId, botConfig);
|
|
175
|
+
|
|
176
|
+
if (user.isBlacklisted) {
|
|
177
|
+
throw new Error(`User '${username}' is blacklisted.`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const mainCommandName = botConfigCache.commandAliases.get(commandName) || commandName;
|
|
181
|
+
let dbCommand = botConfigCache.commands.get(mainCommandName);
|
|
182
|
+
|
|
183
|
+
// Если команда не найдена в БД, проверяем runtime registry (временные команды)
|
|
184
|
+
if (!dbCommand) {
|
|
185
|
+
const runtimeRegistry = getRuntimeCommandRegistry();
|
|
186
|
+
const tempCommand = runtimeRegistry.get(botId, mainCommandName);
|
|
187
|
+
|
|
188
|
+
if (tempCommand) {
|
|
189
|
+
// Преобразуем временную команду в формат dbCommand
|
|
190
|
+
dbCommand = {
|
|
191
|
+
name: tempCommand.name,
|
|
192
|
+
isEnabled: true,
|
|
193
|
+
allowedChatTypes: JSON.stringify(tempCommand.allowedChatTypes || ['chat', 'private']),
|
|
194
|
+
permissionId: tempCommand.permissionId || null,
|
|
195
|
+
cooldown: tempCommand.cooldown || 0,
|
|
196
|
+
isTemporary: true
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!dbCommand || (!dbCommand.isEnabled && !user.isOwner)) {
|
|
202
|
+
throw new Error(`Command '${commandName}' not found or is disabled.`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// WebSocket - универсальный транспорт
|
|
206
|
+
if (typeChat !== 'websocket') {
|
|
207
|
+
const allowedTypes = JSON.parse(dbCommand.allowedChatTypes || '[]');
|
|
208
|
+
if (!allowedTypes.includes(typeChat) && !user.isOwner) {
|
|
209
|
+
throw new Error(`Command '${commandName}' cannot be used in this chat type.`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const permission = dbCommand.permissionId ? botConfigCache.permissionsById.get(dbCommand.permissionId) : null;
|
|
214
|
+
if (permission && !user.hasPermission(permission.name) && !user.isOwner) {
|
|
215
|
+
throw new Error(`User '${username}' has insufficient permissions.`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const domain = (permission?.name || '').split('.')[0] || 'user';
|
|
219
|
+
const bypassCooldownPermission = `${domain}.cooldown.bypass`;
|
|
220
|
+
|
|
221
|
+
if (dbCommand.cooldown > 0 && !user.isOwner && !user.hasPermission(bypassCooldownPermission)) {
|
|
222
|
+
const cooldownKey = `${botId}:${dbCommand.name}:${user.id}`;
|
|
223
|
+
const now = Date.now();
|
|
224
|
+
const lastUsed = cooldowns.get(cooldownKey);
|
|
225
|
+
|
|
226
|
+
if (lastUsed && (now - lastUsed < dbCommand.cooldown * 1000)) {
|
|
227
|
+
const timeLeft = Math.ceil((dbCommand.cooldown * 1000 - (now - lastUsed)) / 1000);
|
|
228
|
+
throw new Error(`Command on cooldown for user '${username}'. Please wait ${timeLeft} seconds.`);
|
|
229
|
+
}
|
|
230
|
+
cooldowns.set(cooldownKey, now);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return this._executeCommandInProcess(botId, dbCommand.name, args, user, typeChat);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async _executeCommandInProcess(botId, commandName, args, user, typeChat) {
|
|
237
|
+
return new Promise((resolve, reject) => {
|
|
238
|
+
const child = this.processManager.getProcess(botId);
|
|
239
|
+
if (!child || child.killed) {
|
|
240
|
+
return reject(new Error('Bot is not running'));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const requestId = uuidv4();
|
|
244
|
+
|
|
245
|
+
// Таймаут на выполнение команды
|
|
246
|
+
const timeout = setTimeout(() => {
|
|
247
|
+
reject(new Error('Command execution timed out.'));
|
|
248
|
+
}, 10000);
|
|
249
|
+
|
|
250
|
+
this.processManager.addCommandRequest(requestId, {
|
|
251
|
+
resolve: (result) => {
|
|
252
|
+
clearTimeout(timeout);
|
|
253
|
+
|
|
254
|
+
botHistoryStore.addCommandLog(botId, {
|
|
255
|
+
username: user.username,
|
|
256
|
+
command: commandName,
|
|
257
|
+
args: args || {},
|
|
258
|
+
success: true
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
resolve(result);
|
|
262
|
+
},
|
|
263
|
+
reject: (error) => {
|
|
264
|
+
clearTimeout(timeout);
|
|
265
|
+
|
|
266
|
+
botHistoryStore.addCommandLog(botId, {
|
|
267
|
+
username: user.username,
|
|
268
|
+
command: commandName,
|
|
269
|
+
args: args || {},
|
|
270
|
+
success: false,
|
|
271
|
+
error: error.message || String(error)
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
reject(error);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
child.send({
|
|
279
|
+
type: 'execute_command_request',
|
|
280
|
+
requestId,
|
|
281
|
+
payload: {
|
|
282
|
+
commandName,
|
|
283
|
+
args: args || {},
|
|
284
|
+
username: user.username,
|
|
285
|
+
typeChat
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
sendMessageToBot(botId, message, chatType = 'command', username = null) {
|
|
292
|
+
const child = this.processManager.getProcess(botId);
|
|
293
|
+
if (child && child.api) {
|
|
294
|
+
child.api.sendMessage(chatType, message, username);
|
|
295
|
+
return { success: true };
|
|
296
|
+
}
|
|
297
|
+
return { success: false, message: 'Бот не найден или не запущен' };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async handleCommandRegistration(botId, commandConfig) {
|
|
301
|
+
try {
|
|
302
|
+
let permissionId = null;
|
|
303
|
+
|
|
304
|
+
if (commandConfig.permissions) {
|
|
305
|
+
let permission = await this.permissionRepository.findByName(botId, commandConfig.permissions);
|
|
306
|
+
|
|
307
|
+
if (!permission) {
|
|
308
|
+
permission = await this.permissionRepository.create({
|
|
309
|
+
botId,
|
|
310
|
+
name: commandConfig.permissions,
|
|
311
|
+
description: `Автоматически создано для команды ${commandConfig.name}`,
|
|
312
|
+
owner: commandConfig.owner,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
permissionId = permission.id;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const createData = {
|
|
319
|
+
botId,
|
|
320
|
+
name: commandConfig.name,
|
|
321
|
+
description: commandConfig.description,
|
|
322
|
+
aliases: JSON.stringify(commandConfig.aliases || []),
|
|
323
|
+
owner: commandConfig.owner,
|
|
324
|
+
permissionId: permissionId,
|
|
325
|
+
allowedChatTypes: JSON.stringify(commandConfig.allowedChatTypes || []),
|
|
326
|
+
cooldown: commandConfig.cooldown || 0,
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const updateData = {
|
|
330
|
+
description: commandConfig.description,
|
|
331
|
+
owner: commandConfig.owner,
|
|
332
|
+
aliases: JSON.stringify(commandConfig.aliases || []),
|
|
333
|
+
allowedChatTypes: JSON.stringify(commandConfig.allowedChatTypes || []),
|
|
334
|
+
cooldown: commandConfig.cooldown || 0,
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const existingCommand = await this.commandRepository.findByName(botId, commandConfig.name);
|
|
338
|
+
if (existingCommand) {
|
|
339
|
+
// Обновляем permissionId только если он null (не был установлен пользователем)
|
|
340
|
+
if (existingCommand.permissionId === null && permissionId !== null) {
|
|
341
|
+
updateData.permissionId = permissionId;
|
|
342
|
+
}
|
|
343
|
+
await this.commandRepository.update(existingCommand.id, updateData);
|
|
344
|
+
} else {
|
|
345
|
+
await this.commandRepository.create(createData);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
this.cache.deleteBotConfig(botId);
|
|
349
|
+
} catch (error) {
|
|
350
|
+
this.logger.error({ botId, commandName: commandConfig.name, error }, 'Ошибка регистрации команды');
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async handleGroupRegistration(botId, groupConfig) {
|
|
355
|
+
try {
|
|
356
|
+
if (!groupConfig.name || !groupConfig.owner) {
|
|
357
|
+
this.logger.warn({ botId, groupConfig }, 'Пропущена группа без имени или владельца');
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
await this.groupRepository.upsertGroup(botId, groupConfig.name, {
|
|
362
|
+
owner: groupConfig.owner,
|
|
363
|
+
description: groupConfig.description || ''
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
this.logger.debug({ botId, groupName: groupConfig.name }, 'Группа зарегистрирована');
|
|
367
|
+
this.cache.deleteBotConfig(botId);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
this.logger.error({ botId, groupName: groupConfig.name, error }, 'Ошибка регистрации группы');
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async handlePermissionsRegistration(botId, permissions) {
|
|
374
|
+
try {
|
|
375
|
+
for (const perm of permissions) {
|
|
376
|
+
if (!perm.name || !perm.owner) {
|
|
377
|
+
this.logger.warn({ botId, perm }, 'Пропущено право без имени или владельца');
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const existing = await this.permissionRepository.findByName(botId, perm.name);
|
|
382
|
+
if (existing) {
|
|
383
|
+
await this.permissionRepository.update(existing.id, { description: perm.description });
|
|
384
|
+
} else {
|
|
385
|
+
await this.permissionRepository.create({
|
|
386
|
+
botId,
|
|
387
|
+
name: perm.name,
|
|
388
|
+
description: perm.description || '',
|
|
389
|
+
owner: perm.owner,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
this.cache.deleteBotConfig(botId);
|
|
395
|
+
} catch (error) {
|
|
396
|
+
this.logger.error({ botId, error }, 'Ошибка регистрации прав');
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async handleAddPermissionsToGroup(botId, message) {
|
|
401
|
+
try {
|
|
402
|
+
const { groupName, permissionNames } = message;
|
|
403
|
+
|
|
404
|
+
// Находим группу
|
|
405
|
+
const group = await this.groupRepository.findByName(botId, groupName);
|
|
406
|
+
if (!group) {
|
|
407
|
+
this.logger.warn({ botId, groupName }, 'Группа не найдена');
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Добавляем каждое право в группу
|
|
412
|
+
for (const permName of permissionNames) {
|
|
413
|
+
const permission = await this.permissionRepository.findByName(botId, permName);
|
|
414
|
+
if (!permission) {
|
|
415
|
+
this.logger.warn({ botId, groupName, permName }, 'Право не найдено');
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
await this.groupRepository.addPermissionToGroup(group.id, permission.id, this.permissionRepository.prisma);
|
|
420
|
+
this.logger.debug({ botId, groupName, permName }, 'Право добавлено в группу');
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
this.cache.deleteBotConfig(botId);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
this.logger.error({ botId, groupName: message.groupName, error }, 'Ошибка добавления прав в группу');
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
module.exports = CommandExecutionService;
|