Edits history of script submission #22310 for ' Send a message to Microsoft Teams via Bot Framework (msteams)'

  • bun
    One script reply has been approved by the moderators
    Ap­pro­ved
    // Send a message (plain text or Adaptive Card) to a Microsoft Teams conversation
    // using the Bot Framework REST API. Use this for proactive sends or as the
    // outbound leg of a custom Teams bot.
    
    type AzureBot = {
      app_id: string;
      app_password: string;
      // Set only when the Azure Bot is configured as Single Tenant. Leave empty
      // for Multi Tenant bots so the multi-tenant Bot Framework token endpoint is
      // used.
      tenant_id?: string;
    };
    
    type ConversationReference = {
      bot: { id: string; name?: string };
      channelId: string;
      conversation: {
        id: string;
        tenantId?: string;
        isGroup?: boolean;
        conversationType?: string;
      };
      serviceUrl: string;
      user?: { id: string; name?: string };
    };
    
    const BF_RESOURCE = "https://api.botframework.com";
    const BF_TOKEN_ENDPOINT_MULTITENANT =
      "https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token";
    
    export async function main(
      azure_bot: AzureBot,
      conversation_reference: ConversationReference,
      text: string,
      card?: object,
    ): Promise<{ ok: true; activity_id?: string }> {
      const token = await getBotAccessToken(azure_bot);
    
      const activity: Record<string, unknown> = {
        type: "message",
        from: conversation_reference.bot,
        recipient: conversation_reference.user ?? { id: "" },
        conversation: conversation_reference.conversation,
      };
      if (card) {
        activity.attachments = [
          { contentType: "application/vnd.microsoft.card.adaptive", content: card },
        ];
      } else {
        activity.text = text;
      }
    
      const url = `${conversation_reference.serviceUrl.replace(/\/$/, "")}/v3/conversations/${encodeURIComponent(conversation_reference.conversation.id)}/activities`;
    
      const res = await fetch(url, {
        method: "POST",
        headers: {
          authorization: `Bearer ${token}`,
          "content-type": "application/json",
        },
        body: JSON.stringify(activity),
      });
      if (!res.ok) {
        throw new Error(
          `Bot Framework send failed: ${res.status} ${await res.text().catch(() => "")}`,
        );
      }
      const body = (await res.json().catch(() => ({}))) as { id?: string };
      return { ok: true, activity_id: body.id };
    }
    
    async function getBotAccessToken(azure: AzureBot): Promise<string> {
      const isSingleTenant = !!azure.tenant_id;
      const tokenEndpoint = isSingleTenant
        ? `https://login.microsoftonline.com/${azure.tenant_id}/oauth2/token`
        : BF_TOKEN_ENDPOINT_MULTITENANT;
    
      // Single-tenant bots use the v1 endpoint with `resource`; multi-tenant bots
      // use the v2 botframework.com virtual directory with `scope=.../.default`.
      const params = isSingleTenant
        ? new URLSearchParams({
            grant_type: "client_credentials",
            client_id: azure.app_id,
            client_secret: azure.app_password,
            resource: BF_RESOURCE,
          })
        : new URLSearchParams({
            grant_type: "client_credentials",
            client_id: azure.app_id,
            client_secret: azure.app_password,
            scope: `${BF_RESOURCE}/.default`,
          });
    
      const res = await fetch(tokenEndpoint, {
        method: "POST",
        headers: { "content-type": "application/x-www-form-urlencoded" },
        body: params,
      });
      if (!res.ok) {
        throw new Error(
          `AAD token fetch failed: ${res.status} ${await res.text().catch(() => "")}`,
        );
      }
      const json = (await res.json()) as { access_token: string };
      return json.access_token;
    }
    

    Submitted by hugo989 7 days ago