n8n-nodes-msteams-botframework 1.2.12 → 1.2.13

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.
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MsTeamsBotFrameworkTrigger = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const botbuilder_1 = require("botbuilder");
4
6
  // Track processed activity IDs to prevent duplicates
5
7
  const processedActivities = new Map();
6
8
  // Clean up old entries every 5 minutes
@@ -37,7 +39,8 @@ class MsTeamsBotFrameworkTrigger {
37
39
  {
38
40
  name: 'default',
39
41
  httpMethod: 'POST',
40
- responseMode: 'onReceived',
42
+ responseMode: '={{$parameter["respondWhen"]}}',
43
+ responseData: '={{$parameter["responseData"]}}',
41
44
  path: 'webhook',
42
45
  },
43
46
  ],
@@ -77,6 +80,64 @@ class MsTeamsBotFrameworkTrigger {
77
80
  required: true,
78
81
  description: 'The events to listen to',
79
82
  },
83
+ {
84
+ displayName: 'Respond',
85
+ name: 'respondWhen',
86
+ type: 'options',
87
+ options: [
88
+ {
89
+ name: 'Immediately',
90
+ value: 'immediately',
91
+ description: 'Respond immediately (useful for keeping connection alive)',
92
+ },
93
+ {
94
+ name: 'When Last Node Finishes',
95
+ value: 'lastNode',
96
+ description: 'Wait for workflow to complete before responding',
97
+ },
98
+ {
99
+ name: 'Using Respond to Webhook Node',
100
+ value: 'responseNode',
101
+ description: 'Use a Respond to Webhook node to control the response',
102
+ },
103
+ ],
104
+ default: 'immediately',
105
+ description: 'When to respond to the webhook call',
106
+ },
107
+ {
108
+ displayName: 'Response Data',
109
+ name: 'responseData',
110
+ type: 'options',
111
+ displayOptions: {
112
+ show: {
113
+ respondWhen: ['lastNode'],
114
+ },
115
+ },
116
+ options: [
117
+ {
118
+ name: 'All Entries',
119
+ value: 'allEntries',
120
+ description: 'Return all entries from the workflow',
121
+ },
122
+ {
123
+ name: 'First Entry JSON',
124
+ value: 'firstEntryJson',
125
+ description: 'Return only the JSON of the first entry',
126
+ },
127
+ {
128
+ name: 'First Entry Binary',
129
+ value: 'firstEntryBinary',
130
+ description: 'Return only the binary data of the first entry',
131
+ },
132
+ {
133
+ name: 'No Response Body',
134
+ value: 'noData',
135
+ description: 'Return success status without response body',
136
+ },
137
+ ],
138
+ default: 'allEntries',
139
+ description: 'What data should be returned',
140
+ },
80
141
  {
81
142
  displayName: 'Options',
82
143
  name: 'options',
@@ -98,6 +159,13 @@ class MsTeamsBotFrameworkTrigger {
98
159
  default: false,
99
160
  description: 'Whether to include the raw Bot Framework activity in output',
100
161
  },
162
+ {
163
+ displayName: 'Auto Reply',
164
+ name: 'autoReply',
165
+ type: 'boolean',
166
+ default: false,
167
+ description: 'Whether to automatically send a typing indicator',
168
+ },
101
169
  ],
102
170
  },
103
171
  ],
@@ -117,114 +185,136 @@ class MsTeamsBotFrameworkTrigger {
117
185
  };
118
186
  }
