piece-signal-cli-rest-api 0.2.13 → 0.2.15
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/package.json
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "piece-signal-cli-rest-api",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"build": "echo \"piece-signal-cli-rest-api: kein zusätzlicher Build-Schritt erforderlich (bereits als JS ausgeliefert)\"",
|
|
6
|
+
"publish:npm": "npm publish --access public"
|
|
7
|
+
},
|
|
4
8
|
"dependencies": {
|
|
5
9
|
"@sinclair/typebox": "0.34.11",
|
|
6
10
|
"ai": "5.0.104",
|
|
@@ -26,4 +30,4 @@
|
|
|
26
30
|
"types": "./src/index.d.ts",
|
|
27
31
|
"main": "./src/index.js",
|
|
28
32
|
"type": "commonjs"
|
|
29
|
-
}
|
|
33
|
+
}
|
|
@@ -28,11 +28,11 @@ exports.requestApprovalMessage = (0, pieces_framework_1.createAction)({
|
|
|
28
28
|
options: {
|
|
29
29
|
options: [
|
|
30
30
|
{ label: 'Emoji Reactions', value: 'emoji' },
|
|
31
|
-
{ label: 'Text Replies', value: 'text' },
|
|
32
|
-
{ label: '
|
|
31
|
+
{ label: 'Text Replies (Quote)', value: 'text' },
|
|
32
|
+
{ label: 'Direct Messages (1:1 only)', value: 'direct' }
|
|
33
33
|
]
|
|
34
34
|
},
|
|
35
|
-
defaultValue: ['
|
|
35
|
+
defaultValue: ['text']
|
|
36
36
|
}),
|
|
37
37
|
timeout_seconds: pieces_framework_1.Property.Number({
|
|
38
38
|
displayName: 'Timeout (seconds)',
|
|
@@ -44,7 +44,7 @@ exports.requestApprovalMessage = (0, pieces_framework_1.createAction)({
|
|
|
44
44
|
run(context) {
|
|
45
45
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
46
46
|
if (context.executionType === shared_1.ExecutionType.BEGIN) {
|
|
47
|
-
const { number, recipients, message, accept_reaction_modes = ['
|
|
47
|
+
const { number, recipients, message, accept_reaction_modes = ['text'], timeout_seconds = 86400 } = context.propsValue;
|
|
48
48
|
(0, shared_2.assertNotNullOrUndefined)(message, 'message');
|
|
49
49
|
(0, shared_2.assertNotNullOrUndefined)(recipients, 'recipients');
|
|
50
50
|
const apiClient = new api_client_1.SignalCliApiClient(context.auth.props);
|
|
@@ -83,7 +83,9 @@ exports.requestApprovalMessage = (0, pieces_framework_1.createAction)({
|
|
|
83
83
|
const targetAuthor = formattedNumber;
|
|
84
84
|
const createdAt = Math.floor(Date.now() / 1000); // Unix timestamp in seconds
|
|
85
85
|
// Create store key using milliseconds for precise matching
|
|
86
|
-
|
|
86
|
+
// Store-Key mit Timestamp + eindeutiger Flow-Run-ID
|
|
87
|
+
// flowRunId ist pro Flow-Ausführung eindeutig, verhindert Kollisionen
|
|
88
|
+
const storeKey = `approval:${messageTimestampMs}:${context.run.id}`;
|
|
87
89
|
// DEBUG: Log storing approval mapping
|
|
88
90
|
console.log('[RequestApprovalMessage] DEBUG - Storing approval mapping:', {
|
|
89
91
|
storeKey,
|
|
@@ -105,6 +107,7 @@ exports.requestApprovalMessage = (0, pieces_framework_1.createAction)({
|
|
|
105
107
|
messageTimestampMs: messageTimestampMs,
|
|
106
108
|
messageTimestamp: messageTimestamp,
|
|
107
109
|
targetAuthor: targetAuthor,
|
|
110
|
+
recipients: recipients,
|
|
108
111
|
acceptReactionModes: accept_reaction_modes,
|
|
109
112
|
timeoutSeconds: timeout_seconds,
|
|
110
113
|
createdAt: createdAt,
|
|
@@ -9,6 +9,8 @@ const pieces_framework_1 = require("@activepieces/pieces-framework");
|
|
|
9
9
|
const pieces_common_1 = require("@activepieces/pieces-common");
|
|
10
10
|
const ws_1 = tslib_1.__importDefault(require("ws"));
|
|
11
11
|
// Function to cleanup expired approval mappings
|
|
12
|
+
// Diese Funktion ist die EINZIGE Stelle, an der entschieden wird, ob ein Approval abgelaufen ist.
|
|
13
|
+
// Alle abgelaufenen Approvals werden hier gelöscht, bevor das Matching stattfindet.
|
|
12
14
|
function cleanupExpiredApprovals(store) {
|
|
13
15
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
14
16
|
const keysListKey = 'approval:keys';
|
|
@@ -50,17 +52,25 @@ function tryResumeApprovalFlow(message, store, apiUrl) {
|
|
|
50
52
|
if (!dataMessage) {
|
|
51
53
|
return { resumed: false };
|
|
52
54
|
}
|
|
55
|
+
// Check if it's a group message
|
|
56
|
+
const isGroupMessage = !!dataMessage.groupInfo;
|
|
57
|
+
|
|
53
58
|
// Check if it's a reaction or a quote (text reply)
|
|
54
59
|
const isReaction = !!dataMessage.reaction;
|
|
55
60
|
const isTextReply = !!dataMessage.quote;
|
|
56
|
-
|
|
61
|
+
|
|
62
|
+
// If it's neither a reaction nor a quote, and it's a group message, ignore it
|
|
63
|
+
if (!isReaction && !isTextReply && isGroupMessage) {
|
|
57
64
|
return { resumed: false };
|
|
58
65
|
}
|
|
59
66
|
console.log('[ReceiveMessages] DEBUG - Processing message:', {
|
|
60
67
|
hasReaction: isReaction,
|
|
61
68
|
hasQuote: isTextReply,
|
|
69
|
+
isGroupMessage: isGroupMessage,
|
|
62
70
|
});
|
|
63
71
|
// Cleanup expired approvals
|
|
72
|
+
// WICHTIG: cleanupExpiredApprovals ist die EINZIGE Stelle für Timeout-Entscheidungen.
|
|
73
|
+
// Alle nachfolgenden Matching-Schritte arbeiten nur auf bereits gefilterten (nicht abgelaufenen) Approvals.
|
|
64
74
|
yield cleanupExpiredApprovals(store);
|
|
65
75
|
const keysListKey = 'approval:keys';
|
|
66
76
|
const existingKeys = (yield store.get(keysListKey, pieces_framework_1.StoreScope.PROJECT)) || [];
|
|
@@ -77,19 +87,72 @@ function tryResumeApprovalFlow(message, store, apiUrl) {
|
|
|
77
87
|
// signal-cli returns timestamps in SECONDS, convert to milliseconds for matching
|
|
78
88
|
const targetTimestampSeconds = reaction.targetSentTimestamp || reaction.targetTimestamp;
|
|
79
89
|
const targetTimestampMs = targetTimestampSeconds * 1000;
|
|
80
|
-
const targetAuthor = reaction.targetAuthor;
|
|
81
90
|
const reactionEmoji = reaction.emoji;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
const messageSource = message.envelope.source;
|
|
92
|
+
|
|
93
|
+
console.log('[ReceiveMessages] DEBUG - Processing emoji reaction:', {
|
|
94
|
+
targetTimestampMs,
|
|
95
|
+
targetTimestampSeconds,
|
|
96
|
+
reactionEmoji,
|
|
97
|
+
messageSource,
|
|
98
|
+
isGroupMessage,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Alle Approvals durchsuchen: Exakter Timestamp-Match + Empfänger-Validierung
|
|
102
|
+
for (const key of existingKeys) {
|
|
103
|
+
const candidateMapping = yield store.get(key, pieces_framework_1.StoreScope.PROJECT);
|
|
104
|
+
if (!candidateMapping) continue;
|
|
105
|
+
|
|
106
|
+
// Exakter Timestamp-Match (beide vom Signal-Netzwerk, müssen identisch sein)
|
|
107
|
+
if (candidateMapping.messageTimestampMs !== targetTimestampMs) continue;
|
|
108
|
+
|
|
109
|
+
console.log('[ReceiveMessages] DEBUG - Timestamp match found, checking recipient:', {
|
|
110
|
+
key,
|
|
111
|
+
candidateTimestampMs: candidateMapping.messageTimestampMs,
|
|
112
|
+
recipients: candidateMapping.recipients,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Empfänger-Validierung (1:1 vs. Gruppe)
|
|
116
|
+
const recipients = candidateMapping.recipients || [];
|
|
117
|
+
let isValidRecipient = false;
|
|
118
|
+
|
|
119
|
+
if (isGroupMessage) {
|
|
120
|
+
// Bei Gruppen: Prüfe Gruppen-ID
|
|
121
|
+
const groupId = dataMessage.groupInfo.groupId;
|
|
122
|
+
isValidRecipient = Array.isArray(recipients) && recipients.includes(groupId);
|
|
123
|
+
console.log('[ReceiveMessages] DEBUG - Group recipient check:', {
|
|
124
|
+
groupId,
|
|
125
|
+
isValidRecipient,
|
|
126
|
+
});
|
|
127
|
+
} else {
|
|
128
|
+
// Bei 1:1: Prüfe, ob Absender in recipients-Liste steht
|
|
129
|
+
isValidRecipient = Array.isArray(recipients) && recipients.includes(messageSource);
|
|
130
|
+
console.log('[ReceiveMessages] DEBUG - 1:1 recipient check:', {
|
|
131
|
+
messageSource,
|
|
132
|
+
isValidRecipient,
|
|
133
|
+
});
|
|
89
134
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
135
|
+
|
|
136
|
+
if (!isValidRecipient) continue;
|
|
137
|
+
|
|
138
|
+
// Prüfe, ob Emoji-Modus erlaubt ist
|
|
139
|
+
const acceptModes = candidateMapping.acceptReactionModes || [];
|
|
140
|
+
if (acceptModes.includes('emoji')) {
|
|
141
|
+
mapping = candidateMapping;
|
|
142
|
+
storeKey = key;
|
|
143
|
+
responseContent = reactionEmoji;
|
|
144
|
+
reactionType = 'emoji';
|
|
145
|
+
console.log('[ReceiveMessages] DEBUG - Emoji reaction match found:', {
|
|
146
|
+
key,
|
|
147
|
+
flowRunId: mapping.flowRunId,
|
|
148
|
+
reactionEmoji,
|
|
149
|
+
});
|
|
150
|
+
break; // Gefunden, Suche beenden
|
|
151
|
+
} else {
|
|
152
|
+
console.log('[ReceiveMessages] DEBUG - Emoji mode not allowed for this approval:', {
|
|
153
|
+
key,
|
|
154
|
+
acceptModes,
|
|
155
|
+
});
|
|
93
156
|
}
|
|
94
157
|
}
|
|
95
158
|
}
|
|
@@ -99,20 +162,111 @@ function tryResumeApprovalFlow(message, store, apiUrl) {
|
|
|
99
162
|
// signal-cli returns timestamps in SECONDS, convert to milliseconds for matching
|
|
100
163
|
const quoteTimestampSeconds = quote.id || quote.timestamp;
|
|
101
164
|
const quoteTimestampMs = quoteTimestampSeconds * 1000;
|
|
102
|
-
const quoteAuthor = quote.author;
|
|
103
165
|
const replyText = dataMessage.message || '';
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
166
|
+
const messageSource = message.envelope.source;
|
|
167
|
+
|
|
168
|
+
console.log('[ReceiveMessages] DEBUG - Processing text reply (quote):', {
|
|
169
|
+
quoteTimestampMs,
|
|
170
|
+
quoteTimestampSeconds,
|
|
171
|
+
replyText: replyText.substring(0, 50), // First 50 chars for logging
|
|
172
|
+
messageSource,
|
|
173
|
+
isGroupMessage,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Alle Approvals durchsuchen: Exakter Timestamp-Match + Empfänger-Validierung
|
|
177
|
+
for (const key of existingKeys) {
|
|
178
|
+
const candidateMapping = yield store.get(key, pieces_framework_1.StoreScope.PROJECT);
|
|
179
|
+
if (!candidateMapping) continue;
|
|
180
|
+
|
|
181
|
+
// Exakter Timestamp-Match (beide vom Signal-Netzwerk, müssen identisch sein)
|
|
182
|
+
if (candidateMapping.messageTimestampMs !== quoteTimestampMs) continue;
|
|
183
|
+
|
|
184
|
+
console.log('[ReceiveMessages] DEBUG - Timestamp match found, checking recipient:', {
|
|
185
|
+
key,
|
|
186
|
+
candidateTimestampMs: candidateMapping.messageTimestampMs,
|
|
187
|
+
recipients: candidateMapping.recipients,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Empfänger-Validierung (1:1 vs. Gruppe)
|
|
191
|
+
const recipients = candidateMapping.recipients || [];
|
|
192
|
+
let isValidRecipient = false;
|
|
193
|
+
|
|
194
|
+
if (isGroupMessage) {
|
|
195
|
+
// Bei Gruppen: Prüfe Gruppen-ID
|
|
196
|
+
const groupId = dataMessage.groupInfo.groupId;
|
|
197
|
+
isValidRecipient = Array.isArray(recipients) && recipients.includes(groupId);
|
|
198
|
+
console.log('[ReceiveMessages] DEBUG - Group recipient check:', {
|
|
199
|
+
groupId,
|
|
200
|
+
isValidRecipient,
|
|
201
|
+
});
|
|
202
|
+
} else {
|
|
203
|
+
// Bei 1:1: Prüfe, ob Absender in recipients-Liste steht
|
|
204
|
+
isValidRecipient = Array.isArray(recipients) && recipients.includes(messageSource);
|
|
205
|
+
console.log('[ReceiveMessages] DEBUG - 1:1 recipient check:', {
|
|
206
|
+
messageSource,
|
|
207
|
+
isValidRecipient,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!isValidRecipient) continue;
|
|
212
|
+
|
|
213
|
+
// Prüfe, ob Text-Modus erlaubt ist
|
|
214
|
+
const acceptModes = candidateMapping.acceptReactionModes || [];
|
|
215
|
+
if (acceptModes.includes('text')) {
|
|
216
|
+
mapping = candidateMapping;
|
|
217
|
+
storeKey = key;
|
|
218
|
+
responseContent = replyText;
|
|
111
219
|
reactionType = 'text';
|
|
220
|
+
console.log('[ReceiveMessages] DEBUG - Text reply match found:', {
|
|
221
|
+
key,
|
|
222
|
+
flowRunId: mapping.flowRunId,
|
|
223
|
+
replyText: replyText.substring(0, 50),
|
|
224
|
+
});
|
|
225
|
+
break; // Gefunden, Suche beenden
|
|
226
|
+
} else {
|
|
227
|
+
console.log('[ReceiveMessages] DEBUG - Text mode not allowed for this approval:', {
|
|
228
|
+
key,
|
|
229
|
+
acceptModes,
|
|
230
|
+
});
|
|
112
231
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Handle Direct Messages (1:1 only) - if no reaction/quote found
|
|
235
|
+
if (!mapping && !isGroupMessage) {
|
|
236
|
+
const messageText = dataMessage.message || '';
|
|
237
|
+
const messageSource = message.envelope.source;
|
|
238
|
+
|
|
239
|
+
console.log('[ReceiveMessages] DEBUG - Processing direct message:', {
|
|
240
|
+
messageText: messageText.substring(0, 50), // First 50 chars for logging
|
|
241
|
+
messageSource,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Iterate through all approval keys to find a match
|
|
245
|
+
// Keine Timeout-Prüfung mehr - cleanupExpiredApprovals hat bereits alle abgelaufenen entfernt
|
|
246
|
+
for (const key of existingKeys) {
|
|
247
|
+
const candidateMapping = yield store.get(key, pieces_framework_1.StoreScope.PROJECT);
|
|
248
|
+
if (!candidateMapping) continue;
|
|
249
|
+
|
|
250
|
+
// Check if direct mode is enabled
|
|
251
|
+
const acceptModes = candidateMapping.acceptReactionModes || [];
|
|
252
|
+
if (!acceptModes.includes('direct')) {
|
|
253
|
+
continue; // Direct mode not enabled for this approval
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Check if the message source is one of the recipients
|
|
257
|
+
const recipients = candidateMapping.recipients || [];
|
|
258
|
+
if (Array.isArray(recipients) && recipients.includes(messageSource)) {
|
|
259
|
+
// Found a match! This is a 1:1 conversation response
|
|
260
|
+
mapping = candidateMapping;
|
|
261
|
+
storeKey = key;
|
|
262
|
+
responseContent = messageText; // Use the message text as response
|
|
263
|
+
reactionType = 'direct';
|
|
264
|
+
console.log('[ReceiveMessages] DEBUG - Direct message match found:', {
|
|
265
|
+
key,
|
|
266
|
+
flowRunId: mapping.flowRunId,
|
|
267
|
+
messageText: messageText.substring(0, 50),
|
|
268
|
+
});
|
|
269
|
+
break; // Found a match, stop searching
|
|
116
270
|
}
|
|
117
271
|
}
|
|
118
272
|
}
|
|
@@ -128,19 +282,7 @@ function tryResumeApprovalFlow(message, store, apiUrl) {
|
|
|
128
282
|
reactionType,
|
|
129
283
|
responseContent,
|
|
130
284
|
});
|
|
131
|
-
//
|
|
132
|
-
const ageInSeconds = currentTimestamp - mapping.createdAt;
|
|
133
|
-
const isExpired = ageInSeconds > mapping.timeoutSeconds;
|
|
134
|
-
if (isExpired) {
|
|
135
|
-
// Expired - delete and return
|
|
136
|
-
yield store.delete(storeKey, pieces_framework_1.StoreScope.PROJECT);
|
|
137
|
-
const flowRunMappingKey = `approval:flowRun:${mapping.flowRunId}`;
|
|
138
|
-
yield store.delete(flowRunMappingKey, pieces_framework_1.StoreScope.PROJECT);
|
|
139
|
-
const updatedKeys = existingKeys.filter(key => key !== storeKey);
|
|
140
|
-
yield store.put(keysListKey, updatedKeys, pieces_framework_1.StoreScope.PROJECT);
|
|
141
|
-
console.log('[ReceiveMessages] DEBUG - Approval expired');
|
|
142
|
-
return { resumed: false };
|
|
143
|
-
}
|
|
285
|
+
// Keine Timeout-Prüfung mehr - cleanupExpiredApprovals hat bereits alle abgelaufenen entfernt
|
|
144
286
|
// Resume the flow with responseContent
|
|
145
287
|
const resumeUrl = `${apiUrl}v1/flow-runs/${mapping.flowRunId}/requests/${mapping.requestId}?responseContent=${encodeURIComponent(responseContent)}&reactionType=${reactionType}&responder=${encodeURIComponent(responder || '')}`;
|
|
146
288
|
console.log('[ReceiveMessages] DEBUG - Attempting resume:', {
|