teams-api 0.7.0 → 0.9.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.
Files changed (59) hide show
  1. package/dist/actions/conversation-actions.d.ts +10 -0
  2. package/dist/actions/conversation-actions.d.ts.map +1 -0
  3. package/dist/actions/conversation-actions.js +168 -0
  4. package/dist/actions/conversation-actions.js.map +1 -0
  5. package/dist/actions/definitions.d.ts +5 -10
  6. package/dist/actions/definitions.d.ts.map +1 -1
  7. package/dist/actions/definitions.js +22 -813
  8. package/dist/actions/definitions.js.map +1 -1
  9. package/dist/actions/file-actions.d.ts +8 -0
  10. package/dist/actions/file-actions.d.ts.map +1 -0
  11. package/dist/actions/file-actions.js +124 -0
  12. package/dist/actions/file-actions.js.map +1 -0
  13. package/dist/actions/formatters.d.ts +1 -1
  14. package/dist/actions/formatters.d.ts.map +1 -1
  15. package/dist/actions/message-actions.d.ts +11 -0
  16. package/dist/actions/message-actions.d.ts.map +1 -0
  17. package/dist/actions/message-actions.js +453 -0
  18. package/dist/actions/message-actions.js.map +1 -0
  19. package/dist/actions/search-actions.d.ts +9 -0
  20. package/dist/actions/search-actions.d.ts.map +1 -0
  21. package/dist/actions/search-actions.js +148 -0
  22. package/dist/actions/search-actions.js.map +1 -0
  23. package/dist/actions/utility-actions.d.ts +10 -0
  24. package/dist/actions/utility-actions.d.ts.map +1 -0
  25. package/dist/actions/utility-actions.js +200 -0
  26. package/dist/actions/utility-actions.js.map +1 -0
  27. package/dist/api/attachments.d.ts +106 -0
  28. package/dist/api/attachments.d.ts.map +1 -0
  29. package/dist/api/attachments.js +341 -0
  30. package/dist/api/attachments.js.map +1 -0
  31. package/dist/api/chat-service.d.ts +1 -1
  32. package/dist/api/chat-service.d.ts.map +1 -1
  33. package/dist/api/chat-service.js +8 -1
  34. package/dist/api/chat-service.js.map +1 -1
  35. package/dist/auth/auto-login.d.ts.map +1 -1
  36. package/dist/auth/auto-login.js +4 -1
  37. package/dist/auth/auto-login.js.map +1 -1
  38. package/dist/auth/interactive.d.ts.map +1 -1
  39. package/dist/auth/interactive.js +3 -1
  40. package/dist/auth/interactive.js.map +1 -1
  41. package/dist/auth/token-capture.d.ts +3 -0
  42. package/dist/auth/token-capture.d.ts.map +1 -1
  43. package/dist/auth/token-capture.js +67 -0
  44. package/dist/auth/token-capture.js.map +1 -1
  45. package/dist/cli.js +16 -3
  46. package/dist/cli.js.map +1 -1
  47. package/dist/mcp-server.js +9 -0
  48. package/dist/mcp-server.js.map +1 -1
  49. package/dist/teams-client.d.ts +78 -4
  50. package/dist/teams-client.d.ts.map +1 -1
  51. package/dist/teams-client.js +153 -8
  52. package/dist/teams-client.js.map +1 -1
  53. package/dist/token-store.d.ts.map +1 -1
  54. package/dist/token-store.js +6 -0
  55. package/dist/token-store.js.map +1 -1
  56. package/dist/types.d.ts +75 -0
  57. package/dist/types.d.ts.map +1 -1
  58. package/dist/types.js.map +1 -1
  59. package/package.json +1 -1
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ /**
3
+ * Utility action definitions.
4
+ *
5
+ * Actions: whoami, get-members, get-transcript.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.getTranscript = exports.whoami = exports.getMembers = void 0;
9
+ const formatters_js_1 = require("./formatters.js");
10
+ const conversation_resolution_js_1 = require("./conversation-resolution.js");
11
+ exports.getMembers = {
12
+ name: "get-members",
13
+ title: "Get Conversation Members",
14
+ description: "List members of a conversation. " +
15
+ "Identify the conversation by topic name (--chat), " +
16
+ "person name for 1:1 chats (--to), or direct ID (--conversation-id). " +
17
+ "At least one identifier is required. " +
18
+ "Display names are resolved via the Teams profile API when available, with message history as fallback. " +
19
+ "Note: 1:1 chat members may have empty display names if profile resolution is unavailable.",
20
+ parameters: [...conversation_resolution_js_1.conversationParameters],
21
+ execute: async (client, parameters) => {
22
+ const { conversationId } = await (0, conversation_resolution_js_1.resolveConversationId)(client, parameters);
23
+ return client.getMembers(conversationId);
24
+ },
25
+ formatResult: (result) => {
26
+ const members = result;
27
+ const people = members.filter((member) => member.memberType === "person");
28
+ const bots = members.filter((member) => member.memberType === "bot");
29
+ const lines = [`\n${people.length} people, ${bots.length} bots:\n`];
30
+ for (const member of people) {
31
+ const name = member.displayName || "(unknown)";
32
+ lines.push(` ${name} (${member.role}) — ${member.id}`);
33
+ }
34
+ if (bots.length > 0) {
35
+ lines.push("");
36
+ lines.push(" Bots/Apps:");
37
+ for (const bot of bots) {
38
+ const name = bot.displayName || "(unnamed bot)";
39
+ lines.push(` ${name} — ${bot.id}`);
40
+ }
41
+ }
42
+ return lines.join("\n");
43
+ },
44
+ formatMarkdown: (result) => {
45
+ const members = result;
46
+ const people = members.filter((member) => member.memberType === "person");
47
+ const bots = members.filter((member) => member.memberType === "bot");
48
+ const lines = [
49
+ `## Members (${people.length} people, ${bots.length} bots)`,
50
+ "",
51
+ ];
52
+ if (people.length > 0) {
53
+ lines.push("| Name | Role | ID |");
54
+ lines.push("|------|------|----|");
55
+ for (const member of people) {
56
+ const name = member.displayName || "(unknown)";
57
+ lines.push(`| ${name} | ${member.role} | ${member.id} |`);
58
+ }
59
+ }
60
+ if (bots.length > 0) {
61
+ lines.push("", "### Bots/Apps", "");
62
+ lines.push("| Name | ID |");
63
+ lines.push("|------|----|");
64
+ for (const bot of bots) {
65
+ const name = bot.displayName || "(unnamed bot)";
66
+ lines.push(`| ${name} | ${bot.id} |`);
67
+ }
68
+ }
69
+ return lines.join("\n");
70
+ },
71
+ formatToon: (result) => {
72
+ const members = result;
73
+ const people = members.filter((member) => member.memberType === "person");
74
+ const bots = members.filter((member) => member.memberType === "bot");
75
+ const lines = [
76
+ (0, formatters_js_1.toonHeader)("👥", `${people.length} People, ${bots.length} Bots`),
77
+ ];
78
+ for (const member of people) {
79
+ const name = member.displayName || "(unknown)";
80
+ lines.push("");
81
+ lines.push(` 👤 ${name} · ${member.role}`);
82
+ lines.push(` ${member.id}`);
83
+ }
84
+ if (bots.length > 0) {
85
+ lines.push("");
86
+ lines.push(" 🤖 Bots/Apps:");
87
+ for (const bot of bots) {
88
+ const name = bot.displayName || "(unnamed bot)";
89
+ lines.push(` 🤖 ${name} — ${bot.id}`);
90
+ }
91
+ }
92
+ return lines.join("\n");
93
+ },
94
+ };
95
+ exports.whoami = {
96
+ name: "whoami",
97
+ title: "Current User Info",
98
+ description: "Get the display name and region of the currently authenticated user.",
99
+ parameters: [],
100
+ execute: async (client) => {
101
+ const displayName = await client.getCurrentUserDisplayName();
102
+ const token = client.getToken();
103
+ return { displayName, region: token.region };
104
+ },
105
+ formatResult: (result) => {
106
+ const { displayName, region } = result;
107
+ return `${displayName} (region: ${region})`;
108
+ },
109
+ formatMarkdown: (result) => {
110
+ const { displayName, region } = result;
111
+ return [`## ${displayName}`, "", `- **Region:** ${region}`].join("\n");
112
+ },
113
+ formatToon: (result) => {
114
+ const { displayName, region } = result;
115
+ return [(0, formatters_js_1.toonHeader)("🙋", displayName), ` 📍 region: ${region}`].join("\n");
116
+ },
117
+ };
118
+ exports.getTranscript = {
119
+ name: "get-transcript",
120
+ title: "Get Meeting Transcript",
121
+ description: "Get the meeting transcript from a conversation that contains a recorded meeting. " +
122
+ "Identify the conversation by topic name (--chat), " +
123
+ "person name for 1:1 chats (--to), or direct ID (--conversation-id). " +
124
+ "Use --raw-vtt to get the original VTT file instead of parsed output.",
125
+ parameters: [
126
+ ...conversation_resolution_js_1.conversationParameters,
127
+ {
128
+ name: "rawVtt",
129
+ type: "boolean",
130
+ description: "Return the original VTT file content instead of parsed transcript (default: false)",
131
+ required: false,
132
+ default: false,
133
+ },
134
+ ],
135
+ execute: async (client, parameters) => {
136
+ const { conversationId } = await (0, conversation_resolution_js_1.resolveConversationId)(client, parameters);
137
+ const rawVtt = parameters.rawVtt ?? false;
138
+ const transcriptResult = await client.getTranscript(conversationId);
139
+ if (rawVtt) {
140
+ return { rawVtt: transcriptResult.rawVtt, format: "vtt" };
141
+ }
142
+ return transcriptResult;
143
+ },
144
+ formatResult: (result) => {
145
+ const data = result;
146
+ if ("format" in data && data.format === "vtt") {
147
+ return data.rawVtt;
148
+ }
149
+ const transcript = data;
150
+ const groups = (0, formatters_js_1.groupBySpeaker)(transcript.entries);
151
+ const lines = [
152
+ `\nTranscript: ${transcript.meetingTitle} (${transcript.entries.length} segments)\n`,
153
+ ];
154
+ for (const group of groups) {
155
+ const time = (0, formatters_js_1.formatTimestamp)(group.startTime);
156
+ lines.push(` [${time}] ${group.speaker}:`);
157
+ lines.push(` ${group.segments.join(" ")}`);
158
+ }
159
+ return lines.join("\n");
160
+ },
161
+ formatMarkdown: (result) => {
162
+ const data = result;
163
+ if ("format" in data && data.format === "vtt") {
164
+ return ["```vtt", data.rawVtt, "```"].join("\n");
165
+ }
166
+ const transcript = data;
167
+ const groups = (0, formatters_js_1.groupBySpeaker)(transcript.entries);
168
+ const lines = [
169
+ `## Transcript: ${transcript.meetingTitle}`,
170
+ "",
171
+ `*${transcript.entries.length} segments*`,
172
+ "",
173
+ ];
174
+ for (const group of groups) {
175
+ const time = (0, formatters_js_1.formatTimestamp)(group.startTime);
176
+ lines.push(`**${group.speaker}** *(${time})*`, "");
177
+ lines.push(group.segments.join(" "), "");
178
+ }
179
+ return lines.join("\n");
180
+ },
181
+ formatToon: (result) => {
182
+ const data = result;
183
+ if ("format" in data && data.format === "vtt") {
184
+ return data.rawVtt;
185
+ }
186
+ const transcript = data;
187
+ const groups = (0, formatters_js_1.groupBySpeaker)(transcript.entries);
188
+ const lines = [
189
+ (0, formatters_js_1.toonHeader)("🎙️", `Transcript: ${transcript.meetingTitle} (${transcript.entries.length} segments)`),
190
+ ];
191
+ for (const group of groups) {
192
+ const time = (0, formatters_js_1.formatTimestamp)(group.startTime);
193
+ lines.push("");
194
+ lines.push(` 🗣️ ${group.speaker} · ${time}`);
195
+ lines.push(` ${group.segments.join(" ")}`);
196
+ }
197
+ return lines.join("\n");
198
+ },
199
+ };
200
+ //# sourceMappingURL=utility-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utility-actions.js","sourceRoot":"","sources":["../../src/actions/utility-actions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Attachment parsing and download utilities for Teams messages.
3
+ *
4
+ * Handles two types of attachments:
5
+ * - Inline images (AMS) — embedded as `<img>` tags in message HTML
6
+ * - File attachments (SharePoint) — referenced in `properties.files` JSON
7
+ */
8
+ import type { TeamsToken, ImageAttachment, FileAttachment } from "../types.js";
9
+ /**
10
+ * Extract inline image attachments from message HTML content.
11
+ *
12
+ * Parses `<img>` tags with `itemtype="http://schema.skype.com/AMSImage"` and
13
+ * extracts the AMS object ID, URL, and dimensions from the tag attributes.
14
+ */
15
+ export declare function parseInlineImages(content: string): ImageAttachment[];
16
+ /**
17
+ * Parse file attachments from the raw `properties.files` JSON string.
18
+ *
19
+ * File attachments are SharePoint-hosted documents, videos, and other files
20
+ * shared through Teams. They have a separate schema from inline images.
21
+ */
22
+ export declare function parseFileAttachments(rawFiles: unknown): FileAttachment[];
23
+ /**
24
+ * Fetch an image from the AMS (Async Media Service).
25
+ *
26
+ * Returns the raw binary data and content type. Uses the skype token
27
+ * for authentication (same pattern as transcript fetching).
28
+ *
29
+ * @param view - AMS view name: "imgo" (compressed), "imgpsh_fullsize_anim" (full-size)
30
+ */
31
+ export declare function fetchAmsImage(token: TeamsToken, amsObjectId: string, view?: "imgo" | "imgpsh_fullsize_anim"): Promise<{
32
+ data: Buffer;
33
+ contentType: string;
34
+ size: number;
35
+ }>;
36
+ /**
37
+ * Upload an image to the AMS (Async Media Service).
38
+ *
39
+ * Creates a new AMS object with permissions for the target conversation,
40
+ * uploads the image data, and returns the object ID that can be referenced
41
+ * in message HTML via `<img>` tags.
42
+ *
43
+ * Requires the AMS/IC3 Bearer token (audience: ic3.teams.office.com), not the
44
+ * middle-tier bearer or skype token.
45
+ */
46
+ export declare function uploadAmsImage(token: TeamsToken, imageData: Buffer, fileName: string, conversationId: string): Promise<{
47
+ amsObjectId: string;
48
+ }>;
49
+ /** Response from SharePoint file upload. */
50
+ export interface SharePointUploadResult {
51
+ /** SharePoint item unique ID. */
52
+ itemId: string;
53
+ /** SharePoint site ID. */
54
+ siteId: string;
55
+ /** File name (may differ from input if conflict-renamed). */
56
+ fileName: string;
57
+ /** File extension without dot. */
58
+ fileType: string;
59
+ /** Direct SharePoint file URL. */
60
+ fileUrl: string;
61
+ /** WebDAV URL for the file. */
62
+ webDavUrl: string;
63
+ /** SharePoint site base URL. */
64
+ siteBaseUrl: string;
65
+ /** SharePoint personal site path segment (e.g. "/personal/user_domain_com/"). */
66
+ personalPath: string;
67
+ }
68
+ /**
69
+ * Upload a file to SharePoint OneDrive for Business (Teams Chat Files folder).
70
+ *
71
+ * Files shared in Teams conversations are stored in the sender's OneDrive
72
+ * under "Microsoft Teams Chat Files". This function uploads a file there
73
+ * using the SharePoint REST API, matching the same flow the Teams web client uses.
74
+ *
75
+ * @param email - The sender's corporate email (used to derive the personal site path)
76
+ */
77
+ export declare function uploadSharePointFile(token: TeamsToken, fileData: Buffer, fileName: string, email: string): Promise<SharePointUploadResult>;
78
+ /**
79
+ * Build the `properties.files` JSON string for a message with file attachments.
80
+ *
81
+ * This JSON is included in the message body when sending messages with
82
+ * SharePoint-hosted file attachments.
83
+ */
84
+ export declare function buildFilesPropertyJson(uploadResults: SharePointUploadResult[]): string;
85
+ /**
86
+ * Build an HTML `<img>` tag for an AMS-hosted image.
87
+ *
88
+ * This produces the same markup that the Teams web client generates.
89
+ */
90
+ export declare function buildAmsImageTag(amsObjectId: string, width?: number, height?: number): string;
91
+ /**
92
+ * Download a file attachment from SharePoint.
93
+ *
94
+ * File attachments in Teams are hosted on SharePoint (OneDrive for Business).
95
+ * This function downloads the file content using the SharePoint REST API
96
+ * and the captured SharePoint bearer token.
97
+ *
98
+ * @param fileUrl - Direct SharePoint URL from `FileAttachment.fileUrl`
99
+ */
100
+ export declare function fetchSharePointFile(token: TeamsToken, fileUrl: string, itemId: string): Promise<{
101
+ data: Buffer;
102
+ contentType: string;
103
+ size: number;
104
+ fileName: string;
105
+ }>;
106
+ //# sourceMappingURL=attachments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../../src/api/attachments.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAW/E;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CAuCpE;AAiBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,GAAG,cAAc,EAAE,CA6BxE;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,UAAU,EACjB,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,MAAM,GAAG,sBAA+B,GAC7C,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAyB9D;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAiElC;AAED,4CAA4C;AAC5C,MAAM,WAAW,sBAAsB;IACrC,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,YAAY,EAAE,MAAM,CAAC;CACtB;AA6BD;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,sBAAsB,CAAC,CA6DjC;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,sBAAsB,EAAE,GACtC,MAAM,CAiCR;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAKR;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CA+CD"}
@@ -0,0 +1,341 @@
1
+ "use strict";
2
+ /**
3
+ * Attachment parsing and download utilities for Teams messages.
4
+ *
5
+ * Handles two types of attachments:
6
+ * - Inline images (AMS) — embedded as `<img>` tags in message HTML
7
+ * - File attachments (SharePoint) — referenced in `properties.files` JSON
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.parseInlineImages = parseInlineImages;
11
+ exports.parseFileAttachments = parseFileAttachments;
12
+ exports.fetchAmsImage = fetchAmsImage;
13
+ exports.uploadAmsImage = uploadAmsImage;
14
+ exports.uploadSharePointFile = uploadSharePointFile;
15
+ exports.buildFilesPropertyJson = buildFilesPropertyJson;
16
+ exports.buildAmsImageTag = buildAmsImageTag;
17
+ exports.fetchSharePointFile = fetchSharePointFile;
18
+ const common_js_1 = require("./common.js");
19
+ const AMS_BASE = "https://as-prod.asyncgw.teams.microsoft.com/v1/objects";
20
+ /**
21
+ * Teams client version header required by the AMS API.
22
+ * AMS uses this to determine the "platform id" and rejects requests without it.
23
+ */
24
+ const AMS_CLIENT_VERSION = "1415/26022704215";
25
+ /**
26
+ * Extract inline image attachments from message HTML content.
27
+ *
28
+ * Parses `<img>` tags with `itemtype="http://schema.skype.com/AMSImage"` and
29
+ * extracts the AMS object ID, URL, and dimensions from the tag attributes.
30
+ */
31
+ function parseInlineImages(content) {
32
+ const images = [];
33
+ const imagePattern = /<img\s+[^>]*itemtype="http:\/\/schema\.skype\.com\/AMSImage"[^>]*>/gi;
34
+ let match;
35
+ while ((match = imagePattern.exec(content)) !== null) {
36
+ const tag = match[0];
37
+ const contentPosition = match.index;
38
+ const srcMatch = tag.match(/src="([^"]+)"/);
39
+ if (!srcMatch)
40
+ continue;
41
+ const url = srcMatch[1];
42
+ const amsIdMatch = url.match(/\/objects\/([^/]+)\//);
43
+ if (!amsIdMatch)
44
+ continue;
45
+ const amsObjectId = amsIdMatch[1];
46
+ let width = null;
47
+ let height = null;
48
+ const widthMatch = tag.match(/width:(\d+)px/);
49
+ const heightMatch = tag.match(/height:(\d+)px/);
50
+ if (widthMatch)
51
+ width = Number(widthMatch[1]);
52
+ if (heightMatch)
53
+ height = Number(heightMatch[1]);
54
+ const fullSizeUrl = `${AMS_BASE}/${amsObjectId}/views/imgpsh_fullsize_anim`;
55
+ images.push({
56
+ amsObjectId,
57
+ url,
58
+ fullSizeUrl,
59
+ width,
60
+ height,
61
+ contentPosition,
62
+ });
63
+ }
64
+ return images;
65
+ }
66
+ /**
67
+ * Parse file attachments from the raw `properties.files` JSON string.
68
+ *
69
+ * File attachments are SharePoint-hosted documents, videos, and other files
70
+ * shared through Teams. They have a separate schema from inline images.
71
+ */
72
+ function parseFileAttachments(rawFiles) {
73
+ let entries;
74
+ if (typeof rawFiles === "string") {
75
+ try {
76
+ entries = JSON.parse(rawFiles);
77
+ }
78
+ catch {
79
+ return [];
80
+ }
81
+ }
82
+ else if (Array.isArray(rawFiles)) {
83
+ entries = rawFiles;
84
+ }
85
+ else {
86
+ return [];
87
+ }
88
+ if (!Array.isArray(entries))
89
+ return [];
90
+ return entries
91
+ .filter((entry) => entry["@type"] === "http://schema.skype.com/File" && entry.fileName)
92
+ .map((entry) => ({
93
+ itemId: entry.itemid ?? entry.id ?? "",
94
+ fileName: entry.fileName ?? entry.title ?? "",
95
+ fileType: entry.fileType ?? "",
96
+ fileUrl: entry.fileInfo?.fileUrl ?? entry.objectUrl ?? "",
97
+ shareUrl: entry.fileInfo?.shareUrl ?? "",
98
+ }));
99
+ }
100
+ /**
101
+ * Fetch an image from the AMS (Async Media Service).
102
+ *
103
+ * Returns the raw binary data and content type. Uses the skype token
104
+ * for authentication (same pattern as transcript fetching).
105
+ *
106
+ * @param view - AMS view name: "imgo" (compressed), "imgpsh_fullsize_anim" (full-size)
107
+ */
108
+ async function fetchAmsImage(token, amsObjectId, view = "imgo") {
109
+ const url = `${AMS_BASE}/${amsObjectId}/views/${view}`;
110
+ const response = await (0, common_js_1.fetchWithRetry)(url, {
111
+ headers: {
112
+ Authorization: `skype_token ${token.skypeToken}`,
113
+ },
114
+ });
115
+ if (!response.ok) {
116
+ if (response.status === 401) {
117
+ throw new common_js_1.ApiAuthError(`AMS image fetch authentication failed: ${response.status} ${response.statusText}`);
118
+ }
119
+ throw new Error(`Failed to fetch AMS image: ${response.status} ${response.statusText}`);
120
+ }
121
+ const contentType = response.headers.get("content-type") ?? "image/jpeg";
122
+ const arrayBuffer = await response.arrayBuffer();
123
+ const data = Buffer.from(arrayBuffer);
124
+ return { data, contentType, size: data.length };
125
+ }
126
+ /**
127
+ * Upload an image to the AMS (Async Media Service).
128
+ *
129
+ * Creates a new AMS object with permissions for the target conversation,
130
+ * uploads the image data, and returns the object ID that can be referenced
131
+ * in message HTML via `<img>` tags.
132
+ *
133
+ * Requires the AMS/IC3 Bearer token (audience: ic3.teams.office.com), not the
134
+ * middle-tier bearer or skype token.
135
+ */
136
+ async function uploadAmsImage(token, imageData, fileName, conversationId) {
137
+ if (!token.amsToken) {
138
+ throw new Error("AMS token is required for image upload but was not captured during authentication.");
139
+ }
140
+ // Step 1: Create the AMS object with inline permissions
141
+ const createResponse = await (0, common_js_1.fetchWithRetry)(`${AMS_BASE}/`, {
142
+ method: "POST",
143
+ headers: {
144
+ Authorization: `Bearer ${token.amsToken}`,
145
+ "Content-Type": "application/json",
146
+ "x-ms-client-version": AMS_CLIENT_VERSION,
147
+ },
148
+ body: JSON.stringify({
149
+ type: "pish/image",
150
+ permissions: { [conversationId]: ["read"] },
151
+ sharingMode: "Attached",
152
+ filename: fileName,
153
+ }),
154
+ });
155
+ if (!createResponse.ok) {
156
+ if (createResponse.status === 401) {
157
+ throw new common_js_1.ApiAuthError(`AMS object creation failed: ${createResponse.status} ${createResponse.statusText}`);
158
+ }
159
+ const errorText = await createResponse.text();
160
+ throw new Error(`Failed to create AMS object: ${createResponse.status} ${createResponse.statusText} — ${errorText}`);
161
+ }
162
+ const createData = (await createResponse.json());
163
+ const amsObjectId = createData.id;
164
+ // Step 2: Upload the image content to the fixed "imgpsh" content path
165
+ const uploadResponse = await (0, common_js_1.fetchWithRetry)(`${AMS_BASE}/${amsObjectId}/content/imgpsh`, {
166
+ method: "PUT",
167
+ headers: {
168
+ Authorization: `Bearer ${token.amsToken}`,
169
+ "Content-Type": "application/octet-stream",
170
+ "x-ms-client-version": AMS_CLIENT_VERSION,
171
+ },
172
+ body: imageData,
173
+ });
174
+ if (!uploadResponse.ok) {
175
+ if (uploadResponse.status === 401) {
176
+ throw new common_js_1.ApiAuthError(`AMS image upload failed: ${uploadResponse.status} ${uploadResponse.statusText}`);
177
+ }
178
+ const errorText = await uploadResponse.text();
179
+ throw new Error(`Failed to upload AMS image: ${uploadResponse.status} ${uploadResponse.statusText} — ${errorText}`);
180
+ }
181
+ return { amsObjectId };
182
+ }
183
+ /**
184
+ * Derive the SharePoint personal site URL components from a token and email.
185
+ *
186
+ * The SharePoint host comes from `token.sharePointHost` (captured from the
187
+ * MSAL cache during authentication).
188
+ * The personal path is derived from the email by replacing `.` and `@` with `_`.
189
+ */
190
+ function deriveSharePointSiteInfo(token, email) {
191
+ if (!token.sharePointHost) {
192
+ throw new Error("SharePoint host is required for file upload but was not captured during authentication. " +
193
+ "Re-authenticate to capture the SharePoint host.");
194
+ }
195
+ const siteBaseUrl = `https://${token.sharePointHost}`;
196
+ // email "user.name@company.com" → "user_name_company_com"
197
+ const personalSegment = email.replace(/[.@]/g, "_");
198
+ const personalPath = `/personal/${personalSegment}`;
199
+ return { siteBaseUrl, personalPath };
200
+ }
201
+ /**
202
+ * Upload a file to SharePoint OneDrive for Business (Teams Chat Files folder).
203
+ *
204
+ * Files shared in Teams conversations are stored in the sender's OneDrive
205
+ * under "Microsoft Teams Chat Files". This function uploads a file there
206
+ * using the SharePoint REST API, matching the same flow the Teams web client uses.
207
+ *
208
+ * @param email - The sender's corporate email (used to derive the personal site path)
209
+ */
210
+ async function uploadSharePointFile(token, fileData, fileName, email) {
211
+ if (!token.sharePointToken) {
212
+ throw new Error("SharePoint token is required for file upload but was not captured during authentication. " +
213
+ "Re-authenticate to capture the SharePoint token.");
214
+ }
215
+ const { siteBaseUrl, personalPath } = deriveSharePointSiteInfo(token, email);
216
+ const encodedFileName = encodeURIComponent(fileName);
217
+ const uploadUrl = `${siteBaseUrl}${personalPath}/_api/v2.0/drive/root:/Microsoft%20Teams%20Chat%20Files/${encodedFileName}:/content` +
218
+ `?@name.conflictBehavior=rename&$select=*,sharepointIds,webDavUrl`;
219
+ const response = await (0, common_js_1.fetchWithRetry)(uploadUrl, {
220
+ method: "PUT",
221
+ headers: {
222
+ Authorization: `Bearer ${token.sharePointToken}`,
223
+ "Content-Type": "application/octet-stream",
224
+ },
225
+ body: fileData,
226
+ });
227
+ if (!response.ok) {
228
+ if (response.status === 401) {
229
+ throw new common_js_1.ApiAuthError(`SharePoint file upload authentication failed: ${response.status} ${response.statusText}`);
230
+ }
231
+ const errorText = await response.text();
232
+ throw new Error(`Failed to upload file to SharePoint: ${response.status} ${response.statusText} — ${errorText}`);
233
+ }
234
+ const data = (await response.json());
235
+ const fileExtension = data.name.includes(".")
236
+ ? (data.name.split(".").pop() ?? "")
237
+ : "";
238
+ return {
239
+ itemId: data.sharepointIds.listItemUniqueId,
240
+ siteId: data.sharepointIds.siteId,
241
+ fileName: data.name,
242
+ fileType: fileExtension,
243
+ fileUrl: data.webUrl,
244
+ webDavUrl: data.webDavUrl,
245
+ siteBaseUrl,
246
+ personalPath,
247
+ };
248
+ }
249
+ /**
250
+ * Build the `properties.files` JSON string for a message with file attachments.
251
+ *
252
+ * This JSON is included in the message body when sending messages with
253
+ * SharePoint-hosted file attachments.
254
+ */
255
+ function buildFilesPropertyJson(uploadResults) {
256
+ const files = uploadResults.map((result) => ({
257
+ "@type": "http://schema.skype.com/File",
258
+ version: 2,
259
+ id: result.itemId,
260
+ itemid: result.itemId,
261
+ fileName: result.fileName,
262
+ fileType: result.fileType,
263
+ title: result.fileName,
264
+ type: result.fileType,
265
+ state: "active",
266
+ objectUrl: `${result.siteBaseUrl}${result.personalPath}/Documents/Microsoft%20Teams%20Chat%20Files/${encodeURIComponent(result.fileName)}`,
267
+ baseUrl: `${result.siteBaseUrl}${result.personalPath}/`,
268
+ permissionScope: "users",
269
+ sharepointIds: {
270
+ listItemUniqueId: result.itemId,
271
+ siteId: result.siteId,
272
+ },
273
+ fileInfo: {
274
+ itemId: null,
275
+ fileUrl: `${result.siteBaseUrl}${result.personalPath}/Documents/Microsoft%20Teams%20Chat%20Files/${encodeURIComponent(result.fileName)}`,
276
+ siteUrl: `${result.siteBaseUrl}${result.personalPath}/`,
277
+ serverRelativeUrl: `${result.personalPath}/Documents/Microsoft Teams Chat Files/${result.fileName}`,
278
+ shareUrl: null,
279
+ shareId: null,
280
+ },
281
+ fileChicletState: {
282
+ serviceName: "p2p",
283
+ state: "active",
284
+ },
285
+ }));
286
+ return JSON.stringify(files);
287
+ }
288
+ /**
289
+ * Build an HTML `<img>` tag for an AMS-hosted image.
290
+ *
291
+ * This produces the same markup that the Teams web client generates.
292
+ */
293
+ function buildAmsImageTag(amsObjectId, width, height) {
294
+ const src = `${AMS_BASE}/${amsObjectId}/views/imgo`;
295
+ const styleAttribute = width && height ? ` style="width:${width}px; height:${height}px"` : "";
296
+ return `<img src="${src}" itemscope="" itemtype="http://schema.skype.com/AMSImage"${styleAttribute}>`;
297
+ }
298
+ /**
299
+ * Download a file attachment from SharePoint.
300
+ *
301
+ * File attachments in Teams are hosted on SharePoint (OneDrive for Business).
302
+ * This function downloads the file content using the SharePoint REST API
303
+ * and the captured SharePoint bearer token.
304
+ *
305
+ * @param fileUrl - Direct SharePoint URL from `FileAttachment.fileUrl`
306
+ */
307
+ async function fetchSharePointFile(token, fileUrl, itemId) {
308
+ if (!token.sharePointToken) {
309
+ throw new Error("SharePoint token is required for file download but was not captured during authentication. " +
310
+ "Re-authenticate to capture the SharePoint token.");
311
+ }
312
+ // Extract the SharePoint site base URL and the personal site path from fileUrl.
313
+ // fileUrl looks like: https://tenant-my.sharepoint.com/personal/user_name_company_com/Documents/...
314
+ const parsedUrl = new URL(fileUrl);
315
+ const siteBaseUrl = `${parsedUrl.protocol}//${parsedUrl.host}`;
316
+ const pathSegments = decodeURIComponent(parsedUrl.pathname).split("/");
317
+ // pathSegments: ['', 'personal', 'user_name_company_com', 'Documents', ...]
318
+ const personalPath = `/${pathSegments[1]}/${pathSegments[2]}`;
319
+ // Use the SharePoint drive items API with the item's unique ID.
320
+ // This avoids path-encoding issues with special characters in filenames,
321
+ // and works for files on any user's personal OneDrive.
322
+ const downloadUrl = `${siteBaseUrl}${personalPath}/_api/v2.0/drive/items/${itemId}/content`;
323
+ const response = await (0, common_js_1.fetchWithRetry)(downloadUrl, {
324
+ headers: {
325
+ Authorization: `Bearer ${token.sharePointToken}`,
326
+ },
327
+ });
328
+ if (!response.ok) {
329
+ if (response.status === 401) {
330
+ throw new common_js_1.ApiAuthError(`SharePoint file download authentication failed: ${response.status} ${response.statusText}`);
331
+ }
332
+ throw new Error(`Failed to download SharePoint file: ${response.status} ${response.statusText}`);
333
+ }
334
+ const contentType = response.headers.get("content-type") ?? "application/octet-stream";
335
+ const arrayBuffer = await response.arrayBuffer();
336
+ const data = Buffer.from(arrayBuffer);
337
+ // Extract filename from the original file URL path
338
+ const fileName = pathSegments[pathSegments.length - 1] ?? "unknown";
339
+ return { data, contentType, size: data.length, fileName };
340
+ }
341
+ //# sourceMappingURL=attachments.js.map