Microsoft Teams

コミュニケーション統合コラボレーションAPIBot開発Power PlatformMicrosoft 365

コミュニケーションツール

Microsoft Teams

概要

Microsoft Teamsは、Microsoft 365統合のコラボレーションプラットフォームです。チャット、ビデオ会議、ファイル共有を統合し、最大250名まで対応の大規模ビデオ会議機能を提供します。Enterprise向け機能が充実し、Power Platform統合による豊富な自動化機能を利用できます。

詳細

Microsoft Teams(マイクロソフト チームズ)は、2017年にリリースされたMicrosoft 365統合のビジネスコラボレーションプラットフォームです。現在、2億5千万人のアクティブユーザーを抱え、Microsoft環境の企業ではSlackを上回る成長率を示しています。企業向けセキュリティ機能とOffice 365統合で差別化を図り、エンタープライズ市場でのシェアを拡大しています。

2024-2025年には、Teams AI library、新しいMeeting AI Insights API、Microsoft 365 Agents Toolkitなど、AI機能の大幅な強化が行われました。アプリマニフェストv1.22の導入、カスタムアクティビティアイコンのサポート、会議インサイトAPI、そしてPower Platform(Power Automate、Power Virtual Agents、Copilot Studio)との深い統合により、ノーコード・ローコードでの開発が大幅に効率化されています。

Teams Bot Frameworkを使用することで、JavaScript、C#、Pythonでの高度なBot開発が可能で、Graph APIを活用したMicrosoft 365全体との統合、Adaptive Cards UIによるリッチなユーザーインターフェースの構築ができます。

メリット・デメリット

メリット

  • Microsoft 365完全統合: Office、SharePoint、OneDriveとのシームレス連携
  • エンタープライズセキュリティ: 高度な認証・認可、コンプライアンス機能
  • 大規模会議対応: 最大250名のビデオ会議、ウェビナー機能
  • Power Platform統合: ノーコード・ローコードでの自動化・Bot開発
  • 豊富なAPI: Graph API、Bot Framework、Teams API
  • AI機能強化: Teams AI library、Meeting AI Insights API
  • Azure AD統合: シングルサインオン、条件付きアクセス
  • 開発ツール充実: Teams Toolkit(Microsoft 365 Agents Toolkit)

デメリット

  • Microsoft依存: Microsoft エコシステムへの強い依存
  • 学習コスト: 豊富な機能による複雑性
  • ライセンス体系: 複雑な料金・ライセンス構造
  • カスタマイズ制限: UI・UXのカスタマイズ制限
  • パフォーマンス: 大規模組織での動作重さ
  • 競合他社連携: 他社サービスとの統合に制限

主要リンク

書き方の例

Teams AI Libraryを使用したBot開発(JavaScript)

import { Application, TurnState } from '@microsoft/teams-ai';
import { MemoryStorage } from 'botbuilder';

// ストレージとアプリの初期化
const storage = new MemoryStorage();
const app = new Application({
  storage,
  ai: {
    planner: {
      model: 'gpt-3.5-turbo',
      apiKey: process.env.OPENAI_API_KEY,
    }
  }
});

// メッセージハンドラー
app.activity('message', async (context, state) => {
  const replyText = `Echo: ${context.activity.text}`;
  await context.sendActivity(replyText);
});

// AI アクション登録
app.ai.action('createTask', async (context, state, data) => {
  const { title, description } = data;
  
  // タスク作成ロジック
  state.conversation.tasks = state.conversation.tasks || [];
  state.conversation.tasks.push({ title, description, completed: false });
  
  await context.sendActivity(`Task "${title}" created successfully!`);
  return true;
});

// アプリの起動
app.start();

