piece-signal-cli-rest-api 0.2.14 → 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.14",
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
+ }
@@ -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
- const storeKey = `approval:${messageTimestampMs}:${targetAuthor}`;
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,
@@ -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';
@@ -67,6 +69,8 @@ function tryResumeApprovalFlow(message, store, apiUrl) {
67
69
  isGroupMessage: isGroupMessage,
68
70
  });
69
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.
70
74
  yield cleanupExpiredApprovals(store);
71
75
  const keysListKey = 'approval:keys';
72
76
  const existingKeys = (yield store.get(keysListKey, pieces_framework_1.StoreScope.PROJECT)) || [];
@@ -83,19 +87,72 @@ function tryResumeApprovalFlow(message, store, apiUrl) {
83
87
  // signal-cli returns timestamps in SECONDS, convert to milliseconds for matching
84
88
  const targetTimestampSeconds = reaction.targetSentTimestamp || reaction.targetTimestamp;
85
89
  const targetTimestampMs = targetTimestampSeconds * 1000;
86
- const targetAuthor = reaction.targetAuthor;
87
90
  const reactionEmoji = reaction.emoji;
88
- storeKey = `approval:${targetTimestampMs}:${targetAuthor}`;
89
- mapping = yield store.get(storeKey, pieces_framework_1.StoreScope.PROJECT);
90
- if (mapping) {
91
- const acceptModes = mapping.acceptReactionModes || [];
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
+ });
134
+ }
135
+
136
+ if (!isValidRecipient) continue;
137
+
138
+ // Prüfe, ob Emoji-Modus erlaubt ist
139
+ const acceptModes = candidateMapping.acceptReactionModes || [];
92
140
  if (acceptModes.includes('emoji')) {
93
- responseContent = reactionEmoji; // Return the emoji directly
141
+ mapping = candidateMapping;
142
+ storeKey = key;
143
+ responseContent = reactionEmoji;
94
144
  reactionType = 'emoji';
95
- }
96
- else {
97
- // Mapping found but emoji mode not allowed
98
- mapping = null;
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
+ });
99
156
  }
100
157
  }
101
158
  }
@@ -105,20 +162,72 @@ function tryResumeApprovalFlow(message, store, apiUrl) {
105
162
  // signal-cli returns timestamps in SECONDS, convert to milliseconds for matching
106
163
  const quoteTimestampSeconds = quote.id || quote.timestamp;
107
164
  const quoteTimestampMs = quoteTimestampSeconds * 1000;
108
- const quoteAuthor = quote.author;
109
165
  const replyText = dataMessage.message || '';
110
- // Try to find mapping by quote timestamp
111
- storeKey = `approval:${quoteTimestampMs}:${quoteAuthor}`;
112
- mapping = yield store.get(storeKey, pieces_framework_1.StoreScope.PROJECT);
113
- if (mapping) {
114
- const acceptModes = mapping.acceptReactionModes || [];
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 || [];
115
215
  if (acceptModes.includes('text')) {
116
- responseContent = replyText; // Return the text directly
216
+ mapping = candidateMapping;
217
+ storeKey = key;
218
+ responseContent = replyText;
117
219
  reactionType = 'text';
118
- }
119
- else {
120
- // Mapping found but text mode not allowed
121
- mapping = null;
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
+ });
122
231
  }
123
232
  }
124
233
  }
@@ -127,17 +236,17 @@ function tryResumeApprovalFlow(message, store, apiUrl) {
127
236
  const messageText = dataMessage.message || '';
128
237
  const messageSource = message.envelope.source;
129
238
 
239
+ console.log('[ReceiveMessages] DEBUG - Processing direct message:', {
240
+ messageText: messageText.substring(0, 50), // First 50 chars for logging
241
+ messageSource,
242
+ });
243
+
130
244
  // Iterate through all approval keys to find a match
245
+ // Keine Timeout-Prüfung mehr - cleanupExpiredApprovals hat bereits alle abgelaufenen entfernt
131
246
  for (const key of existingKeys) {
132
247
  const candidateMapping = yield store.get(key, pieces_framework_1.StoreScope.PROJECT);
133
248
  if (!candidateMapping) continue;
134
249
 
135
- // Check if expired
136
- const ageInSeconds = currentTimestamp - candidateMapping.createdAt;
137
- if (ageInSeconds > candidateMapping.timeoutSeconds) {
138
- continue; // Skip expired mappings
139
- }
140
-
141
250
  // Check if direct mode is enabled
142
251
  const acceptModes = candidateMapping.acceptReactionModes || [];
143
252
  if (!acceptModes.includes('direct')) {
@@ -152,6 +261,11 @@ function tryResumeApprovalFlow(message, store, apiUrl) {
152
261
  storeKey = key;
153
262
  responseContent = messageText; // Use the message text as response
154
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
+ });
155
269
  break; // Found a match, stop searching
156
270
  }
157
271
  }
@@ -168,19 +282,7 @@ function tryResumeApprovalFlow(message, store, apiUrl) {
168
282
  reactionType,
169
283
  responseContent,
170
284
  });
171
- // Check if expired
172
- const ageInSeconds = currentTimestamp - mapping.createdAt;
173
- const isExpired = ageInSeconds > mapping.timeoutSeconds;
174
- if (isExpired) {
175
- // Expired - delete and return
176
- yield store.delete(storeKey, pieces_framework_1.StoreScope.PROJECT);
177
- const flowRunMappingKey = `approval:flowRun:${mapping.flowRunId}`;
178
- yield store.delete(flowRunMappingKey, pieces_framework_1.StoreScope.PROJECT);
179
- const updatedKeys = existingKeys.filter(key => key !== storeKey);
180
- yield store.put(keysListKey, updatedKeys, pieces_framework_1.StoreScope.PROJECT);
181
- console.log('[ReceiveMessages] DEBUG - Approval expired');
182
- return { resumed: false };
183
- }
285
+ // Keine Timeout-Prüfung mehr - cleanupExpiredApprovals hat bereits alle abgelaufenen entfernt
184
286
  // Resume the flow with responseContent
185
287
  const resumeUrl = `${apiUrl}v1/flow-runs/${mapping.flowRunId}/requests/${mapping.requestId}?responseContent=${encodeURIComponent(responseContent)}&reactionType=${reactionType}&responder=${encodeURIComponent(responder || '')}`;
186
288
  console.log('[ReceiveMessages] DEBUG - Attempting resume:', {