blockmine 1.20.0 → 1.21.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.local.json +13 -1
- package/CHANGELOG.md +15 -0
- package/backend/src/core/BotManager.js +2 -2
- package/backend/src/core/BotProcess.js +8 -0
- package/backend/src/core/BreakLoopSignal.js +8 -0
- package/backend/src/core/EventGraphManager.js +5 -0
- package/backend/src/core/GraphExecutionEngine.js +36 -639
- package/backend/src/core/NodeRegistry.js +144 -1
- package/backend/src/core/nodes/action_bot_look_at.js +36 -0
- package/backend/src/core/nodes/action_bot_set_variable.js +32 -0
- package/backend/src/core/nodes/action_http_request.js +98 -0
- package/backend/src/core/nodes/action_send_log.js +23 -0
- package/backend/src/core/nodes/action_send_message.js +32 -0
- package/backend/src/core/nodes/array_add_element.js +23 -0
- package/backend/src/core/nodes/array_contains.js +40 -0
- package/backend/src/core/nodes/array_find_index.js +23 -0
- package/backend/src/core/nodes/array_get_by_index.js +23 -0
- package/backend/src/core/nodes/array_get_random_element.js +32 -0
- package/backend/src/core/nodes/array_remove_by_index.js +30 -0
- package/backend/src/core/nodes/bot_get_position.js +20 -0
- package/backend/src/core/nodes/data_array_literal.js +31 -0
- package/backend/src/core/nodes/data_boolean_literal.js +21 -0
- package/backend/src/core/nodes/data_cast.js +34 -0
- package/backend/src/core/nodes/data_get_argument.js +23 -0
- package/backend/src/core/nodes/data_get_bot_look.js +14 -0
- package/backend/src/core/nodes/data_get_entity_field.js +18 -0
- package/backend/src/core/nodes/data_get_server_players.js +18 -0
- package/backend/src/core/nodes/data_get_user_field.js +40 -0
- package/backend/src/core/nodes/data_get_variable.js +23 -0
- package/backend/src/core/nodes/data_length.js +25 -0
- package/backend/src/core/nodes/data_make_object.js +31 -0
- package/backend/src/core/nodes/data_number_literal.js +21 -0
- package/backend/src/core/nodes/data_string_literal.js +34 -0
- package/backend/src/core/nodes/debug_log.js +16 -0
- package/backend/src/core/nodes/flow_branch.js +15 -0
- package/backend/src/core/nodes/flow_break.js +14 -0
- package/backend/src/core/nodes/flow_for_each.js +39 -0
- package/backend/src/core/nodes/flow_sequence.js +16 -0
- package/backend/src/core/nodes/flow_switch.js +47 -0
- package/backend/src/core/nodes/flow_while.js +64 -0
- package/backend/src/core/nodes/logic_compare.js +33 -0
- package/backend/src/core/nodes/logic_operation.js +35 -0
- package/backend/src/core/nodes/math_operation.js +31 -0
- package/backend/src/core/nodes/math_random_number.js +43 -0
- package/backend/src/core/nodes/object_create.js +40 -0
- package/backend/src/core/nodes/object_delete.js +26 -0
- package/backend/src/core/nodes/object_get.js +23 -0
- package/backend/src/core/nodes/object_has_key.js +30 -0
- package/backend/src/core/nodes/object_set.js +27 -0
- package/backend/src/core/nodes/string_concat.js +27 -0
- package/backend/src/core/nodes/string_contains.js +41 -0
- package/backend/src/core/nodes/string_ends_with.js +43 -0
- package/backend/src/core/nodes/string_equals.js +36 -0
- package/backend/src/core/nodes/string_length.js +36 -0
- package/backend/src/core/nodes/string_matches.js +39 -0
- package/backend/src/core/nodes/string_split.js +37 -0
- package/backend/src/core/nodes/string_starts_with.js +43 -0
- package/backend/src/core/nodes/user_check_blacklist.js +37 -0
- package/backend/src/core/nodes/user_get_groups.js +36 -0
- package/backend/src/core/nodes/user_get_permissions.js +36 -0
- package/backend/src/core/nodes/user_set_blacklist.js +37 -0
- package/frontend/dist/assets/index-B9GedHEa.js +8352 -0
- package/frontend/dist/assets/index-zLiy9MDx.css +1 -0
- package/frontend/dist/index.html +2 -2
- package/frontend/package.json +1 -0
- package/package.json +1 -1
- package/frontend/dist/assets/index-BFd7YoAj.css +0 -1
- package/frontend/dist/assets/index-CMMutadc.js +0 -8352
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
const User = require('./UserService');
|
|
2
1
|
const { PrismaClient } = require('@prisma/client');
|
|
3
2
|
const prisma = new PrismaClient();
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
constructor() {
|
|
7
|
-
super("Loop break signal");
|
|
8
|
-
this.name = "BreakLoopSignal";
|
|
9
|
-
}
|
|
10
|
-
}
|
|
4
|
+
const BreakLoopSignal = require('./BreakLoopSignal');
|
|
11
5
|
|
|
12
6
|
class GraphExecutionEngine {
|
|
13
7
|
constructor(nodeRegistry, botManagerOrApi = null) {
|
|
@@ -102,6 +96,18 @@ class GraphExecutionEngine {
|
|
|
102
96
|
}
|
|
103
97
|
|
|
104
98
|
async executeNode(node) {
|
|
99
|
+
const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
|
|
100
|
+
if (nodeConfig && typeof nodeConfig.executor === 'function') {
|
|
101
|
+
const helpers = {
|
|
102
|
+
resolvePinValue: this.resolvePinValue.bind(this),
|
|
103
|
+
traverse: this.traverse.bind(this),
|
|
104
|
+
memo: this.memo,
|
|
105
|
+
clearLoopBodyMemo: this.clearLoopBodyMemo.bind(this),
|
|
106
|
+
};
|
|
107
|
+
await nodeConfig.executor.call(this, node, this.context, helpers);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
105
111
|
const execCacheKey = `${node.id}_executed`;
|
|
106
112
|
if (this.memo.has(execCacheKey)) {
|
|
107
113
|
return;
|
|
@@ -109,203 +115,13 @@ class GraphExecutionEngine {
|
|
|
109
115
|
this.memo.set(execCacheKey, true);
|
|
110
116
|
|
|
111
117
|
switch (node.type) {
|
|
112
|
-
case 'user:set_blacklist': {
|
|
113
|
-
const userObject = await this.resolvePinValue(node, 'user', null);
|
|
114
|
-
const blacklistStatus = await this.resolvePinValue(node, 'blacklist_status', false);
|
|
115
|
-
let updatedUser = null;
|
|
116
|
-
|
|
117
|
-
if (userObject && userObject.username) {
|
|
118
|
-
const user = await User.getUser(userObject.username, this.context.botId);
|
|
119
|
-
if (user) {
|
|
120
|
-
updatedUser = await prisma.user.update({
|
|
121
|
-
where: { id: user.id },
|
|
122
|
-
data: { isBlacklisted: blacklistStatus }
|
|
123
|
-
});
|
|
124
|
-
User.clearCache(user.username, this.context.botId);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
this.memo.set(`${node.id}:updated_user`, updatedUser);
|
|
128
|
-
await this.traverse(node, 'exec');
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
case 'action:send_message': {
|
|
132
|
-
let message = String(await this.resolvePinValue(node, 'message', ''));
|
|
133
|
-
const chatType = await this.resolvePinValue(node, 'chat_type', this.context.typeChat);
|
|
134
|
-
const recipient = await this.resolvePinValue(node, 'recipient', this.context.user?.username);
|
|
135
|
-
|
|
136
|
-
// Парсим и заменяем переменные в формате {varName}
|
|
137
|
-
const variablePattern = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
138
|
-
const matches = [...message.matchAll(variablePattern)];
|
|
139
|
-
|
|
140
|
-
for (const match of matches) {
|
|
141
|
-
const varName = match[1];
|
|
142
|
-
const varValue = await this.resolvePinValue(node, `var_${varName}`, '');
|
|
143
|
-
message = message.replace(match[0], String(varValue));
|
|
144
|
-
}
|
|
145
118
|
|
|
146
|
-
this.context.bot.sendMessage(chatType, message, recipient);
|
|
147
|
-
await this.traverse(node, 'exec');
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
case 'action:server_command': {
|
|
151
|
-
const command = await this.resolvePinValue(node, 'command', '');
|
|
152
|
-
if (command) this.context.bot.executeCommand(command);
|
|
153
|
-
await this.traverse(node, 'exec');
|
|
154
|
-
break;
|
|
155
|
-
}
|
|
156
|
-
case 'action:send_log': {
|
|
157
|
-
const message = await this.resolvePinValue(node, 'message', '');
|
|
158
|
-
if (this.botManager?.appendLog && this.context?.botId) {
|
|
159
|
-
this.botManager.appendLog(this.context.botId, `[Graph] ${message}`);
|
|
160
|
-
} else {
|
|
161
|
-
console.log(`[Graph Log] ${message}`);
|
|
162
|
-
}
|
|
163
|
-
await this.traverse(node, 'exec');
|
|
164
|
-
break;
|
|
165
|
-
}
|
|
166
|
-
case 'action:bot_look_at': {
|
|
167
|
-
const target = await this.resolvePinValue(node, 'target');
|
|
168
|
-
const yOffset = await this.resolvePinValue(node, 'add_y', 0);
|
|
169
|
-
|
|
170
|
-
if (target && this.context.bot?.lookAt) {
|
|
171
|
-
let finalPosition;
|
|
172
|
-
if (target.position) {
|
|
173
|
-
finalPosition = { ...target.position };
|
|
174
|
-
} else if (target.x !== undefined && target.y !== undefined && target.z !== undefined) {
|
|
175
|
-
finalPosition = { ...target };
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (finalPosition) {
|
|
179
|
-
finalPosition.y += Number(yOffset || 0);
|
|
180
|
-
this.context.bot.lookAt(finalPosition);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
await this.traverse(node, 'exec');
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
case 'action:bot_set_variable': {
|
|
187
|
-
const varName = await this.resolvePinValue(node, 'name', '');
|
|
188
|
-
const varValue = await this.resolvePinValue(node, 'value');
|
|
189
|
-
let shouldPersist = await this.resolvePinValue(node, 'persist', false);
|
|
190
|
-
|
|
191
|
-
if (this.context.eventType === 'command') {
|
|
192
|
-
shouldPersist = false;
|
|
193
|
-
}
|
|
194
119
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
await this.traverse(node, 'exec');
|
|
202
|
-
break;
|
|
203
|
-
}
|
|
204
|
-
case 'flow:branch': {
|
|
205
|
-
const condition = await this.resolvePinValue(node, 'condition', false);
|
|
206
|
-
await this.traverse(node, condition ? 'exec_true' : 'exec_false');
|
|
207
|
-
break;
|
|
208
|
-
}
|
|
209
|
-
case 'flow:break': {
|
|
210
|
-
throw new BreakLoopSignal();
|
|
211
|
-
}
|
|
212
|
-
case 'flow:for_each': {
|
|
213
|
-
const array = await this.resolvePinValue(node, 'array', []);
|
|
214
|
-
if (Array.isArray(array)) {
|
|
215
|
-
try {
|
|
216
|
-
for (let i = 0; i < array.length; i++) {
|
|
217
|
-
const element = array[i];
|
|
218
|
-
this.memo.set(`${node.id}:element`, element);
|
|
219
|
-
this.memo.set(`${node.id}:index`, i);
|
|
220
|
-
this.clearLoopBodyMemo(node);
|
|
221
|
-
await this.traverse(node, 'loop_body');
|
|
222
|
-
}
|
|
223
|
-
} catch (e) {
|
|
224
|
-
if (e instanceof BreakLoopSignal) {
|
|
225
|
-
} else {
|
|
226
|
-
throw e;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
await this.traverse(node, 'completed');
|
|
231
|
-
break;
|
|
232
|
-
}
|
|
233
|
-
case 'flow:while': {
|
|
234
|
-
let iteration = 0;
|
|
235
|
-
const maxIterations = 1000;
|
|
236
|
-
|
|
237
|
-
try {
|
|
238
|
-
while (iteration < maxIterations) {
|
|
239
|
-
const condition = await this.resolvePinValue(node, 'condition', false);
|
|
240
|
-
if (!condition) break;
|
|
241
|
-
|
|
242
|
-
this.memo.set(`${node.id}:iteration`, iteration);
|
|
243
|
-
this.clearLoopBodyMemo(node);
|
|
244
|
-
await this.traverse(node, 'loop_body');
|
|
245
|
-
iteration++;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (iteration >= maxIterations) {
|
|
249
|
-
console.warn(`[GraphExecutionEngine] Цикл while достиг максимального количества итераций (${maxIterations})`);
|
|
250
|
-
}
|
|
251
|
-
} catch (e) {
|
|
252
|
-
if (e instanceof BreakLoopSignal) {
|
|
253
|
-
} else {
|
|
254
|
-
throw e;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
await this.traverse(node, 'completed');
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
261
|
-
case 'flow:sequence': {
|
|
262
|
-
const pinCount = node.data?.pinCount || 2;
|
|
263
|
-
for (let i = 0; i < pinCount; i++) {
|
|
264
|
-
await this.traverse(node, `exec_${i}`);
|
|
265
|
-
}
|
|
266
|
-
break;
|
|
267
|
-
}
|
|
268
|
-
case 'flow:switch': {
|
|
269
|
-
const value = await this.resolvePinValue(node, 'value');
|
|
270
|
-
const caseCount = node.data?.caseCount || 0;
|
|
271
|
-
let matched = false;
|
|
272
|
-
|
|
273
|
-
for (let i = 0; i < caseCount; i++) {
|
|
274
|
-
const caseValue = node.data?.[`case_${i}`];
|
|
275
|
-
if (caseValue !== undefined) {
|
|
276
|
-
let isMatch = false;
|
|
277
|
-
|
|
278
|
-
if (Array.isArray(value) && Array.isArray(caseValue)) {
|
|
279
|
-
isMatch = JSON.stringify(value) === JSON.stringify(caseValue);
|
|
280
|
-
} else if (typeof value === 'object' && typeof caseValue === 'object' && value !== null && caseValue !== null) {
|
|
281
|
-
isMatch = JSON.stringify(value) === JSON.stringify(caseValue);
|
|
282
|
-
} else if (typeof value === 'number' && typeof caseValue === 'number') {
|
|
283
|
-
isMatch = value === caseValue;
|
|
284
|
-
} else if (typeof value === 'boolean' && typeof caseValue === 'boolean') {
|
|
285
|
-
isMatch = value === caseValue;
|
|
286
|
-
} else {
|
|
287
|
-
isMatch = String(value) === String(caseValue);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (isMatch) {
|
|
291
|
-
await this.traverse(node, `case_${i}`);
|
|
292
|
-
matched = true;
|
|
293
|
-
break;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
if (!matched) {
|
|
299
|
-
await this.traverse(node, 'default');
|
|
300
|
-
}
|
|
301
|
-
break;
|
|
302
|
-
}
|
|
303
|
-
case 'debug:log': {
|
|
304
|
-
const value = await this.resolvePinValue(node, 'value');
|
|
305
|
-
console.log('[Graph Debug]', value);
|
|
306
|
-
await this.traverse(node, 'exec');
|
|
307
|
-
break;
|
|
308
|
-
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
309
125
|
case 'string:contains':
|
|
310
126
|
case 'string:matches':
|
|
311
127
|
case 'string:equals': {
|
|
@@ -407,77 +223,24 @@ class GraphExecutionEngine {
|
|
|
407
223
|
return this.memo.get(cacheKey);
|
|
408
224
|
}
|
|
409
225
|
|
|
226
|
+
const nodeConfig = this.nodeRegistry.getNodeConfig(node.type);
|
|
227
|
+
if (nodeConfig && typeof nodeConfig.evaluator === 'function') {
|
|
228
|
+
const helpers = {
|
|
229
|
+
resolvePinValue: this.resolvePinValue.bind(this),
|
|
230
|
+
memo: this.memo,
|
|
231
|
+
};
|
|
232
|
+
const result = await nodeConfig.evaluator.call(this, node, pinId, this.context, helpers);
|
|
233
|
+
|
|
234
|
+
const isVolatile = this.isNodeVolatile(node);
|
|
235
|
+
if (!isVolatile) {
|
|
236
|
+
this.memo.set(cacheKey, result);
|
|
237
|
+
}
|
|
238
|
+
return result;
|
|
239
|
+
}
|
|
240
|
+
|
|
410
241
|
let result;
|
|
411
242
|
|
|
412
243
|
switch (node.type) {
|
|
413
|
-
case 'user:check_blacklist': {
|
|
414
|
-
const userIdentifier = await this.resolvePinValue(node, 'user', null);
|
|
415
|
-
let isBlacklisted = false;
|
|
416
|
-
let usernameToFind = null;
|
|
417
|
-
|
|
418
|
-
if (typeof userIdentifier === 'string') {
|
|
419
|
-
usernameToFind = userIdentifier;
|
|
420
|
-
} else if (userIdentifier && typeof userIdentifier === 'object' && userIdentifier.username) {
|
|
421
|
-
usernameToFind = userIdentifier.username;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (usernameToFind) {
|
|
425
|
-
const user = await User.getUser(usernameToFind, this.context.botId);
|
|
426
|
-
if (user) {
|
|
427
|
-
isBlacklisted = user.isBlacklisted;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
result = isBlacklisted;
|
|
431
|
-
break;
|
|
432
|
-
}
|
|
433
|
-
case 'user:get_groups': {
|
|
434
|
-
const userIdentifier = await this.resolvePinValue(node, 'user', null);
|
|
435
|
-
let groups = [];
|
|
436
|
-
let usernameToFind = null;
|
|
437
|
-
|
|
438
|
-
if (typeof userIdentifier === 'string') {
|
|
439
|
-
usernameToFind = userIdentifier;
|
|
440
|
-
} else if (userIdentifier && typeof userIdentifier === 'object' && userIdentifier.username) {
|
|
441
|
-
usernameToFind = userIdentifier.username;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
if (usernameToFind) {
|
|
445
|
-
const user = await User.getUser(usernameToFind, this.context.botId);
|
|
446
|
-
if (user && user.groups) {
|
|
447
|
-
groups = user.groups.map(g => g.group.name);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
result = groups;
|
|
451
|
-
break;
|
|
452
|
-
}
|
|
453
|
-
case 'user:get_permissions': {
|
|
454
|
-
const userIdentifier = await this.resolvePinValue(node, 'user', null);
|
|
455
|
-
let permissions = [];
|
|
456
|
-
let usernameToFind = null;
|
|
457
|
-
|
|
458
|
-
if (typeof userIdentifier === 'string') {
|
|
459
|
-
usernameToFind = userIdentifier;
|
|
460
|
-
} else if (userIdentifier && typeof userIdentifier === 'object' && userIdentifier.username) {
|
|
461
|
-
usernameToFind = userIdentifier.username;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
if (usernameToFind) {
|
|
465
|
-
const user = await User.getUser(usernameToFind, this.context.botId);
|
|
466
|
-
if (user && user.permissionsSet) {
|
|
467
|
-
permissions = Array.from(user.permissionsSet);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
result = permissions;
|
|
471
|
-
break;
|
|
472
|
-
}
|
|
473
|
-
case 'bot:get_position': {
|
|
474
|
-
if (this.context.bot?.entity?.position) {
|
|
475
|
-
result = this.context.bot.entity.position;
|
|
476
|
-
} else {
|
|
477
|
-
result = null;
|
|
478
|
-
}
|
|
479
|
-
break;
|
|
480
|
-
}
|
|
481
244
|
case 'user:set_blacklist':
|
|
482
245
|
result = this.memo.get(`${node.id}:updated_user`);
|
|
483
246
|
break;
|
|
@@ -506,342 +269,10 @@ class GraphExecutionEngine {
|
|
|
506
269
|
case 'event:entityGone':
|
|
507
270
|
result = this.context[pinId];
|
|
508
271
|
break;
|
|
509
|
-
|
|
510
|
-
case '
|
|
511
|
-
|
|
512
|
-
if (!varName) {
|
|
513
|
-
console.warn('[GraphExecutionEngine] data:get_variable: не указано имя переменной', node.data);
|
|
514
|
-
result = null;
|
|
515
|
-
} else {
|
|
516
|
-
result = this.context.variables.hasOwnProperty(varName) ? this.context.variables[varName] : null;
|
|
517
|
-
}
|
|
518
|
-
break;
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
case 'data:get_argument': {
|
|
523
|
-
const args = this.context.args || {};
|
|
524
|
-
const argName = node.data?.argumentName || '';
|
|
525
|
-
if (pinId === 'value') {
|
|
526
|
-
result = args && argName && args[argName] !== undefined ? args[argName] : defaultValue;
|
|
527
|
-
} else if (pinId === 'exists') {
|
|
528
|
-
result = args && argName && args[argName] !== undefined;
|
|
529
|
-
}
|
|
530
|
-
break;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
case 'data:length': {
|
|
534
|
-
const data = await this.resolvePinValue(node, 'data');
|
|
535
|
-
if (Array.isArray(data) || typeof data === 'string') {
|
|
536
|
-
result = data.length;
|
|
537
|
-
} else {
|
|
538
|
-
result = 0;
|
|
539
|
-
}
|
|
540
|
-
break;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
case 'math:operation': {
|
|
544
|
-
const op = node.data?.operation || '+';
|
|
545
|
-
const a = Number(await this.resolvePinValue(node, 'a', 0));
|
|
546
|
-
const b = Number(await this.resolvePinValue(node, 'b', 0));
|
|
547
|
-
switch (op) {
|
|
548
|
-
case '+': result = a + b; break;
|
|
549
|
-
case '-': result = a - b; break;
|
|
550
|
-
case '*': result = a * b; break;
|
|
551
|
-
case '/': result = b !== 0 ? a / b : 0; break;
|
|
552
|
-
default: result = 0;
|
|
553
|
-
}
|
|
554
|
-
break;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
case 'math:random_number': {
|
|
558
|
-
const minRaw = await this.resolvePinValue(node, 'min', node.data.min ?? '0');
|
|
559
|
-
const maxRaw = await this.resolvePinValue(node, 'max', node.data.max ?? '1');
|
|
560
|
-
|
|
561
|
-
const minStr = String(minRaw);
|
|
562
|
-
const maxStr = String(maxRaw);
|
|
563
|
-
|
|
564
|
-
const min = parseFloat(minStr.replace(',', '.'));
|
|
565
|
-
const max = parseFloat(maxStr.replace(',', '.'));
|
|
566
|
-
|
|
567
|
-
if (isNaN(min) || isNaN(max)) {
|
|
568
|
-
result = NaN;
|
|
569
|
-
break;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
const produceFloat = minStr.includes('.') || minStr.includes(',') || !Number.isInteger(min) ||
|
|
573
|
-
maxStr.includes('.') || maxStr.includes(',') || !Number.isInteger(max);
|
|
574
|
-
|
|
575
|
-
if (produceFloat) {
|
|
576
|
-
result = Math.random() * (max - min) + min;
|
|
577
|
-
} else {
|
|
578
|
-
const minInt = Math.ceil(min);
|
|
579
|
-
const maxInt = Math.floor(max);
|
|
580
|
-
result = Math.floor(Math.random() * (maxInt - minInt + 1)) + minInt;
|
|
581
|
-
}
|
|
582
|
-
break;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
case 'logic:operation': {
|
|
586
|
-
const op = node.data?.operation || 'AND';
|
|
587
|
-
const inputs = [];
|
|
588
|
-
const pinCount = node.data?.pinCount || 2;
|
|
589
|
-
|
|
590
|
-
for (let i = 0; i < pinCount; i++) {
|
|
591
|
-
const value = await this.resolvePinValue(node, `pin_${i}`, false);
|
|
592
|
-
inputs.push(value);
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
switch (op) {
|
|
596
|
-
case 'AND': result = inputs.every(Boolean); break;
|
|
597
|
-
case 'OR': result = inputs.some(Boolean); break;
|
|
598
|
-
case 'NOT': result = !inputs[0]; break;
|
|
599
|
-
default: result = false;
|
|
600
|
-
}
|
|
601
|
-
break;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
case 'string:concat': {
|
|
605
|
-
const pinCount = node.data?.pinCount || 2;
|
|
606
|
-
let finalString = '';
|
|
607
|
-
for (let i = 0; i < pinCount; i++) {
|
|
608
|
-
const part = await this.resolvePinValue(node, `pin_${i}`, '');
|
|
609
|
-
finalString += String(part ?? '');
|
|
610
|
-
}
|
|
611
|
-
result = finalString;
|
|
612
|
-
break;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
case 'data:array_literal': {
|
|
616
|
-
const numPins = node.data?.pinCount || 0;
|
|
617
|
-
const items = [];
|
|
618
|
-
for (let i = 0; i < numPins; i++) {
|
|
619
|
-
const value = await this.resolvePinValue(node, `pin_${i}`) ||
|
|
620
|
-
node.data?.[`item_${i}`] ||
|
|
621
|
-
node.data?.[`value_${i}`];
|
|
622
|
-
items.push(value);
|
|
623
|
-
}
|
|
624
|
-
result = items;
|
|
625
|
-
break;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
case 'data:make_object': {
|
|
629
|
-
const numPins = node.data?.pinCount || 0;
|
|
630
|
-
const obj = {};
|
|
631
|
-
for (let i = 0; i < numPins; i++) {
|
|
632
|
-
const key = node.data[`key_${i}`];
|
|
633
|
-
if (key) {
|
|
634
|
-
obj[key] = await this.resolvePinValue(node, `value_${i}`);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
result = obj;
|
|
638
|
-
break;
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
case 'data:get_entity_field': {
|
|
642
|
-
const entity = await this.resolvePinValue(node, 'entity');
|
|
643
|
-
result = entity ? entity[pinId] : defaultValue;
|
|
644
|
-
break;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
case 'data:cast': {
|
|
648
|
-
const value = await this.resolvePinValue(node, 'value');
|
|
649
|
-
const targetType = node.data?.targetType || 'String';
|
|
650
|
-
switch (targetType) {
|
|
651
|
-
case 'String': result = String(value ?? ''); break;
|
|
652
|
-
case 'Number': result = Number(value); if (isNaN(result)) result = 0; break;
|
|
653
|
-
case 'Boolean': result = ['true', '1', 'yes'].includes(String(value).toLowerCase()); break;
|
|
654
|
-
default: result = value;
|
|
655
|
-
}
|
|
656
|
-
break;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
case 'string:contains': {
|
|
660
|
-
if (pinId === 'result') {
|
|
661
|
-
const haystack = String(await this.resolvePinValue(node, 'haystack', ''));
|
|
662
|
-
const needle = String(await this.resolvePinValue(node, 'needle', ''));
|
|
663
|
-
const caseSensitive = await this.resolvePinValue(node, 'case_sensitive', false);
|
|
664
|
-
|
|
665
|
-
if (caseSensitive) {
|
|
666
|
-
return haystack.includes(needle);
|
|
667
|
-
} else {
|
|
668
|
-
return haystack.toLowerCase().includes(needle.toLowerCase());
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
break;
|
|
672
|
-
}
|
|
673
|
-
case 'string:matches': {
|
|
674
|
-
if (pinId === 'result') {
|
|
675
|
-
const str = String(await this.resolvePinValue(node, 'string', ''));
|
|
676
|
-
const regexStr = String(await this.resolvePinValue(node, 'regex', ''));
|
|
677
|
-
try {
|
|
678
|
-
result = new RegExp(regexStr).test(str);
|
|
679
|
-
} catch (e) { result = false; }
|
|
680
|
-
}
|
|
681
|
-
break;
|
|
682
|
-
}
|
|
683
|
-
case 'data:string_literal':
|
|
684
|
-
result = await this.resolvePinValue(node, 'value', '');
|
|
685
|
-
break;
|
|
686
|
-
|
|
687
|
-
case 'data:get_user_field': {
|
|
688
|
-
const userIdentifier = await this.resolvePinValue(node, 'user');
|
|
689
|
-
let userObject = null;
|
|
690
|
-
|
|
691
|
-
if (userIdentifier && typeof userIdentifier === 'object' && userIdentifier.username) {
|
|
692
|
-
userObject = userIdentifier;
|
|
693
|
-
} else if (typeof userIdentifier === 'string' && userIdentifier.length > 0) {
|
|
694
|
-
userObject = await User.getUser(userIdentifier, this.context.botId);
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
if (userObject) {
|
|
698
|
-
if (pinId === 'username') {
|
|
699
|
-
result = userObject.username;
|
|
700
|
-
} else if (pinId === 'groups') {
|
|
701
|
-
result = userObject.groups ? userObject.groups.map(g => g.group?.name).filter(Boolean) : [];
|
|
702
|
-
} else if (pinId === 'permissions') {
|
|
703
|
-
result = userObject.permissionsSet ? Array.from(userObject.permissionsSet) : [];
|
|
704
|
-
} else if (pinId === 'isBlacklisted') {
|
|
705
|
-
result = !!userObject.isBlacklisted;
|
|
706
|
-
} else {
|
|
707
|
-
result = defaultValue;
|
|
708
|
-
}
|
|
709
|
-
} else {
|
|
710
|
-
result = defaultValue;
|
|
711
|
-
}
|
|
712
|
-
break;
|
|
713
|
-
}
|
|
714
|
-
case 'data:get_server_players':
|
|
715
|
-
result = this.context.players || [];
|
|
716
|
-
break;
|
|
717
|
-
case 'data:get_bot_look':
|
|
718
|
-
result = this.context.botState ? { yaw: this.context.botState.yaw, pitch: this.context.botState.pitch } : null;
|
|
719
|
-
break;
|
|
720
|
-
|
|
721
|
-
case 'array:get_random_element': {
|
|
722
|
-
const arr = await this.resolvePinValue(node, 'array', []);
|
|
723
|
-
if (!Array.isArray(arr) || arr.length === 0) {
|
|
724
|
-
result = null;
|
|
725
|
-
} else {
|
|
726
|
-
const randomIndex = Math.floor(Math.random() * arr.length);
|
|
727
|
-
this.memo.set(`${node.id}:index`, randomIndex);
|
|
728
|
-
if (pinId === 'element') {
|
|
729
|
-
result = arr[randomIndex];
|
|
730
|
-
} else if (pinId === 'index') {
|
|
731
|
-
result = randomIndex;
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
break;
|
|
735
|
-
}
|
|
736
|
-
case 'array:add_element': {
|
|
737
|
-
const arr = await this.resolvePinValue(node, 'array', []);
|
|
738
|
-
const element = await this.resolvePinValue(node, 'element', null);
|
|
739
|
-
result = Array.isArray(arr) ? [...arr, element] : [element];
|
|
740
|
-
break;
|
|
741
|
-
}
|
|
742
|
-
case 'array:remove_by_index': {
|
|
743
|
-
const arr = await this.resolvePinValue(node, 'array', []);
|
|
744
|
-
const index = await this.resolvePinValue(node, 'index', -1);
|
|
745
|
-
if (!Array.isArray(arr) || index < 0 || index >= arr.length) {
|
|
746
|
-
result = arr || [];
|
|
747
|
-
} else {
|
|
748
|
-
const newArr = [...arr];
|
|
749
|
-
newArr.splice(index, 1);
|
|
750
|
-
result = newArr;
|
|
751
|
-
}
|
|
752
|
-
break;
|
|
753
|
-
}
|
|
754
|
-
case 'array:get_by_index': {
|
|
755
|
-
const arr = await this.resolvePinValue(node, 'array', []);
|
|
756
|
-
const index = await this.resolvePinValue(node, 'index', -1);
|
|
757
|
-
result = (!Array.isArray(arr) || index < 0 || index >= arr.length) ? null : arr[index];
|
|
758
|
-
break;
|
|
759
|
-
}
|
|
760
|
-
case 'array:find_index': {
|
|
761
|
-
const arr = await this.resolvePinValue(node, 'array', []);
|
|
762
|
-
const element = await this.resolvePinValue(node, 'element', null);
|
|
763
|
-
result = Array.isArray(arr) ? arr.indexOf(element) : -1;
|
|
764
|
-
break;
|
|
765
|
-
}
|
|
766
|
-
case 'array:contains': {
|
|
767
|
-
const arr = await this.resolvePinValue(node, 'array', []);
|
|
768
|
-
const element = await this.resolvePinValue(node, 'element', null);
|
|
769
|
-
if (Array.isArray(arr)) {
|
|
770
|
-
const index = arr.indexOf(element);
|
|
771
|
-
this.memo.set(`${node.id}:index`, index);
|
|
772
|
-
if (pinId === 'result') {
|
|
773
|
-
result = index !== -1;
|
|
774
|
-
} else if (pinId === 'index') {
|
|
775
|
-
result = index;
|
|
776
|
-
}
|
|
777
|
-
} else {
|
|
778
|
-
this.memo.set(`${node.id}:index`, -1);
|
|
779
|
-
if (pinId === 'result') {
|
|
780
|
-
result = false;
|
|
781
|
-
} else if (pinId === 'index') {
|
|
782
|
-
result = -1;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
break;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
case 'object:create': {
|
|
790
|
-
if (node.data?.advanced) {
|
|
791
|
-
try {
|
|
792
|
-
result = JSON.parse(node.data.jsonValue || '{}');
|
|
793
|
-
} catch (e) {
|
|
794
|
-
console.error('Ошибка парсинга JSON в object:create:', e);
|
|
795
|
-
result = {};
|
|
796
|
-
}
|
|
797
|
-
} else {
|
|
798
|
-
const numPins = node.data?.pinCount || 0;
|
|
799
|
-
const obj = {};
|
|
800
|
-
for (let i = 0; i < numPins; i++) {
|
|
801
|
-
const key = node.data[`key_${i}`];
|
|
802
|
-
if (key) {
|
|
803
|
-
obj[key] = await this.resolvePinValue(node, `value_${i}`);
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
result = obj;
|
|
807
|
-
}
|
|
808
|
-
break;
|
|
809
|
-
}
|
|
810
|
-
case 'object:get': {
|
|
811
|
-
const obj = await this.resolvePinValue(node, 'object', {});
|
|
812
|
-
const key = await this.resolvePinValue(node, 'key', '');
|
|
813
|
-
result = obj[key] ?? defaultValue;
|
|
814
|
-
break;
|
|
815
|
-
}
|
|
816
|
-
case 'object:set': {
|
|
817
|
-
const obj = await this.resolvePinValue(node, 'object', {});
|
|
818
|
-
const key = await this.resolvePinValue(node, 'key', '');
|
|
819
|
-
const val = await this.resolvePinValue(node, 'value');
|
|
820
|
-
const newObj = { ...obj };
|
|
821
|
-
newObj[key] = val;
|
|
822
|
-
result = newObj;
|
|
823
|
-
break;
|
|
824
|
-
}
|
|
825
|
-
case 'object:delete': {
|
|
826
|
-
const obj = await this.resolvePinValue(node, 'object', {});
|
|
827
|
-
const key = await this.resolvePinValue(node, 'key', '');
|
|
828
|
-
const newObj = { ...obj };
|
|
829
|
-
delete newObj[key];
|
|
830
|
-
result = newObj;
|
|
831
|
-
break;
|
|
832
|
-
}
|
|
833
|
-
case 'object:has_key': {
|
|
834
|
-
const obj = await this.resolvePinValue(node, 'object', {});
|
|
835
|
-
const key = await this.resolvePinValue(node, 'key', '');
|
|
836
|
-
const exists = obj.hasOwnProperty(key);
|
|
837
|
-
this.memo.set(`${node.id}:value`, exists ? obj[key] : null);
|
|
838
|
-
if (pinId === 'result') {
|
|
839
|
-
result = exists;
|
|
840
|
-
} else if (pinId === 'value') {
|
|
841
|
-
result = this.memo.get(`${node.id}:value`);
|
|
842
|
-
}
|
|
272
|
+
case 'event:health':
|
|
273
|
+
case 'event:botDied':
|
|
274
|
+
result = this.context[pinId];
|
|
843
275
|
break;
|
|
844
|
-
}
|
|
845
276
|
|
|
846
277
|
case 'flow:for_each': {
|
|
847
278
|
if (pinId === 'element') {
|
|
@@ -851,41 +282,7 @@ class GraphExecutionEngine {
|
|
|
851
282
|
}
|
|
852
283
|
break;
|
|
853
284
|
}
|
|
854
|
-
case 'flow:while': {
|
|
855
|
-
if (pinId === 'iteration') {
|
|
856
|
-
result = this.memo.get(`${node.id}:iteration`);
|
|
857
|
-
}
|
|
858
|
-
break;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
case 'string:equals': {
|
|
862
|
-
const strA = String(await this.resolvePinValue(node, 'a', ''));
|
|
863
|
-
const strB = String(await this.resolvePinValue(node, 'b', ''));
|
|
864
|
-
const caseSensitive = node.data?.case_sensitive ?? true;
|
|
865
|
-
if (pinId === 'result') {
|
|
866
|
-
result = caseSensitive ? strA === strB : strA.toLowerCase() === strB.toLowerCase();
|
|
867
|
-
}
|
|
868
|
-
break;
|
|
869
|
-
}
|
|
870
285
|
|
|
871
|
-
case 'logic:compare': {
|
|
872
|
-
const op = node.data?.operation || '==';
|
|
873
|
-
const valA = await this.resolvePinValue(node, 'a');
|
|
874
|
-
const valB = await this.resolvePinValue(node, 'b');
|
|
875
|
-
if (pinId === 'result') {
|
|
876
|
-
switch (op) {
|
|
877
|
-
case '==': result = valA == valB; break;
|
|
878
|
-
case '!=': result = valA != valB; break;
|
|
879
|
-
case '>': result = valA > valB; break;
|
|
880
|
-
case '<': result = valA < valB; break;
|
|
881
|
-
case '>=': result = valA >= valB; break;
|
|
882
|
-
case '<=': result = valA <= valB; break;
|
|
883
|
-
default: result = false;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
break;
|
|
887
|
-
}
|
|
888
|
-
|
|
889
286
|
default:
|
|
890
287
|
result = defaultValue;
|
|
891
288
|
break;
|