Bot Framework(C#)

using Microsoft.Bot.Builder;
using Microsoft.Bot.Framework;
using Microsoft.Teams.AI;

public class TeamsBot : ActivityHandler
{
    private readonly ILogger<TeamsBot> _logger;
    private readonly IConfiguration _config;

    public TeamsBot(ILogger<TeamsBot> logger, IConfiguration config)
    {
        _logger = logger;
        _config = config;
    }

    protected override async Task OnMessageActivityAsync(
        ITurnContext<IMessageActivity> turnContext, 
        CancellationToken cancellationToken)
    {
        var replyText = $"Echo: {turnContext.Activity.Text}";
        await turnContext.SendActivityAsync(
            MessageFactory.Text(replyText), 
            cancellationToken);
    }

    protected override async Task OnMembersAddedAsync(
        IList<ChannelAccount> membersAdded,
        ITurnContext<IConversationUpdateActivity> turnContext,
        CancellationToken cancellationToken)
    {
        var welcomeText = "Welcome to the Teams Bot!";
        foreach (var member in membersAdded)
        {
            if (member.Id != turnContext.Activity.Recipient.Id)
            {
                await turnContext.SendActivityAsync(
                    MessageFactory.Text(welcomeText), 
                    cancellationToken);
            }
        }
    }
}

// Startup.cs での設定
public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient().AddControllers().AddNewtonsoftJson();
    
    // Bot Frameworkの設定
    services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
    services.AddTransient<IBot, TeamsBot>();
    
    // Teams AI の設定
    services.AddSingleton<IStorage, MemoryStorage>();
}

Adaptive Cards を使用したリッチUI

// Adaptive Card の作成
const adaptiveCard = {
  type: 'AdaptiveCard',
  version: '1.4',
  body: [
    {
      type: 'TextBlock',
      text: 'Task Management',
      weight: 'Bolder',
      size: 'Medium'
    },
    {
      type: 'Input.Text',
      id: 'taskTitle',
      placeholder: 'Enter task title'
    },
    {
      type: 'Input.Text',
      id: 'taskDescription',
      placeholder: 'Enter task description',
      isMultiline: true
    }
  ],
  actions: [
    {
      type: 'Action.Submit',
      title: 'Create Task',
      data: {
        action: 'createTask'
      }
    }
  ]
};

// Adaptive Card の送信
app.message('create task', async (context, state) => {
  const cardAttachment = {
    contentType: 'application/vnd.microsoft.card.adaptive',
    content: adaptiveCard
  };

  await context.sendActivity({
    attachments: [cardAttachment]
  });
});

// Adaptive Card の送信データ処理
app.adaptiveCards.actionSubmit('createTask', async (context, state, data) => {
  const { taskTitle, taskDescription } = data;
  
  // タスク作成処理
  await context.sendActivity(`Task "${taskTitle}" created: ${taskDescription}`);
});

Microsoft Graph API統合

import { Client } from '@microsoft/microsoft-graph-client';

// Graph クライアントの初期化
const getGraphClient = (accessToken) => {
  return Client.init({
    authProvider: (done) => {
      done(null, accessToken);
    }
  });
};

// チャンネル一覧取得
app.message('list channels', async (context, state) => {
  try {
    const token = await getAccessToken(context);
    const graphClient = getGraphClient(token);
    
    const teamId = context.activity.channelData.team.id;
    const channels = await graphClient
      .api(`/teams/${teamId}/channels`)
      .get();
    
    const channelList = channels.value
      .map(channel => `- ${channel.displayName}`)
      .join('\n');
    
    await context.sendActivity(`Channels:\n${channelList}`);
  } catch (error) {
    await context.sendActivity('Error accessing Graph API');
  }
});

// ファイル一覧取得(SharePoint統合)
app.message('list files', async (context, state) => {
  try {
    const token = await getAccessToken(context);
    const graphClient = getGraphClient(token);
    
    const teamId = context.activity.channelData.team.id;
    const files = await graphClient
      .api(`/groups/${teamId}/drive/root/children`)
      .get();
    
    const fileList = files.value
      .map(file => `- [${file.name}](${file.webUrl})`)
      .join('\n');
    
    await context.sendActivity(`Files:\n${fileList}`);
  } catch (error) {
    await context.sendActivity('Error accessing files');
  }
});

Power Automate 統合

// Power Automate フローのトリガー
app.message('trigger flow', async (context, state) => {
  const flowUrl = process.env.POWER_AUTOMATE_FLOW_URL;
  const payload = {
    userMessage: context.activity.text,
    userId: context.activity.from.id,
    teamId: context.activity.channelData.team.id,
    channelId: context.activity.channelData.channel.id
  };

  try {
    const response = await fetch(flowUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(payload)
    });

    if (response.ok) {
      const result = await response.json();
      await context.sendActivity(`Flow executed: ${result.message}`);
    } else {
      await context.sendActivity('Failed to trigger flow');
    }
  } catch (error) {
    await context.sendActivity('Error triggering Power Automate flow');
  }
});

