n8n-nodes-msteams-botframework 1.0.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/README.md +231 -0
- package/dist/credentials/MsTeamsBotFrameworkApi.credentials.d.ts +9 -0
- package/dist/credentials/MsTeamsBotFrameworkApi.credentials.js +55 -0
- package/dist/nodes/MsTeamsBotFramework/MsTeamsBotFramework.node.d.ts +5 -0
- package/dist/nodes/MsTeamsBotFramework/MsTeamsBotFramework.node.js +398 -0
- package/dist/nodes/MsTeamsBotFramework/msteams.svg +14 -0
- package/index.js +4 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# n8n-nodes-msteams-botframework
|
|
2
|
+
|
|
3
|
+
Custom n8n node để tương tác với Microsoft Teams thông qua Azure Bot Framework.
|
|
4
|
+
|
|
5
|
+
## Tính năng
|
|
6
|
+
|
|
7
|
+
Node này cho phép bạn:
|
|
8
|
+
- ✉️ Gửi tin nhắn text đến channel hoặc user
|
|
9
|
+
- 🎴 Gửi Adaptive Cards
|
|
10
|
+
- 💬 Reply vào tin nhắn cụ thể
|
|
11
|
+
- ✏️ Cập nhật tin nhắn đã gửi
|
|
12
|
+
- 🗑️ Xóa tin nhắn
|
|
13
|
+
|
|
14
|
+
## Yêu cầu
|
|
15
|
+
|
|
16
|
+
- n8n version 0.187.0 trở lên
|
|
17
|
+
- Azure Bot Service đã được cấu hình
|
|
18
|
+
- Microsoft App ID và App Password
|
|
19
|
+
|
|
20
|
+
## Cài đặt
|
|
21
|
+
|
|
22
|
+
### Cài đặt trong n8n Community Node
|
|
23
|
+
|
|
24
|
+
1. Trong n8n, vào **Settings** > **Community Nodes**
|
|
25
|
+
2. Chọn **Install** và nhập: `n8n-nodes-msteams-botframework`
|
|
26
|
+
3. Chấp nhận rủi ro và cài đặt
|
|
27
|
+
|
|
28
|
+
### Cài đặt từ source (Development)
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Clone repository
|
|
32
|
+
git clone <repository-url>
|
|
33
|
+
cd n8n-nodes-msteams-botframework
|
|
34
|
+
|
|
35
|
+
# Cài đặt dependencies
|
|
36
|
+
npm install
|
|
37
|
+
|
|
38
|
+
# Build
|
|
39
|
+
npm run build
|
|
40
|
+
|
|
41
|
+
# Link vào n8n local
|
|
42
|
+
npm link
|
|
43
|
+
cd ~/.n8n/custom
|
|
44
|
+
npm link n8n-nodes-msteams-botframework
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Cấu hình Azure Bot Service
|
|
48
|
+
|
|
49
|
+
### 1. Tạo Azure Bot
|
|
50
|
+
|
|
51
|
+
1. Đăng nhập vào [Azure Portal](https://portal.azure.com)
|
|
52
|
+
2. Tạo **Azure Bot** resource mới
|
|
53
|
+
3. Chọn **Type of App**: Multi Tenant
|
|
54
|
+
4. Sau khi tạo, vào **Configuration** để lấy thông tin:
|
|
55
|
+
- **Microsoft App ID**
|
|
56
|
+
- **Microsoft App Password** (Client Secret)
|
|
57
|
+
|
|
58
|
+
### 2. Cấu hình Teams Channel
|
|
59
|
+
|
|
60
|
+
1. Trong Azure Bot, vào **Channels**
|
|
61
|
+
2. Thêm **Microsoft Teams** channel
|
|
62
|
+
3. Chấp nhận Terms of Service
|
|
63
|
+
4. Enable channel
|
|
64
|
+
|
|
65
|
+
### 3. Cấu hình Messaging Endpoint (Nếu cần nhận webhook)
|
|
66
|
+
|
|
67
|
+
1. Vào **Configuration** > **Messaging endpoint**
|
|
68
|
+
2. Nhập URL: `https://your-n8n-instance.com/webhook/msteams`
|
|
69
|
+
3. Lưu thay đổi
|
|
70
|
+
|
|
71
|
+
## Sử dụng trong n8n
|
|
72
|
+
|
|
73
|
+
### 1. Tạo Credentials
|
|
74
|
+
|
|
75
|
+
1. Trong n8n workflow, thêm node **MS Teams Bot Framework**
|
|
76
|
+
2. Tạo credential mới:
|
|
77
|
+
- **Microsoft App ID**: Paste App ID từ Azure
|
|
78
|
+
- **Microsoft App Password**: Paste App Password từ Azure
|
|
79
|
+
3. Lưu credential
|
|
80
|
+
|
|
81
|
+
### 2. Gửi tin nhắn đơn giản
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"operation": "sendMessage",
|
|
86
|
+
"serviceUrl": "https://smba.trafficmanager.net/amer/",
|
|
87
|
+
"conversationId": "19:meeting_xxx...",
|
|
88
|
+
"messageText": "Hello from n8n!"
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 3. Gửi Adaptive Card
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"operation": "sendAdaptiveCard",
|
|
97
|
+
"serviceUrl": "https://smba.trafficmanager.net/amer/",
|
|
98
|
+
"conversationId": "19:meeting_xxx...",
|
|
99
|
+
"adaptiveCardJson": {
|
|
100
|
+
"type": "AdaptiveCard",
|
|
101
|
+
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
|
102
|
+
"version": "1.4",
|
|
103
|
+
"body": [
|
|
104
|
+
{
|
|
105
|
+
"type": "TextBlock",
|
|
106
|
+
"text": "Hello from n8n!",
|
|
107
|
+
"size": "Large",
|
|
108
|
+
"weight": "Bolder"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"type": "TextBlock",
|
|
112
|
+
"text": "This is an Adaptive Card",
|
|
113
|
+
"wrap": true
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
"actions": [
|
|
117
|
+
{
|
|
118
|
+
"type": "Action.OpenUrl",
|
|
119
|
+
"title": "Learn More",
|
|
120
|
+
"url": "https://n8n.io"
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 4. Reply vào tin nhắn
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"operation": "replyToMessage",
|
|
132
|
+
"serviceUrl": "https://smba.trafficmanager.net/amer/",
|
|
133
|
+
"conversationId": "19:meeting_xxx...",
|
|
134
|
+
"activityId": "1234567890",
|
|
135
|
+
"replyText": "This is a reply!"
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Lấy thông tin cần thiết
|
|
140
|
+
|
|
141
|
+
### Service URL
|
|
142
|
+
|
|
143
|
+
Service URL thường có trong webhook payload từ Teams. Các giá trị phổ biến:
|
|
144
|
+
- `https://smba.trafficmanager.net/amer/` (Americas)
|
|
145
|
+
- `https://smba.trafficmanager.net/emea/` (Europe/Middle East/Africa)
|
|
146
|
+
- `https://smba.trafficmanager.net/apac/` (Asia Pacific)
|
|
147
|
+
|
|
148
|
+
### Conversation ID
|
|
149
|
+
|
|
150
|
+
Conversation ID có thể lấy từ:
|
|
151
|
+
- Webhook payload khi nhận tin nhắn từ Teams
|
|
152
|
+
- Teams URL (phần sau `conversations/`)
|
|
153
|
+
- Graph API
|
|
154
|
+
|
|
155
|
+
Ví dụ: `19:meeting_MjdhNjc4...@thread.v2`
|
|
156
|
+
|
|
157
|
+
### Activity ID
|
|
158
|
+
|
|
159
|
+
Activity ID là ID của tin nhắn, có thể lấy từ:
|
|
160
|
+
- Response khi gửi tin nhắn (trả về `activityId`)
|
|
161
|
+
- Webhook payload khi nhận tin nhắn
|
|
162
|
+
|
|
163
|
+
## Ví dụ Workflow
|
|
164
|
+
|
|
165
|
+
### Workflow 1: Nhận webhook và reply
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
Webhook Trigger (MS Teams)
|
|
169
|
+
↓
|
|
170
|
+
MS Teams Bot Framework (Reply to Message)
|
|
171
|
+
↓
|
|
172
|
+
[Success]
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Workflow 2: Schedule message với Adaptive Card
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
Schedule Trigger (Cron)
|
|
179
|
+
↓
|
|
180
|
+
MS Teams Bot Framework (Send Adaptive Card)
|
|
181
|
+
↓
|
|
182
|
+
[Success]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Workflow 3: Theo dõi GitHub và thông báo Teams
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
GitHub Trigger (On Push)
|
|
189
|
+
↓
|
|
190
|
+
Function (Format message)
|
|
191
|
+
↓
|
|
192
|
+
MS Teams Bot Framework (Send Message)
|
|
193
|
+
↓
|
|
194
|
+
[Success]
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Troubleshooting
|
|
198
|
+
|
|
199
|
+
### Lỗi 401 Unauthorized
|
|
200
|
+
- Kiểm tra lại App ID và App Password
|
|
201
|
+
- Đảm bảo Bot đã được add vào Team/Channel
|
|
202
|
+
- Verify token chưa hết hạn
|
|
203
|
+
|
|
204
|
+
### Lỗi 404 Not Found
|
|
205
|
+
- Kiểm tra Service URL có đúng không
|
|
206
|
+
- Verify Conversation ID còn tồn tại
|
|
207
|
+
- Đảm bảo bot còn trong conversation
|
|
208
|
+
|
|
209
|
+
### Adaptive Card không hiển thị
|
|
210
|
+
- Validate JSON schema tại [Adaptive Cards Designer](https://adaptivecards.io/designer/)
|
|
211
|
+
- Kiểm tra version phù hợp với Teams (khuyến nghị 1.4)
|
|
212
|
+
- Đảm bảo cú pháp JSON đúng
|
|
213
|
+
|
|
214
|
+
## Tài liệu tham khảo
|
|
215
|
+
|
|
216
|
+
- [Azure Bot Service Documentation](https://docs.microsoft.com/en-us/azure/bot-service/)
|
|
217
|
+
- [Bot Framework REST API](https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference)
|
|
218
|
+
- [Adaptive Cards](https://adaptivecards.io/)
|
|
219
|
+
- [Teams Bot Overview](https://docs.microsoft.com/en-us/microsoftteams/platform/bots/what-are-bots)
|
|
220
|
+
|
|
221
|
+
## Đóng góp
|
|
222
|
+
|
|
223
|
+
Contributions, issues và feature requests đều được chào đón!
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
MIT
|
|
228
|
+
|
|
229
|
+
## Tác giả
|
|
230
|
+
|
|
231
|
+
Tạo bởi cộng đồng n8n
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class MsTeamsBotFrameworkApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
documentationUrl: string;
|
|
6
|
+
properties: INodeProperties[];
|
|
7
|
+
authenticate: IAuthenticateGeneric;
|
|
8
|
+
test: ICredentialTestRequest;
|
|
9
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MsTeamsBotFrameworkApi = void 0;
|
|
4
|
+
class MsTeamsBotFrameworkApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'msTeamsBotFrameworkApi';
|
|
7
|
+
this.displayName = 'MS Teams Bot Framework API';
|
|
8
|
+
this.documentationUrl = 'https://docs.microsoft.com/en-us/azure/bot-service/';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'Microsoft App ID',
|
|
12
|
+
name: 'appId',
|
|
13
|
+
type: 'string',
|
|
14
|
+
default: '',
|
|
15
|
+
required: true,
|
|
16
|
+
description: 'The Microsoft App ID from Azure Bot Service',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
displayName: 'Microsoft App Password',
|
|
20
|
+
name: 'appPassword',
|
|
21
|
+
type: 'string',
|
|
22
|
+
typeOptions: {
|
|
23
|
+
password: true,
|
|
24
|
+
},
|
|
25
|
+
default: '',
|
|
26
|
+
required: true,
|
|
27
|
+
description: 'The Microsoft App Password (Client Secret) from Azure Bot Service',
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
this.authenticate = {
|
|
31
|
+
type: 'generic',
|
|
32
|
+
properties: {
|
|
33
|
+
headers: {
|
|
34
|
+
Authorization: '={{$credentials.token}}',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
this.test = {
|
|
39
|
+
request: {
|
|
40
|
+
baseURL: 'https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token',
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: {
|
|
43
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
44
|
+
},
|
|
45
|
+
body: {
|
|
46
|
+
grant_type: 'client_credentials',
|
|
47
|
+
client_id: '={{$credentials.appId}}',
|
|
48
|
+
client_secret: '={{$credentials.appPassword}}',
|
|
49
|
+
scope: 'https://api.botframework.com/.default',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.MsTeamsBotFrameworkApi = MsTeamsBotFrameworkApi;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class MsTeamsBotFramework implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MsTeamsBotFramework = void 0;
|
|
7
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
class MsTeamsBotFramework {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.description = {
|
|
12
|
+
displayName: 'MS Teams Bot Framework',
|
|
13
|
+
name: 'msTeamsBotFramework',
|
|
14
|
+
icon: 'file:msteams.svg',
|
|
15
|
+
group: ['transform'],
|
|
16
|
+
version: 1,
|
|
17
|
+
subtitle: '={{$parameter["operation"]}}',
|
|
18
|
+
description: 'Send messages and interact with MS Teams using Azure Bot Framework',
|
|
19
|
+
defaults: {
|
|
20
|
+
name: 'MS Teams Bot',
|
|
21
|
+
},
|
|
22
|
+
inputs: ['main'],
|
|
23
|
+
outputs: ['main'],
|
|
24
|
+
credentials: [
|
|
25
|
+
{
|
|
26
|
+
name: 'msTeamsBotFrameworkApi',
|
|
27
|
+
required: true,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
properties: [
|
|
31
|
+
{
|
|
32
|
+
displayName: 'Operation',
|
|
33
|
+
name: 'operation',
|
|
34
|
+
type: 'options',
|
|
35
|
+
noDataExpression: true,
|
|
36
|
+
options: [
|
|
37
|
+
{
|
|
38
|
+
name: 'Send Message',
|
|
39
|
+
value: 'sendMessage',
|
|
40
|
+
description: 'Send a message to a channel or user',
|
|
41
|
+
action: 'Send a message',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'Send Adaptive Card',
|
|
45
|
+
value: 'sendAdaptiveCard',
|
|
46
|
+
description: 'Send an Adaptive Card to a channel or user',
|
|
47
|
+
action: 'Send an adaptive card',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'Reply to Message',
|
|
51
|
+
value: 'replyToMessage',
|
|
52
|
+
description: 'Reply to a specific message',
|
|
53
|
+
action: 'Reply to a message',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'Update Message',
|
|
57
|
+
value: 'updateMessage',
|
|
58
|
+
description: 'Update an existing message',
|
|
59
|
+
action: 'Update a message',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'Delete Message',
|
|
63
|
+
value: 'deleteMessage',
|
|
64
|
+
description: 'Delete a message',
|
|
65
|
+
action: 'Delete a message',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
default: 'sendMessage',
|
|
69
|
+
},
|
|
70
|
+
// Send Message fields
|
|
71
|
+
{
|
|
72
|
+
displayName: 'Service URL',
|
|
73
|
+
name: 'serviceUrl',
|
|
74
|
+
type: 'string',
|
|
75
|
+
default: '',
|
|
76
|
+
required: true,
|
|
77
|
+
displayOptions: {
|
|
78
|
+
show: {
|
|
79
|
+
operation: ['sendMessage', 'sendAdaptiveCard', 'replyToMessage'],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
description: 'Service URL from the Teams activity (e.g., https://smba.trafficmanager.net/amer/)',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
displayName: 'Conversation ID',
|
|
86
|
+
name: 'conversationId',
|
|
87
|
+
type: 'string',
|
|
88
|
+
default: '',
|
|
89
|
+
required: true,
|
|
90
|
+
displayOptions: {
|
|
91
|
+
show: {
|
|
92
|
+
operation: ['sendMessage', 'sendAdaptiveCard', 'replyToMessage', 'updateMessage', 'deleteMessage'],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
description: 'The conversation ID where to send the message',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
displayName: 'Message Text',
|
|
99
|
+
name: 'messageText',
|
|
100
|
+
type: 'string',
|
|
101
|
+
typeOptions: {
|
|
102
|
+
rows: 4,
|
|
103
|
+
},
|
|
104
|
+
default: '',
|
|
105
|
+
required: true,
|
|
106
|
+
displayOptions: {
|
|
107
|
+
show: {
|
|
108
|
+
operation: ['sendMessage'],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
description: 'The text message to send',
|
|
112
|
+
},
|
|
113
|
+
// Adaptive Card fields
|
|
114
|
+
{
|
|
115
|
+
displayName: 'Adaptive Card JSON',
|
|
116
|
+
name: 'adaptiveCardJson',
|
|
117
|
+
type: 'json',
|
|
118
|
+
default: '{\n "type": "AdaptiveCard",\n "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",\n "version": "1.4",\n "body": [\n {\n "type": "TextBlock",\n "text": "Hello from n8n!",\n "size": "Large",\n "weight": "Bolder"\n }\n ]\n}',
|
|
119
|
+
required: true,
|
|
120
|
+
displayOptions: {
|
|
121
|
+
show: {
|
|
122
|
+
operation: ['sendAdaptiveCard'],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
description: 'The Adaptive Card JSON to send',
|
|
126
|
+
},
|
|
127
|
+
// Reply to Message fields
|
|
128
|
+
{
|
|
129
|
+
displayName: 'Activity ID',
|
|
130
|
+
name: 'activityId',
|
|
131
|
+
type: 'string',
|
|
132
|
+
default: '',
|
|
133
|
+
required: true,
|
|
134
|
+
displayOptions: {
|
|
135
|
+
show: {
|
|
136
|
+
operation: ['replyToMessage', 'updateMessage', 'deleteMessage'],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
description: 'The ID of the activity/message to reply to, update, or delete',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
displayName: 'Reply Text',
|
|
143
|
+
name: 'replyText',
|
|
144
|
+
type: 'string',
|
|
145
|
+
typeOptions: {
|
|
146
|
+
rows: 4,
|
|
147
|
+
},
|
|
148
|
+
default: '',
|
|
149
|
+
required: true,
|
|
150
|
+
displayOptions: {
|
|
151
|
+
show: {
|
|
152
|
+
operation: ['replyToMessage'],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
description: 'The reply message text',
|
|
156
|
+
},
|
|
157
|
+
// Update Message fields
|
|
158
|
+
{
|
|
159
|
+
displayName: 'Updated Message Text',
|
|
160
|
+
name: 'updatedText',
|
|
161
|
+
type: 'string',
|
|
162
|
+
typeOptions: {
|
|
163
|
+
rows: 4,
|
|
164
|
+
},
|
|
165
|
+
default: '',
|
|
166
|
+
required: true,
|
|
167
|
+
displayOptions: {
|
|
168
|
+
show: {
|
|
169
|
+
operation: ['updateMessage'],
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
description: 'The updated message text',
|
|
173
|
+
},
|
|
174
|
+
// Additional options
|
|
175
|
+
{
|
|
176
|
+
displayName: 'Additional Fields',
|
|
177
|
+
name: 'additionalFields',
|
|
178
|
+
type: 'collection',
|
|
179
|
+
placeholder: 'Add Field',
|
|
180
|
+
default: {},
|
|
181
|
+
options: [
|
|
182
|
+
{
|
|
183
|
+
displayName: 'Channel ID',
|
|
184
|
+
name: 'channelId',
|
|
185
|
+
type: 'string',
|
|
186
|
+
default: 'msteams',
|
|
187
|
+
description: 'The channel ID (default: msteams)',
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
displayName: 'Tenant ID',
|
|
191
|
+
name: 'tenantId',
|
|
192
|
+
type: 'string',
|
|
193
|
+
default: '',
|
|
194
|
+
description: 'The tenant ID for the conversation',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
displayName: 'Message Type',
|
|
198
|
+
name: 'messageType',
|
|
199
|
+
type: 'options',
|
|
200
|
+
options: [
|
|
201
|
+
{
|
|
202
|
+
name: 'Message',
|
|
203
|
+
value: 'message',
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: 'Notification',
|
|
207
|
+
value: 'notification',
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
default: 'message',
|
|
211
|
+
description: 'The type of message to send',
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
async execute() {
|
|
219
|
+
var _a, _b, _c, _d;
|
|
220
|
+
const items = this.getInputData();
|
|
221
|
+
const returnData = [];
|
|
222
|
+
const credentials = await this.getCredentials('msTeamsBotFrameworkApi');
|
|
223
|
+
const appId = credentials.appId;
|
|
224
|
+
const appPassword = credentials.appPassword;
|
|
225
|
+
for (let i = 0; i < items.length; i++) {
|
|
226
|
+
try {
|
|
227
|
+
const operation = this.getNodeParameter('operation', i);
|
|
228
|
+
const conversationId = this.getNodeParameter('conversationId', i, '');
|
|
229
|
+
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
230
|
+
const channelId = additionalFields.channelId || 'msteams';
|
|
231
|
+
// Get access token
|
|
232
|
+
const tokenResponse = await axios_1.default.post('https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token', new URLSearchParams({
|
|
233
|
+
grant_type: 'client_credentials',
|
|
234
|
+
client_id: appId,
|
|
235
|
+
client_secret: appPassword,
|
|
236
|
+
scope: 'https://api.botframework.com/.default',
|
|
237
|
+
}), {
|
|
238
|
+
headers: {
|
|
239
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
const accessToken = tokenResponse.data.access_token;
|
|
243
|
+
let result;
|
|
244
|
+
if (operation === 'sendMessage') {
|
|
245
|
+
const serviceUrl = this.getNodeParameter('serviceUrl', i);
|
|
246
|
+
const messageText = this.getNodeParameter('messageText', i);
|
|
247
|
+
const activity = {
|
|
248
|
+
type: 'message',
|
|
249
|
+
text: messageText,
|
|
250
|
+
channelId,
|
|
251
|
+
serviceUrl,
|
|
252
|
+
conversation: {
|
|
253
|
+
id: conversationId,
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
result = await axios_1.default.post(`${serviceUrl}v3/conversations/${conversationId}/activities`, activity, {
|
|
257
|
+
headers: {
|
|
258
|
+
Authorization: `Bearer ${accessToken}`,
|
|
259
|
+
'Content-Type': 'application/json',
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
returnData.push({
|
|
263
|
+
json: {
|
|
264
|
+
success: true,
|
|
265
|
+
activityId: result.data.id,
|
|
266
|
+
...result.data,
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
else if (operation === 'sendAdaptiveCard') {
|
|
271
|
+
const serviceUrl = this.getNodeParameter('serviceUrl', i);
|
|
272
|
+
const adaptiveCardJson = this.getNodeParameter('adaptiveCardJson', i);
|
|
273
|
+
let cardData;
|
|
274
|
+
try {
|
|
275
|
+
cardData = typeof adaptiveCardJson === 'string' ? JSON.parse(adaptiveCardJson) : adaptiveCardJson;
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid Adaptive Card JSON', { itemIndex: i });
|
|
279
|
+
}
|
|
280
|
+
const activity = {
|
|
281
|
+
type: 'message',
|
|
282
|
+
channelId,
|
|
283
|
+
serviceUrl,
|
|
284
|
+
conversation: {
|
|
285
|
+
id: conversationId,
|
|
286
|
+
},
|
|
287
|
+
attachments: [
|
|
288
|
+
{
|
|
289
|
+
contentType: 'application/vnd.microsoft.card.adaptive',
|
|
290
|
+
content: cardData,
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
};
|
|
294
|
+
result = await axios_1.default.post(`${serviceUrl}v3/conversations/${conversationId}/activities`, activity, {
|
|
295
|
+
headers: {
|
|
296
|
+
Authorization: `Bearer ${accessToken}`,
|
|
297
|
+
'Content-Type': 'application/json',
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
returnData.push({
|
|
301
|
+
json: {
|
|
302
|
+
success: true,
|
|
303
|
+
activityId: result.data.id,
|
|
304
|
+
...result.data,
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
else if (operation === 'replyToMessage') {
|
|
309
|
+
const serviceUrl = this.getNodeParameter('serviceUrl', i);
|
|
310
|
+
const activityId = this.getNodeParameter('activityId', i);
|
|
311
|
+
const replyText = this.getNodeParameter('replyText', i);
|
|
312
|
+
const activity = {
|
|
313
|
+
type: 'message',
|
|
314
|
+
text: replyText,
|
|
315
|
+
channelId,
|
|
316
|
+
serviceUrl,
|
|
317
|
+
conversation: {
|
|
318
|
+
id: conversationId,
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
result = await axios_1.default.post(`${serviceUrl}v3/conversations/${conversationId}/activities/${activityId}`, activity, {
|
|
322
|
+
headers: {
|
|
323
|
+
Authorization: `Bearer ${accessToken}`,
|
|
324
|
+
'Content-Type': 'application/json',
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
returnData.push({
|
|
328
|
+
json: {
|
|
329
|
+
success: true,
|
|
330
|
+
activityId: result.data.id,
|
|
331
|
+
...result.data,
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
else if (operation === 'updateMessage') {
|
|
336
|
+
const activityId = this.getNodeParameter('activityId', i);
|
|
337
|
+
const updatedText = this.getNodeParameter('updatedText', i);
|
|
338
|
+
const serviceUrl = this.getNodeParameter('serviceUrl', i, 'https://smba.trafficmanager.net/amer/');
|
|
339
|
+
const activity = {
|
|
340
|
+
type: 'message',
|
|
341
|
+
text: updatedText,
|
|
342
|
+
id: activityId,
|
|
343
|
+
channelId,
|
|
344
|
+
conversation: {
|
|
345
|
+
id: conversationId,
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
result = await axios_1.default.put(`${serviceUrl}v3/conversations/${conversationId}/activities/${activityId}`, activity, {
|
|
349
|
+
headers: {
|
|
350
|
+
Authorization: `Bearer ${accessToken}`,
|
|
351
|
+
'Content-Type': 'application/json',
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
returnData.push({
|
|
355
|
+
json: {
|
|
356
|
+
success: true,
|
|
357
|
+
activityId: result.data.id,
|
|
358
|
+
...result.data,
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
else if (operation === 'deleteMessage') {
|
|
363
|
+
const activityId = this.getNodeParameter('activityId', i);
|
|
364
|
+
const serviceUrl = this.getNodeParameter('serviceUrl', i, 'https://smba.trafficmanager.net/amer/');
|
|
365
|
+
result = await axios_1.default.delete(`${serviceUrl}v3/conversations/${conversationId}/activities/${activityId}`, {
|
|
366
|
+
headers: {
|
|
367
|
+
Authorization: `Bearer ${accessToken}`,
|
|
368
|
+
'Content-Type': 'application/json',
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
returnData.push({
|
|
372
|
+
json: {
|
|
373
|
+
success: true,
|
|
374
|
+
message: 'Message deleted successfully',
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
if (this.continueOnFail()) {
|
|
381
|
+
returnData.push({
|
|
382
|
+
json: {
|
|
383
|
+
error: error.message,
|
|
384
|
+
details: ((_a = error.response) === null || _a === void 0 ? void 0 : _a.data) || error,
|
|
385
|
+
},
|
|
386
|
+
});
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `MS Teams Bot Framework error: ${error.message}`, {
|
|
390
|
+
itemIndex: i,
|
|
391
|
+
description: ((_d = (_c = (_b = error.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.error) === null || _d === void 0 ? void 0 : _d.message) || error.message,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return [returnData];
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
exports.MsTeamsBotFramework = MsTeamsBotFramework;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="a" x1="32" y1="3" x2="32" y2="61" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop offset="0" stop-color="#5a62c3"/>
|
|
5
|
+
<stop offset=".5" stop-color="#4d55bd"/>
|
|
6
|
+
<stop offset="1" stop-color="#3940ab"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
</defs>
|
|
9
|
+
<rect x="3" y="3" width="58" height="58" rx="8" fill="url(#a)"/>
|
|
10
|
+
<path d="M47.5 20h-8a1.5 1.5 0 0 0-1.5 1.5v21a1.5 1.5 0 0 0 1.5 1.5h8a1.5 1.5 0 0 0 1.5-1.5v-21a1.5 1.5 0 0 0-1.5-1.5z" fill="#fff"/>
|
|
11
|
+
<circle cx="43.5" cy="27.5" r="7.5" fill="#fff" opacity=".7"/>
|
|
12
|
+
<path d="M16.5 20h19a1.5 1.5 0 0 1 1.5 1.5v21a1.5 1.5 0 0 1-1.5 1.5h-19a1.5 1.5 0 0 1-1.5-1.5v-21a1.5 1.5 0 0 1 1.5-1.5z" fill="#fff"/>
|
|
13
|
+
<text x="26" y="36" font-family="Arial, sans-serif" font-size="20" font-weight="bold" fill="#5a62c3" text-anchor="middle">T</text>
|
|
14
|
+
</svg>
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-msteams-botframework",
|
|
3
|
+
"version": "1.0.0",
|
|
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
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/express": "^4.17.6",
|
|
46
|
+
"@types/node": "^18.16.0",
|
|
47
|
+
"@typescript-eslint/parser": "^5.59.0",
|
|
48
|
+
"eslint": "^8.39.0",
|
|
49
|
+
"eslint-plugin-n8n-nodes-base": "^1.12.0",
|
|
50
|
+
"gulp": "^4.0.2",
|
|
51
|
+
"n8n-workflow": "^1.0.0",
|
|
52
|
+
"prettier": "^2.8.8",
|
|
53
|
+
"typescript": "^5.0.4"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"n8n-workflow": "*"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"botbuilder": "^4.20.0",
|
|
60
|
+
"axios": "^1.6.0"
|
|
61
|
+
}
|
|
62
|
+
}
|