119
187
  async webhook() {
120
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
121
- const bodyData = this.getBodyData();
188
+ const credentials = await this.getCredentials('msTeamsBotFrameworkApi');
189
+ const req = this.getRequestObject();
122
190
  const events = this.getNodeParameter('events', []);
123
191
  const options = this.getNodeParameter('options', {});
124
- // Get the activity from body
125
- const activity = bodyData;
126
- // Validate activity object
127
- if (!activity || !activity.type) {
128
- return {
129
- webhookResponse: { status: 'ok', message: 'Invalid activity' },
130
- };
131
- }
132
- // Check for duplicate activity
133
- const activityKey = `${activity.id || 'unknown'}_${activity.timestamp || Date.now()}`;
134
- if (processedActivities.has(activityKey)) {
135
- return {
136
- webhookResponse: { status: 'ok' },
137
- };
138
- }
139
- // Check if we should process this activity type
140
- if (!events.includes(activity.type)) {
141
- return {
142
- webhookResponse: { status: 'ok' },
143
- };
144
- }
145
- // Ignore bot messages if configured
146
- if (options.ignoreBotMessages && ((_a = activity.from) === null || _a === void 0 ? void 0 : _a.role) === 'bot') {
147
- return {
148
- webhookResponse: { status: 'ok' },
149
- };
150
- }
151
- // Mark this activity as processed
152
- processedActivities.set(activityKey, Date.now());
153
- // Extract useful data from activity
154
- const activityData = {
155
- type: activity.type,
156
- text: activity.text || '',
157
- timestamp: activity.timestamp,
158
- id: activity.id,
159
- conversation: {
160
- id: (_b = activity.conversation) === null || _b === void 0 ? void 0 : _b.id,
161
- name: (_c = activity.conversation) === null || _c === void 0 ? void 0 : _c.name,
162
- conversationType: (_d = activity.conversation) === null || _d === void 0 ? void 0 : _d.conversationType,
163
- tenantId: (_e = activity.conversation) === null || _e === void 0 ? void 0 : _e.tenantId,
164
- },
165
- from: {
166
- id: (_f = activity.from) === null || _f === void 0 ? void 0 : _f.id,
167
- name: (_g = activity.from) === null || _g === void 0 ? void 0 : _g.name,
168
- aadObjectId: (_h = activity.from) === null || _h === void 0 ? void 0 : _h.aadObjectId,
169
- role: (_j = activity.from) === null || _j === void 0 ? void 0 : _j.role,
170
- },
171
- recipient: {
172
- id: (_k = activity.recipient) === null || _k === void 0 ? void 0 : _k.id,
173
- name: (_l = activity.recipient) === null || _l === void 0 ? void 0 : _l.name,
174
- },
175
- channelId: activity.channelId,
176
- serviceUrl: activity.serviceUrl,
177
- locale: activity.locale,
178
- };
179
- // Add type-specific data
180
- switch (activity.type) {
181
- case 'message':
182
- activityData.message = {
183
- text: activity.text || '',
184
- textFormat: activity.textFormat,
185
- attachments: activity.attachments || [],
186
- mentions: ((_m = activity.entities) === null || _m === void 0 ? void 0 : _m.filter((e) => e.type === 'mention')) || [],
187
- };
188
- break;
189
- case 'conversationUpdate':
190
- activityData.conversationUpdate = {
191
- membersAdded: activity.membersAdded || [],
192
- membersRemoved: activity.membersRemoved || [],
193
- };
194
- break;
195
- case 'messageReaction':
196
- activityData.messageReaction = {
197
- reactionsAdded: activity.reactionsAdded || [],
198
- reactionsRemoved: activity.reactionsRemoved || [],
199
- replyToId: activity.replyToId,
200
- };
201
- break;
202
- case 'messageUpdate':
203
- activityData.messageUpdate = {
204
- text: activity.text || '',
205
- updatedText: activity.text || '',
192
+ const appId = credentials.appId;
193
+ const appPassword = credentials.appPassword;
194
+ // Create Bot Framework Adapter
195
+ const adapter = new botbuilder_1.BotFrameworkAdapter({
196
+ appId,
197
+ appPassword,
198
+ });
199
+ // Process the incoming activity
200
+ let activityData = null;
201
+ let shouldTrigger = false;
202
+ try {
203
+ await adapter.processActivity(req, req.res, async (context) => {
204
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
205
+ const activity = context.activity;
206
+ // Check for duplicate activity
207
+ const activityKey = `${activity.id}_${activity.timestamp}`;
208
+ if (processedActivities.has(activityKey)) {
209
+ // Already processed this activity, skip
210
+ shouldTrigger = false;
211
+ return;
212
+ }
213
+ // Check if we should process this activity type
214
+ if (!events.includes(activity.type)) {
215
+ shouldTrigger = false;
216
+ return;
217
+ }
218
+ // Ignore bot messages if configured
219
+ if (options.ignoreBotMessages && ((_a = activity.from) === null || _a === void 0 ? void 0 : _a.role) === 'bot') {
220
+ shouldTrigger = false;
221
+ return;
222
+ }
223
+ // Mark this activity as processed
224
+ processedActivities.set(activityKey, Date.now());
225
+ shouldTrigger = true;
226
+ // Send typing indicator if auto reply is enabled
227
+ if (options.autoReply && activity.type === 'message') {
228
+ await context.sendActivity({ type: 'typing' });
229
+ }
230
+ // Extract useful data from activity
231
+ activityData = {
232
+ type: activity.type,
233
+ text: activity.text,
234
+ timestamp: activity.timestamp,
235
+ id: activity.id,
236
+ conversation: {
237
+ id: (_b = activity.conversation) === null || _b === void 0 ? void 0 : _b.id,
238
+ name: (_c = activity.conversation) === null || _c === void 0 ? void 0 : _c.name,
239
+ conversationType: (_d = activity.conversation) === null || _d === void 0 ? void 0 : _d.conversationType,
240
+ tenantId: (_e = activity.conversation) === null || _e === void 0 ? void 0 : _e.tenantId,
241
+ },
242
+ from: {
243
+ id: (_f = activity.from) === null || _f === void 0 ? void 0 : _f.id,
244
+ name: (_g = activity.from) === null || _g === void 0 ? void 0 : _g.name,
245
+ aadObjectId: (_h = activity.from) === null || _h === void 0 ? void 0 : _h.aadObjectId,
246
+ role: (_j = activity.from) === null || _j === void 0 ? void 0 : _j.role,
247
+ },
248
+ recipient: {
249
+ id: (_k = activity.recipient) === null || _k === void 0 ? void 0 : _k.id,
250
+ name: (_l = activity.recipient) === null || _l === void 0 ? void 0 : _l.name,
251
+ },
252
+ channelId: activity.channelId,
253
+ serviceUrl: activity.serviceUrl,
254
+ locale: activity.locale,
206
255
  };
207
- break;
208
- case 'messageDelete':
209
- activityData.messageDelete = {
210
- deletedMessageId: activity.id,
256
+ // Add type-specific data
257
+ switch (activity.type) {
258
+ case 'message':
259
+ activityData.message = {
260
+ text: activity.text,
261
+ textFormat: activity.textFormat,
262
+ attachments: activity.attachments,
263
+ mentions: (_m = activity.entities) === null || _m === void 0 ? void 0 : _m.filter((e) => e.type === 'mention'),
264
+ };
265
+ break;
266
+ case 'conversationUpdate':
267
+ activityData.conversationUpdate = {
268
+ membersAdded: activity.membersAdded,
269
+ membersRemoved: activity.membersRemoved,
270
+ };
271
+ break;
272
+ case 'messageReaction':
273
+ activityData.messageReaction = {
274
+ reactionsAdded: activity.reactionsAdded,
275
+ reactionsRemoved: activity.reactionsRemoved,
276
+ replyToId: activity.replyToId,
277
+ };
278
+ break;
279
+ case 'messageUpdate':
280
+ activityData.messageUpdate = {
281
+ text: activity.text,
282
+ updatedText: activity.text,
283
+ };
284
+ break;
285
+ case 'messageDelete':
286
+ activityData.messageDelete = {
287
+ deletedMessageId: activity.id,
288
+ };
289
+ break;
290
+ }
291
+ // Include raw activity if requested
292
+ if (options.includeRawActivity) {
293
+ activityData.rawActivity = activity;
294
+ }
295
+ });
296
+ if (!shouldTrigger || !activityData) {
297
+ // Don't trigger workflow - Bot Framework Adapter already sent 200 OK
298
+ return {
299
+ noWebhookResponse: true,
211
300
  };
212
- break;
301
+ }
302
+ // Return the activity data to the workflow
303
+ return {
304
+ workflowData: [
305
+ [
306
+ {
307
+ json: activityData,
308
+ },
309
+ ],
310
+ ],
311
+ };
213
312
  }
214
- // Include raw activity if requested
215
- if (options.includeRawActivity) {
216
- activityData.rawActivity = activity;
313
+ catch (error) {
314
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to process Teams webhook: ${error.message}`, {
315
+ description: error.message,
316
+ });
217
317
  }
218
- // Return the activity data to the workflow
219
- return {
220
- workflowData: [
221
- [
222
- {
223
- json: activityData,
224
- },
225
- ],
226
- ],
227
- };
228
318
  }
229
319
  }
230
320
  exports.MsTeamsBotFrameworkTrigger = MsTeamsBotFrameworkTrigger;
package/package.json CHANGED
@@ -1,64 +1,64 @@
1
- {
2
- "name": "n8n-nodes-msteams-botframework",
3
- "version": "1.2.12",
4
- "description": "n8n node for MS Teams Azure Bot Framework",
5
- "keywords": [
6
- "n8n-community-node-package",
7
- "n8n",
8
- "msteams",
9
- "microsoft teams",
10
- "bot framework",
11
- "azure"
12
- ],
13
- "license": "MIT",
14
- "homepage": "https://weon.vn",
15
- "author": {
16
- "name": "Weon Software",
17
- "email": "doannv@weon.vn"
18
- },
19
- "repository": {
20
- "type": "git",
21
- "url": "git+https://github.com/weonVN/n8n-nodes-msteams-botframework.git"
22
- },
23
- "main": "index.js",
24
- "scripts": {
25
- "build": "tsc && gulp build:icons",
26
- "dev": "tsc --watch",
27
- "format": "prettier nodes credentials --write",
28
- "lint": "eslint nodes credentials package.json",
29
- "lintfix": "eslint nodes credentials package.json --fix",
30
- "prepublishOnly": "npm run build"
31
- },
32
- "files": [
33
- "dist"
34
- ],
35
- "n8n": {
36
- "n8nNodesApiVersion": 1,
37
- "credentials": [
38
- "dist/credentials/MsTeamsBotFrameworkApi.credentials.js"
39
- ],
40
- "nodes": [
41
- "dist/nodes/MsTeamsBotFramework/MsTeamsBotFramework.node.js",
42
- "dist/nodes/MsTeamsBotFramework/MsTeamsBotFrameworkTrigger.node.js",
43
- "dist/nodes/MsTeamsBotFramework/MsTeamsAIBot.node.js"
44
- ]
45
- },
46
- "devDependencies": {
47
- "@types/express": "^4.17.6",
48
- "@types/node": "^18.16.0",
49
- "@typescript-eslint/parser": "^5.59.0",
50
- "eslint": "^8.39.0",
51
- "eslint-plugin-n8n-nodes-base": "^1.12.0",
52
- "gulp": "^4.0.2",
53
- "n8n-workflow": "^1.0.0",
54
- "prettier": "^2.8.8",
55
- "typescript": "^5.0.4"
56
- },
57
- "peerDependencies": {
58
- "n8n-workflow": "*"
59
- },
60
- "dependencies": {
61
- "botbuilder": "^4.20.0",
62
- "axios": "^1.6.0"
63
- }
64
- }
1
+ {
2
+ "name": "n8n-nodes-msteams-botframework",
3
+ "version": "1.2.13",
4
+ "description": "n8n node for MS Teams Azure Bot Framework",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "n8n",
8
+ "msteams",
9
+ "microsoft teams",
10
+ "bot framework",
11
+ "azure"
12
+ ],
13
+ "license": "MIT",
14
+ "homepage": "https://weon.vn",
15
+ "author": {
16
+ "name": "Weon Software",
17
+ "email": "doannv@weon.vn"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/weon-software/n8n-nodes-msteams-botframework.git"
22
+ },
23
+ "main": "index.js",
24
+ "scripts": {
25
+ "build": "tsc && gulp build:icons",
26
+ "dev": "tsc --watch",
27
+ "format": "prettier nodes credentials --write",
28
+ "lint": "eslint nodes credentials package.json",
29
+ "lintfix": "eslint nodes credentials package.json --fix",
30
+ "prepublishOnly": "npm run build"
31
+ },
32
+ "files": [
33
+ "dist"
34
+ ],
35
+ "n8n": {
36
+ "n8nNodesApiVersion": 1,
37
+ "credentials": [
38
+ "dist/credentials/MsTeamsBotFrameworkApi.credentials.js"
39
+ ],
40
+ "nodes": [
41
+ "dist/nodes/MsTeamsBotFramework/MsTeamsBotFramework.node.js",
42
+ "dist/nodes/MsTeamsBotFramework/MsTeamsBotFrameworkTrigger.node.js",
43
+ "dist/nodes/MsTeamsBotFramework/MsTeamsAIBot.node.js"
44
+ ]
45
+ },
46
+ "devDependencies": {
47
+ "@types/express": "^4.17.6",
48
+ "@types/node": "^22.10.0",
49
+ "@typescript-eslint/parser": "^8.18.0",
50
+ "eslint": "^8.57.0",
51
+ "eslint-plugin-n8n-nodes-base": "^1.16.0",
52
+ "gulp": "^5.0.0",
53
+ "n8n-workflow": "~2.2.2",
54
+ "prettier": "^3.4.0",
55
+ "typescript": "~5.7.0"
56
+ },
57
+ "peerDependencies": {
58
+ "n8n-workflow": ">=2.0.0"
59
+ },
60
+ "dependencies": {
61
+ "botbuilder": "^4.20.0",
62
+ "axios": "^1.6.0"
63
+ }
64
+ }