Meeting AI Insights API の活用(2024新機能)

import { Client } from '@microsoft/microsoft-graph-client';

// 会議インサイトの取得
app.message('meeting insights', async (context, state) => {
  try {
    const token = await getAccessToken(context);
    const graphClient = getGraphClient(token);
    
    // 最新の会議を取得
    const meetings = await graphClient
      .api('/me/onlineMeetings')
      .orderby('creationDateTime desc')
      .top(1)
      .get();
    
    if (meetings.value.length > 0) {
      const meetingId = meetings.value[0].id;
      
      // 会議インサイトを取得
      const insights = await graphClient
        .api(`/me/onlineMeetings/${meetingId}/insights`)
        .get();
      
      const summary = insights.summary || 'No summary available';
      const actionItems = insights.actionItems || [];
      
      let message = `**Meeting Summary:**\n${summary}\n\n`;
      
      if (actionItems.length > 0) {
        message += '**Action Items:**\n';
        actionItems.forEach((item, index) => {
          message += `${index + 1}. ${item.description}\n`;
        });
      }
      
      await context.sendActivity(message);
    } else {
      await context.sendActivity('No recent meetings found');
    }
  } catch (error) {
    await context.sendActivity('Error retrieving meeting insights');
  }
});

Teams Toolkit マニフェスト設定

{
  "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.22/MicrosoftTeams.schema.json",
  "manifestVersion": "1.22",
  "version": "1.0.0",
  "id": "${{TEAMS_APP_ID}}",
  "developer": {
    "name": "Your Company",
    "websiteUrl": "https://www.yourcompany.com",
    "privacyUrl": "https://www.yourcompany.com/privacy",
    "termsOfUseUrl": "https://www.yourcompany.com/terms"
  },
  "icons": {
    "color": "color.png",
    "outline": "outline.png"
  },
  "name": {
    "short": "Teams AI Bot",
    "full": "Teams AI Powered Bot"
  },
  "description": {
    "short": "AI-powered Teams bot",
    "full": "Advanced Teams bot with AI capabilities"
  },
  "accentColor": "#FFFFFF",
  "bots": [
    {
      "botId": "${{BOT_ID}}",
      "scopes": [
        "personal",
        "team",
        "groupchat"
      ],
      "supportsFiles": false,
      "isNotificationOnly": false,
      "commandLists": [
        {
          "scopes": [
            "personal",
            "team",
            "groupchat"
          ],
          "commands": [
            {
              "title": "Help",
              "description": "Show help information"
            },
            {
              "title": "Create Task",
              "description": "Create a new task"
            }
          ]
        }
      ]
    }
  ],
  "permissions": [
    "identity",
    "messageTeamMembers"
  ],
  "validDomains": [
    "${{BOT_DOMAIN}}"
  ]
}

環境変数設定

# .env ファイル
# Bot Framework
BOT_ID=your-bot-id
BOT_PASSWORD=your-bot-password
MicrosoftAppId=your-app-id
MicrosoftAppPassword=your-app-password

# Azure OpenAI / OpenAI
AZURE_OPENAI_API_KEY=your-azure-openai-key
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/
OPENAI_API_KEY=your-openai-key

# Microsoft Graph
MICROSOFT_APP_ID=your-graph-app-id
MICROSOFT_APP_SECRET=your-graph-app-secret

# Power Automate
POWER_AUTOMATE_FLOW_URL=https://prod-xx.eastus.logic.azure.com/workflows/xxx

# Teams Toolkit
TEAMS_APP_ID=your-teams-app-id
BOT_DOMAIN=your-bot-domain.com

Docker 設定例

FROM node:18-alpine

WORKDIR /app

# 依存関係のインストール
COPY package*.json ./
RUN npm ci --only=production

# アプリケーションのコピー
COPY . .

# 環境変数
ENV NODE_ENV=production
ENV PORT=3978

EXPOSE 3978

# アプリケーション実行
CMD ["npm", "start"]