blockmine 1.23.0 → 1.23.2
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/agents/code-architect.md +34 -0
- package/.claude/agents/code-explorer.md +51 -0
- package/.claude/agents/code-reviewer.md +46 -0
- package/.claude/commands/feature-dev.md +125 -0
- package/.claude/settings.json +1 -1
- package/.claude/settings.local.json +3 -1
- package/.claude/skills/frontend-design/SKILL.md +42 -0
- package/CHANGELOG.md +31 -16
- package/backend/prisma/migrations/20251116111851_add_execution_trace/migration.sql +22 -22
- package/backend/prisma/migrations/20251120154914_add_panel_api_keys/migration.sql +21 -21
- package/backend/prisma/migrations/20251121110241_add_proxy_table/migration.sql +45 -45
- package/backend/prisma/migrations/migration_lock.toml +2 -2
- package/backend/src/api/routes/auth.js +669 -669
- package/backend/src/api/routes/bots.js +2451 -2451
- package/backend/src/api/routes/panel.js +66 -66
- package/backend/src/api/routes/panelApiKeys.js +179 -179
- package/backend/src/api/routes/plugins.js +376 -376
- package/backend/src/api/routes/system.js +174 -174
- package/backend/src/core/EventGraphManager.js +194 -194
- package/backend/src/core/GraphExecutionEngine.js +28 -1
- package/backend/src/core/node-registries/actions.js +2 -2
- package/backend/src/core/nodes/actions/http_request.js +23 -4
- package/backend/src/core/nodes/actions/send_message.js +2 -12
- package/backend/src/core/nodes/data/string_literal.js +2 -13
- package/backend/src/core/services/BotLifecycleService.js +835 -835
- package/frontend/dist/assets/{index-B1serztM.js → index-DqzDkFsP.js} +185 -185
- package/frontend/dist/index.html +1 -1
- package/package.json +2 -1
- package/CLAUDE.md +0 -284
|
@@ -1,195 +1,195 @@
|
|
|
1
|
-
const prismaService = require('./PrismaService');
|
|
2
|
-
const { safeJsonParse } = require('./utils/jsonParser');
|
|
3
|
-
const { parseVariables } = require('./utils/variableParser');
|
|
4
|
-
const validationService = require('./services/ValidationService');
|
|
5
|
-
const botHistoryStore = require('./BotHistoryStore');
|
|
6
|
-
|
|
7
|
-
const prisma = prismaService.getClient();
|
|
8
|
-
|
|
9
|
-
class EventGraphManager {
|
|
10
|
-
constructor(botManager = null) {
|
|
11
|
-
this.botManager = botManager;
|
|
12
|
-
this.activeGraphs = new Map();
|
|
13
|
-
this.graphStates = new Map();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
setBotManager(botManager) {
|
|
17
|
-
this.botManager = botManager;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async loadGraphsForBot(botId) {
|
|
21
|
-
console.log(`[EventGraphs] Загрузка графов для бота ${botId}...`);
|
|
22
|
-
const botGraphs = await prisma.eventGraph.findMany({
|
|
23
|
-
where: { botId, isEnabled: true },
|
|
24
|
-
include: { triggers: true },
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const graphsByEvent = new Map();
|
|
28
|
-
for (const graph of botGraphs) {
|
|
29
|
-
if (!graph.triggers || graph.triggers.length === 0 || !graph.graphJson) continue;
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
const parsedGraph = validationService.parseGraph(graph.graphJson, `EventGraph ID ${graph.id}`);
|
|
33
|
-
if (!validationService.hasValidBasicStructure(parsedGraph)) continue;
|
|
34
|
-
|
|
35
|
-
const initialState = {};
|
|
36
|
-
if (graph.variables) {
|
|
37
|
-
const parsedVars = safeJsonParse(graph.variables, [], `EventGraph ID ${graph.id} variables`);
|
|
38
|
-
Object.assign(initialState, parseVariables(parsedVars, `EventGraph ID ${graph.id}`));
|
|
39
|
-
}
|
|
40
|
-
this.graphStates.set(`${botId}-${graph.id}`, initialState);
|
|
41
|
-
|
|
42
|
-
for (const trigger of graph.triggers) {
|
|
43
|
-
if (!graphsByEvent.has(trigger.eventType)) {
|
|
44
|
-
graphsByEvent.set(trigger.eventType, []);
|
|
45
|
-
}
|
|
46
|
-
graphsByEvent.get(trigger.eventType).push({
|
|
47
|
-
id: graph.id,
|
|
48
|
-
name: graph.name,
|
|
49
|
-
nodes: parsedGraph.nodes,
|
|
50
|
-
connections: parsedGraph.connections,
|
|
51
|
-
variables: parsedGraph.variables || [],
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
} catch (e) {
|
|
55
|
-
console.error(`[EventGraphs] Ошибка парсинга JSON для графа ID ${graph.id}:`, e);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
this.activeGraphs.set(botId, graphsByEvent);
|
|
60
|
-
console.log(`[EventGraphs] Загружено ${botGraphs.length} графов, сгруппировано по ${graphsByEvent.size} событиям для бота ${botId}.`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
unloadGraphsForBot(botId) {
|
|
64
|
-
this.activeGraphs.delete(botId);
|
|
65
|
-
for (const key of this.graphStates.keys()) {
|
|
66
|
-
if (key.startsWith(`${botId}-`)) {
|
|
67
|
-
this.graphStates.delete(key);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
console.log(`[EventGraphs] Графы и их состояния для бота ${botId} выгружены.`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async handleEvent(botId, eventType, args) {
|
|
74
|
-
this.broadcastEventToApi(botId, eventType, args);
|
|
75
|
-
|
|
76
|
-
const graphsForBot = this.activeGraphs.get(botId);
|
|
77
|
-
if (!graphsForBot) return;
|
|
78
|
-
|
|
79
|
-
const graphsToRun = graphsForBot.get(eventType);
|
|
80
|
-
if (!graphsToRun || graphsToRun.length === 0) return;
|
|
81
|
-
|
|
82
|
-
for (const graph of graphsToRun) {
|
|
83
|
-
try {
|
|
84
|
-
await this.executeGraphInChildProcess(botId, eventType, graph, args);
|
|
85
|
-
} catch (error) {
|
|
86
|
-
console.error(`[EventGraphManager] Error sending event to child process for '${eventType}':`, error);
|
|
87
|
-
this.botManager.appendLog(botId, `[ERROR] Error in event graph: ${error.message}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Отправляет граф в child process для выполнения
|
|
94
|
-
*/
|
|
95
|
-
async executeGraphInChildProcess(botId, eventType, graph, eventArgs) {
|
|
96
|
-
if (!graph || !graph.nodes || graph.nodes.length === 0) return;
|
|
97
|
-
|
|
98
|
-
const childProcess = this.botManager.getChildProcess(botId);
|
|
99
|
-
if (!childProcess || !childProcess.send) {
|
|
100
|
-
console.error(`[EventGraphManager] No child process found for bot ${botId}`);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
childProcess.send({
|
|
105
|
-
type: 'execute_event_graph',
|
|
106
|
-
botId: botId,
|
|
107
|
-
graph: graph,
|
|
108
|
-
eventType: eventType,
|
|
109
|
-
eventArgs: eventArgs
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Отправляет события в WebSocket API
|
|
115
|
-
*/
|
|
116
|
-
broadcastEventToApi(botId, eventType, args) {
|
|
117
|
-
try {
|
|
118
|
-
// Динамический импорт для избежания циклической зависимости
|
|
119
|
-
const { getIOSafe } = require('../real-time/socketHandler');
|
|
120
|
-
const { broadcastToApiClients } = require('../real-time/botApi');
|
|
121
|
-
|
|
122
|
-
const io = getIOSafe();
|
|
123
|
-
if (!io) return;
|
|
124
|
-
|
|
125
|
-
switch (eventType) {
|
|
126
|
-
case 'chat':
|
|
127
|
-
case 'private':
|
|
128
|
-
case 'global':
|
|
129
|
-
case 'clan':
|
|
130
|
-
const chatData = {
|
|
131
|
-
type: eventType,
|
|
132
|
-
username: args.username,
|
|
133
|
-
message: args.message,
|
|
134
|
-
raw_message: args.rawText || args.raw_message,
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
broadcastToApiClients(io, botId, 'chat:message', chatData);
|
|
138
|
-
|
|
139
|
-
botHistoryStore.addChatMessage(botId, {
|
|
140
|
-
type: eventType,
|
|
141
|
-
username: args.username,
|
|
142
|
-
message: args.message
|
|
143
|
-
});
|
|
144
|
-
break;
|
|
145
|
-
|
|
146
|
-
case 'playerJoined':
|
|
147
|
-
broadcastToApiClients(io, botId, 'player:join', {
|
|
148
|
-
username: args.user?.username || args.username,
|
|
149
|
-
});
|
|
150
|
-
break;
|
|
151
|
-
|
|
152
|
-
case 'playerLeft':
|
|
153
|
-
broadcastToApiClients(io, botId, 'player:leave', {
|
|
154
|
-
username: args.user?.username || args.username,
|
|
155
|
-
});
|
|
156
|
-
break;
|
|
157
|
-
|
|
158
|
-
case 'health':
|
|
159
|
-
broadcastToApiClients(io, botId, 'bot:health', {
|
|
160
|
-
health: args.health,
|
|
161
|
-
food: args.food,
|
|
162
|
-
});
|
|
163
|
-
break;
|
|
164
|
-
|
|
165
|
-
case 'death':
|
|
166
|
-
broadcastToApiClients(io, botId, 'bot:death', {});
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
} catch (error) {
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Отправляет кастомное событие от плагина в WebSocket API
|
|
175
|
-
*/
|
|
176
|
-
emitCustomApiEvent(botId, eventName, payload = {}) {
|
|
177
|
-
try {
|
|
178
|
-
const { getIOSafe } = require('../real-time/socketHandler');
|
|
179
|
-
const { broadcastToApiClients } = require('../real-time/botApi');
|
|
180
|
-
|
|
181
|
-
const io = getIOSafe();
|
|
182
|
-
if (!io) return;
|
|
183
|
-
|
|
184
|
-
broadcastToApiClients(io, botId, 'plugin:custom_event', {
|
|
185
|
-
eventName,
|
|
186
|
-
payload,
|
|
187
|
-
timestamp: new Date().toISOString(),
|
|
188
|
-
});
|
|
189
|
-
} catch (error) {
|
|
190
|
-
// Игнорируем другие ошибки
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
1
|
+
const prismaService = require('./PrismaService');
|
|
2
|
+
const { safeJsonParse } = require('./utils/jsonParser');
|
|
3
|
+
const { parseVariables } = require('./utils/variableParser');
|
|
4
|
+
const validationService = require('./services/ValidationService');
|
|
5
|
+
const botHistoryStore = require('./BotHistoryStore');
|
|
6
|
+
|
|
7
|
+
const prisma = prismaService.getClient();
|
|
8
|
+
|
|
9
|
+
class EventGraphManager {
|
|
10
|
+
constructor(botManager = null) {
|
|
11
|
+
this.botManager = botManager;
|
|
12
|
+
this.activeGraphs = new Map();
|
|
13
|
+
this.graphStates = new Map();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
setBotManager(botManager) {
|
|
17
|
+
this.botManager = botManager;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async loadGraphsForBot(botId) {
|
|
21
|
+
console.log(`[EventGraphs] Загрузка графов для бота ${botId}...`);
|
|
22
|
+
const botGraphs = await prisma.eventGraph.findMany({
|
|
23
|
+
where: { botId, isEnabled: true },
|
|
24
|
+
include: { triggers: true },
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const graphsByEvent = new Map();
|
|
28
|
+
for (const graph of botGraphs) {
|
|
29
|
+
if (!graph.triggers || graph.triggers.length === 0 || !graph.graphJson) continue;
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const parsedGraph = validationService.parseGraph(graph.graphJson, `EventGraph ID ${graph.id}`);
|
|
33
|
+
if (!validationService.hasValidBasicStructure(parsedGraph)) continue;
|
|
34
|
+
|
|
35
|
+
const initialState = {};
|
|
36
|
+
if (graph.variables) {
|
|
37
|
+
const parsedVars = safeJsonParse(graph.variables, [], `EventGraph ID ${graph.id} variables`);
|
|
38
|
+
Object.assign(initialState, parseVariables(parsedVars, `EventGraph ID ${graph.id}`));
|
|
39
|
+
}
|
|
40
|
+
this.graphStates.set(`${botId}-${graph.id}`, initialState);
|
|
41
|
+
|
|
42
|
+
for (const trigger of graph.triggers) {
|
|
43
|
+
if (!graphsByEvent.has(trigger.eventType)) {
|
|
44
|
+
graphsByEvent.set(trigger.eventType, []);
|
|
45
|
+
}
|
|
46
|
+
graphsByEvent.get(trigger.eventType).push({
|
|
47
|
+
id: graph.id,
|
|
48
|
+
name: graph.name,
|
|
49
|
+
nodes: parsedGraph.nodes,
|
|
50
|
+
connections: parsedGraph.connections,
|
|
51
|
+
variables: parsedGraph.variables || [],
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.error(`[EventGraphs] Ошибка парсинга JSON для графа ID ${graph.id}:`, e);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this.activeGraphs.set(botId, graphsByEvent);
|
|
60
|
+
console.log(`[EventGraphs] Загружено ${botGraphs.length} графов, сгруппировано по ${graphsByEvent.size} событиям для бота ${botId}.`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
unloadGraphsForBot(botId) {
|
|
64
|
+
this.activeGraphs.delete(botId);
|
|
65
|
+
for (const key of this.graphStates.keys()) {
|
|
66
|
+
if (key.startsWith(`${botId}-`)) {
|
|
67
|
+
this.graphStates.delete(key);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
console.log(`[EventGraphs] Графы и их состояния для бота ${botId} выгружены.`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async handleEvent(botId, eventType, args) {
|
|
74
|
+
this.broadcastEventToApi(botId, eventType, args);
|
|
75
|
+
|
|
76
|
+
const graphsForBot = this.activeGraphs.get(botId);
|
|
77
|
+
if (!graphsForBot) return;
|
|
78
|
+
|
|
79
|
+
const graphsToRun = graphsForBot.get(eventType);
|
|
80
|
+
if (!graphsToRun || graphsToRun.length === 0) return;
|
|
81
|
+
|
|
82
|
+
for (const graph of graphsToRun) {
|
|
83
|
+
try {
|
|
84
|
+
await this.executeGraphInChildProcess(botId, eventType, graph, args);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error(`[EventGraphManager] Error sending event to child process for '${eventType}':`, error);
|
|
87
|
+
this.botManager.appendLog(botId, `[ERROR] Error in event graph: ${error.message}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Отправляет граф в child process для выполнения
|
|
94
|
+
*/
|
|
95
|
+
async executeGraphInChildProcess(botId, eventType, graph, eventArgs) {
|
|
96
|
+
if (!graph || !graph.nodes || graph.nodes.length === 0) return;
|
|
97
|
+
|
|
98
|
+
const childProcess = this.botManager.getChildProcess(botId);
|
|
99
|
+
if (!childProcess || !childProcess.send) {
|
|
100
|
+
console.error(`[EventGraphManager] No child process found for bot ${botId}`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
childProcess.send({
|
|
105
|
+
type: 'execute_event_graph',
|
|
106
|
+
botId: botId,
|
|
107
|
+
graph: graph,
|
|
108
|
+
eventType: eventType,
|
|
109
|
+
eventArgs: eventArgs
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Отправляет события в WebSocket API
|
|
115
|
+
*/
|
|
116
|
+
broadcastEventToApi(botId, eventType, args) {
|
|
117
|
+
try {
|
|
118
|
+
// Динамический импорт для избежания циклической зависимости
|
|
119
|
+
const { getIOSafe } = require('../real-time/socketHandler');
|
|
120
|
+
const { broadcastToApiClients } = require('../real-time/botApi');
|
|
121
|
+
|
|
122
|
+
const io = getIOSafe();
|
|
123
|
+
if (!io) return;
|
|
124
|
+
|
|
125
|
+
switch (eventType) {
|
|
126
|
+
case 'chat':
|
|
127
|
+
case 'private':
|
|
128
|
+
case 'global':
|
|
129
|
+
case 'clan':
|
|
130
|
+
const chatData = {
|
|
131
|
+
type: eventType,
|
|
132
|
+
username: args.username,
|
|
133
|
+
message: args.message,
|
|
134
|
+
raw_message: args.rawText || args.raw_message,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
broadcastToApiClients(io, botId, 'chat:message', chatData);
|
|
138
|
+
|
|
139
|
+
botHistoryStore.addChatMessage(botId, {
|
|
140
|
+
type: eventType,
|
|
141
|
+
username: args.username,
|
|
142
|
+
message: args.message
|
|
143
|
+
});
|
|
144
|
+
break;
|
|
145
|
+
|
|
146
|
+
case 'playerJoined':
|
|
147
|
+
broadcastToApiClients(io, botId, 'player:join', {
|
|
148
|
+
username: args.user?.username || args.username,
|
|
149
|
+
});
|
|
150
|
+
break;
|
|
151
|
+
|
|
152
|
+
case 'playerLeft':
|
|
153
|
+
broadcastToApiClients(io, botId, 'player:leave', {
|
|
154
|
+
username: args.user?.username || args.username,
|
|
155
|
+
});
|
|
156
|
+
break;
|
|
157
|
+
|
|
158
|
+
case 'health':
|
|
159
|
+
broadcastToApiClients(io, botId, 'bot:health', {
|
|
160
|
+
health: args.health,
|
|
161
|
+
food: args.food,
|
|
162
|
+
});
|
|
163
|
+
break;
|
|
164
|
+
|
|
165
|
+
case 'death':
|
|
166
|
+
broadcastToApiClients(io, botId, 'bot:death', {});
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
} catch (error) {
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Отправляет кастомное событие от плагина в WebSocket API
|
|
175
|
+
*/
|
|
176
|
+
emitCustomApiEvent(botId, eventName, payload = {}) {
|
|
177
|
+
try {
|
|
178
|
+
const { getIOSafe } = require('../real-time/socketHandler');
|
|
179
|
+
const { broadcastToApiClients } = require('../real-time/botApi');
|
|
180
|
+
|
|
181
|
+
const io = getIOSafe();
|
|
182
|
+
if (!io) return;
|
|
183
|
+
|
|
184
|
+
broadcastToApiClients(io, botId, 'plugin:custom_event', {
|
|
185
|
+
eventName,
|
|
186
|
+
payload,
|
|
187
|
+
timestamp: new Date().toISOString(),
|
|
188
|
+
});
|
|
189
|
+
} catch (error) {
|
|
190
|
+
// Игнорируем другие ошибки
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
195
|
module.exports = EventGraphManager;
|
|
@@ -462,7 +462,34 @@ class GraphExecutionEngine {
|
|
|
462
462
|
const sourceNode = this.activeGraph.nodes.find(n => n.id === connection.sourceNodeId);
|
|
463
463
|
return await this.evaluateOutputPin(sourceNode, connection.sourcePinId, defaultValue);
|
|
464
464
|
}
|
|
465
|
-
|
|
465
|
+
|
|
466
|
+
let value = node.data && node.data[pinId] !== undefined ? node.data[pinId] : defaultValue;
|
|
467
|
+
|
|
468
|
+
// Автоматически заменяем переменные {varName} в строковых значениях
|
|
469
|
+
if (typeof value === 'string' && value.includes('{')) {
|
|
470
|
+
value = await this._replaceVariablesInString(value, node);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return value;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Заменяет переменные {varName} на значения из пинов
|
|
478
|
+
* @private
|
|
479
|
+
*/
|
|
480
|
+
async _replaceVariablesInString(text, node) {
|
|
481
|
+
const variablePattern = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
482
|
+
const matches = [...text.matchAll(variablePattern)];
|
|
483
|
+
|
|
484
|
+
let result = text;
|
|
485
|
+
|
|
486
|
+
for (const match of matches) {
|
|
487
|
+
const varName = match[1];
|
|
488
|
+
const varValue = await this.resolvePinValue(node, `var_${varName}`, '');
|
|
489
|
+
result = result.replace(match[0], String(varValue));
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return result;
|
|
466
493
|
}
|
|
467
494
|
|
|
468
495
|
async evaluateOutputPin(node, pinId, defaultValue = null) {
|
|
@@ -10,7 +10,6 @@ function registerNodes(registry) {
|
|
|
10
10
|
category: 'Действия',
|
|
11
11
|
description: 'Отправляет сообщение в чат. Поддерживает переменные в формате {varName}',
|
|
12
12
|
graphType: GRAPH_TYPES.ALL,
|
|
13
|
-
dynamicPins: true,
|
|
14
13
|
executor: require('../nodes/actions/send_message').execute,
|
|
15
14
|
pins: {
|
|
16
15
|
inputs: [
|
|
@@ -112,7 +111,8 @@ function registerNodes(registry) {
|
|
|
112
111
|
{ id: 'exec', name: 'Выполнить', type: 'Exec', required: true },
|
|
113
112
|
{ id: 'url', name: 'URL', type: 'String', required: true },
|
|
114
113
|
{ id: 'method', name: 'Метод', type: 'String', required: false },
|
|
115
|
-
{ id: '
|
|
114
|
+
{ id: 'queryParams', name: 'Query Params', type: 'Object', required: false },
|
|
115
|
+
{ id: 'headers', name: 'Headers', type: 'Object', required: false },
|
|
116
116
|
{ id: 'body', name: 'Тело (JSON)', type: 'Wildcard', required: false },
|
|
117
117
|
{ id: 'timeout', name: 'Таймаут (мс)', type: 'Number', required: false }
|
|
118
118
|
],
|
|
@@ -9,16 +9,35 @@
|
|
|
9
9
|
async function execute(node, context, helpers) {
|
|
10
10
|
const { resolvePinValue, traverse, memo } = helpers;
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
let url = await resolvePinValue(node, 'url', '');
|
|
13
13
|
const method = await resolvePinValue(node, 'method', node.data?.method || 'GET');
|
|
14
|
-
const
|
|
14
|
+
const headersInput = await resolvePinValue(node, 'headers', null);
|
|
15
|
+
const queryParamsInput = await resolvePinValue(node, 'queryParams', null);
|
|
15
16
|
const body = await resolvePinValue(node, 'body', '');
|
|
16
17
|
const timeout = await resolvePinValue(node, 'timeout', 5000);
|
|
17
18
|
|
|
19
|
+
if (queryParamsInput) {
|
|
20
|
+
try {
|
|
21
|
+
const params = typeof queryParamsInput === 'string'
|
|
22
|
+
? JSON.parse(queryParamsInput)
|
|
23
|
+
: queryParamsInput;
|
|
24
|
+
|
|
25
|
+
const urlObj = new URL(url);
|
|
26
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
27
|
+
urlObj.searchParams.append(key, value);
|
|
28
|
+
});
|
|
29
|
+
url = urlObj.toString();
|
|
30
|
+
} catch (e) {
|
|
31
|
+
console.error('[HTTP Request] Ошибка обработки query params:', e);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
18
35
|
let headers = {};
|
|
19
|
-
if (
|
|
36
|
+
if (headersInput) {
|
|
20
37
|
try {
|
|
21
|
-
headers =
|
|
38
|
+
headers = typeof headersInput === 'string'
|
|
39
|
+
? JSON.parse(headersInput)
|
|
40
|
+
: headersInput;
|
|
22
41
|
} catch (e) {
|
|
23
42
|
console.error('[HTTP Request] Ошибка парсинга headers:', e);
|
|
24
43
|
headers = {};
|
|
@@ -8,21 +8,11 @@
|
|
|
8
8
|
async function execute(node, context, helpers) {
|
|
9
9
|
const { resolvePinValue, traverse } = helpers;
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// resolvePinValue теперь автоматически заменяет {varName} на значения
|
|
12
|
+
const message = String(await resolvePinValue(node, 'message', ''));
|
|
12
13
|
const chatType = await resolvePinValue(node, 'chat_type', context.typeChat);
|
|
13
14
|
const recipient = await resolvePinValue(node, 'recipient', context.user?.username);
|
|
14
15
|
|
|
15
|
-
// Парсим и заменяем переменные в формате {varName}
|
|
16
|
-
const variablePattern = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
17
|
-
const matches = [...message.matchAll(variablePattern)];
|
|
18
|
-
|
|
19
|
-
for (const match of matches) {
|
|
20
|
-
const varName = match[1];
|
|
21
|
-
// Для динамических пинов, созданных на фронтенде, значение нужно будет получить, используя resolvePinValue
|
|
22
|
-
const varValue = await resolvePinValue(node, `var_${varName}`, '');
|
|
23
|
-
message = message.replace(match[0], String(varValue));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
16
|
context.bot.sendMessage(chatType, message, recipient);
|
|
27
17
|
await traverse(node, 'exec');
|
|
28
18
|
}
|
|
@@ -10,19 +10,8 @@ async function evaluate(node, pinId, context, helpers) {
|
|
|
10
10
|
const { resolvePinValue } = helpers;
|
|
11
11
|
|
|
12
12
|
if (pinId === 'value') {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// Парсим и заменяем переменные в формате {varName}
|
|
16
|
-
const variablePattern = /\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
17
|
-
const matches = [...text.matchAll(variablePattern)];
|
|
18
|
-
|
|
19
|
-
for (const match of matches) {
|
|
20
|
-
const varName = match[1];
|
|
21
|
-
// Получаем значение из динамического пина
|
|
22
|
-
const varValue = await resolvePinValue(node, `var_${varName}`, '');
|
|
23
|
-
text = text.replace(match[0], String(varValue));
|
|
24
|
-
}
|
|
25
|
-
|
|
13
|
+
// resolvePinValue автоматически заменит переменные {varName}
|
|
14
|
+
const text = String(await resolvePinValue(node, 'value', ''));
|
|
26
15
|
return text;
|
|
27
16
|
}
|
|
28
17
|
|