メッセージを送受信する

会話型エージェントはメッセージングを通じてユーザーと通信し、シームレスな対話を可能にします。 テキストまたは音声による対話を通じて、ユーザーとの実際の会話をシミュレートできます。 エージェントの会話がインタラクティブで、動的で、適応性が高く、ユーザーフレンドリーであることを確認する必要があります。

メッセージの内容

エージェントとユーザー間のメッセージ対話には、次のようなさまざまな種類のメッセージ コンテンツを含めることができます。

コンテンツ タイプ ユーザーからエージェントへ エージェントからユーザーへ
リッチ テキストと絵文字 ✔️ ✔️
画像 ✔️ ✔️
アダプティブ カード ✔️

リッチ テキスト メッセージと絵文字を使用する

Teams エージェントは、リッチ テキストと絵文字を送信できます。 Teams では、UTF-16 を介した絵文字をサポートしています。たとえば、ニヤリと笑う顔の U+1F600 などです。

画像メッセージを使う

エージェント メッセージをポップにするために、ユーザーは画像を添付ファイルとして追加できます。

  • 画像は、PNG、JPEG、GIF 形式で最大 1024 ピクセル× 1 MB までです。 アニメーション GIF はサポートされていません。

  • XML を使用して、各画像の高さと幅を指定できます。 Markdown では、イメージ サイズは既定で 256×256 です。 例:

    • ✔️ : <img src="http://aka.ms/Fo983c" alt="Duck on a rock" height="150" width="223"></img>
    • ❌: ![Duck on a rock](http://aka.ms/Fo983c).

添付ファイルの詳細については、「 メッセージにメディア添付ファイルを追加する」を参照してください。

注:

GCC High および DoD 環境では、外部画像リンクをレンダリングできないため、ボット メッセージまたはカードに base64 でエンコードされたコンテンツとして画像を埋め込みます。 詳細については、「Microsoft Teams の制限と仕様」を参照してください。

アダプティブ カードを使用する

会話型エージェントには、ビジネス ワークフローを簡略化するアダプティブ カードを含めることができます。 アダプティブ カードには、カスタマイズ可能な豊富なテキスト、音声、画像、ボタン、および入力フィールドが用意されています。 エージェントでアダプティブ カードを作成し、Teams、Web サイトなどの複数のアプリに表示できます。

詳細については、以下を参照してください:

次のコードは、単純なアダプティブ カードを送信する例を示しています。

例: シンプルなアダプティブ カードを送信する
{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.5",
    "body": [
    {
        "items": [
        {
            "size": "large",
            "text": "Simple Adaptive Card example with a Textbox",
            "type": "TextBlock",
            "weight": "bolder",
            "wrap": true
        },
        ],
        "spacing": "extraLarge",
        "type": "Container",
        "verticalContentAlignment": "center"
    }
    ]
}

メッセージを送受信する

メッセージの送受信は、エージェントのコア機能です。

チャットでは、各メッセージは messageType: message 型の Activity オブジェクトです。 他のユーザーがメッセージを送信すると、Microsoft Teams はそれをエージェントに投稿します。 Teams は JSON オブジェクトをエージェントのメッセージング エンドポイントに送信し、メッセージングに許可するエンドポイントは 1 つだけです。 エージェントは、メッセージをチェックしてそのタイプを把握し、それに応じて応答します。

基本的な会話は、単一の REST API である Teams SDK Framework コネクタを通じて管理されます。 この API により、エージェントは Teams やその他のチャネルと対話できます。 Bot Builder SDK は、次の機能を提供します。

  • Teams SDK フレームワーク コネクタへの簡単なアクセス。
  • 会話のフローと状態を管理するツール。
  • 自然言語処理 (NLP) などの Cognitive Services を追加する簡単な方法。

エージェントは、 Text プロパティを使用して Teams からメッセージを取得し、1 つまたは複数の応答をユーザーに送り返すことができます。

詳細については、「 エージェントメッセージのユーザー属性」を参照してください。

次の表は、エージェントが受信してアクションを実行できるアクティビティの一覧です。

メッセージの種類 ペイロード オブジェクト 範囲
メッセージの受信アクティビティ メッセージ アクティビティ すべて
メッセージの編集アクティビティの受信 メッセージの編集アクティビティ すべて
削除されていないメッセージのアクティビティを受信する メッセージの取り消しアクティビティ すべて
論理的な削除メッセージのアクティビティを受信する メッセージの論理的な削除アクティビティ すべて

メッセージの受信アクティビティ

テキスト メッセージを受信するには、Activity オブジェクトの Text プロパティを使用します。 エージェントのアクティビティ ハンドラーで、ターン コンテキスト オブジェクトの Activity を使用して、1 つのメッセージ要求を読み取ります。

次のコードは、メッセージ アクティビティの受信の例を示しています。

app.OnMessage(async context =>
{
    await context.Send($"Echo: {context.Activity.Text}");
});

app.on('message', async ({ activity, send }) => {
    await send(`Echo: '${activity.text}'`);
});

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    await ctx.send(f"Echo: {ctx.activity.text}")

{
    "type": "message",
    "id": "1485983408511",
    "timestamp": "2017-02-01T21:10:07.437Z",
    "localTimestamp": "2017-02-01T14:10:07.437-07:00",
    "serviceUrl": "https://smba.trafficmanager.net/amer/",
    "channelId": "msteams",
    "from": {
        "id": "29:1XJKJMvc5GBtc2JwZq0oj8tHZmzrQgFmB39ATiQWA85gQtHieVkKilBZ9XHoq9j7Zaqt7CZ-NJWi7me2kHTL3Bw",
        "name": "Megan Bowen",
        "aadObjectId": "7faf8ab2-3d56-4244-b585-20c8a42ed2b8"
    },
    "conversation": {
        "conversationType": "personal",
        "id": "a:17I0kl9EkpE1O9PH5TWrzrLNwnWWcfrU7QZjKR0WSfOpzbfcAg2IaydGElSo10tVr4C7Fc6GtieTJX663WuJCc1uA83n4CSrHSgGBj5XNYLcVlJAs2ZX8DbYBPck201w-"
    },
    "recipient": {
        "id": "28:c9e8c047-2a74-40a2-b28a-b162d5f5327c",
        "name": "Teams TestAgent"
    },
    "textFormat": "plain",
    "text": "Hello Teams TestAgent.Sending bold-italic rich text",
    "attachments": [
      {
            "contentType": "text/html",
            "content": "<div><div>Hello Teams TestAgent. Sending <strong>bold</strong>-<em>italic</em> rich text.</div>\n</div>"
      } 
    ],
    "entities": [
      { 
        "locale": "en-US",
        "country": "US",
        "platform": "Windows",
        "timezone": "America/Los_Angeles",
        "type": "clientInfo"
      }
    ],
    "channelData": {
        "tenant": {
            "id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
        }
    },
    "locale": "en-US"
}

開封確認メッセージを受け取る

Teams の 開封確認 設定を使用すると、チャット メッセージの送信者は、1 対 1 およびグループ チャットでメッセージが受信者によって開封されたときに通知を受け取ることができます。 受信者がメッセージを読むと、メッセージの横に [表示] が表示されます。 また、 開封確認 設定を使用して、開封確認イベントを受信するようにエージェントを構成するオプションもあります。 開封確認イベントは、次のような方法でユーザー エクスペリエンスを向上させるのに役立ちます。

  • アプリ ユーザーがパーソナル チャットでメッセージを読んでいない場合にフォローアップ メッセージを送信するようにエージェントを構成できます。

  • 開封確認を使用してフィードバック ループを作成し、エージェントのエクスペリエンスを調整できます。

注:

  • 開封確認は、ユーザーとエージェントのチャット シナリオでのみサポートされます。
  • エージェントの開封確認は、チーム、チャネル、グループ チャットの範囲をサポートしていません。
  • 管理者またはユーザーが 開封確認 設定を無効にした場合、エージェントは開封確認イベントを受信しません。

エージェントの開封確認イベントを受信するには、次のことを確認してください。

  • 次のように、アプリ マニフェストRSCChatMessageReadReceipt.Read.Chat アクセス許可を追加します。

    
    "webApplicationInfo": {
    
         "id": "38f0ca43-1c38-4c39-8097e-47f62c686500",
         "resource": ""
    },
    "authorization": {
        "permissions": {
        "orgwide": [],
         "resourceSpecific": [
            {
            "name": "ChatMessageReadReceipt.Read.Chat",
            "type": "Application"
            }
            ]
         }
     }
    
    

Graph API を使用して RSC アクセス許可を追加することもできます。 詳細については、consentedPermissionSetを参照してください。

  • メソッド OnReadReceiptcontext.Activity.Value.LastReadMessageId でオーバーライドします。

    context.Activity.Value.LastReadMessageIdmethod は、メッセージが受信者によって読まれたかどうかを確認するのに役立ちます。 compareMessageIdLastReadMessageId 以下の場合、メッセージは開封済みです。 OnReadReceipt メソッドをオーバーライドして、次のメソッドで開封確認を受け取るcontext.Activity.Value.LastReadMessageId:

    app.OnReadReceipt(async context =>
    
    {
        var lastReadMessageId = context.Activity.Value.LastReadMessageId;
        await context.Send("User read the agent's message");
    });
    

次の例は、エージェントが受け取る開封確認イベント要求を示しています。

    {
        "name": "application/vnd.microsoft.readReceipt",
        "type": "event",
        "timestamp": "2023-08-16T17:23:11.1366686Z",
        "id": "f:b4783e72-9d7b-2ed9-ccef-ab446c873007",
        "channelId": "msteams",
        "serviceUrl": "https://smba.trafficmanager.net/amer/",
        "from": {
            "id": "29:1-8Iuh70W9pRqV8tQK8o2nVjxz33RRGDKLf4Bh7gKnrzN8s7e4vCyrFwjkPbTCX_Co8c4aXwWvq3RBLr-WkkVMw",
            "aadObjectId": "5b649834-7412-4cce-9e69-176e95a394f5"
        },
        "conversation": {
            "conversationType": "personal",
            "tenantId": "6babcaad-604b-40ac-a9d7-9fd97c0b779f",
            "id": "a:1xlimp68NSUxEqK0ap2rXuwC9ITauHgV2M4RaDPkeRhV8qMaFn-RyilMZ62YiVdqs8pp43yQaRKvv_U2S2gOS5nM-y_pOxVe4BW1qMGPtqD0Bv3pw-nJXF0zhDlZHMZ1Z"
        },
        "recipient": {
            "id": "28:9901a8b6-4fef-428b-80b1-ddb59361adeb",
            "name": "Test Agent"
        },
        "channelData": {
            "tenant": {
                "id": "6babcaad-604b-40ac-a9d7-9fd97c0b779f"
            }
        },
        "value": {
            "lastReadMessageId": "1692206589131"
        }
    }
    
  • エージェントが開封確認イベントを受信するテナントの開封確認管理 設定 または ユーザー設定 が有効になっています。 管理者またはユーザーは、開封確認の設定を有効または無効にする必要があります。

ユーザー対エージェント チャット シナリオでエージェントが有効になると、ユーザーがエージェントのメッセージを読むと、エージェントは開封確認イベントをすぐに受信します。 イベント数をカウントしてユーザー エンゲージメントを追跡したり、コンテキスト対応メッセージを送信したりすることもできます。

メッセージの編集アクティビティの受信

メッセージを編集すると、エージェントはメッセージ編集アクティビティの通知を受け取ります。

エージェントでメッセージの編集アクティビティ通知を取得するには、 OnMessageEdit ハンドラーをオーバーライドできます。

送信されたメッセージが編集されたときに OnMessageEdit を使用した編集メッセージ アクティビティ通知の例を次に示します。

app.OnMessageEdit(async context =>
{
    await context.Send("message is updated");
}); 
app.on('messageEdit', async ({ activity, send }) => {
    const editedMessage = activity.text;
    await send(`The edited message is ${editedMessage}`);
});
{
"type":"messageUpdate",
"timestamp":"2022-10-28T17:19:39.4615413Z",
"localTimestamp":"2022-10-28T10:19:39.4615413-07:00",
"id":"1666977568748",
"channelId":"msteams",
"serviceUrl":"https://canary.botapi.skype.com/amer/",
"from": {
    "id":"29:1BLjP9j3_PM4mubmQZsYPx7jDyLeLf_YVA9sVPV08KMAFMjJWB_EUGveb9EVDh9TslNp9qjnzEBy3kgw01Jf1Kg",
    "name":"Mike Wilber",
    "aadObjectId":"520e4d1e-2108-43ee-a092-46a9507c6200"caching
},
"conversation":{
    "conversationType":"personal",
    "tenantId":"528dbe3f-15e0-4e37-84a1-00cc305847dd","id":"a:1pweuGJ44RkB90tiJNQ_I6g3vyuP4CYA_f-v6f0Vd-Bs3Ce85C73Ah1y8TvyjESsTHWjjgw-gnsuIuCUOWkfOCq6qaUYsk2_-fj93XXXHUMAUzhFFvTnaCU7V4WiMqRPB"
},
"recipient":{
    "id":"28:0d569679-gb4j-479a-b0d8-238b6e6b1149",
    "name":"TestAgent"
},
"entities":[
    {
        "locale":"en-US",
        "country":"US",
        "platform":"Web",
        "timezone":"America/Los_Angeles",
        "type":"clientInfo"
    }
],
"channelData":{
    "eventType":"editMessage",
    "tenant":{"id":"528dbe3f-15e0-4e37-84a1-00cc305847dd"}
},
"locale":"en-US",
"localTimezone":"America/Los_Angeles"
}  
PUT {Service URL of your agent}/v3/conversations/{conversationId}/activities/{activityId}
{
    "type": "message",
    "text": "This message has been updated"
}

メッセージを送信する

テキスト メッセージを送信するには、アクティビティとして送信する文字列を指定します。 エージェントのアクティビティ ハンドラーで、ターン コンテキスト オブジェクトの context.Send(...) メソッドを使用して、単一のメッセージ応答を送信します。 オブジェクトの multiple context.Send(...) calls メソッドを使用して、複数の応答を送信します。

次のコードは、ユーザーが会話に追加されたときにメッセージを送信する例を示しています。

app.OnMembersAdded(async context =>
{
    foreach (var member in context.Activity.MembersAdded)
    {
        if (member.Id != context.Activity.Recipient.Id)
        {
            await context.Send("Hello and welcome!");
        }
    }
});
   app.on('membersAdded', async ({ activity, send }) => {
    for (const member of activity.membersAdded ?? []) {
        if (member.id !== activity.recipient.id) {
            await send(`Welcome to the team ${member.name}`);
        }
    }
});
@app.on_members_added
async def handle_members_added(ctx: ActivityContext):
    for member in ctx.activity.members_added:
        if member.id != ctx.activity.recipient.id:
            await ctx.send(f"Welcome your new team member {member.id}")
{
    "type": "message",
    "from": {
        "id": "28:c9e8c047-2a34-40a1-b28a-b162d5f5327c",
        "name": "Teams TestAgent"
    },
    "conversation": {
        "id": "a:17I0kl8EkpE1O9PH5TWrzrLNwnWWcfrU7QZjKR0WSfOpzbfcAg2IaydGElSo10tVr4C7Fc6GtieTJX663WuJCc1uA83n4CSrHSgGBj5XNYLcVlJAs2ZX8DbYBPck201w-",
        "name": "Convo1"
   },
   "recipient": {
        "id": "29:1XJKJMvc5GBtc2JwZq0oj8tHZmzrQgFmB25ATiQWA85gQtHieVkKilBZ9XHoq9j7Zaqt7CZ-NJWi7me2kHTL3Bw",
        "name": "Megan Bowen"
    },
    "text": "My agent's reply",
    "replyToId": "1632474074231"
}
HTTP Request: {Service URL of your agent}/v3/conversations/{conversationId}/activities
{
    "type": "message",
    "from": {
        "id": "28:c9e8c047-2a34-40a1-b28a-b162d5f5327c",
        "name": "Teams TestAgent"
    },
    "conversation": {
        "id":"a:17I0kl8EkpE1O9PH5TWrzrLNwnWWcfrU7QZjKR0WSfOpzbfcAg2IaydGElSo10tVr4C7Fc6GtieTJX663WuJCc1uA83n4CSrHSgGBj5XNYLcVlJAs2ZX8DbYBPck201w-",
        "name": "Convo1"
    },
    "recipient": {
        "id": "29:1XJKJMvc5GBtc2JwZq0oj8tHZmzrQgFmB25ATiQWA85gQtHieVkKilBZ9XHoq9j7Zaqt7CZ-NJWi7me2kHTL3Bw",
        "name": "Megan Bowen"
    },
    "text": "My agent's reply"
}

注:

  • メッセージ分割は、テキスト メッセージと添付ファイルが同じアクティビティ ペイロードで送信されたときに発生します。 Teams では、このアクティビティが 2 つの別々のアクティビティ (1 つはテキスト メッセージを含むアクティビティ、もう 1 つは添付ファイルを含むアクティビティ) に分割されます。 アクティビティが分割されると、メッセージを事前に 更新または削除 するために使用されるメッセージ ID は応答として受信されません。 メッセージ分割に依存するのではなく、個別のアクティビティを送信することをお勧めします。
  • 送信されたメッセージをローカライズして個人用設定を提供できます。 詳細については、「 アプリをローカライズする」を参照してください。

ユーザーとエージェントの間で送信されるメッセージには、メッセージ内の内部チャネル データが含まれます。 このデータにより、エージェントはそのチャネルで適切に通信できます。 Bot Builder SDK を使用すると、メッセージ構造を変更できます。

削除されていないメッセージのアクティビティを受信する

メッセージの削除を取り消すと、エージェントはメッセージの削除取り消しアクティビティの通知を受け取ります。

エージェントで削除されていないメッセージ アクティビティ通知を取得するには、ハンドラー OnMessageUndelete オーバーライドできます。

削除されたメッセージが復元されたときに OnMessageUndelete を使用したメッセージの取り消しアクティビティ通知の例を次に示します。

app.OnMessageUndelete(async context =>
{
    await context.Send("message is undeleted");
});
app.on('messageUndelete', async ({ activity, send }) => {
    const undeletedMessage = activity.text;
    await send(`Previously the message was deleted. After undeleting, the message is now: "${undeletedMessage}"`);
});
{
"type":"messageUpdate",
"timestamp":"2022-10-28T17:19:39.4615413Z",
"localTimestamp":"2022-10-28T10:19:39.4615413-07:00",
"id":"1666977568748",
"channelId":"msteams",
"serviceUrl":"https://canary.botapi.skype.com/amer/",
"from": {
    "id":"29:1BLjP9j3_TM4mubmQZsYEo7jDyLeLf_YVA9sVPVO7KMAFMjJWB_EUGveb9EVDh9LgoNp9qjnzEBy4kgw83Jf1Kg",
    "name":"Alex Wilber",
    "aadObjectId":"976e4d1e-2108-43ee-a092-46a9507c5606"
},
"conversation":{
    "conversationType":"personal",
    "tenantId":"528dbe3f-15e0-4e37-84a1-00cc305847dd","id":"a:1tewuGJ44RkB90tiJNQ_I4q8vyuN5CYA_f-v6f0Vd-Bs3Ce85C73Ah1y8TvyjESsTHWjjgw-gnsuIuCUOWkfOCq6qaUYsk2_-fj93XXXHUMAUzhFFvTnaCU7V4WiMqXQL"
},
"recipient":{
    "id":"28:0d469698-ab9d-479a-b0d8-758b6e6b1234",
    "name":"Testbot"
},
"entities":[
    {
           "locale":"en-US",
        "country":"US",
        "platform":"Web",
        "timezone":"America/Los_Angeles",
        "type":"clientInfo"
    }
],
"channelData":{
    "eventType":"undeleteMessage",
    "tenant":{"id":"528dbe3f-15e0-4e37-84a1-00cc305847dd"}
},
"locale":"en-US",
"localTimezone":"America/Los_Angeles"
}  
PUT {Service URL of your agent}/v3/conversations/{conversationId}/activities/{activityId}
{
    "type": "message",
    "text": "This message has been updated"
}

論理的な削除メッセージのアクティビティを受信する

メッセージを論理的に削除すると、エージェントは論理的な削除メッセージ アクティビティの通知を受け取ります。

エージェントで論理的な削除メッセージ アクティビティ通知を取得するには、ハンドラー OnMessageSoftDelete オーバーライドできます。

次の例は、メッセージが論理的に削除されたときの OnMessageSoftDelete を使用した論理的な削除メッセージ アクティビティ通知を示しています。

app.OnMessageSoftDelete(async context =>
{
    await context.Send("message is soft deleted");
}); 
app.on('messageSoftDelete', async ({ activity, send }) => {
    const messageId = activity.id;
    await send(`The deleted message id is ${messageId}`);
});

{
"type":"messageDelete",
"timestamp":"2022-10-28T17:19:43.1612052Z",
"localTimestamp":"2022-10-28T10:19:43.1612052-07:00",
"id":"1666977568748",
"channelId":"msteams",
"serviceUrl":"https://canary.botapi.skype.com/amer/",
"from": {
    "id":"29:1BLjP9j3_TM4mubmQZsYEo7jDyLeLf_YVA9sVPVO7KMAFMjJWB_EUGveb9EVDh9LgoNp9qjnzEBy4kgw83Jf1Kg",
    "name":"Alex Wilber",
    "aadObjectId":"976e4d1e-2108-43ee-a092-46a9507c5606"
},
"conversation":{
    "conversationType":"personal",
    "tenantId":"528dbe3f-15e0-4e37-84a1-00cc305847dd","id":"a:1tewuGJ44RkB90tiJNQ_I4q8vyuN5CYA_f-v6f0Vd-Bs3Ce85C73Ah1y8TvyjESsTHWjjgw-gnsuIuCUOWkfOCq6qaUYsk2_-fj93XXXHUMAUzhFFvTnaCU7V4WiMqXQL"
},
"recipient":{
    "id":"28:0d469698-ab9d-479a-b0d8-758b6e6b1235",
    "name":"Testagent"
},
"entities":[
    {
        "locale":"en-US",
        "country":"US",
        "platform":"Web",
        "timezone":"America/Los_Angeles",
        "type":"clientInfo"
    }
],
"channelData":{
    "eventType":"softDeleteMessage",
    "tenant":{"id":"528dbe3f-15e0-4e37-84a1-00cc305847dd"}
},
"locale":"en-US",
"localTimezone":"America/Los_Angeles"
}  

エージェントから送信されたメッセージを更新および削除する

重要

このセクションのコード サンプルは、Bot Framework SDK のバージョン 4.6 以降のバージョンに基づいています。 それ以前のバージョンのドキュメントをお探しの場合は、ドキュメントの「レガシ SDK」フォルダーにある「 ボット - v3 SDK 」セクションを参照してください。

エージェントは、メッセージをデータの静的スナップショットとして使用する代わりに、送信後にメッセージを動的に更新できます。 メッセージは、Teams SDK フレームワークの context.Api.Conversations.Activities.DeleteAsync(...) メソッドを使用して削除することもできます。

注:

エージェントは、Microsoft Teams でユーザーから送信されたメッセージを更新または削除することはできません。

メッセージを更新する

ポーリングの更新、ボタンを押した後の使用可能なアクションの変更、その他の非同期状態の変更などのシナリオでは、動的メッセージ更新を使用できます。

新しいメッセージが元の種類と一致する必要はありません。 たとえば、元のメッセージに添付ファイルが含まれている場合、新しいメッセージは単純なテキスト メッセージにすることができます。

サンプル コード参照

既存のメッセージを更新するには、既存のアクティビティ ID を持つ新しい Activity オブジェクトをコンテキストに渡します。Api.Conversations.Activities.UpdateAsync(...)method of theTurnContext クラスに含まれます。

app.OnMessage(async context =>
{
    // Send initial message
    var response = await context.Send("Your Message");
    var conversationId = context.Activity.Conversation.Id;
    var activityId = response.Id;

    var updatedActivity = new MessageActivity("The new text for the activity");

    await context.Api.Conversations.Activities.UpdateAsync(conversationId, activityId, updatedActivity);
});

既存のメッセージを更新するには、既存のアクティビティ ID を持つ新しい Activity オブジェクトを TurnContext オブジェクトの updateActivity メソッドに渡します。

app.on('message', async ({ activity, api, send }) => {
    // Send initial message
    const response = await send('Your Message');
    const conversationId = activity.conversation.id;
    const activityId = response.id;

    await api.conversations.activities(conversationId).update(activityId, {
        type: 'message',
        text: 'The new text for the activity'
    });
});

既存のメッセージを更新するには、既存のアクティビティ ID を持つ新しい Activity オブジェクトを TurnContext クラスの context.Api.Conversations.Activities.UpdateAsync(...) メソッドに渡します。

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    # Send initial message
    response = await ctx.send("Your Message")
    conversation_id = ctx.activity.conversation.id
    activity_id = response.id

    await ctx.api.conversations.activities(conversation_id).update(
        activity_id, MessageActivityInput(text="The new text for the activity")
    )

注:

任意の Web プログラミング技術で Teams アプリを開発し、Bot Framework REST API を直接呼び出すことができますが、すべてのトークン処理を自分で実行する必要があります。 これを行うには、API 要求で [認証] セキュリティ手順を実装する必要があります。

会話内の既存のアクティビティを更新するには、リクエスト エンドポイントに conversationIdactivityId を含めます。 このシナリオを完了するには、元の POST 呼び出しによって返されたアクティビティ ID をキャッシュする必要があります。

PUT /v3/conversations/{conversationId}/activities/{activityId}
要求 応答
Activity オブジェクト。 ResourceResponse オブジェクト。

メッセージを更新したので、受信アクティビティのボタン選択時に既存のカードを更新します。

カードを更新する

ボタン選択時に既存のカードを更新するには、着信アクティビティの ReplyToIdを使用できます。

サンプル コード参照

ボタン選択で既存のカードを更新するには、更新されたカードとアクティビティ ID として ReplyToId を含む新しい Activity オブジェクトを TurnContext クラスの context.Api.Conversations.Activities.UpdateAsync(...) メソッドに渡します。

app.OnMessage(async context =>
{
    var conversationId = context.Activity.Conversation.Id;
    var activityId = context.Activity.ReplyToId;

    var updatedActivity = new MessageActivity();
    updatedActivity.Attachments.Add(card.ToAttachment());

    await context.Api.Conversations.Activities.UpdateAsync(conversationId, activityId, updatedActivity);
});

ボタン選択で既存のカードを更新するには、更新されたカードとアクティビティ ID として replyToId を含む新しい Activity オブジェクトを TurnContext オブジェクトの updateActivity メソッドに渡します。

app.on('message', async ({ activity, api }) => {
    const conversationId = activity.conversation.id;
    const activityId = activity.replyToId;

    await api.conversations.activities(conversationId).update(activityId, {
        type: 'message',
        attachments: [card]
    });
});

ボタン クリックで既存のカードを更新するには、更新されたカードとアクティビティ ID として reply_to_id を含む新しい Activity オブジェクトを TurnContext クラスの ctx.api.conversations.activities(conversation_id).update(...) メソッドに渡します。

@app.on_message
async def handle_update_card(ctx: ActivityContext[MessageActivity]):
    conversation_id = ctx.activity.conversation.id
    activity_id = ctx.activity.reply_to_id

    await ctx.api.conversations.activities(conversation_id).update(
        activity_id, MessageActivityInput().add_card(card)
    )

注:

任意の Web プログラミング技術で Teams アプリを開発し、Bot Framework REST API を直接呼び出すことができますが、すべてのトークン処理を自分で実行する必要があります。 これを行うには、API 要求で [認証] セキュリティ手順を実装する必要があります。

会話内の既存のアクティビティを更新するには、リクエスト エンドポイントに conversationIdactivityId を含めます。 このシナリオを完了するには、元の POST 呼び出しによって返されたアクティビティ ID をキャッシュする必要があります。

PUT /v3/conversations/{conversationId}/activities/{activityId}
要求 応答
Activity オブジェクト。 ResourceResponse オブジェクト。

カードが更新されたので、Teams SDK フレームワークを使用してメッセージを削除できます。

メッセージを削除する

Teams SDK フレームワークでは、すべてのメッセージに固有のアクティビティ識別子があります。 メッセージは、Teams SDK フレームワークの context.Api.Conversations.Activities.DeleteAsync(...) メソッドを使用して削除できます。

サンプル コード参照

メッセージを削除するには、そのアクティビティの ID を TurnContext クラスの context.Api.Conversations.Activities.DeleteAsync(...) メソッドに渡します。

app.OnMessage(async context =>
{
    var conversationId = context.Activity.Conversation.Id;

    foreach (var activityId in _list)
    {
        await context.Api.Conversations.Activities.DeleteAsync(conversationId, activityId);
    }
});

サンプル コード参照

メッセージを削除するには、そのアクティビティの ID を TurnContext オブジェクトの context.Api.Conversations.Activities.DeleteAsync(...) メソッドに渡します。

app.on('message', async ({ activity, api }) => {
    const conversationId = activity.conversation.id;

    for (const activityId of activityIds) {
        await api.conversations.activities(conversationId).delete(activityId);
    }
});

そのメッセージを削除するには、そのアクティビティの ID を TurnContext オブジェクトの delete_activity メソッドに渡します。

@app.on_message
async def handle_delete(ctx: ActivityContext[MessageActivity]):
    conversation_id = ctx.activity.conversation.id

    for activity_id in _list:
        await ctx.api.conversations.activities(conversation_id).delete(activity_id)

会話内の既存のアクティビティを削除するには、リクエスト エンドポイントに conversationIdactivityId を含めます。

DELETE /v3/conversations/{conversationId}/activities/{activityId}
要求および応答 [説明]
該当なし 操作の結果を示す HTTP 状態コード。 応答の本文には何も指定されません。

引用付き返信

引用付き返信により、エージェントは会話内の前のメッセージを参照できます。 ユーザーが別のメッセージを引用するメッセージを送信すると、エージェントは引用されたコンテンツに関する構造化されたメタデータを受け取ります。 エージェントは、以前のメッセージを引用したメッセージを送信することもできます。

引用付き返信を受け取る

ユーザーがメッセージを引用してエージェントに送信すると、引用された返信メタデータが受信アクティビティで利用できるようになります。 GetQuotedMessages メソッドを使用して、引用符で囲まれたすべての応答エンティティにアクセスします。

app.OnMessage(async context =>
{
    var quotes = context.Activity.GetQuotedMessages();

    if (quotes.Count > 0)
    {
        var quote = quotes[0].QuotedReply;
        await context.Reply(
            $"You quoted message {quote.MessageId} from {quote.SenderName}: \"{quote.Preview}\"");
    }
});

ユーザーがメッセージを引用してエージェントに送信すると、引用された返信メタデータが受信アクティビティで利用できるようになります。 getQuotedMessages メソッドを使用して、引用符で囲まれたすべての応答エンティティにアクセスします。

app.on('message', async ({ activity, reply }) => {
  const quotes = activity.getQuotedMessages();

  if (quotes.length > 0) {
    const quote = quotes[0].quotedReply;
    await reply(
      `You quoted message ${quote.messageId} from ${quote.senderName}: "${quote.preview}"`
    );
  }
});

ユーザーがメッセージを引用してエージェントに送信すると、引用された返信メタデータが受信アクティビティで利用できるようになります。 get_quoted_messages メソッドを使用して、引用符で囲まれたすべての応答エンティティにアクセスします。

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    quotes = ctx.activity.get_quoted_messages()

    if quotes:
        quote = quotes[0].quoted_reply
        await ctx.reply(
            f"You quoted message {quote.message_id} from {quote.sender_name}: \"{quote.preview}\""
        )

引用付き返信を送信する

エージェントが Reply() を呼び出すと、SDK は受信メッセージを参照する引用符付きの応答エンティティを自動的にスタンプします。 返信は、Teams に引用符付き返信として表示されます。

app.OnMessage(async context =>
{
    // Reply() automatically quotes the inbound message
    await context.Reply("Got it!");
});

同じ会話で別のメッセージを引用するには (受信メッセージではなく)、引用するメッセージ ID で Quote() メソッドを使用します。

app.OnMessage(async context =>
{
    // Quote a specific message by its ID
    var parentMessageId = "1772050244572";
    await context.Quote(parentMessageId, "Referencing an earlier message");
});

エージェントが reply() を呼び出すと、SDK は受信メッセージを参照する引用符付きの応答エンティティを自動的にスタンプします。 返信は、Teams に引用符付き返信として表示されます。

app.on('message', async ({ reply }) => {
  // reply() automatically quotes the inbound message
  await reply('Got it!');
});

同じ会話で別のメッセージを引用するには (受信メッセージではなく)、引用するメッセージ ID で quote() メソッドを使用します。

app.on('message', async ({ quote }) => {
  // Quote a specific message by its ID
  const parentMessageId = '1772050244572';
  await quote(parentMessageId, 'Referencing an earlier message');
});

エージェントが reply() を呼び出すと、SDK は受信メッセージを参照する引用符付きの応答エンティティを自動的にスタンプします。 返信は、Teams に引用符付き返信として表示されます。

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    # reply() automatically quotes the inbound message
    await ctx.reply("Got it!")

同じ会話で別のメッセージを引用するには (受信メッセージではなく)、引用するメッセージ ID で quote() メソッドを使用します。

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    # Quote a specific message by its ID
    parent_message_id = "1772050244572"
    await ctx.quote(parent_message_id, "Referencing an earlier message")

事前にメッセージを送信するための引用付き返信を作成します

プロアクティブなシナリオ ( app.Send() を使用する) の場合、または複数のメッセージを引用する場合は、メッセージ アクティビティで AddQuote() メソッドを使用します。 メッセージ ID と省略可能な応答テキストを渡します。

var parentMessageId = "1772050244572";
var firstMessageId = "1772050244573";
var secondMessageId = "1772050244574";

// Single quote with response below it
var msg = new MessageActivity()
    .AddQuote(parentMessageId, "Here is my response");
await app.Send(conversationId, msg);

// Multiple quotes with interleaved responses
msg = new MessageActivity()
    .AddQuote(firstMessageId, "response to first")
    .AddQuote(secondMessageId, "response to second");
await app.Send(conversationId, msg);

// Grouped quotes — omit response to group quotes together
msg = new MessageActivity("see below for previous messages")
    .AddQuote(firstMessageId)
    .AddQuote(secondMessageId, "response to both");
await app.Send(conversationId, msg);

プロアクティブなシナリオ ( app.send() を使用する) の場合、または複数のメッセージを引用する場合は、メッセージ アクティビティで addQuote() メソッドを使用します。 メッセージ ID と省略可能な応答テキストを渡します。

import { MessageActivity } from '@microsoft/teams.api';

const parentMessageId = '1772050244572';
const firstMessageId = '1772050244573';
const secondMessageId = '1772050244574';

// Single quote with response below it
let msg = new MessageActivity()
  .addQuote(parentMessageId, 'Here is my response');
await app.send(conversationId, msg);

// Multiple quotes with interleaved responses
msg = new MessageActivity()
  .addQuote(firstMessageId, 'response to first')
  .addQuote(secondMessageId, 'response to second');
await app.send(conversationId, msg);

// Grouped quotes — omit response to group quotes together
msg = new MessageActivity('see below for previous messages')
  .addQuote(firstMessageId)
  .addQuote(secondMessageId, 'response to both');
await app.send(conversationId, msg);

プロアクティブなシナリオ ( app.send() を使用する) の場合、または複数のメッセージを引用する場合は、メッセージ アクティビティで add_quote() メソッドを使用します。 メッセージ ID と省略可能な応答テキストを渡します。

from microsoft_teams.api.activities.message import MessageActivityInput

parent_message_id = "1772050244572"
first_message_id = "1772050244573"
second_message_id = "1772050244574"

# Single quote with response below it
msg = (MessageActivityInput()
    .add_quote(parent_message_id, "Here is my response"))
await app.send(conversation_id, msg)

# Multiple quotes with interleaved responses
msg = (MessageActivityInput()
    .add_quote(first_message_id, "response to first")
    .add_quote(second_message_id, "response to second"))
await app.send(conversation_id, msg)

# Grouped quotes — omit response to group quotes together
msg = (MessageActivityInput(text="see below for previous messages")
    .add_quote(first_message_id)
    .add_quote(second_message_id, "response to both"))
await app.send(conversation_id, msg)

Teams チャネル データでメッセージを送信する

channelData オブジェクトにはチーム固有の情報が含まれており、チーム ID とチャネル ID の確定的なソースです。 必要に応じて、キャッシュしてこれらの ID をローカル ストレージのキーとして使用できます。 SDK の App は、 channelData オブジェクトから重要な情報を抽出して、アクセスできるようにします。 ただし、 turnContext オブジェクトからいつでも元のデータにアクセスできます。

channelData オブジェクトは、チャネルの外部で行われる個人的な会話のメッセージには含まれません。

エージェントに送信されるアクティビティの典型的な channelData オブジェクトには、次の情報が含まれています。

  • eventType: Teams エージェントでの会話イベントの場合にのみ渡される Teams イベントの種類。
  • tenant.id: すべてのコンテキストで渡される Microsoft Entra テナント ID。
  • team: 個人用チャットではなく、チャネル コンテキストでのみ渡されます。
  • channel: エージェントがメンションされたときにチャネルのコンテキストでのみ渡されるか、エージェントが追加された Teams 内のチャネル内のイベントに対してのみ渡されます。
  • channelData.teamsTeamId: 非推奨です。 このプロパティは下位互換性のためにのみ含まれます。
  • channelData.teamsChannelId: 非推奨です。 このプロパティは下位互換性のためにのみ含まれます。

次のコードは、channelData オブジェクト (channelCreated イベント) の例を示しています。

"channelData": {
    "eventType": "channelCreated",
    "tenant": {
        "id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
    },
    "channel": {
        "id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype",
        "name": "My New Channel"
    },
    "team": {
        "id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype"
    }
}

Teamsチャネルデータ

channelData オブジェクトにはチーム固有の情報が含まれており、チーム ID とチャネル ID の確定的なソースです。 必要に応じて、キャッシュしてこれらの ID をローカル ストレージのキーとして使用できます。 SDK の App は、 channelData オブジェクトから重要な情報を抽出して、アクセスできるようにします。 ただし、 turnContext オブジェクトからいつでも元のデータにアクセスできます。

channelData オブジェクトは、チャネルの外部で行われる個人的な会話のメッセージには含まれません。

エージェントに送信されるアクティビティの典型的な channelData オブジェクトには、次の情報が含まれています。

  • eventType: チャネル変更イベントの場合にのみ渡される Teams イベントの種類。
  • tenant.id: すべてのコンテキストで渡される Microsoft Entra テナント ID。
  • team: 個人用チャットではなく、チャネル コンテキストでのみ渡されます。
    • id: チャネルの GUID。
    • name: (how-to/conversations/subscribe-to-conversation-events.md#team-renamed) の場合にのみ渡されるチームの名前。
  • channel: エージェントがメンションされたときにチャネルのコンテキストでのみ渡されるか、エージェントが追加された Teams 内のチャネル内のイベントに対してのみ渡されます。
  • channelData.teamsTeamId: 非推奨です。 このプロパティは下位互換性のためにのみ含まれます。
  • channelData.teamsChannelId: 非推奨です。 このプロパティは下位互換性のためにのみ含まれます。

channelData オブジェクトの例

次のコードは、channelData オブジェクト (channelCreated イベント) の例を示しています。

"channelData": {
    "eventType": "channelCreated",
    "tenant": {
        "id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
    },
    "channel": {
        "id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype",
        "name": "My New Channel"
    },
    "team": {
        "id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype"
    }
}

エージェント会話型 API からのステータス コード

Teams アプリでこれらのエラーを適切に処理してください。 次の表に、エラー コードと、エラーが生成される原因の説明を示します。

状態コード エラー コードとメッセージ値 説明 再試行要求 開発者の処置
400 コード: Bad Argument
メッセージ: *シナリオ固有
エージェントによって提供された無効な要求ペイロード。 詳細については、エラー メッセージを参照してください。 不要 エラーの要求ペイロードを再評価します。 詳細については、返されたエラー メッセージを確認してください。
401 コード: BotNotRegistered
メッセージ: このエージェントの登録が見つかりません。
このエージェントの登録が見つかりませんでした。 不要 エージェント ID とパスワードを確認します。 ボット ID (Microsoft Entra ID) が Teams 開発者ポータルに登録されているか、'Teams' チャネルが有効な Azure の Azure ボット チャネル登録を介して登録されていることを確認します。
403 コード: BotDisabledByAdmin
メッセージ: テナント管理者がこのエージェントを無効にしました
ユーザーとエージェント アプリ間の対話を管理がブロックしました。 管理は、アプリ ポリシー内でユーザーに対してアプリを許可する必要があります。 詳細については、 アプリ ポリシーに関するページを参照してください。 不要 会話内のユーザーによってエージェントとの対話が明示的に開始され、エージェントがブロックされなくなったことを示すまで、会話への投稿を停止します。
403 コード: BotNotInConversationRoster
メッセージ: エージェントは会話名簿に含まれていません。
エージェントは会話に参加していません。 アプリを会話で再インストールする必要があります。 不要 別の会話要求を送信する前に、エージェントが再度追加されたことを示す installationUpdate イベントを待ちます。
403 コード: ConversationBlockedByUser
メッセージ: ユーザーがエージェントとの会話をブロックしました。
ユーザーがモデレーション設定を通じて、個人用チャットまたはチャネルでエージェントをブロックしました。 不要 キャッシュから会話を削除します。 会話内のユーザーによってエージェントとの対話が明示的に開始され、エージェントがブロックされなくなったことを示すまで、会話への投稿の試行をやめます。
403 コード: ForbiddenOperationException
メッセージ: エージェントがユーザーの個人用スコープにインストールされていません
プロアクティブ メッセージは、個人用スコープにインストールされていないエージェントによって送信されます。 不要 別の会話要求を送信する前に、個人用スコープにアプリをインストールしてください。
403 コード: InvalidBotApiHost
メッセージ: エージェント API ホストが無効です。 GCC テナントの場合は、 https://smba.infra.gcc.teams.microsoft.com を呼び出します。
エージェントが、GCC テナントに属する会話のパブリック API エンドポイントを呼び出しました。 不要 会話のサービス URL を更新して、要求を https://smba.infra.gcc.teams.microsoft.com して再試行します。
403 コード: NotEnoughPermissions
メッセージ: *シナリオ固有
エージェントには、要求された操作を実行するために必要なアクセス許可がありません。 不要 エラー メッセージから必要なアクションを決定します。
404 コード: ActivityNotFoundInConversation
メッセージ: 会話が見つかりません。
指定されたメッセージ ID が会話で見つかりませんでした。 メッセージが存在しないか、削除されます。 不要 送信されたメッセージ ID が想定される値であるかどうかを確認します。 ID がキャッシュされている場合は削除します。
404 コード: ConversationNotFound
メッセージ: 会話が見つかりません。
会話が存在しないため、会話が見つかりませんでした。または削除されました。 不要 送信された会話 ID が予期された値であるかどうかを確認します。 ID がキャッシュされている場合は削除します。
412 コード: PreconditionFailed
メッセージ: Precondition failed, please try again.
同じ会話に対する複数の同時操作が原因で、依存関係の 1 つで前提条件が失敗しました。 はい エクスポネンシャル バックオフを使用して再試行します。
413 コード: MessageSizeTooBig
メッセージ: メッセージ サイズが大きすぎます。
受信要求のサイズが大きすぎます。 詳細については、「 エージェント メッセージの書式設定」を参照してください。 不要 ペイロード サイズを小さくします。
429 コード: Throttled
メッセージ: 要求が多すぎます。 また、後に再試行するタイミングも返します。
エージェントから送信された要求が多すぎます。 詳細については、「 レート制限」を参照してください。 はい Retry-After ヘッダーを使用してバックオフ時間を判断を再試行してください。
500 コード: ServiceError
メッセージ: *各種
内部サーバー エラー。 不要 開発者コミュニティで問題を報告します。
開発者コミュニティ フォーラム。
502 コード: ServiceError
メッセージ: *各種
サービスの依存関係の問題。 はい エクスポネンシャル バックオフを使用して再試行します。 問題が解決しない場合は、 開発者コミュニティ フォーラムで問題を報告します。
503 サービスを利用できません。 はい エクスポネンシャル バックオフを使用して再試行します。 問題が解決しない場合は、 開発者コミュニティで問題を報告します。
504 ゲートウェイ タイムアウト。 はい エクスポネンシャル バックオフを使用して再試行します。 問題が解決しない場合は、 開発者コミュニティで問題を報告します。

状態コードの再試行ガイダンス

各状態コードの一般的な再試行ガイダンスは次の表に示されています。エージェントは、指定されていない状態コードの再試行を避ける必要があります。

状態コード 再試行戦略
403 GCC API https://smba.infra.gcc.teams.microsoft.com for InvalidBotApiHost を呼び出して再試行します。
412 指数バックオフを使用して再試行します。
429 Retry-Afterヘッダーを使用して再試行し、待機時間 (秒単位) と要求間の待機時間を確認します (使用可能な場合)。 それ以外の場合は、可能であればスレッド ID で指数バックオフを使用して再試行します。
502 指数バックオフを使用して再試行します。
503 指数バックオフを使用して再試行します。
504 指数バックオフを使用して再試行します。

エージェントの要求ヘッダー

エージェントへの現在の送信要求には、エージェントがペイロード全体をアンパックせずにトラフィックをルーティングするのに役立つ情報がヘッダーまたは URL に含まれていません。 アクティビティは、https://<your_domain>/api/messages のような URL を介してエージェントに送信されます。 ヘッダーに会話 ID とテナント ID を表示する要求を受信します。

要求ヘッダー フィールド

非同期フローと同期フローの両方で、エージェントに送信されるすべての要求に 2 つの非標準要求ヘッダー フィールドが追加されます。 次の表に、要求ヘッダー フィールドとその値を示します。

フィールド キー
x-ms-conversation-id 該当する場合は要求アクティビティに対応し、確認または検証された会話 ID。
x-ms-tenant-id 要求アクティビティの会話に対応するテナント ID。

テナントまたは会話 ID がアクティビティに存在しない場合、またはサービス側で検証されていない場合、値は空になります。

画像はヘッダー フィールドを示しています。

メンションしたメッセージのみを受信する

エージェントが @mentionedしているチャネル メッセージまたはチャット メッセージのみを取得できるようにするには、メッセージをフィルター処理する必要があります。 次のコード スニペットを使用して、エージェントが @mentionedするメッセージのみを受信できるようにします。

  app.OnMessage(async context =>
{
    if (!context.Activity.GetMentions().Any(mention => mention.Mentioned.Id.Equals(context.Activity.Recipient.Id, StringComparison.OrdinalIgnoreCase)))
    {
        return;
    }

    await context.Send("Using RSC the agent can receive messages across channels or chats in team without being @mentioned.");
});

エージェントにすべてのメッセージを受信させたい場合は、 @mention メッセージをフィルタリングする必要はありません。

次の手順

エージェントとのチャネルおよびグループ チャットの会話

関連項目

Teams エージェントの会話イベント