@openvole/paw-msteams 0.1.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 +65 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +277 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -0
- package/vole-paw.json +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# @openvole/paw-msteams
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@openvole/paw-msteams)
|
|
4
|
+
|
|
5
|
+
Microsoft Teams channel Paw for OpenVole. Connects your OpenVole agent to Microsoft Teams using the Azure Bot Framework.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @openvole/paw-msteams
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Azure Bot Registration
|
|
14
|
+
|
|
15
|
+
1. Go to the [Azure Portal](https://portal.azure.com) and create a new **Azure Bot** resource.
|
|
16
|
+
2. Under **Configuration**, note the **Microsoft App ID**.
|
|
17
|
+
3. Go to **Manage Password** and create a new **Client Secret** — this is your App Password.
|
|
18
|
+
4. Under **Channels**, add **Microsoft Teams** as a channel.
|
|
19
|
+
5. In your Teams admin center, allow sideloading or publish the bot to your organization.
|
|
20
|
+
|
|
21
|
+
## Configuration
|
|
22
|
+
|
|
23
|
+
Add the paw to your `vole.config.json`:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"paws": [
|
|
28
|
+
{
|
|
29
|
+
"name": "@openvole/paw-msteams",
|
|
30
|
+
"permissions": {
|
|
31
|
+
"network": ["login.microsoftonline.com", "smba.trafficmanager.net", "*.botframework.com"],
|
|
32
|
+
"listen": [3978],
|
|
33
|
+
"env": ["MSTEAMS_APP_ID", "MSTEAMS_APP_PASSWORD", "MSTEAMS_TENANT_ID", "MSTEAMS_PORT", "MSTEAMS_ALLOW_FROM"]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Environment Variables
|
|
41
|
+
|
|
42
|
+
| Variable | Required | Description |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `MSTEAMS_APP_ID` | Yes | Microsoft App ID from Azure Bot registration |
|
|
45
|
+
| `MSTEAMS_APP_PASSWORD` | Yes | Client secret from Azure Bot registration |
|
|
46
|
+
| `MSTEAMS_TENANT_ID` | No | Azure AD tenant ID (for single-tenant bots) |
|
|
47
|
+
| `MSTEAMS_PORT` | No | HTTP server port (default: `3978`) |
|
|
48
|
+
| `MSTEAMS_ALLOW_FROM` | No | Comma-separated list of allowed user names/IDs |
|
|
49
|
+
|
|
50
|
+
## Setup
|
|
51
|
+
|
|
52
|
+
1. Set the required environment variables.
|
|
53
|
+
2. Start OpenVole with the paw enabled.
|
|
54
|
+
3. The bot listens on `http://localhost:3978/api/messages` for incoming Bot Framework messages.
|
|
55
|
+
4. Configure your Azure Bot's **Messaging Endpoint** to point to this URL (use a tunnel like ngrok for local development).
|
|
56
|
+
|
|
57
|
+
## Tools
|
|
58
|
+
|
|
59
|
+
- **msteams_send** — Send a message to a specific Teams conversation
|
|
60
|
+
- **msteams_reply** — Reply to the current Teams conversation
|
|
61
|
+
- **msteams_get_conversations** — List active Teams conversations
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { definePaw } from "@openvole/paw-sdk";
|
|
3
|
+
|
|
4
|
+
// src/paw.ts
|
|
5
|
+
import { z } from "@openvole/paw-sdk";
|
|
6
|
+
import { createIpcTransport } from "@openvole/paw-sdk";
|
|
7
|
+
|
|
8
|
+
// src/msteams.ts
|
|
9
|
+
import http from "http";
|
|
10
|
+
import {
|
|
11
|
+
BotFrameworkAdapter,
|
|
12
|
+
TurnContext,
|
|
13
|
+
ActivityTypes
|
|
14
|
+
} from "botbuilder";
|
|
15
|
+
var MSTeamsClient = class {
|
|
16
|
+
constructor(appId, appPassword, tenantId) {
|
|
17
|
+
this.tenantId = tenantId;
|
|
18
|
+
this.port = Number(process.env.MSTEAMS_PORT) || 3978;
|
|
19
|
+
this.adapter = new BotFrameworkAdapter({
|
|
20
|
+
appId,
|
|
21
|
+
appPassword
|
|
22
|
+
});
|
|
23
|
+
this.adapter.onTurnError = async (context, error) => {
|
|
24
|
+
console.error("[paw-msteams] Turn error:", error);
|
|
25
|
+
try {
|
|
26
|
+
await context.sendActivity("Sorry, something went wrong processing your message.");
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
adapter;
|
|
32
|
+
server;
|
|
33
|
+
port;
|
|
34
|
+
messageCallback;
|
|
35
|
+
conversationReferences = /* @__PURE__ */ new Map();
|
|
36
|
+
/** Register a callback for incoming messages */
|
|
37
|
+
onMessage(callback) {
|
|
38
|
+
this.messageCallback = callback;
|
|
39
|
+
}
|
|
40
|
+
/** Send a message to a conversation and return the activity ID */
|
|
41
|
+
async sendMessage(conversationId, text) {
|
|
42
|
+
const reference = this.conversationReferences.get(conversationId);
|
|
43
|
+
if (!reference) {
|
|
44
|
+
throw new Error(`No conversation reference found for ${conversationId}`);
|
|
45
|
+
}
|
|
46
|
+
let activityId = "";
|
|
47
|
+
await this.adapter.continueConversation(reference, async (context) => {
|
|
48
|
+
const response = await context.sendActivity(text);
|
|
49
|
+
activityId = response?.id ?? "";
|
|
50
|
+
});
|
|
51
|
+
return activityId;
|
|
52
|
+
}
|
|
53
|
+
/** Edit (update) an existing message */
|
|
54
|
+
async editMessage(conversationId, activityId, text) {
|
|
55
|
+
const reference = this.conversationReferences.get(conversationId);
|
|
56
|
+
if (!reference) {
|
|
57
|
+
throw new Error(`No conversation reference found for ${conversationId}`);
|
|
58
|
+
}
|
|
59
|
+
await this.adapter.continueConversation(reference, async (context) => {
|
|
60
|
+
await context.updateActivity({
|
|
61
|
+
id: activityId,
|
|
62
|
+
type: ActivityTypes.Message,
|
|
63
|
+
text
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** Get all active conversation IDs and their references */
|
|
68
|
+
getConversations() {
|
|
69
|
+
const result = [];
|
|
70
|
+
for (const [conversationId, ref] of this.conversationReferences) {
|
|
71
|
+
result.push({
|
|
72
|
+
conversationId,
|
|
73
|
+
tenantId: ref.conversation?.tenantId
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
/** Start the HTTP server listening for Bot Framework messages */
|
|
79
|
+
async start() {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
this.server = http.createServer(async (req, res) => {
|
|
82
|
+
if (req.method === "POST" && req.url === "/api/messages") {
|
|
83
|
+
await this.adapter.process(req, res, async (context) => {
|
|
84
|
+
const activity = context.activity;
|
|
85
|
+
if (activity.conversation) {
|
|
86
|
+
const ref = TurnContext.getConversationReference(activity);
|
|
87
|
+
this.conversationReferences.set(activity.conversation.id, ref);
|
|
88
|
+
}
|
|
89
|
+
if (activity.type === ActivityTypes.Message && activity.text) {
|
|
90
|
+
const conversationId = activity.conversation?.id ?? "";
|
|
91
|
+
const userName = activity.from?.name ?? activity.from?.id ?? "unknown";
|
|
92
|
+
if (this.messageCallback) {
|
|
93
|
+
this.messageCallback(activity.text, conversationId, userName);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
} else {
|
|
98
|
+
res.writeHead(404);
|
|
99
|
+
res.end();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
this.server.on("error", (err) => {
|
|
103
|
+
console.error("[paw-msteams] Server error:", err);
|
|
104
|
+
reject(err);
|
|
105
|
+
});
|
|
106
|
+
this.server.listen(this.port, () => {
|
|
107
|
+
console.log(`[paw-msteams] Listening on port ${this.port} at /api/messages`);
|
|
108
|
+
resolve();
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/** Stop the HTTP server */
|
|
113
|
+
async stop() {
|
|
114
|
+
return new Promise((resolve) => {
|
|
115
|
+
if (this.server) {
|
|
116
|
+
this.server.close(() => {
|
|
117
|
+
console.log("[paw-msteams] Server stopped");
|
|
118
|
+
resolve();
|
|
119
|
+
});
|
|
120
|
+
} else {
|
|
121
|
+
resolve();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// src/paw.ts
|
|
128
|
+
var client;
|
|
129
|
+
var transport;
|
|
130
|
+
var pendingTasks = /* @__PURE__ */ new Map();
|
|
131
|
+
function parseAllowList(envVar) {
|
|
132
|
+
if (!envVar) return null;
|
|
133
|
+
const entries = envVar.split(",").map((s) => s.trim()).filter(Boolean);
|
|
134
|
+
return entries.length > 0 ? new Set(entries) : null;
|
|
135
|
+
}
|
|
136
|
+
var paw = {
|
|
137
|
+
name: "@openvole/paw-msteams",
|
|
138
|
+
version: "0.1.0",
|
|
139
|
+
description: "Microsoft Teams channel for OpenVole",
|
|
140
|
+
tools: [
|
|
141
|
+
{
|
|
142
|
+
name: "msteams_send",
|
|
143
|
+
description: "Send a message to a Microsoft Teams conversation",
|
|
144
|
+
parameters: z.object({
|
|
145
|
+
conversation_id: z.string().describe("The Teams conversation ID"),
|
|
146
|
+
text: z.string().describe("The message text to send")
|
|
147
|
+
}),
|
|
148
|
+
async execute(params) {
|
|
149
|
+
const { conversation_id, text } = params;
|
|
150
|
+
if (!client) throw new Error("Teams client not initialized");
|
|
151
|
+
const activityId = await client.sendMessage(conversation_id, text);
|
|
152
|
+
return { ok: true, activityId };
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: "msteams_reply",
|
|
157
|
+
description: "Reply to the current Teams conversation",
|
|
158
|
+
parameters: z.object({
|
|
159
|
+
text: z.string().describe("The reply text")
|
|
160
|
+
}),
|
|
161
|
+
async execute(params) {
|
|
162
|
+
const { text } = params;
|
|
163
|
+
if (!client) throw new Error("Teams client not initialized");
|
|
164
|
+
const conversations = client.getConversations();
|
|
165
|
+
if (conversations.length === 0) throw new Error("No active conversations");
|
|
166
|
+
const lastConversation = conversations[conversations.length - 1];
|
|
167
|
+
const activityId = await client.sendMessage(lastConversation.conversationId, text);
|
|
168
|
+
return { ok: true, activityId };
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: "msteams_get_conversations",
|
|
173
|
+
description: "List active Teams conversations",
|
|
174
|
+
parameters: z.object({}),
|
|
175
|
+
async execute() {
|
|
176
|
+
if (!client) throw new Error("Teams client not initialized");
|
|
177
|
+
return client.getConversations();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
],
|
|
181
|
+
async onLoad() {
|
|
182
|
+
const appId = process.env.MSTEAMS_APP_ID;
|
|
183
|
+
const appPassword = process.env.MSTEAMS_APP_PASSWORD;
|
|
184
|
+
if (!appId || !appPassword) {
|
|
185
|
+
console.error(
|
|
186
|
+
"[paw-msteams] MSTEAMS_APP_ID and MSTEAMS_APP_PASSWORD must be set \u2014 bot will not start"
|
|
187
|
+
);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const tenantId = process.env.MSTEAMS_TENANT_ID;
|
|
191
|
+
const allowFrom = parseAllowList(process.env.MSTEAMS_ALLOW_FROM);
|
|
192
|
+
if (allowFrom) {
|
|
193
|
+
console.log(`[paw-msteams] Restricted to ${allowFrom.size} allowed user(s)`);
|
|
194
|
+
} else {
|
|
195
|
+
console.warn("[paw-msteams] MSTEAMS_ALLOW_FROM not set \u2014 bot accepts messages from anyone");
|
|
196
|
+
}
|
|
197
|
+
transport = createIpcTransport();
|
|
198
|
+
transport.subscribe(["task:completed", "task:failed"]);
|
|
199
|
+
transport.onBusEvent((event, data) => {
|
|
200
|
+
const taskData = data;
|
|
201
|
+
const taskId = taskData?.taskId;
|
|
202
|
+
if (!taskId) return;
|
|
203
|
+
const origin = pendingTasks.get(taskId);
|
|
204
|
+
if (!origin) return;
|
|
205
|
+
pendingTasks.delete(taskId);
|
|
206
|
+
if (event === "task:completed" && taskData.result) {
|
|
207
|
+
const text = taskData.result;
|
|
208
|
+
if (origin.placeholderActivityId) {
|
|
209
|
+
client?.editMessage(origin.conversationId, origin.placeholderActivityId, text).catch((err) => {
|
|
210
|
+
console.error("[paw-msteams] Failed to edit placeholder, sending new message:", err);
|
|
211
|
+
client?.sendMessage(origin.conversationId, text);
|
|
212
|
+
});
|
|
213
|
+
} else {
|
|
214
|
+
client?.sendMessage(origin.conversationId, text).catch((err) => {
|
|
215
|
+
console.error("[paw-msteams] Failed to send reply:", err);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
} else if (event === "task:failed") {
|
|
219
|
+
const errorMsg = taskData.error || "Something went wrong processing your request.";
|
|
220
|
+
if (origin.placeholderActivityId) {
|
|
221
|
+
client?.editMessage(origin.conversationId, origin.placeholderActivityId, errorMsg).catch(() => {
|
|
222
|
+
client?.sendMessage(origin.conversationId, errorMsg);
|
|
223
|
+
});
|
|
224
|
+
} else {
|
|
225
|
+
client?.sendMessage(origin.conversationId, errorMsg).catch((err) => {
|
|
226
|
+
console.error("[paw-msteams] Failed to send error reply:", err);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
try {
|
|
232
|
+
client = new MSTeamsClient(appId, appPassword, tenantId);
|
|
233
|
+
client.onMessage(async (text, conversationId, userName) => {
|
|
234
|
+
if (allowFrom && !allowFrom.has(userName)) {
|
|
235
|
+
console.log(`[paw-msteams] Ignored message from unauthorized user ${userName}`);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
console.log(`[paw-msteams] Message from ${userName}: ${text}`);
|
|
239
|
+
try {
|
|
240
|
+
const placeholderActivityId = await client.sendMessage(
|
|
241
|
+
conversationId,
|
|
242
|
+
"Thinking..."
|
|
243
|
+
);
|
|
244
|
+
const { taskId } = await transport.createTask(text, {
|
|
245
|
+
sessionId: `msteams:${conversationId}`,
|
|
246
|
+
source: "msteams",
|
|
247
|
+
conversationId,
|
|
248
|
+
from: userName
|
|
249
|
+
});
|
|
250
|
+
pendingTasks.set(taskId, {
|
|
251
|
+
conversationId,
|
|
252
|
+
placeholderActivityId
|
|
253
|
+
});
|
|
254
|
+
} catch (err) {
|
|
255
|
+
console.error("[paw-msteams] Failed to create task:", err);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
await client.start();
|
|
259
|
+
} catch (err) {
|
|
260
|
+
console.error("[paw-msteams] Failed to start bot:", err);
|
|
261
|
+
client = void 0;
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
async onUnload() {
|
|
265
|
+
await client?.stop();
|
|
266
|
+
client = void 0;
|
|
267
|
+
transport = void 0;
|
|
268
|
+
pendingTasks.clear();
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// src/index.ts
|
|
273
|
+
var index_default = definePaw(paw);
|
|
274
|
+
export {
|
|
275
|
+
index_default as default
|
|
276
|
+
};
|
|
277
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/paw.ts","../src/msteams.ts"],"sourcesContent":["import { definePaw } from '@openvole/paw-sdk'\nimport { paw } from './paw.js'\n\nexport default definePaw(paw)\n","import { z, type PawDefinition } from '@openvole/paw-sdk'\nimport { createIpcTransport } from '@openvole/paw-sdk'\nimport { MSTeamsClient } from './msteams.js'\n\nlet client: MSTeamsClient | undefined\nlet transport: ReturnType<typeof createIpcTransport> | undefined\n\n/** Map from taskId to the originating conversation + placeholder for reply routing */\nconst pendingTasks = new Map<string, { conversationId: string; placeholderActivityId: string }>()\n\n/** Parse comma-separated names/IDs from env var */\nfunction parseAllowList(envVar: string | undefined): Set<string> | null {\n\tif (!envVar) return null\n\tconst entries = envVar\n\t\t.split(',')\n\t\t.map((s) => s.trim())\n\t\t.filter(Boolean)\n\treturn entries.length > 0 ? new Set(entries) : null\n}\n\nexport const paw: PawDefinition = {\n\tname: '@openvole/paw-msteams',\n\tversion: '0.1.0',\n\tdescription: 'Microsoft Teams channel for OpenVole',\n\n\ttools: [\n\t\t{\n\t\t\tname: 'msteams_send',\n\t\t\tdescription: 'Send a message to a Microsoft Teams conversation',\n\t\t\tparameters: z.object({\n\t\t\t\tconversation_id: z.string().describe('The Teams conversation ID'),\n\t\t\t\ttext: z.string().describe('The message text to send'),\n\t\t\t}),\n\t\t\tasync execute(params) {\n\t\t\t\tconst { conversation_id, text } = params as { conversation_id: string; text: string }\n\t\t\t\tif (!client) throw new Error('Teams client not initialized')\n\t\t\t\tconst activityId = await client.sendMessage(conversation_id, text)\n\t\t\t\treturn { ok: true, activityId }\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'msteams_reply',\n\t\t\tdescription: 'Reply to the current Teams conversation',\n\t\t\tparameters: z.object({\n\t\t\t\ttext: z.string().describe('The reply text'),\n\t\t\t}),\n\t\t\tasync execute(params) {\n\t\t\t\tconst { text } = params as { text: string }\n\t\t\t\tif (!client) throw new Error('Teams client not initialized')\n\t\t\t\t// Reply to the most recent conversation from pendingTasks\n\t\t\t\tconst conversations = client.getConversations()\n\t\t\t\tif (conversations.length === 0) throw new Error('No active conversations')\n\t\t\t\tconst lastConversation = conversations[conversations.length - 1]\n\t\t\t\tconst activityId = await client.sendMessage(lastConversation.conversationId, text)\n\t\t\t\treturn { ok: true, activityId }\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: 'msteams_get_conversations',\n\t\t\tdescription: 'List active Teams conversations',\n\t\t\tparameters: z.object({}),\n\t\t\tasync execute() {\n\t\t\t\tif (!client) throw new Error('Teams client not initialized')\n\t\t\t\treturn client.getConversations()\n\t\t\t},\n\t\t},\n\t],\n\n\tasync onLoad() {\n\t\tconst appId = process.env.MSTEAMS_APP_ID\n\t\tconst appPassword = process.env.MSTEAMS_APP_PASSWORD\n\n\t\tif (!appId || !appPassword) {\n\t\t\tconsole.error(\n\t\t\t\t'[paw-msteams] MSTEAMS_APP_ID and MSTEAMS_APP_PASSWORD must be set — bot will not start',\n\t\t\t)\n\t\t\treturn\n\t\t}\n\n\t\tconst tenantId = process.env.MSTEAMS_TENANT_ID\n\t\tconst allowFrom = parseAllowList(process.env.MSTEAMS_ALLOW_FROM)\n\n\t\tif (allowFrom) {\n\t\t\tconsole.log(`[paw-msteams] Restricted to ${allowFrom.size} allowed user(s)`)\n\t\t} else {\n\t\t\tconsole.warn('[paw-msteams] MSTEAMS_ALLOW_FROM not set — bot accepts messages from anyone')\n\t\t}\n\n\t\ttransport = createIpcTransport()\n\n\t\t// Subscribe to task lifecycle events so we can send replies back\n\t\ttransport.subscribe(['task:completed', 'task:failed'])\n\n\t\ttransport.onBusEvent((event, data) => {\n\t\t\tconst taskData = data as {\n\t\t\t\ttaskId?: string\n\t\t\t\tresult?: string\n\t\t\t\terror?: string\n\t\t\t}\n\t\t\tconst taskId = taskData?.taskId\n\t\t\tif (!taskId) return\n\n\t\t\tconst origin = pendingTasks.get(taskId)\n\t\t\tif (!origin) return\n\n\t\t\tpendingTasks.delete(taskId)\n\n\t\t\tif (event === 'task:completed' && taskData.result) {\n\t\t\t\tconst text = taskData.result\n\t\t\t\tif (origin.placeholderActivityId) {\n\t\t\t\t\tclient?.editMessage(origin.conversationId, origin.placeholderActivityId, text)\n\t\t\t\t\t\t.catch((err) => {\n\t\t\t\t\t\t\tconsole.error('[paw-msteams] Failed to edit placeholder, sending new message:', err)\n\t\t\t\t\t\t\tclient?.sendMessage(origin.conversationId, text)\n\t\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tclient?.sendMessage(origin.conversationId, text)\n\t\t\t\t\t\t.catch((err) => {\n\t\t\t\t\t\t\tconsole.error('[paw-msteams] Failed to send reply:', err)\n\t\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t} else if (event === 'task:failed') {\n\t\t\t\tconst errorMsg =\n\t\t\t\t\ttaskData.error || 'Something went wrong processing your request.'\n\t\t\t\tif (origin.placeholderActivityId) {\n\t\t\t\t\tclient?.editMessage(origin.conversationId, origin.placeholderActivityId, errorMsg)\n\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\tclient?.sendMessage(origin.conversationId, errorMsg)\n\t\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tclient?.sendMessage(origin.conversationId, errorMsg)\n\t\t\t\t\t\t.catch((err) => {\n\t\t\t\t\t\t\tconsole.error('[paw-msteams] Failed to send error reply:', err)\n\t\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\t\ttry {\n\t\t\tclient = new MSTeamsClient(appId, appPassword, tenantId)\n\n\t\t\tclient.onMessage(async (text, conversationId, userName) => {\n\t\t\t\t// Check allow list\n\t\t\t\tif (allowFrom && !allowFrom.has(userName)) {\n\t\t\t\t\tconsole.log(`[paw-msteams] Ignored message from unauthorized user ${userName}`)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconsole.log(`[paw-msteams] Message from ${userName}: ${text}`)\n\n\t\t\t\ttry {\n\t\t\t\t\t// Send \"Thinking...\" placeholder immediately\n\t\t\t\t\tconst placeholderActivityId = await client!.sendMessage(\n\t\t\t\t\t\tconversationId,\n\t\t\t\t\t\t'Thinking...',\n\t\t\t\t\t)\n\n\t\t\t\t\tconst { taskId } = await transport!.createTask(text, {\n\t\t\t\t\t\tsessionId: `msteams:${conversationId}`,\n\t\t\t\t\t\tsource: 'msteams',\n\t\t\t\t\t\tconversationId,\n\t\t\t\t\t\tfrom: userName,\n\t\t\t\t\t})\n\n\t\t\t\t\tpendingTasks.set(taskId, {\n\t\t\t\t\t\tconversationId,\n\t\t\t\t\t\tplaceholderActivityId,\n\t\t\t\t\t})\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconsole.error('[paw-msteams] Failed to create task:', err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tawait client.start()\n\t\t} catch (err) {\n\t\t\tconsole.error('[paw-msteams] Failed to start bot:', err)\n\t\t\tclient = undefined\n\t\t}\n\t},\n\n\tasync onUnload() {\n\t\tawait client?.stop()\n\t\tclient = undefined\n\t\ttransport = undefined\n\t\tpendingTasks.clear()\n\t},\n}\n","import http from 'node:http'\nimport {\n\tBotFrameworkAdapter,\n\tTurnContext,\n\ttype ConversationReference,\n\ttype Activity,\n\tActivityTypes,\n} from 'botbuilder'\n\ntype MessageCallback = (text: string, conversationId: string, userName: string) => void\n\nexport class MSTeamsClient {\n\tprivate adapter: BotFrameworkAdapter\n\tprivate server: http.Server | undefined\n\tprivate port: number\n\tprivate messageCallback: MessageCallback | undefined\n\tprivate conversationReferences = new Map<string, Partial<ConversationReference>>()\n\n\tconstructor(\n\t\tappId: string,\n\t\tappPassword: string,\n\t\tprivate tenantId?: string,\n\t) {\n\t\tthis.port = Number(process.env.MSTEAMS_PORT) || 3978\n\n\t\tthis.adapter = new BotFrameworkAdapter({\n\t\t\tappId,\n\t\t\tappPassword,\n\t\t})\n\n\t\t// Error handler\n\t\tthis.adapter.onTurnError = async (context, error) => {\n\t\t\tconsole.error('[paw-msteams] Turn error:', error)\n\t\t\ttry {\n\t\t\t\tawait context.sendActivity('Sorry, something went wrong processing your message.')\n\t\t\t} catch {\n\t\t\t\t// Ignore send failures during error handling\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Register a callback for incoming messages */\n\tonMessage(callback: MessageCallback): void {\n\t\tthis.messageCallback = callback\n\t}\n\n\t/** Send a message to a conversation and return the activity ID */\n\tasync sendMessage(conversationId: string, text: string): Promise<string> {\n\t\tconst reference = this.conversationReferences.get(conversationId)\n\t\tif (!reference) {\n\t\t\tthrow new Error(`No conversation reference found for ${conversationId}`)\n\t\t}\n\n\t\tlet activityId = ''\n\t\tawait this.adapter.continueConversation(reference, async (context) => {\n\t\t\tconst response = await context.sendActivity(text)\n\t\t\tactivityId = response?.id ?? ''\n\t\t})\n\n\t\treturn activityId\n\t}\n\n\t/** Edit (update) an existing message */\n\tasync editMessage(conversationId: string, activityId: string, text: string): Promise<void> {\n\t\tconst reference = this.conversationReferences.get(conversationId)\n\t\tif (!reference) {\n\t\t\tthrow new Error(`No conversation reference found for ${conversationId}`)\n\t\t}\n\n\t\tawait this.adapter.continueConversation(reference, async (context) => {\n\t\t\tawait context.updateActivity({\n\t\t\t\tid: activityId,\n\t\t\t\ttype: ActivityTypes.Message,\n\t\t\t\ttext,\n\t\t\t} as Partial<Activity>)\n\t\t})\n\t}\n\n\t/** Get all active conversation IDs and their references */\n\tgetConversations(): Array<{ conversationId: string; tenantId?: string }> {\n\t\tconst result: Array<{ conversationId: string; tenantId?: string }> = []\n\t\tfor (const [conversationId, ref] of this.conversationReferences) {\n\t\t\tresult.push({\n\t\t\t\tconversationId,\n\t\t\t\ttenantId: (ref.conversation as { tenantId?: string })?.tenantId,\n\t\t\t})\n\t\t}\n\t\treturn result\n\t}\n\n\t/** Start the HTTP server listening for Bot Framework messages */\n\tasync start(): Promise<void> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.server = http.createServer(async (req, res) => {\n\t\t\t\tif (req.method === 'POST' && req.url === '/api/messages') {\n\t\t\t\t\tawait this.adapter.process(req, res, async (context: TurnContext) => {\n\t\t\t\t\t\t// Store conversation reference for proactive messaging\n\t\t\t\t\t\tconst activity = context.activity\n\t\t\t\t\t\tif (activity.conversation) {\n\t\t\t\t\t\t\tconst ref = TurnContext.getConversationReference(activity)\n\t\t\t\t\t\t\tthis.conversationReferences.set(activity.conversation.id, ref)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Only process message activities with text\n\t\t\t\t\t\tif (activity.type === ActivityTypes.Message && activity.text) {\n\t\t\t\t\t\t\tconst conversationId = activity.conversation?.id ?? ''\n\t\t\t\t\t\t\tconst userName =\n\t\t\t\t\t\t\t\tactivity.from?.name ??\n\t\t\t\t\t\t\t\tactivity.from?.id ??\n\t\t\t\t\t\t\t\t'unknown'\n\n\t\t\t\t\t\t\tif (this.messageCallback) {\n\t\t\t\t\t\t\t\tthis.messageCallback(activity.text, conversationId, userName)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tres.writeHead(404)\n\t\t\t\t\tres.end()\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tthis.server.on('error', (err) => {\n\t\t\t\tconsole.error('[paw-msteams] Server error:', err)\n\t\t\t\treject(err)\n\t\t\t})\n\n\t\t\tthis.server.listen(this.port, () => {\n\t\t\t\tconsole.log(`[paw-msteams] Listening on port ${this.port} at /api/messages`)\n\t\t\t\tresolve()\n\t\t\t})\n\t\t})\n\t}\n\n\t/** Stop the HTTP server */\n\tasync stop(): Promise<void> {\n\t\treturn new Promise((resolve) => {\n\t\t\tif (this.server) {\n\t\t\t\tthis.server.close(() => {\n\t\t\t\t\tconsole.log('[paw-msteams] Server stopped')\n\t\t\t\t\tresolve()\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tresolve()\n\t\t\t}\n\t\t})\n\t}\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openvole/paw-msteams",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Microsoft Teams channel Paw for OpenVole",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsup",
|
|
9
|
+
"typecheck": "tsc --noEmit"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"botbuilder": "^4.23.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/node": "^22.0.0",
|
|
16
|
+
"tsup": "^8.3.0",
|
|
17
|
+
"typescript": "^5.6.0",
|
|
18
|
+
"@openvole/paw-sdk": "^0.3.0"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20.0.0"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"vole-paw.json",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@openvole/paw-sdk": "^0.3.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/vole-paw.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openvole/paw-msteams",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Microsoft Teams channel for OpenVole",
|
|
5
|
+
"entry": "./dist/index.js",
|
|
6
|
+
"brain": false,
|
|
7
|
+
"inProcess": false,
|
|
8
|
+
"transport": "ipc",
|
|
9
|
+
"tools": [
|
|
10
|
+
{ "name": "msteams_send", "description": "Send a message to a Microsoft Teams conversation" },
|
|
11
|
+
{ "name": "msteams_reply", "description": "Reply to the current Teams conversation" },
|
|
12
|
+
{ "name": "msteams_get_conversations", "description": "List active Teams conversations" }
|
|
13
|
+
],
|
|
14
|
+
"permissions": {
|
|
15
|
+
"network": ["login.microsoftonline.com", "smba.trafficmanager.net", "*.botframework.com"],
|
|
16
|
+
"listen": [3978],
|
|
17
|
+
"filesystem": [],
|
|
18
|
+
"env": ["MSTEAMS_APP_ID", "MSTEAMS_APP_PASSWORD", "MSTEAMS_TENANT_ID", "MSTEAMS_PORT", "MSTEAMS_ALLOW_FROM"]
|
|
19
|
+
}
|
|
20
|
+
}
|