@chat-adapter/teams 4.0.2 → 4.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/dist/index.d.ts +16 -7
- package/dist/index.js +101 -7
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CardElement, BaseFormatConverter,
|
|
1
|
+
import { CardElement, BaseFormatConverter, AdapterPostableMessage, Root, Adapter, ChatInstance, WebhookOptions, RawMessage, EmojiValue, FetchOptions, Message, ThreadInfo, FormattedContent } from 'chat';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Teams Adaptive Card converter for cross-platform cards.
|
|
@@ -56,7 +56,7 @@ declare class TeamsFormatConverter extends BaseFormatConverter {
|
|
|
56
56
|
/**
|
|
57
57
|
* Override renderPostable to convert @mentions in plain strings.
|
|
58
58
|
*/
|
|
59
|
-
renderPostable(message:
|
|
59
|
+
renderPostable(message: AdapterPostableMessage): string;
|
|
60
60
|
/**
|
|
61
61
|
* Render an AST to Teams format.
|
|
62
62
|
* Teams accepts standard markdown, so we just stringify cleanly.
|
|
@@ -93,6 +93,7 @@ declare class TeamsAdapter implements Adapter<TeamsThreadId, unknown> {
|
|
|
93
93
|
readonly userName: string;
|
|
94
94
|
readonly botUserId?: string;
|
|
95
95
|
private botAdapter;
|
|
96
|
+
private graphClient;
|
|
96
97
|
private chat;
|
|
97
98
|
private logger;
|
|
98
99
|
private formatConverter;
|
|
@@ -125,13 +126,13 @@ declare class TeamsAdapter implements Adapter<TeamsThreadId, unknown> {
|
|
|
125
126
|
*/
|
|
126
127
|
private createAttachment;
|
|
127
128
|
private normalizeMentions;
|
|
128
|
-
postMessage(threadId: string, message:
|
|
129
|
+
postMessage(threadId: string, message: AdapterPostableMessage): Promise<RawMessage<unknown>>;
|
|
129
130
|
/**
|
|
130
|
-
* Extract card element from a
|
|
131
|
+
* Extract card element from a AdapterPostableMessage if present.
|
|
131
132
|
*/
|
|
132
133
|
private extractCard;
|
|
133
134
|
/**
|
|
134
|
-
* Extract files from a
|
|
135
|
+
* Extract files from a AdapterPostableMessage if present.
|
|
135
136
|
*/
|
|
136
137
|
private extractFiles;
|
|
137
138
|
/**
|
|
@@ -139,7 +140,7 @@ declare class TeamsAdapter implements Adapter<TeamsThreadId, unknown> {
|
|
|
139
140
|
* Uses inline data URIs for small files.
|
|
140
141
|
*/
|
|
141
142
|
private filesToAttachments;
|
|
142
|
-
editMessage(threadId: string, messageId: string, message:
|
|
143
|
+
editMessage(threadId: string, messageId: string, message: AdapterPostableMessage): Promise<RawMessage<unknown>>;
|
|
143
144
|
deleteMessage(threadId: string, messageId: string): Promise<void>;
|
|
144
145
|
addReaction(_threadId: string, _messageId: string, _emoji: EmojiValue | string): Promise<void>;
|
|
145
146
|
removeReaction(_threadId: string, _messageId: string, _emoji: EmojiValue | string): Promise<void>;
|
|
@@ -152,7 +153,15 @@ declare class TeamsAdapter implements Adapter<TeamsThreadId, unknown> {
|
|
|
152
153
|
* If no cached values are found, defaults are used (which may not work for all tenants).
|
|
153
154
|
*/
|
|
154
155
|
openDM(userId: string): Promise<string>;
|
|
155
|
-
fetchMessages(
|
|
156
|
+
fetchMessages(threadId: string, options?: FetchOptions): Promise<Message<unknown>[]>;
|
|
157
|
+
/**
|
|
158
|
+
* Extract plain text from a Graph API message.
|
|
159
|
+
*/
|
|
160
|
+
private extractTextFromGraphMessage;
|
|
161
|
+
/**
|
|
162
|
+
* Extract attachments from a Graph API message.
|
|
163
|
+
*/
|
|
164
|
+
private extractAttachmentsFromGraphMessage;
|
|
156
165
|
fetchThread(threadId: string): Promise<ThreadInfo>;
|
|
157
166
|
encodeThreadId(platformData: TeamsThreadId): string;
|
|
158
167
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
import { ClientSecretCredential } from "@azure/identity";
|
|
3
|
+
import { Client } from "@microsoft/microsoft-graph-client";
|
|
4
|
+
import {
|
|
5
|
+
TokenCredentialAuthenticationProvider
|
|
6
|
+
} from "@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials";
|
|
2
7
|
import {
|
|
3
8
|
ActivityTypes,
|
|
4
9
|
CloudAdapter,
|
|
@@ -321,6 +326,7 @@ var TeamsAdapter = class {
|
|
|
321
326
|
userName;
|
|
322
327
|
botUserId;
|
|
323
328
|
botAdapter;
|
|
329
|
+
graphClient = null;
|
|
324
330
|
chat = null;
|
|
325
331
|
logger = null;
|
|
326
332
|
formatConverter = new TeamsFormatConverter();
|
|
@@ -338,6 +344,20 @@ var TeamsAdapter = class {
|
|
|
338
344
|
MicrosoftAppTenantId: config.appType === "SingleTenant" ? config.appTenantId : void 0
|
|
339
345
|
});
|
|
340
346
|
this.botAdapter = new ServerlessCloudAdapter(auth);
|
|
347
|
+
if (config.appTenantId) {
|
|
348
|
+
const credential = new ClientSecretCredential(
|
|
349
|
+
config.appTenantId,
|
|
350
|
+
config.appId,
|
|
351
|
+
config.appPassword
|
|
352
|
+
);
|
|
353
|
+
const authProvider = new TokenCredentialAuthenticationProvider(
|
|
354
|
+
credential,
|
|
355
|
+
{
|
|
356
|
+
scopes: ["https://graph.microsoft.com/.default"]
|
|
357
|
+
}
|
|
358
|
+
);
|
|
359
|
+
this.graphClient = Client.initWithMiddleware({ authProvider });
|
|
360
|
+
}
|
|
341
361
|
}
|
|
342
362
|
async initialize(chat) {
|
|
343
363
|
this.chat = chat;
|
|
@@ -697,7 +717,7 @@ var TeamsAdapter = class {
|
|
|
697
717
|
};
|
|
698
718
|
}
|
|
699
719
|
/**
|
|
700
|
-
* Extract card element from a
|
|
720
|
+
* Extract card element from a AdapterPostableMessage if present.
|
|
701
721
|
*/
|
|
702
722
|
extractCard(message) {
|
|
703
723
|
if (isCardElement(message)) {
|
|
@@ -709,7 +729,7 @@ var TeamsAdapter = class {
|
|
|
709
729
|
return null;
|
|
710
730
|
}
|
|
711
731
|
/**
|
|
712
|
-
* Extract files from a
|
|
732
|
+
* Extract files from a AdapterPostableMessage if present.
|
|
713
733
|
*/
|
|
714
734
|
extractFiles(message) {
|
|
715
735
|
if (typeof message === "object" && message !== null && "files" in message) {
|
|
@@ -911,11 +931,85 @@ var TeamsAdapter = class {
|
|
|
911
931
|
serviceUrl
|
|
912
932
|
});
|
|
913
933
|
}
|
|
914
|
-
async fetchMessages(
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
934
|
+
async fetchMessages(threadId, options = {}) {
|
|
935
|
+
if (!this.graphClient) {
|
|
936
|
+
throw new NotImplementedError(
|
|
937
|
+
"Teams fetchMessages requires appTenantId to be configured for Microsoft Graph API access.",
|
|
938
|
+
"fetchMessages"
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
const { conversationId } = this.decodeThreadId(threadId);
|
|
942
|
+
const limit = options.limit || 50;
|
|
943
|
+
try {
|
|
944
|
+
this.logger?.debug("Teams Graph API: fetching messages", {
|
|
945
|
+
conversationId,
|
|
946
|
+
limit
|
|
947
|
+
});
|
|
948
|
+
const response = await this.graphClient.api(`/chats/${encodeURIComponent(conversationId)}/messages`).top(limit).orderby("createdDateTime desc").get();
|
|
949
|
+
const graphMessages = response.value || [];
|
|
950
|
+
this.logger?.debug("Teams Graph API: fetched messages", {
|
|
951
|
+
count: graphMessages.length
|
|
952
|
+
});
|
|
953
|
+
return graphMessages.map((msg) => {
|
|
954
|
+
const isFromBot = msg.from?.application?.id === this.config.appId || msg.from?.user?.id === this.config.appId;
|
|
955
|
+
return {
|
|
956
|
+
id: msg.id,
|
|
957
|
+
threadId,
|
|
958
|
+
text: this.extractTextFromGraphMessage(msg),
|
|
959
|
+
formatted: this.formatConverter.toAst(
|
|
960
|
+
this.extractTextFromGraphMessage(msg)
|
|
961
|
+
),
|
|
962
|
+
raw: msg,
|
|
963
|
+
author: {
|
|
964
|
+
userId: msg.from?.user?.id || msg.from?.application?.id || "unknown",
|
|
965
|
+
userName: msg.from?.user?.displayName || msg.from?.application?.displayName || "unknown",
|
|
966
|
+
fullName: msg.from?.user?.displayName || msg.from?.application?.displayName || "unknown",
|
|
967
|
+
isBot: !!msg.from?.application,
|
|
968
|
+
isMe: isFromBot
|
|
969
|
+
},
|
|
970
|
+
metadata: {
|
|
971
|
+
dateSent: msg.createdDateTime ? new Date(msg.createdDateTime) : /* @__PURE__ */ new Date(),
|
|
972
|
+
edited: !!msg.lastModifiedDateTime
|
|
973
|
+
},
|
|
974
|
+
attachments: this.extractAttachmentsFromGraphMessage(msg)
|
|
975
|
+
};
|
|
976
|
+
});
|
|
977
|
+
} catch (error) {
|
|
978
|
+
this.logger?.error("Teams Graph API: fetchMessages error", { error });
|
|
979
|
+
if (error instanceof Error && error.message?.includes("403")) {
|
|
980
|
+
throw new NotImplementedError(
|
|
981
|
+
"Teams fetchMessages requires one of these Azure AD app permissions: ChatMessage.Read.Chat, Chat.Read.All, or Chat.Read.WhereInstalled",
|
|
982
|
+
"fetchMessages"
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
throw error;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Extract plain text from a Graph API message.
|
|
990
|
+
*/
|
|
991
|
+
extractTextFromGraphMessage(msg) {
|
|
992
|
+
if (msg.body?.contentType === "text") {
|
|
993
|
+
return msg.body.content || "";
|
|
994
|
+
}
|
|
995
|
+
if (msg.body?.content) {
|
|
996
|
+
return msg.body.content.replace(/<[^>]*>/g, "").trim();
|
|
997
|
+
}
|
|
998
|
+
return "";
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Extract attachments from a Graph API message.
|
|
1002
|
+
*/
|
|
1003
|
+
extractAttachmentsFromGraphMessage(msg) {
|
|
1004
|
+
if (!msg.attachments?.length) {
|
|
1005
|
+
return [];
|
|
1006
|
+
}
|
|
1007
|
+
return msg.attachments.map((att) => ({
|
|
1008
|
+
type: att.contentType?.includes("image") ? "image" : "file",
|
|
1009
|
+
name: att.name || void 0,
|
|
1010
|
+
url: att.contentUrl || void 0,
|
|
1011
|
+
mimeType: att.contentType || void 0
|
|
1012
|
+
}));
|
|
919
1013
|
}
|
|
920
1014
|
async fetchThread(threadId) {
|
|
921
1015
|
const { conversationId } = this.decodeThreadId(threadId);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/cards.ts","../src/markdown.ts"],"sourcesContent":["import type { Activity, ConversationReference } from \"botbuilder\";\nimport {\n ActivityTypes,\n CloudAdapter,\n ConfigurationBotFrameworkAuthentication,\n type TurnContext,\n} from \"botbuilder\";\n\n/** Extended CloudAdapter that exposes processActivity for serverless environments */\nclass ServerlessCloudAdapter extends CloudAdapter {\n handleActivity(\n authHeader: string,\n activity: Activity,\n logic: (context: TurnContext) => Promise<void>,\n ) {\n return this.processActivity(authHeader, activity, logic);\n }\n}\n\nimport type {\n ActionEvent,\n Adapter,\n Attachment,\n ChatInstance,\n EmojiValue,\n FetchOptions,\n FileUpload,\n FormattedContent,\n Logger,\n Message,\n PostableMessage,\n RawMessage,\n ReactionEvent,\n ThreadInfo,\n WebhookOptions,\n} from \"chat\";\nimport {\n convertEmojiPlaceholders,\n defaultEmojiResolver,\n isCardElement,\n NotImplementedError,\n} from \"chat\";\nimport { cardToAdaptiveCard } from \"./cards\";\nimport { TeamsFormatConverter } from \"./markdown\";\n\nexport interface TeamsAdapterConfig {\n /** Microsoft App ID */\n appId: string;\n /** Microsoft App Password */\n appPassword: string;\n /** Microsoft App Type */\n appType?: \"MultiTenant\" | \"SingleTenant\";\n /** Microsoft App Tenant ID */\n appTenantId?: string;\n /** Override bot username (optional) */\n userName?: string;\n}\n\n/** Teams-specific thread ID data */\nexport interface TeamsThreadId {\n conversationId: string;\n serviceUrl: string;\n replyToId?: string;\n}\n\nexport class TeamsAdapter implements Adapter<TeamsThreadId, unknown> {\n readonly name = \"teams\";\n readonly userName: string;\n readonly botUserId?: string;\n\n private botAdapter: ServerlessCloudAdapter;\n private chat: ChatInstance | null = null;\n private logger: Logger | null = null;\n private formatConverter = new TeamsFormatConverter();\n private config: TeamsAdapterConfig;\n\n constructor(config: TeamsAdapterConfig) {\n this.config = config;\n this.userName = config.userName || \"bot\";\n\n if (config.appType === \"SingleTenant\" && !config.appTenantId) {\n throw new Error(\"appTenantId is required for SingleTenant app type\");\n }\n\n // Pass empty config object, credentials go via factory\n const auth = new ConfigurationBotFrameworkAuthentication({\n MicrosoftAppId: config.appId,\n MicrosoftAppPassword: config.appPassword,\n MicrosoftAppType: config.appType || \"MultiTenant\",\n MicrosoftAppTenantId:\n config.appType === \"SingleTenant\" ? config.appTenantId : undefined,\n });\n\n this.botAdapter = new ServerlessCloudAdapter(auth);\n }\n\n async initialize(chat: ChatInstance): Promise<void> {\n this.chat = chat;\n this.logger = chat.getLogger(this.name);\n }\n\n async handleWebhook(\n request: Request,\n options?: WebhookOptions,\n ): Promise<Response> {\n const body = await request.text();\n this.logger?.debug(\"Teams webhook raw body\", { body });\n\n let activity: Activity;\n try {\n activity = JSON.parse(body);\n } catch (e) {\n this.logger?.error(\"Failed to parse request body\", { error: e });\n return new Response(\"Invalid JSON\", { status: 400 });\n }\n\n // Get the auth header for token validation\n const authHeader = request.headers.get(\"authorization\") || \"\";\n\n try {\n // Use handleActivity which takes the activity directly\n // instead of mocking Node.js req/res objects\n await this.botAdapter.handleActivity(\n authHeader,\n activity,\n async (context) => {\n await this.handleTurn(context, options);\n },\n );\n\n return new Response(JSON.stringify({}), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (error) {\n this.logger?.error(\"Bot adapter process error\", { error });\n return new Response(JSON.stringify({ error: \"Internal error\" }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n }\n\n private async handleTurn(\n context: TurnContext,\n options?: WebhookOptions,\n ): Promise<void> {\n if (!this.chat) {\n this.logger?.warn(\"Chat instance not initialized, ignoring event\");\n return;\n }\n\n const activity = context.activity;\n\n // Cache serviceUrl and tenantId for the user - needed for opening DMs later\n if (activity.from?.id && activity.serviceUrl) {\n const userId = activity.from.id;\n const tenantId = (activity.channelData as { tenant?: { id?: string } })\n ?.tenant?.id;\n const ttl = 30 * 24 * 60 * 60 * 1000; // 30 days\n\n // Store serviceUrl and tenantId for DM creation\n this.chat\n .getState()\n .set(`teams:serviceUrl:${userId}`, activity.serviceUrl, ttl);\n if (tenantId) {\n this.chat.getState().set(`teams:tenantId:${userId}`, tenantId, ttl);\n }\n }\n\n // Handle message reactions\n if (activity.type === ActivityTypes.MessageReaction) {\n this.handleReactionActivity(activity, options);\n return;\n }\n\n // Handle adaptive card actions (button clicks)\n if (activity.type === ActivityTypes.Invoke) {\n await this.handleInvokeActivity(context, options);\n return;\n }\n\n // Only handle message activities\n if (activity.type !== ActivityTypes.Message) {\n this.logger?.debug(\"Ignoring non-message activity\", {\n type: activity.type,\n });\n return;\n }\n\n // Check if this message activity is actually a button click (Action.Submit)\n // Teams sends Action.Submit as a message with value.actionId\n const actionValue = activity.value as\n | { actionId?: string; value?: string }\n | undefined;\n if (actionValue?.actionId) {\n this.handleMessageAction(activity, actionValue, options);\n return;\n }\n\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n replyToId: activity.replyToId,\n });\n\n // Let Chat class handle async processing and waitUntil\n this.chat.processMessage(\n this,\n threadId,\n this.parseTeamsMessage(activity, threadId),\n options,\n );\n }\n\n /**\n * Handle Action.Submit button clicks sent as message activities.\n * Teams sends these with type \"message\" and value.actionId.\n */\n private handleMessageAction(\n activity: Activity,\n actionValue: { actionId?: string; value?: string },\n options?: WebhookOptions,\n ): void {\n if (!this.chat || !actionValue.actionId) return;\n\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n });\n\n const actionEvent: Omit<ActionEvent, \"thread\"> & { adapter: TeamsAdapter } =\n {\n actionId: actionValue.actionId,\n value: actionValue.value,\n user: {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name || \"unknown\",\n isBot: false,\n isMe: false,\n },\n messageId: activity.replyToId || activity.id || \"\",\n threadId,\n adapter: this,\n raw: activity,\n };\n\n this.logger?.debug(\"Processing Teams message action (Action.Submit)\", {\n actionId: actionValue.actionId,\n value: actionValue.value,\n messageId: actionEvent.messageId,\n threadId,\n });\n\n this.chat.processAction(actionEvent, options);\n }\n\n /**\n * Handle invoke activities (adaptive card actions, etc.).\n */\n private async handleInvokeActivity(\n context: TurnContext,\n options?: WebhookOptions,\n ): Promise<void> {\n const activity = context.activity;\n\n // Handle adaptive card action invokes\n if (activity.name === \"adaptiveCard/action\") {\n await this.handleAdaptiveCardAction(context, activity, options);\n return;\n }\n\n this.logger?.debug(\"Ignoring unsupported invoke\", {\n name: activity.name,\n });\n }\n\n /**\n * Handle adaptive card button clicks.\n * The action data is in activity.value with our { actionId, value } structure.\n */\n private async handleAdaptiveCardAction(\n context: TurnContext,\n activity: Activity,\n options?: WebhookOptions,\n ): Promise<void> {\n if (!this.chat) return;\n\n // Activity.value contains our action data\n const actionData = activity.value?.action?.data as\n | { actionId?: string; value?: string }\n | undefined;\n\n if (!actionData?.actionId) {\n this.logger?.debug(\"Adaptive card action missing actionId\", {\n value: activity.value,\n });\n // Send acknowledgment response\n await context.sendActivity({\n type: ActivityTypes.InvokeResponse,\n value: { status: 200 },\n });\n return;\n }\n\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n });\n\n const actionEvent: Omit<ActionEvent, \"thread\"> & { adapter: TeamsAdapter } =\n {\n actionId: actionData.actionId,\n value: actionData.value,\n user: {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name || \"unknown\",\n isBot: false,\n isMe: false,\n },\n messageId: activity.replyToId || activity.id || \"\",\n threadId,\n adapter: this,\n raw: activity,\n };\n\n this.logger?.debug(\"Processing Teams adaptive card action\", {\n actionId: actionData.actionId,\n value: actionData.value,\n messageId: actionEvent.messageId,\n threadId,\n });\n\n this.chat.processAction(actionEvent, options);\n\n // Send acknowledgment response to prevent timeout\n await context.sendActivity({\n type: ActivityTypes.InvokeResponse,\n value: { status: 200 },\n });\n }\n\n /**\n * Handle Teams reaction events (reactionsAdded/reactionsRemoved).\n */\n private handleReactionActivity(\n activity: Activity,\n options?: WebhookOptions,\n ): void {\n if (!this.chat) return;\n\n // Extract the message ID from conversation ID\n // Format: \"19:xxx@thread.tacv2;messageid=1767297849909\"\n const conversationId = activity.conversation?.id || \"\";\n const messageIdMatch = conversationId.match(/messageid=(\\d+)/);\n const messageId = messageIdMatch?.[1] || activity.replyToId || \"\";\n\n // Build thread ID - KEEP the full conversation ID including ;messageid=XXX\n // This is required for Teams to reply in the correct thread\n const threadId = this.encodeThreadId({\n conversationId: conversationId,\n serviceUrl: activity.serviceUrl || \"\",\n });\n\n const user = {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name,\n isBot: false,\n isMe: this.isMessageFromSelf(activity),\n };\n\n // Process added reactions\n const reactionsAdded = activity.reactionsAdded || [];\n for (const reaction of reactionsAdded) {\n const rawEmoji = reaction.type || \"\";\n const emojiValue = defaultEmojiResolver.fromTeams(rawEmoji);\n\n const event: Omit<ReactionEvent, \"adapter\" | \"thread\"> = {\n emoji: emojiValue,\n rawEmoji,\n added: true,\n user,\n messageId,\n threadId,\n raw: activity,\n };\n\n this.logger?.debug(\"Processing Teams reaction added\", {\n emoji: emojiValue.name,\n rawEmoji,\n messageId,\n });\n\n this.chat.processReaction({ ...event, adapter: this }, options);\n }\n\n // Process removed reactions\n const reactionsRemoved = activity.reactionsRemoved || [];\n for (const reaction of reactionsRemoved) {\n const rawEmoji = reaction.type || \"\";\n const emojiValue = defaultEmojiResolver.fromTeams(rawEmoji);\n\n const event: Omit<ReactionEvent, \"adapter\" | \"thread\"> = {\n emoji: emojiValue,\n rawEmoji,\n added: false,\n user,\n messageId,\n threadId,\n raw: activity,\n };\n\n this.logger?.debug(\"Processing Teams reaction removed\", {\n emoji: emojiValue.name,\n rawEmoji,\n messageId,\n });\n\n this.chat.processReaction({ ...event, adapter: this }, options);\n }\n }\n\n private parseTeamsMessage(\n activity: Activity,\n threadId: string,\n ): Message<unknown> {\n const text = activity.text || \"\";\n // Normalize mentions - format converter will convert <at>name</at> to @name\n const normalizedText = this.normalizeMentions(text, activity);\n\n const isMe = this.isMessageFromSelf(activity);\n\n return {\n id: activity.id || \"\",\n threadId,\n text: this.formatConverter.extractPlainText(normalizedText),\n formatted: this.formatConverter.toAst(normalizedText),\n raw: activity,\n author: {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name || \"unknown\",\n isBot: activity.from?.role === \"bot\",\n isMe,\n },\n metadata: {\n dateSent: activity.timestamp\n ? new Date(activity.timestamp)\n : new Date(),\n edited: false,\n },\n attachments: (activity.attachments || [])\n .filter(\n (att) =>\n att.contentType !== \"application/vnd.microsoft.card.adaptive\",\n )\n .map((att) => this.createAttachment(att)),\n };\n }\n\n /**\n * Create an Attachment object from a Teams attachment.\n */\n private createAttachment(att: {\n contentType?: string;\n contentUrl?: string;\n name?: string;\n }): Attachment {\n const url = att.contentUrl;\n\n // Determine type based on contentType\n let type: Attachment[\"type\"] = \"file\";\n if (att.contentType?.startsWith(\"image/\")) {\n type = \"image\";\n } else if (att.contentType?.startsWith(\"video/\")) {\n type = \"video\";\n } else if (att.contentType?.startsWith(\"audio/\")) {\n type = \"audio\";\n }\n\n return {\n type,\n url,\n name: att.name,\n mimeType: att.contentType,\n fetchData: url\n ? async () => {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch file: ${response.status} ${response.statusText}`,\n );\n }\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer);\n }\n : undefined,\n };\n }\n\n private normalizeMentions(text: string, _activity: Activity): string {\n // Don't strip mentions - the format converter will convert <at>name</at> to @name\n // Just trim any leading/trailing whitespace that might result from mention placement\n return text.trim();\n }\n\n async postMessage(\n threadId: string,\n message: PostableMessage,\n ): Promise<RawMessage<unknown>> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n // Check for files to upload\n const files = this.extractFiles(message);\n const fileAttachments =\n files.length > 0 ? await this.filesToAttachments(files) : [];\n\n // Check if message contains a card\n const card = this.extractCard(message);\n let activity: Partial<Activity>;\n\n if (card) {\n // Render card as Adaptive Card\n const adaptiveCard = cardToAdaptiveCard(card);\n\n activity = {\n type: ActivityTypes.Message,\n // Don't include text - Teams shows both text and card if text is present\n attachments: [\n {\n contentType: \"application/vnd.microsoft.card.adaptive\",\n content: adaptiveCard,\n },\n ...fileAttachments,\n ],\n };\n\n this.logger?.debug(\"Teams API: sendActivity (adaptive card)\", {\n conversationId,\n serviceUrl,\n fileCount: fileAttachments.length,\n });\n } else {\n // Regular text message\n const text = convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"teams\",\n );\n\n activity = {\n type: ActivityTypes.Message,\n text,\n textFormat: \"markdown\",\n attachments: fileAttachments.length > 0 ? fileAttachments : undefined,\n };\n\n this.logger?.debug(\"Teams API: sendActivity (message)\", {\n conversationId,\n serviceUrl,\n textLength: text.length,\n fileCount: fileAttachments.length,\n });\n }\n\n // Use the adapter to send the message\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n let messageId = \"\";\n\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n const response = await context.sendActivity(activity);\n messageId = response?.id || \"\";\n },\n );\n\n this.logger?.debug(\"Teams API: sendActivity response\", { messageId });\n\n return {\n id: messageId,\n threadId,\n raw: activity,\n };\n }\n\n /**\n * Extract card element from a PostableMessage if present.\n */\n private extractCard(\n message: PostableMessage,\n ): import(\"chat\").CardElement | null {\n if (isCardElement(message)) {\n return message;\n }\n if (typeof message === \"object\" && message !== null && \"card\" in message) {\n return message.card;\n }\n return null;\n }\n\n /**\n * Extract files from a PostableMessage if present.\n */\n private extractFiles(message: PostableMessage): FileUpload[] {\n if (typeof message === \"object\" && message !== null && \"files\" in message) {\n return (message as { files?: FileUpload[] }).files ?? [];\n }\n return [];\n }\n\n /**\n * Convert files to Teams attachments.\n * Uses inline data URIs for small files.\n */\n private async filesToAttachments(\n files: FileUpload[],\n ): Promise<Array<{ contentType: string; contentUrl: string; name: string }>> {\n const attachments: Array<{\n contentType: string;\n contentUrl: string;\n name: string;\n }> = [];\n\n for (const file of files) {\n // Convert data to Buffer\n let buffer: Buffer;\n if (Buffer.isBuffer(file.data)) {\n buffer = file.data;\n } else if (file.data instanceof ArrayBuffer) {\n buffer = Buffer.from(file.data);\n } else if (file.data instanceof Blob) {\n const arrayBuffer = await file.data.arrayBuffer();\n buffer = Buffer.from(arrayBuffer);\n } else {\n continue;\n }\n\n // Create data URI\n const mimeType = file.mimeType || \"application/octet-stream\";\n const base64 = buffer.toString(\"base64\");\n const dataUri = `data:${mimeType};base64,${base64}`;\n\n attachments.push({\n contentType: mimeType,\n contentUrl: dataUri,\n name: file.filename,\n });\n }\n\n return attachments;\n }\n\n async editMessage(\n threadId: string,\n messageId: string,\n message: PostableMessage,\n ): Promise<RawMessage<unknown>> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n // Check if message contains a card\n const card = this.extractCard(message);\n let activity: Partial<Activity>;\n\n if (card) {\n // Render card as Adaptive Card\n const adaptiveCard = cardToAdaptiveCard(card);\n\n activity = {\n id: messageId,\n type: ActivityTypes.Message,\n // Don't include text - Teams shows both text and card if text is present\n attachments: [\n {\n contentType: \"application/vnd.microsoft.card.adaptive\",\n content: adaptiveCard,\n },\n ],\n };\n\n this.logger?.debug(\"Teams API: updateActivity (adaptive card)\", {\n conversationId,\n messageId,\n });\n } else {\n // Regular text message\n const text = convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"teams\",\n );\n\n activity = {\n id: messageId,\n type: ActivityTypes.Message,\n text,\n textFormat: \"markdown\",\n };\n\n this.logger?.debug(\"Teams API: updateActivity\", {\n conversationId,\n messageId,\n textLength: text.length,\n });\n }\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n await context.updateActivity(activity);\n },\n );\n\n this.logger?.debug(\"Teams API: updateActivity response\", { ok: true });\n\n return {\n id: messageId,\n threadId,\n raw: activity,\n };\n }\n\n async deleteMessage(threadId: string, messageId: string): Promise<void> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n this.logger?.debug(\"Teams API: deleteActivity\", {\n conversationId,\n messageId,\n });\n\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n await context.deleteActivity(messageId);\n },\n );\n\n this.logger?.debug(\"Teams API: deleteActivity response\", { ok: true });\n }\n\n async addReaction(\n _threadId: string,\n _messageId: string,\n _emoji: EmojiValue | string,\n ): Promise<void> {\n throw new NotImplementedError(\n \"Teams Bot Framework does not expose reaction APIs\",\n \"addReaction\",\n );\n }\n\n async removeReaction(\n _threadId: string,\n _messageId: string,\n _emoji: EmojiValue | string,\n ): Promise<void> {\n throw new NotImplementedError(\n \"Teams Bot Framework does not expose reaction APIs\",\n \"removeReaction\",\n );\n }\n\n async startTyping(threadId: string): Promise<void> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n this.logger?.debug(\"Teams API: sendActivity (typing)\", { conversationId });\n\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n await context.sendActivity({ type: ActivityTypes.Typing });\n },\n );\n\n this.logger?.debug(\"Teams API: sendActivity (typing) response\", {\n ok: true,\n });\n }\n\n /**\n * Open a direct message conversation with a user.\n * Returns a thread ID that can be used to post messages.\n *\n * The serviceUrl and tenantId are automatically resolved from cached user interactions.\n * If no cached values are found, defaults are used (which may not work for all tenants).\n */\n async openDM(userId: string): Promise<string> {\n // Look up cached serviceUrl and tenantId for this user from state\n const cachedServiceUrl = await this.chat\n ?.getState()\n .get<string>(`teams:serviceUrl:${userId}`);\n const cachedTenantId = await this.chat\n ?.getState()\n .get<string>(`teams:tenantId:${userId}`);\n\n const serviceUrl =\n cachedServiceUrl || \"https://smba.trafficmanager.net/teams/\";\n // Use cached tenant ID, config tenant ID, or undefined (will fail for multi-tenant)\n const tenantId = cachedTenantId || this.config.appTenantId;\n\n this.logger?.debug(\"Teams: creating 1:1 conversation\", {\n userId,\n serviceUrl,\n tenantId,\n cachedServiceUrl: !!cachedServiceUrl,\n cachedTenantId: !!cachedTenantId,\n });\n\n if (!tenantId) {\n throw new Error(\n \"Cannot open DM: tenant ID not found. User must interact with the bot first (via @mention) to cache their tenant ID.\",\n );\n }\n\n let conversationId = \"\";\n\n // Create the 1:1 conversation using createConversationAsync\n // The conversation ID is captured from within the callback, not from the return value\n // biome-ignore lint/suspicious/noExplicitAny: BotBuilder types are incomplete\n await (this.botAdapter as any).createConversationAsync(\n this.config.appId,\n \"msteams\",\n serviceUrl,\n \"\", // empty audience\n {\n isGroup: false,\n bot: { id: this.config.appId, name: this.userName },\n members: [{ id: userId }],\n tenantId,\n channelData: {\n tenant: { id: tenantId },\n },\n },\n async (turnContext: TurnContext) => {\n // Capture the conversation ID from the new context\n conversationId = turnContext?.activity?.conversation?.id || \"\";\n this.logger?.debug(\"Teams: conversation created in callback\", {\n conversationId,\n activityId: turnContext?.activity?.id,\n });\n },\n );\n\n if (!conversationId) {\n throw new Error(\"Failed to create 1:1 conversation - no ID returned\");\n }\n\n this.logger?.debug(\"Teams: 1:1 conversation created\", { conversationId });\n\n return this.encodeThreadId({\n conversationId,\n serviceUrl,\n });\n }\n\n async fetchMessages(\n _threadId: string,\n _options: FetchOptions = {},\n ): Promise<Message<unknown>[]> {\n throw new NotImplementedError(\n \"Teams does not provide a bot API to fetch message history. Use Microsoft Graph API instead.\",\n \"fetchMessages\",\n );\n }\n\n async fetchThread(threadId: string): Promise<ThreadInfo> {\n const { conversationId } = this.decodeThreadId(threadId);\n\n return {\n id: threadId,\n channelId: conversationId,\n metadata: {},\n };\n }\n\n encodeThreadId(platformData: TeamsThreadId): string {\n // Base64 encode both since conversationId and serviceUrl can contain special characters\n const encodedConversationId = Buffer.from(\n platformData.conversationId,\n ).toString(\"base64url\");\n const encodedServiceUrl = Buffer.from(platformData.serviceUrl).toString(\n \"base64url\",\n );\n return `teams:${encodedConversationId}:${encodedServiceUrl}`;\n }\n\n /**\n * Check if a thread is a direct message conversation.\n * Teams DMs have conversation IDs that don't start with \"19:\" (which is for groups/channels).\n */\n isDM(threadId: string): boolean {\n const { conversationId } = this.decodeThreadId(threadId);\n // Group chats and channels start with \"19:\", DMs don't\n return !conversationId.startsWith(\"19:\");\n }\n\n decodeThreadId(threadId: string): TeamsThreadId {\n const parts = threadId.split(\":\");\n if (parts.length !== 3 || parts[0] !== \"teams\") {\n throw new Error(`Invalid Teams thread ID: ${threadId}`);\n }\n const conversationId = Buffer.from(\n parts[1] as string,\n \"base64url\",\n ).toString(\"utf-8\");\n const serviceUrl = Buffer.from(parts[2] as string, \"base64url\").toString(\n \"utf-8\",\n );\n return { conversationId, serviceUrl };\n }\n\n parseMessage(raw: unknown): Message<unknown> {\n const activity = raw as Activity;\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n });\n return this.parseTeamsMessage(activity, threadId);\n }\n\n /**\n * Check if a Teams activity is from this bot.\n *\n * Teams bot IDs can appear in different formats:\n * - Just the app ID: \"abc123-def456-...\"\n * - With prefix: \"28:abc123-def456-...\"\n *\n * We check both exact match and suffix match (after colon delimiter)\n * to handle all formats safely.\n */\n private isMessageFromSelf(activity: Activity): boolean {\n const fromId = activity.from?.id;\n if (!fromId || !this.config.appId) {\n return false;\n }\n\n // Exact match (bot ID is just the app ID)\n if (fromId === this.config.appId) {\n return true;\n }\n\n // Teams format: \"28:{appId}\" or similar prefix patterns\n // Check if it ends with our appId after a colon delimiter\n if (fromId.endsWith(`:${this.config.appId}`)) {\n return true;\n }\n\n return false;\n }\n\n renderFormatted(content: FormattedContent): string {\n return this.formatConverter.fromAst(content);\n }\n}\n\nexport function createTeamsAdapter(config: TeamsAdapterConfig): TeamsAdapter {\n return new TeamsAdapter(config);\n}\n\n// Re-export card converter for advanced use\nexport { cardToAdaptiveCard, cardToFallbackText } from \"./cards\";\nexport { TeamsFormatConverter } from \"./markdown\";\n","/**\n * Teams Adaptive Card converter for cross-platform cards.\n *\n * Converts CardElement to Microsoft Adaptive Cards format.\n * @see https://adaptivecards.io/\n */\n\nimport {\n type ActionsElement,\n type ButtonElement,\n type CardChild,\n type CardElement,\n convertEmojiPlaceholders,\n type DividerElement,\n type FieldsElement,\n type ImageElement,\n type SectionElement,\n type TextElement,\n} from \"chat\";\n\n/**\n * Convert emoji placeholders in text to Teams format.\n */\nfunction convertEmoji(text: string): string {\n return convertEmojiPlaceholders(text, \"teams\");\n}\n\n// Adaptive Card types (simplified)\nexport interface AdaptiveCard {\n type: \"AdaptiveCard\";\n $schema: string;\n version: string;\n body: AdaptiveCardElement[];\n actions?: AdaptiveCardAction[];\n}\n\nexport interface AdaptiveCardElement {\n type: string;\n [key: string]: unknown;\n}\n\nexport interface AdaptiveCardAction {\n type: string;\n title: string;\n data?: Record<string, unknown>;\n style?: string;\n}\n\nconst ADAPTIVE_CARD_SCHEMA =\n \"http://adaptivecards.io/schemas/adaptive-card.json\";\nconst ADAPTIVE_CARD_VERSION = \"1.4\";\n\n/**\n * Convert a CardElement to a Teams Adaptive Card.\n */\nexport function cardToAdaptiveCard(card: CardElement): AdaptiveCard {\n const body: AdaptiveCardElement[] = [];\n const actions: AdaptiveCardAction[] = [];\n\n // Add title as TextBlock\n if (card.title) {\n body.push({\n type: \"TextBlock\",\n text: convertEmoji(card.title),\n weight: \"bolder\",\n size: \"large\",\n wrap: true,\n });\n }\n\n // Add subtitle as TextBlock\n if (card.subtitle) {\n body.push({\n type: \"TextBlock\",\n text: convertEmoji(card.subtitle),\n isSubtle: true,\n wrap: true,\n });\n }\n\n // Add header image if present\n if (card.imageUrl) {\n body.push({\n type: \"Image\",\n url: card.imageUrl,\n size: \"stretch\",\n });\n }\n\n // Convert children\n for (const child of card.children) {\n const result = convertChildToAdaptive(child);\n body.push(...result.elements);\n actions.push(...result.actions);\n }\n\n const adaptiveCard: AdaptiveCard = {\n type: \"AdaptiveCard\",\n $schema: ADAPTIVE_CARD_SCHEMA,\n version: ADAPTIVE_CARD_VERSION,\n body,\n };\n\n if (actions.length > 0) {\n adaptiveCard.actions = actions;\n }\n\n return adaptiveCard;\n}\n\ninterface ConvertResult {\n elements: AdaptiveCardElement[];\n actions: AdaptiveCardAction[];\n}\n\n/**\n * Convert a card child element to Adaptive Card elements.\n */\nfunction convertChildToAdaptive(child: CardChild): ConvertResult {\n switch (child.type) {\n case \"text\":\n return { elements: [convertTextToElement(child)], actions: [] };\n case \"image\":\n return { elements: [convertImageToElement(child)], actions: [] };\n case \"divider\":\n return { elements: [convertDividerToElement(child)], actions: [] };\n case \"actions\":\n return convertActionsToElements(child);\n case \"section\":\n return convertSectionToElements(child);\n case \"fields\":\n return { elements: [convertFieldsToElement(child)], actions: [] };\n default:\n return { elements: [], actions: [] };\n }\n}\n\nfunction convertTextToElement(element: TextElement): AdaptiveCardElement {\n const textBlock: AdaptiveCardElement = {\n type: \"TextBlock\",\n text: convertEmoji(element.content),\n wrap: true,\n };\n\n if (element.style === \"bold\") {\n textBlock.weight = \"bolder\";\n } else if (element.style === \"muted\") {\n textBlock.isSubtle = true;\n }\n\n return textBlock;\n}\n\nfunction convertImageToElement(element: ImageElement): AdaptiveCardElement {\n return {\n type: \"Image\",\n url: element.url,\n altText: element.alt || \"Image\",\n size: \"auto\",\n };\n}\n\nfunction convertDividerToElement(\n _element: DividerElement,\n): AdaptiveCardElement {\n // Adaptive Cards don't have a native divider, use a separator container\n return {\n type: \"Container\",\n separator: true,\n items: [],\n };\n}\n\nfunction convertActionsToElements(element: ActionsElement): ConvertResult {\n // In Adaptive Cards, actions go at the card level, not inline\n const actions: AdaptiveCardAction[] = element.children.map((button) =>\n convertButtonToAction(button),\n );\n\n return { elements: [], actions };\n}\n\nfunction convertButtonToAction(button: ButtonElement): AdaptiveCardAction {\n const action: AdaptiveCardAction = {\n type: \"Action.Submit\",\n title: convertEmoji(button.label),\n data: {\n actionId: button.id,\n value: button.value,\n },\n };\n\n if (button.style === \"primary\") {\n action.style = \"positive\";\n } else if (button.style === \"danger\") {\n action.style = \"destructive\";\n }\n\n return action;\n}\n\nfunction convertSectionToElements(element: SectionElement): ConvertResult {\n const elements: AdaptiveCardElement[] = [];\n const actions: AdaptiveCardAction[] = [];\n\n // Wrap section in a container\n const containerItems: AdaptiveCardElement[] = [];\n\n for (const child of element.children) {\n const result = convertChildToAdaptive(child);\n containerItems.push(...result.elements);\n actions.push(...result.actions);\n }\n\n if (containerItems.length > 0) {\n elements.push({\n type: \"Container\",\n items: containerItems,\n });\n }\n\n return { elements, actions };\n}\n\nfunction convertFieldsToElement(element: FieldsElement): AdaptiveCardElement {\n // Use FactSet for key-value pairs\n const facts = element.children.map((field) => ({\n title: convertEmoji(field.label),\n value: convertEmoji(field.value),\n }));\n\n return {\n type: \"FactSet\",\n facts,\n };\n}\n\n/**\n * Generate fallback text from a card element.\n * Used when adaptive cards aren't supported.\n */\nexport function cardToFallbackText(card: CardElement): string {\n const parts: string[] = [];\n\n if (card.title) {\n parts.push(`**${convertEmoji(card.title)}**`);\n }\n\n if (card.subtitle) {\n parts.push(convertEmoji(card.subtitle));\n }\n\n for (const child of card.children) {\n const text = childToFallbackText(child);\n if (text) {\n parts.push(text);\n }\n }\n\n return parts.join(\"\\n\\n\");\n}\n\nfunction childToFallbackText(child: CardChild): string | null {\n switch (child.type) {\n case \"text\":\n return convertEmoji(child.content);\n case \"fields\":\n return child.children\n .map((f) => `**${convertEmoji(f.label)}**: ${convertEmoji(f.value)}`)\n .join(\"\\n\");\n case \"actions\":\n return `[${child.children.map((b) => convertEmoji(b.label)).join(\"] [\")}]`;\n case \"section\":\n return child.children\n .map((c) => childToFallbackText(c))\n .filter(Boolean)\n .join(\"\\n\");\n default:\n return null;\n }\n}\n","/**\n * Teams-specific format conversion using AST-based parsing.\n *\n * Teams supports a subset of HTML for formatting:\n * - Bold: <b> or <strong>\n * - Italic: <i> or <em>\n * - Strikethrough: <s> or <strike>\n * - Links: <a href=\"url\">text</a>\n * - Code: <pre> and <code>\n *\n * Teams also accepts standard markdown in most cases.\n */\n\nimport {\n BaseFormatConverter,\n type Code,\n type Content,\n type Delete,\n type Emphasis,\n type InlineCode,\n type Link,\n type Paragraph,\n type PostableMessage,\n parseMarkdown,\n type Root,\n type Strong,\n type Text,\n} from \"chat\";\n\nexport class TeamsFormatConverter extends BaseFormatConverter {\n /**\n * Convert @mentions to Teams format in plain text.\n * @name → <at>name</at>\n */\n private convertMentionsToTeams(text: string): string {\n return text.replace(/@(\\w+)/g, \"<at>$1</at>\");\n }\n\n /**\n * Override renderPostable to convert @mentions in plain strings.\n */\n override renderPostable(message: PostableMessage): string {\n if (typeof message === \"string\") {\n return this.convertMentionsToTeams(message);\n }\n if (\"raw\" in message) {\n return this.convertMentionsToTeams(message.raw);\n }\n if (\"markdown\" in message) {\n return this.fromAst(parseMarkdown(message.markdown));\n }\n if (\"ast\" in message) {\n return this.fromAst(message.ast);\n }\n return \"\";\n }\n\n /**\n * Render an AST to Teams format.\n * Teams accepts standard markdown, so we just stringify cleanly.\n */\n fromAst(ast: Root): string {\n const parts: string[] = [];\n\n for (const node of ast.children) {\n parts.push(this.nodeToTeams(node as Content));\n }\n\n return parts.join(\"\\n\\n\");\n }\n\n /**\n * Parse Teams message into an AST.\n * Converts Teams HTML/mentions to standard markdown format.\n */\n toAst(teamsText: string): Root {\n // Convert Teams HTML to markdown, then parse\n let markdown = teamsText;\n\n // Convert @mentions from Teams format: <at>Name</at> -> @Name\n markdown = markdown.replace(/<at>([^<]+)<\\/at>/gi, \"@$1\");\n\n // Convert HTML tags to markdown\n // Bold: <b>, <strong> -> **text**\n markdown = markdown.replace(\n /<(b|strong)>([^<]+)<\\/(b|strong)>/gi,\n \"**$2**\",\n );\n\n // Italic: <i>, <em> -> _text_\n markdown = markdown.replace(/<(i|em)>([^<]+)<\\/(i|em)>/gi, \"_$2_\");\n\n // Strikethrough: <s>, <strike> -> ~~text~~\n markdown = markdown.replace(\n /<(s|strike)>([^<]+)<\\/(s|strike)>/gi,\n \"~~$2~~\",\n );\n\n // Links: <a href=\"url\">text</a> -> [text](url)\n markdown = markdown.replace(\n /<a[^>]+href=\"([^\"]+)\"[^>]*>([^<]+)<\\/a>/gi,\n \"[$2]($1)\",\n );\n\n // Code: <code>text</code> -> `text`\n markdown = markdown.replace(/<code>([^<]+)<\\/code>/gi, \"`$1`\");\n\n // Pre: <pre>text</pre> -> ```text```\n markdown = markdown.replace(/<pre>([^<]+)<\\/pre>/gi, \"```\\n$1\\n```\");\n\n // Strip remaining HTML tags\n markdown = markdown.replace(/<[^>]+>/g, \"\");\n\n // Decode HTML entities\n markdown = markdown\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/&/g, \"&\")\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n\n return parseMarkdown(markdown);\n }\n\n private nodeToTeams(node: Content): string {\n switch (node.type) {\n case \"paragraph\":\n return (node as Paragraph).children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\");\n\n case \"text\": {\n // Convert @mentions to Teams format <at>mention</at>\n const textValue = (node as Text).value;\n return textValue.replace(/@(\\w+)/g, \"<at>$1</at>\");\n }\n\n case \"strong\":\n // Teams supports **text** markdown\n return `**${(node as Strong).children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\")}**`;\n\n case \"emphasis\":\n // Teams supports _text_ markdown\n return `_${(node as Emphasis).children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\")}_`;\n\n case \"delete\":\n // Teams supports ~~text~~ markdown\n return `~~${(node as Delete).children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\")}~~`;\n\n case \"inlineCode\":\n return `\\`${(node as InlineCode).value}\\``;\n\n case \"code\": {\n const codeNode = node as Code;\n return `\\`\\`\\`${codeNode.lang || \"\"}\\n${codeNode.value}\\n\\`\\`\\``;\n }\n\n case \"link\": {\n const linkNode = node as Link;\n const linkText = linkNode.children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\");\n // Standard markdown link format\n return `[${linkText}](${linkNode.url})`;\n }\n\n case \"blockquote\":\n return node.children\n .map((child) => `> ${this.nodeToTeams(child as Content)}`)\n .join(\"\\n\");\n\n case \"list\":\n return node.children\n .map((item, i) => {\n const prefix = node.ordered ? `${i + 1}.` : \"-\";\n const content = item.children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\");\n return `${prefix} ${content}`;\n })\n .join(\"\\n\");\n\n case \"listItem\":\n return node.children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\");\n\n case \"break\":\n return \"\\n\";\n\n case \"thematicBreak\":\n return \"---\";\n\n default:\n // For unsupported nodes, try to extract text\n if (\"children\" in node && Array.isArray(node.children)) {\n return node.children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\");\n }\n if (\"value\" in node) {\n return String(node.value);\n }\n return \"\";\n }\n }\n}\n"],"mappings":"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","names":["convertEmojiPlaceholders","convertEmojiPlaceholders"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/cards.ts","../src/markdown.ts"],"sourcesContent":["import { ClientSecretCredential } from \"@azure/identity\";\nimport { Client } from \"@microsoft/microsoft-graph-client\";\nimport {\n TokenCredentialAuthenticationProvider,\n type TokenCredentialAuthenticationProviderOptions,\n} from \"@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials\";\nimport type { Activity, ConversationReference } from \"botbuilder\";\nimport {\n ActivityTypes,\n CloudAdapter,\n ConfigurationBotFrameworkAuthentication,\n type TurnContext,\n} from \"botbuilder\";\n\n/** Extended CloudAdapter that exposes processActivity for serverless environments */\nclass ServerlessCloudAdapter extends CloudAdapter {\n handleActivity(\n authHeader: string,\n activity: Activity,\n logic: (context: TurnContext) => Promise<void>,\n ) {\n return this.processActivity(authHeader, activity, logic);\n }\n}\n\nimport type {\n ActionEvent,\n Adapter,\n AdapterPostableMessage,\n Attachment,\n ChatInstance,\n EmojiValue,\n FetchOptions,\n FileUpload,\n FormattedContent,\n Logger,\n Message,\n RawMessage,\n ReactionEvent,\n ThreadInfo,\n WebhookOptions,\n} from \"chat\";\nimport {\n convertEmojiPlaceholders,\n defaultEmojiResolver,\n isCardElement,\n NotImplementedError,\n} from \"chat\";\nimport { cardToAdaptiveCard } from \"./cards\";\nimport { TeamsFormatConverter } from \"./markdown\";\n\n/** Microsoft Graph API chat message type */\ninterface GraphChatMessage {\n id: string;\n createdDateTime?: string;\n lastModifiedDateTime?: string;\n body?: {\n content?: string;\n contentType?: \"text\" | \"html\";\n };\n from?: {\n user?: {\n id?: string;\n displayName?: string;\n };\n application?: {\n id?: string;\n displayName?: string;\n };\n };\n attachments?: Array<{\n id?: string;\n contentType?: string;\n contentUrl?: string;\n name?: string;\n }>;\n}\n\nexport interface TeamsAdapterConfig {\n /** Microsoft App ID */\n appId: string;\n /** Microsoft App Password */\n appPassword: string;\n /** Microsoft App Type */\n appType?: \"MultiTenant\" | \"SingleTenant\";\n /** Microsoft App Tenant ID */\n appTenantId?: string;\n /** Override bot username (optional) */\n userName?: string;\n}\n\n/** Teams-specific thread ID data */\nexport interface TeamsThreadId {\n conversationId: string;\n serviceUrl: string;\n replyToId?: string;\n}\n\nexport class TeamsAdapter implements Adapter<TeamsThreadId, unknown> {\n readonly name = \"teams\";\n readonly userName: string;\n readonly botUserId?: string;\n\n private botAdapter: ServerlessCloudAdapter;\n private graphClient: Client | null = null;\n private chat: ChatInstance | null = null;\n private logger: Logger | null = null;\n private formatConverter = new TeamsFormatConverter();\n private config: TeamsAdapterConfig;\n\n constructor(config: TeamsAdapterConfig) {\n this.config = config;\n this.userName = config.userName || \"bot\";\n\n if (config.appType === \"SingleTenant\" && !config.appTenantId) {\n throw new Error(\"appTenantId is required for SingleTenant app type\");\n }\n\n // Pass empty config object, credentials go via factory\n const auth = new ConfigurationBotFrameworkAuthentication({\n MicrosoftAppId: config.appId,\n MicrosoftAppPassword: config.appPassword,\n MicrosoftAppType: config.appType || \"MultiTenant\",\n MicrosoftAppTenantId:\n config.appType === \"SingleTenant\" ? config.appTenantId : undefined,\n });\n\n this.botAdapter = new ServerlessCloudAdapter(auth);\n\n // Initialize Microsoft Graph client for message history (requires tenant ID)\n if (config.appTenantId) {\n const credential = new ClientSecretCredential(\n config.appTenantId,\n config.appId,\n config.appPassword,\n );\n\n const authProvider = new TokenCredentialAuthenticationProvider(\n credential,\n {\n scopes: [\"https://graph.microsoft.com/.default\"],\n } as TokenCredentialAuthenticationProviderOptions,\n );\n\n this.graphClient = Client.initWithMiddleware({ authProvider });\n }\n }\n\n async initialize(chat: ChatInstance): Promise<void> {\n this.chat = chat;\n this.logger = chat.getLogger(this.name);\n }\n\n async handleWebhook(\n request: Request,\n options?: WebhookOptions,\n ): Promise<Response> {\n const body = await request.text();\n this.logger?.debug(\"Teams webhook raw body\", { body });\n\n let activity: Activity;\n try {\n activity = JSON.parse(body);\n } catch (e) {\n this.logger?.error(\"Failed to parse request body\", { error: e });\n return new Response(\"Invalid JSON\", { status: 400 });\n }\n\n // Get the auth header for token validation\n const authHeader = request.headers.get(\"authorization\") || \"\";\n\n try {\n // Use handleActivity which takes the activity directly\n // instead of mocking Node.js req/res objects\n await this.botAdapter.handleActivity(\n authHeader,\n activity,\n async (context) => {\n await this.handleTurn(context, options);\n },\n );\n\n return new Response(JSON.stringify({}), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (error) {\n this.logger?.error(\"Bot adapter process error\", { error });\n return new Response(JSON.stringify({ error: \"Internal error\" }), {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n }\n\n private async handleTurn(\n context: TurnContext,\n options?: WebhookOptions,\n ): Promise<void> {\n if (!this.chat) {\n this.logger?.warn(\"Chat instance not initialized, ignoring event\");\n return;\n }\n\n const activity = context.activity;\n\n // Cache serviceUrl and tenantId for the user - needed for opening DMs later\n if (activity.from?.id && activity.serviceUrl) {\n const userId = activity.from.id;\n const tenantId = (activity.channelData as { tenant?: { id?: string } })\n ?.tenant?.id;\n const ttl = 30 * 24 * 60 * 60 * 1000; // 30 days\n\n // Store serviceUrl and tenantId for DM creation\n this.chat\n .getState()\n .set(`teams:serviceUrl:${userId}`, activity.serviceUrl, ttl);\n if (tenantId) {\n this.chat.getState().set(`teams:tenantId:${userId}`, tenantId, ttl);\n }\n }\n\n // Handle message reactions\n if (activity.type === ActivityTypes.MessageReaction) {\n this.handleReactionActivity(activity, options);\n return;\n }\n\n // Handle adaptive card actions (button clicks)\n if (activity.type === ActivityTypes.Invoke) {\n await this.handleInvokeActivity(context, options);\n return;\n }\n\n // Only handle message activities\n if (activity.type !== ActivityTypes.Message) {\n this.logger?.debug(\"Ignoring non-message activity\", {\n type: activity.type,\n });\n return;\n }\n\n // Check if this message activity is actually a button click (Action.Submit)\n // Teams sends Action.Submit as a message with value.actionId\n const actionValue = activity.value as\n | { actionId?: string; value?: string }\n | undefined;\n if (actionValue?.actionId) {\n this.handleMessageAction(activity, actionValue, options);\n return;\n }\n\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n replyToId: activity.replyToId,\n });\n\n // Let Chat class handle async processing and waitUntil\n this.chat.processMessage(\n this,\n threadId,\n this.parseTeamsMessage(activity, threadId),\n options,\n );\n }\n\n /**\n * Handle Action.Submit button clicks sent as message activities.\n * Teams sends these with type \"message\" and value.actionId.\n */\n private handleMessageAction(\n activity: Activity,\n actionValue: { actionId?: string; value?: string },\n options?: WebhookOptions,\n ): void {\n if (!this.chat || !actionValue.actionId) return;\n\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n });\n\n const actionEvent: Omit<ActionEvent, \"thread\"> & { adapter: TeamsAdapter } =\n {\n actionId: actionValue.actionId,\n value: actionValue.value,\n user: {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name || \"unknown\",\n isBot: false,\n isMe: false,\n },\n messageId: activity.replyToId || activity.id || \"\",\n threadId,\n adapter: this,\n raw: activity,\n };\n\n this.logger?.debug(\"Processing Teams message action (Action.Submit)\", {\n actionId: actionValue.actionId,\n value: actionValue.value,\n messageId: actionEvent.messageId,\n threadId,\n });\n\n this.chat.processAction(actionEvent, options);\n }\n\n /**\n * Handle invoke activities (adaptive card actions, etc.).\n */\n private async handleInvokeActivity(\n context: TurnContext,\n options?: WebhookOptions,\n ): Promise<void> {\n const activity = context.activity;\n\n // Handle adaptive card action invokes\n if (activity.name === \"adaptiveCard/action\") {\n await this.handleAdaptiveCardAction(context, activity, options);\n return;\n }\n\n this.logger?.debug(\"Ignoring unsupported invoke\", {\n name: activity.name,\n });\n }\n\n /**\n * Handle adaptive card button clicks.\n * The action data is in activity.value with our { actionId, value } structure.\n */\n private async handleAdaptiveCardAction(\n context: TurnContext,\n activity: Activity,\n options?: WebhookOptions,\n ): Promise<void> {\n if (!this.chat) return;\n\n // Activity.value contains our action data\n const actionData = activity.value?.action?.data as\n | { actionId?: string; value?: string }\n | undefined;\n\n if (!actionData?.actionId) {\n this.logger?.debug(\"Adaptive card action missing actionId\", {\n value: activity.value,\n });\n // Send acknowledgment response\n await context.sendActivity({\n type: ActivityTypes.InvokeResponse,\n value: { status: 200 },\n });\n return;\n }\n\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n });\n\n const actionEvent: Omit<ActionEvent, \"thread\"> & { adapter: TeamsAdapter } =\n {\n actionId: actionData.actionId,\n value: actionData.value,\n user: {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name || \"unknown\",\n isBot: false,\n isMe: false,\n },\n messageId: activity.replyToId || activity.id || \"\",\n threadId,\n adapter: this,\n raw: activity,\n };\n\n this.logger?.debug(\"Processing Teams adaptive card action\", {\n actionId: actionData.actionId,\n value: actionData.value,\n messageId: actionEvent.messageId,\n threadId,\n });\n\n this.chat.processAction(actionEvent, options);\n\n // Send acknowledgment response to prevent timeout\n await context.sendActivity({\n type: ActivityTypes.InvokeResponse,\n value: { status: 200 },\n });\n }\n\n /**\n * Handle Teams reaction events (reactionsAdded/reactionsRemoved).\n */\n private handleReactionActivity(\n activity: Activity,\n options?: WebhookOptions,\n ): void {\n if (!this.chat) return;\n\n // Extract the message ID from conversation ID\n // Format: \"19:xxx@thread.tacv2;messageid=1767297849909\"\n const conversationId = activity.conversation?.id || \"\";\n const messageIdMatch = conversationId.match(/messageid=(\\d+)/);\n const messageId = messageIdMatch?.[1] || activity.replyToId || \"\";\n\n // Build thread ID - KEEP the full conversation ID including ;messageid=XXX\n // This is required for Teams to reply in the correct thread\n const threadId = this.encodeThreadId({\n conversationId: conversationId,\n serviceUrl: activity.serviceUrl || \"\",\n });\n\n const user = {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name,\n isBot: false,\n isMe: this.isMessageFromSelf(activity),\n };\n\n // Process added reactions\n const reactionsAdded = activity.reactionsAdded || [];\n for (const reaction of reactionsAdded) {\n const rawEmoji = reaction.type || \"\";\n const emojiValue = defaultEmojiResolver.fromTeams(rawEmoji);\n\n const event: Omit<ReactionEvent, \"adapter\" | \"thread\"> = {\n emoji: emojiValue,\n rawEmoji,\n added: true,\n user,\n messageId,\n threadId,\n raw: activity,\n };\n\n this.logger?.debug(\"Processing Teams reaction added\", {\n emoji: emojiValue.name,\n rawEmoji,\n messageId,\n });\n\n this.chat.processReaction({ ...event, adapter: this }, options);\n }\n\n // Process removed reactions\n const reactionsRemoved = activity.reactionsRemoved || [];\n for (const reaction of reactionsRemoved) {\n const rawEmoji = reaction.type || \"\";\n const emojiValue = defaultEmojiResolver.fromTeams(rawEmoji);\n\n const event: Omit<ReactionEvent, \"adapter\" | \"thread\"> = {\n emoji: emojiValue,\n rawEmoji,\n added: false,\n user,\n messageId,\n threadId,\n raw: activity,\n };\n\n this.logger?.debug(\"Processing Teams reaction removed\", {\n emoji: emojiValue.name,\n rawEmoji,\n messageId,\n });\n\n this.chat.processReaction({ ...event, adapter: this }, options);\n }\n }\n\n private parseTeamsMessage(\n activity: Activity,\n threadId: string,\n ): Message<unknown> {\n const text = activity.text || \"\";\n // Normalize mentions - format converter will convert <at>name</at> to @name\n const normalizedText = this.normalizeMentions(text, activity);\n\n const isMe = this.isMessageFromSelf(activity);\n\n return {\n id: activity.id || \"\",\n threadId,\n text: this.formatConverter.extractPlainText(normalizedText),\n formatted: this.formatConverter.toAst(normalizedText),\n raw: activity,\n author: {\n userId: activity.from?.id || \"unknown\",\n userName: activity.from?.name || \"unknown\",\n fullName: activity.from?.name || \"unknown\",\n isBot: activity.from?.role === \"bot\",\n isMe,\n },\n metadata: {\n dateSent: activity.timestamp\n ? new Date(activity.timestamp)\n : new Date(),\n edited: false,\n },\n attachments: (activity.attachments || [])\n .filter(\n (att) =>\n att.contentType !== \"application/vnd.microsoft.card.adaptive\",\n )\n .map((att) => this.createAttachment(att)),\n };\n }\n\n /**\n * Create an Attachment object from a Teams attachment.\n */\n private createAttachment(att: {\n contentType?: string;\n contentUrl?: string;\n name?: string;\n }): Attachment {\n const url = att.contentUrl;\n\n // Determine type based on contentType\n let type: Attachment[\"type\"] = \"file\";\n if (att.contentType?.startsWith(\"image/\")) {\n type = \"image\";\n } else if (att.contentType?.startsWith(\"video/\")) {\n type = \"video\";\n } else if (att.contentType?.startsWith(\"audio/\")) {\n type = \"audio\";\n }\n\n return {\n type,\n url,\n name: att.name,\n mimeType: att.contentType,\n fetchData: url\n ? async () => {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch file: ${response.status} ${response.statusText}`,\n );\n }\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer);\n }\n : undefined,\n };\n }\n\n private normalizeMentions(text: string, _activity: Activity): string {\n // Don't strip mentions - the format converter will convert <at>name</at> to @name\n // Just trim any leading/trailing whitespace that might result from mention placement\n return text.trim();\n }\n\n async postMessage(\n threadId: string,\n message: AdapterPostableMessage,\n ): Promise<RawMessage<unknown>> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n // Check for files to upload\n const files = this.extractFiles(message);\n const fileAttachments =\n files.length > 0 ? await this.filesToAttachments(files) : [];\n\n // Check if message contains a card\n const card = this.extractCard(message);\n let activity: Partial<Activity>;\n\n if (card) {\n // Render card as Adaptive Card\n const adaptiveCard = cardToAdaptiveCard(card);\n\n activity = {\n type: ActivityTypes.Message,\n // Don't include text - Teams shows both text and card if text is present\n attachments: [\n {\n contentType: \"application/vnd.microsoft.card.adaptive\",\n content: adaptiveCard,\n },\n ...fileAttachments,\n ],\n };\n\n this.logger?.debug(\"Teams API: sendActivity (adaptive card)\", {\n conversationId,\n serviceUrl,\n fileCount: fileAttachments.length,\n });\n } else {\n // Regular text message\n const text = convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"teams\",\n );\n\n activity = {\n type: ActivityTypes.Message,\n text,\n textFormat: \"markdown\",\n attachments: fileAttachments.length > 0 ? fileAttachments : undefined,\n };\n\n this.logger?.debug(\"Teams API: sendActivity (message)\", {\n conversationId,\n serviceUrl,\n textLength: text.length,\n fileCount: fileAttachments.length,\n });\n }\n\n // Use the adapter to send the message\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n let messageId = \"\";\n\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n const response = await context.sendActivity(activity);\n messageId = response?.id || \"\";\n },\n );\n\n this.logger?.debug(\"Teams API: sendActivity response\", { messageId });\n\n return {\n id: messageId,\n threadId,\n raw: activity,\n };\n }\n\n /**\n * Extract card element from a AdapterPostableMessage if present.\n */\n private extractCard(\n message: AdapterPostableMessage,\n ): import(\"chat\").CardElement | null {\n if (isCardElement(message)) {\n return message;\n }\n if (typeof message === \"object\" && message !== null && \"card\" in message) {\n return message.card;\n }\n return null;\n }\n\n /**\n * Extract files from a AdapterPostableMessage if present.\n */\n private extractFiles(message: AdapterPostableMessage): FileUpload[] {\n if (typeof message === \"object\" && message !== null && \"files\" in message) {\n return (message as { files?: FileUpload[] }).files ?? [];\n }\n return [];\n }\n\n /**\n * Convert files to Teams attachments.\n * Uses inline data URIs for small files.\n */\n private async filesToAttachments(\n files: FileUpload[],\n ): Promise<Array<{ contentType: string; contentUrl: string; name: string }>> {\n const attachments: Array<{\n contentType: string;\n contentUrl: string;\n name: string;\n }> = [];\n\n for (const file of files) {\n // Convert data to Buffer\n let buffer: Buffer;\n if (Buffer.isBuffer(file.data)) {\n buffer = file.data;\n } else if (file.data instanceof ArrayBuffer) {\n buffer = Buffer.from(file.data);\n } else if (file.data instanceof Blob) {\n const arrayBuffer = await file.data.arrayBuffer();\n buffer = Buffer.from(arrayBuffer);\n } else {\n continue;\n }\n\n // Create data URI\n const mimeType = file.mimeType || \"application/octet-stream\";\n const base64 = buffer.toString(\"base64\");\n const dataUri = `data:${mimeType};base64,${base64}`;\n\n attachments.push({\n contentType: mimeType,\n contentUrl: dataUri,\n name: file.filename,\n });\n }\n\n return attachments;\n }\n\n async editMessage(\n threadId: string,\n messageId: string,\n message: AdapterPostableMessage,\n ): Promise<RawMessage<unknown>> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n // Check if message contains a card\n const card = this.extractCard(message);\n let activity: Partial<Activity>;\n\n if (card) {\n // Render card as Adaptive Card\n const adaptiveCard = cardToAdaptiveCard(card);\n\n activity = {\n id: messageId,\n type: ActivityTypes.Message,\n // Don't include text - Teams shows both text and card if text is present\n attachments: [\n {\n contentType: \"application/vnd.microsoft.card.adaptive\",\n content: adaptiveCard,\n },\n ],\n };\n\n this.logger?.debug(\"Teams API: updateActivity (adaptive card)\", {\n conversationId,\n messageId,\n });\n } else {\n // Regular text message\n const text = convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"teams\",\n );\n\n activity = {\n id: messageId,\n type: ActivityTypes.Message,\n text,\n textFormat: \"markdown\",\n };\n\n this.logger?.debug(\"Teams API: updateActivity\", {\n conversationId,\n messageId,\n textLength: text.length,\n });\n }\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n await context.updateActivity(activity);\n },\n );\n\n this.logger?.debug(\"Teams API: updateActivity response\", { ok: true });\n\n return {\n id: messageId,\n threadId,\n raw: activity,\n };\n }\n\n async deleteMessage(threadId: string, messageId: string): Promise<void> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n this.logger?.debug(\"Teams API: deleteActivity\", {\n conversationId,\n messageId,\n });\n\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n await context.deleteActivity(messageId);\n },\n );\n\n this.logger?.debug(\"Teams API: deleteActivity response\", { ok: true });\n }\n\n async addReaction(\n _threadId: string,\n _messageId: string,\n _emoji: EmojiValue | string,\n ): Promise<void> {\n throw new NotImplementedError(\n \"Teams Bot Framework does not expose reaction APIs\",\n \"addReaction\",\n );\n }\n\n async removeReaction(\n _threadId: string,\n _messageId: string,\n _emoji: EmojiValue | string,\n ): Promise<void> {\n throw new NotImplementedError(\n \"Teams Bot Framework does not expose reaction APIs\",\n \"removeReaction\",\n );\n }\n\n async startTyping(threadId: string): Promise<void> {\n const { conversationId, serviceUrl } = this.decodeThreadId(threadId);\n\n const conversationReference = {\n channelId: \"msteams\",\n serviceUrl,\n conversation: { id: conversationId },\n };\n\n this.logger?.debug(\"Teams API: sendActivity (typing)\", { conversationId });\n\n await this.botAdapter.continueConversationAsync(\n this.config.appId,\n conversationReference as Partial<ConversationReference>,\n async (context) => {\n await context.sendActivity({ type: ActivityTypes.Typing });\n },\n );\n\n this.logger?.debug(\"Teams API: sendActivity (typing) response\", {\n ok: true,\n });\n }\n\n /**\n * Open a direct message conversation with a user.\n * Returns a thread ID that can be used to post messages.\n *\n * The serviceUrl and tenantId are automatically resolved from cached user interactions.\n * If no cached values are found, defaults are used (which may not work for all tenants).\n */\n async openDM(userId: string): Promise<string> {\n // Look up cached serviceUrl and tenantId for this user from state\n const cachedServiceUrl = await this.chat\n ?.getState()\n .get<string>(`teams:serviceUrl:${userId}`);\n const cachedTenantId = await this.chat\n ?.getState()\n .get<string>(`teams:tenantId:${userId}`);\n\n const serviceUrl =\n cachedServiceUrl || \"https://smba.trafficmanager.net/teams/\";\n // Use cached tenant ID, config tenant ID, or undefined (will fail for multi-tenant)\n const tenantId = cachedTenantId || this.config.appTenantId;\n\n this.logger?.debug(\"Teams: creating 1:1 conversation\", {\n userId,\n serviceUrl,\n tenantId,\n cachedServiceUrl: !!cachedServiceUrl,\n cachedTenantId: !!cachedTenantId,\n });\n\n if (!tenantId) {\n throw new Error(\n \"Cannot open DM: tenant ID not found. User must interact with the bot first (via @mention) to cache their tenant ID.\",\n );\n }\n\n let conversationId = \"\";\n\n // Create the 1:1 conversation using createConversationAsync\n // The conversation ID is captured from within the callback, not from the return value\n // biome-ignore lint/suspicious/noExplicitAny: BotBuilder types are incomplete\n await (this.botAdapter as any).createConversationAsync(\n this.config.appId,\n \"msteams\",\n serviceUrl,\n \"\", // empty audience\n {\n isGroup: false,\n bot: { id: this.config.appId, name: this.userName },\n members: [{ id: userId }],\n tenantId,\n channelData: {\n tenant: { id: tenantId },\n },\n },\n async (turnContext: TurnContext) => {\n // Capture the conversation ID from the new context\n conversationId = turnContext?.activity?.conversation?.id || \"\";\n this.logger?.debug(\"Teams: conversation created in callback\", {\n conversationId,\n activityId: turnContext?.activity?.id,\n });\n },\n );\n\n if (!conversationId) {\n throw new Error(\"Failed to create 1:1 conversation - no ID returned\");\n }\n\n this.logger?.debug(\"Teams: 1:1 conversation created\", { conversationId });\n\n return this.encodeThreadId({\n conversationId,\n serviceUrl,\n });\n }\n\n async fetchMessages(\n threadId: string,\n options: FetchOptions = {},\n ): Promise<Message<unknown>[]> {\n if (!this.graphClient) {\n throw new NotImplementedError(\n \"Teams fetchMessages requires appTenantId to be configured for Microsoft Graph API access.\",\n \"fetchMessages\",\n );\n }\n\n const { conversationId } = this.decodeThreadId(threadId);\n const limit = options.limit || 50;\n\n try {\n this.logger?.debug(\"Teams Graph API: fetching messages\", {\n conversationId,\n limit,\n });\n\n // Teams conversation IDs:\n // - Channels: \"19:xxx@thread.tacv2\"\n // - Group chats: \"19:xxx@thread.v2\"\n // - 1:1 chats: other formats (e.g., \"a]xxx\", \"8:orgid:xxx\")\n // For Graph API, we use /chats/{chat-id}/messages for all chat types\n\n const response = await this.graphClient\n .api(`/chats/${encodeURIComponent(conversationId)}/messages`)\n .top(limit)\n .orderby(\"createdDateTime desc\")\n .get();\n\n const graphMessages = response.value || [];\n\n this.logger?.debug(\"Teams Graph API: fetched messages\", {\n count: graphMessages.length,\n });\n\n return graphMessages.map((msg: GraphChatMessage) => {\n const isFromBot =\n msg.from?.application?.id === this.config.appId ||\n msg.from?.user?.id === this.config.appId;\n\n return {\n id: msg.id,\n threadId,\n text: this.extractTextFromGraphMessage(msg),\n formatted: this.formatConverter.toAst(\n this.extractTextFromGraphMessage(msg),\n ),\n raw: msg,\n author: {\n userId:\n msg.from?.user?.id || msg.from?.application?.id || \"unknown\",\n userName:\n msg.from?.user?.displayName ||\n msg.from?.application?.displayName ||\n \"unknown\",\n fullName:\n msg.from?.user?.displayName ||\n msg.from?.application?.displayName ||\n \"unknown\",\n isBot: !!msg.from?.application,\n isMe: isFromBot,\n },\n metadata: {\n dateSent: msg.createdDateTime\n ? new Date(msg.createdDateTime)\n : new Date(),\n edited: !!msg.lastModifiedDateTime,\n },\n attachments: this.extractAttachmentsFromGraphMessage(msg),\n };\n });\n } catch (error) {\n this.logger?.error(\"Teams Graph API: fetchMessages error\", { error });\n\n // Check if it's a permission error\n if (error instanceof Error && error.message?.includes(\"403\")) {\n throw new NotImplementedError(\n \"Teams fetchMessages requires one of these Azure AD app permissions: ChatMessage.Read.Chat, Chat.Read.All, or Chat.Read.WhereInstalled\",\n \"fetchMessages\",\n );\n }\n\n throw error;\n }\n }\n\n /**\n * Extract plain text from a Graph API message.\n */\n private extractTextFromGraphMessage(msg: GraphChatMessage): string {\n // body.content contains the message text (HTML or text depending on contentType)\n if (msg.body?.contentType === \"text\") {\n return msg.body.content || \"\";\n }\n // For HTML content, strip tags (basic implementation)\n if (msg.body?.content) {\n return msg.body.content.replace(/<[^>]*>/g, \"\").trim();\n }\n return \"\";\n }\n\n /**\n * Extract attachments from a Graph API message.\n */\n private extractAttachmentsFromGraphMessage(\n msg: GraphChatMessage,\n ): Attachment[] {\n if (!msg.attachments?.length) {\n return [];\n }\n\n return msg.attachments.map((att) => ({\n type: att.contentType?.includes(\"image\") ? \"image\" : \"file\",\n name: att.name || undefined,\n url: att.contentUrl || undefined,\n mimeType: att.contentType || undefined,\n }));\n }\n\n async fetchThread(threadId: string): Promise<ThreadInfo> {\n const { conversationId } = this.decodeThreadId(threadId);\n\n return {\n id: threadId,\n channelId: conversationId,\n metadata: {},\n };\n }\n\n encodeThreadId(platformData: TeamsThreadId): string {\n // Base64 encode both since conversationId and serviceUrl can contain special characters\n const encodedConversationId = Buffer.from(\n platformData.conversationId,\n ).toString(\"base64url\");\n const encodedServiceUrl = Buffer.from(platformData.serviceUrl).toString(\n \"base64url\",\n );\n return `teams:${encodedConversationId}:${encodedServiceUrl}`;\n }\n\n /**\n * Check if a thread is a direct message conversation.\n * Teams DMs have conversation IDs that don't start with \"19:\" (which is for groups/channels).\n */\n isDM(threadId: string): boolean {\n const { conversationId } = this.decodeThreadId(threadId);\n // Group chats and channels start with \"19:\", DMs don't\n return !conversationId.startsWith(\"19:\");\n }\n\n decodeThreadId(threadId: string): TeamsThreadId {\n const parts = threadId.split(\":\");\n if (parts.length !== 3 || parts[0] !== \"teams\") {\n throw new Error(`Invalid Teams thread ID: ${threadId}`);\n }\n const conversationId = Buffer.from(\n parts[1] as string,\n \"base64url\",\n ).toString(\"utf-8\");\n const serviceUrl = Buffer.from(parts[2] as string, \"base64url\").toString(\n \"utf-8\",\n );\n return { conversationId, serviceUrl };\n }\n\n parseMessage(raw: unknown): Message<unknown> {\n const activity = raw as Activity;\n const threadId = this.encodeThreadId({\n conversationId: activity.conversation?.id || \"\",\n serviceUrl: activity.serviceUrl || \"\",\n });\n return this.parseTeamsMessage(activity, threadId);\n }\n\n /**\n * Check if a Teams activity is from this bot.\n *\n * Teams bot IDs can appear in different formats:\n * - Just the app ID: \"abc123-def456-...\"\n * - With prefix: \"28:abc123-def456-...\"\n *\n * We check both exact match and suffix match (after colon delimiter)\n * to handle all formats safely.\n */\n private isMessageFromSelf(activity: Activity): boolean {\n const fromId = activity.from?.id;\n if (!fromId || !this.config.appId) {\n return false;\n }\n\n // Exact match (bot ID is just the app ID)\n if (fromId === this.config.appId) {\n return true;\n }\n\n // Teams format: \"28:{appId}\" or similar prefix patterns\n // Check if it ends with our appId after a colon delimiter\n if (fromId.endsWith(`:${this.config.appId}`)) {\n return true;\n }\n\n return false;\n }\n\n renderFormatted(content: FormattedContent): string {\n return this.formatConverter.fromAst(content);\n }\n}\n\nexport function createTeamsAdapter(config: TeamsAdapterConfig): TeamsAdapter {\n return new TeamsAdapter(config);\n}\n\n// Re-export card converter for advanced use\nexport { cardToAdaptiveCard, cardToFallbackText } from \"./cards\";\nexport { TeamsFormatConverter } from \"./markdown\";\n","/**\n * Teams Adaptive Card converter for cross-platform cards.\n *\n * Converts CardElement to Microsoft Adaptive Cards format.\n * @see https://adaptivecards.io/\n */\n\nimport {\n type ActionsElement,\n type ButtonElement,\n type CardChild,\n type CardElement,\n convertEmojiPlaceholders,\n type DividerElement,\n type FieldsElement,\n type ImageElement,\n type SectionElement,\n type TextElement,\n} from \"chat\";\n\n/**\n * Convert emoji placeholders in text to Teams format.\n */\nfunction convertEmoji(text: string): string {\n return convertEmojiPlaceholders(text, \"teams\");\n}\n\n// Adaptive Card types (simplified)\nexport interface AdaptiveCard {\n type: \"AdaptiveCard\";\n $schema: string;\n version: string;\n body: AdaptiveCardElement[];\n actions?: AdaptiveCardAction[];\n}\n\nexport interface AdaptiveCardElement {\n type: string;\n [key: string]: unknown;\n}\n\nexport interface AdaptiveCardAction {\n type: string;\n title: string;\n data?: Record<string, unknown>;\n style?: string;\n}\n\nconst ADAPTIVE_CARD_SCHEMA =\n \"http://adaptivecards.io/schemas/adaptive-card.json\";\nconst ADAPTIVE_CARD_VERSION = \"1.4\";\n\n/**\n * Convert a CardElement to a Teams Adaptive Card.\n */\nexport function cardToAdaptiveCard(card: CardElement): AdaptiveCard {\n const body: AdaptiveCardElement[] = [];\n const actions: AdaptiveCardAction[] = [];\n\n // Add title as TextBlock\n if (card.title) {\n body.push({\n type: \"TextBlock\",\n text: convertEmoji(card.title),\n weight: \"bolder\",\n size: \"large\",\n wrap: true,\n });\n }\n\n // Add subtitle as TextBlock\n if (card.subtitle) {\n body.push({\n type: \"TextBlock\",\n text: convertEmoji(card.subtitle),\n isSubtle: true,\n wrap: true,\n });\n }\n\n // Add header image if present\n if (card.imageUrl) {\n body.push({\n type: \"Image\",\n url: card.imageUrl,\n size: \"stretch\",\n });\n }\n\n // Convert children\n for (const child of card.children) {\n const result = convertChildToAdaptive(child);\n body.push(...result.elements);\n actions.push(...result.actions);\n }\n\n const adaptiveCard: AdaptiveCard = {\n type: \"AdaptiveCard\",\n $schema: ADAPTIVE_CARD_SCHEMA,\n version: ADAPTIVE_CARD_VERSION,\n body,\n };\n\n if (actions.length > 0) {\n adaptiveCard.actions = actions;\n }\n\n return adaptiveCard;\n}\n\ninterface ConvertResult {\n elements: AdaptiveCardElement[];\n actions: AdaptiveCardAction[];\n}\n\n/**\n * Convert a card child element to Adaptive Card elements.\n */\nfunction convertChildToAdaptive(child: CardChild): ConvertResult {\n switch (child.type) {\n case \"text\":\n return { elements: [convertTextToElement(child)], actions: [] };\n case \"image\":\n return { elements: [convertImageToElement(child)], actions: [] };\n case \"divider\":\n return { elements: [convertDividerToElement(child)], actions: [] };\n case \"actions\":\n return convertActionsToElements(child);\n case \"section\":\n return convertSectionToElements(child);\n case \"fields\":\n return { elements: [convertFieldsToElement(child)], actions: [] };\n default:\n return { elements: [], actions: [] };\n }\n}\n\nfunction convertTextToElement(element: TextElement): AdaptiveCardElement {\n const textBlock: AdaptiveCardElement = {\n type: \"TextBlock\",\n text: convertEmoji(element.content),\n wrap: true,\n };\n\n if (element.style === \"bold\") {\n textBlock.weight = \"bolder\";\n } else if (element.style === \"muted\") {\n textBlock.isSubtle = true;\n }\n\n return textBlock;\n}\n\nfunction convertImageToElement(element: ImageElement): AdaptiveCardElement {\n return {\n type: \"Image\",\n url: element.url,\n altText: element.alt || \"Image\",\n size: \"auto\",\n };\n}\n\nfunction convertDividerToElement(\n _element: DividerElement,\n): AdaptiveCardElement {\n // Adaptive Cards don't have a native divider, use a separator container\n return {\n type: \"Container\",\n separator: true,\n items: [],\n };\n}\n\nfunction convertActionsToElements(element: ActionsElement): ConvertResult {\n // In Adaptive Cards, actions go at the card level, not inline\n const actions: AdaptiveCardAction[] = element.children.map((button) =>\n convertButtonToAction(button),\n );\n\n return { elements: [], actions };\n}\n\nfunction convertButtonToAction(button: ButtonElement): AdaptiveCardAction {\n const action: AdaptiveCardAction = {\n type: \"Action.Submit\",\n title: convertEmoji(button.label),\n data: {\n actionId: button.id,\n value: button.value,\n },\n };\n\n if (button.style === \"primary\") {\n action.style = \"positive\";\n } else if (button.style === \"danger\") {\n action.style = \"destructive\";\n }\n\n return action;\n}\n\nfunction convertSectionToElements(element: SectionElement): ConvertResult {\n const elements: AdaptiveCardElement[] = [];\n const actions: AdaptiveCardAction[] = [];\n\n // Wrap section in a container\n const containerItems: AdaptiveCardElement[] = [];\n\n for (const child of element.children) {\n const result = convertChildToAdaptive(child);\n containerItems.push(...result.elements);\n actions.push(...result.actions);\n }\n\n if (containerItems.length > 0) {\n elements.push({\n type: \"Container\",\n items: containerItems,\n });\n }\n\n return { elements, actions };\n}\n\nfunction convertFieldsToElement(element: FieldsElement): AdaptiveCardElement {\n // Use FactSet for key-value pairs\n const facts = element.children.map((field) => ({\n title: convertEmoji(field.label),\n value: convertEmoji(field.value),\n }));\n\n return {\n type: \"FactSet\",\n facts,\n };\n}\n\n/**\n * Generate fallback text from a card element.\n * Used when adaptive cards aren't supported.\n */\nexport function cardToFallbackText(card: CardElement): string {\n const parts: string[] = [];\n\n if (card.title) {\n parts.push(`**${convertEmoji(card.title)}**`);\n }\n\n if (card.subtitle) {\n parts.push(convertEmoji(card.subtitle));\n }\n\n for (const child of card.children) {\n const text = childToFallbackText(child);\n if (text) {\n parts.push(text);\n }\n }\n\n return parts.join(\"\\n\\n\");\n}\n\nfunction childToFallbackText(child: CardChild): string | null {\n switch (child.type) {\n case \"text\":\n return convertEmoji(child.content);\n case \"fields\":\n return child.children\n .map((f) => `**${convertEmoji(f.label)}**: ${convertEmoji(f.value)}`)\n .join(\"\\n\");\n case \"actions\":\n return `[${child.children.map((b) => convertEmoji(b.label)).join(\"] [\")}]`;\n case \"section\":\n return child.children\n .map((c) => childToFallbackText(c))\n .filter(Boolean)\n .join(\"\\n\");\n default:\n return null;\n }\n}\n","/**\n * Teams-specific format conversion using AST-based parsing.\n *\n * Teams supports a subset of HTML for formatting:\n * - Bold: <b> or <strong>\n * - Italic: <i> or <em>\n * - Strikethrough: <s> or <strike>\n * - Links: <a href=\"url\">text</a>\n * - Code: <pre> and <code>\n *\n * Teams also accepts standard markdown in most cases.\n */\n\nimport {\n type AdapterPostableMessage,\n BaseFormatConverter,\n type Code,\n type Content,\n type Delete,\n type Emphasis,\n type InlineCode,\n type Link,\n type Paragraph,\n parseMarkdown,\n type Root,\n type Strong,\n type Text,\n} from \"chat\";\n\nexport class TeamsFormatConverter extends BaseFormatConverter {\n /**\n * Convert @mentions to Teams format in plain text.\n * @name → <at>name</at>\n */\n private convertMentionsToTeams(text: string): string {\n return text.replace(/@(\\w+)/g, \"<at>$1</at>\");\n }\n\n /**\n * Override renderPostable to convert @mentions in plain strings.\n */\n override renderPostable(message: AdapterPostableMessage): string {\n if (typeof message === \"string\") {\n return this.convertMentionsToTeams(message);\n }\n if (\"raw\" in message) {\n return this.convertMentionsToTeams(message.raw);\n }\n if (\"markdown\" in message) {\n return this.fromAst(parseMarkdown(message.markdown));\n }\n if (\"ast\" in message) {\n return this.fromAst(message.ast);\n }\n return \"\";\n }\n\n /**\n * Render an AST to Teams format.\n * Teams accepts standard markdown, so we just stringify cleanly.\n */\n fromAst(ast: Root): string {\n const parts: string[] = [];\n\n for (const node of ast.children) {\n parts.push(this.nodeToTeams(node as Content));\n }\n\n return parts.join(\"\\n\\n\");\n }\n\n /**\n * Parse Teams message into an AST.\n * Converts Teams HTML/mentions to standard markdown format.\n */\n toAst(teamsText: string): Root {\n // Convert Teams HTML to markdown, then parse\n let markdown = teamsText;\n\n // Convert @mentions from Teams format: <at>Name</at> -> @Name\n markdown = markdown.replace(/<at>([^<]+)<\\/at>/gi, \"@$1\");\n\n // Convert HTML tags to markdown\n // Bold: <b>, <strong> -> **text**\n markdown = markdown.replace(\n /<(b|strong)>([^<]+)<\\/(b|strong)>/gi,\n \"**$2**\",\n );\n\n // Italic: <i>, <em> -> _text_\n markdown = markdown.replace(/<(i|em)>([^<]+)<\\/(i|em)>/gi, \"_$2_\");\n\n // Strikethrough: <s>, <strike> -> ~~text~~\n markdown = markdown.replace(\n /<(s|strike)>([^<]+)<\\/(s|strike)>/gi,\n \"~~$2~~\",\n );\n\n // Links: <a href=\"url\">text</a> -> [text](url)\n markdown = markdown.replace(\n /<a[^>]+href=\"([^\"]+)\"[^>]*>([^<]+)<\\/a>/gi,\n \"[$2]($1)\",\n );\n\n // Code: <code>text</code> -> `text`\n markdown = markdown.replace(/<code>([^<]+)<\\/code>/gi, \"`$1`\");\n\n // Pre: <pre>text</pre> -> ```text```\n markdown = markdown.replace(/<pre>([^<]+)<\\/pre>/gi, \"```\\n$1\\n```\");\n\n // Strip remaining HTML tags\n markdown = markdown.replace(/<[^>]+>/g, \"\");\n\n // Decode HTML entities\n markdown = markdown\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/&/g, \"&\")\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n\n return parseMarkdown(markdown);\n }\n\n private nodeToTeams(node: Content): string {\n switch (node.type) {\n case \"paragraph\":\n return (node as Paragraph).children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\");\n\n case \"text\": {\n // Convert @mentions to Teams format <at>mention</at>\n const textValue = (node as Text).value;\n return textValue.replace(/@(\\w+)/g, \"<at>$1</at>\");\n }\n\n case \"strong\":\n // Teams supports **text** markdown\n return `**${(node as Strong).children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\")}**`;\n\n case \"emphasis\":\n // Teams supports _text_ markdown\n return `_${(node as Emphasis).children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\")}_`;\n\n case \"delete\":\n // Teams supports ~~text~~ markdown\n return `~~${(node as Delete).children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\")}~~`;\n\n case \"inlineCode\":\n return `\\`${(node as InlineCode).value}\\``;\n\n case \"code\": {\n const codeNode = node as Code;\n return `\\`\\`\\`${codeNode.lang || \"\"}\\n${codeNode.value}\\n\\`\\`\\``;\n }\n\n case \"link\": {\n const linkNode = node as Link;\n const linkText = linkNode.children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\");\n // Standard markdown link format\n return `[${linkText}](${linkNode.url})`;\n }\n\n case \"blockquote\":\n return node.children\n .map((child) => `> ${this.nodeToTeams(child as Content)}`)\n .join(\"\\n\");\n\n case \"list\":\n return node.children\n .map((item, i) => {\n const prefix = node.ordered ? `${i + 1}.` : \"-\";\n const content = item.children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\");\n return `${prefix} ${content}`;\n })\n .join(\"\\n\");\n\n case \"listItem\":\n return node.children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\");\n\n case \"break\":\n return \"\\n\";\n\n case \"thematicBreak\":\n return \"---\";\n\n default:\n // For unsupported nodes, try to extract text\n if (\"children\" in node && Array.isArray(node.children)) {\n return node.children\n .map((child) => this.nodeToTeams(child as Content))\n .join(\"\");\n }\n if (\"value\" in node) {\n return String(node.value);\n }\n return \"\";\n }\n }\n}\n"],"mappings":"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","names":["convertEmojiPlaceholders","convertEmojiPlaceholders"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chat-adapter/teams",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Microsoft Teams adapter for chat",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,8 +16,10 @@
|
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
+
"@azure/identity": "^4.13.0",
|
|
20
|
+
"@microsoft/microsoft-graph-client": "^3.0.7",
|
|
19
21
|
"botbuilder": "^4.23.1",
|
|
20
|
-
"chat": "4.0
|
|
22
|
+
"chat": "4.1.0"
|
|
21
23
|
},
|
|
22
24
|
"devDependencies": {
|
|
23
25
|
"@types/node": "^22.10.2",
|