'Teams Invoke_response being rejected by Teams?

I have a teams app running on a standalone server with an AzureBot setup for message extensions on the app. I've got a class implementing message handling extending TeamsActivityHandler which all seems to be working. The issue is when I come to respond with an InvokeResponse. I place this into an activity with the right type, I get no errors but Teams seems to be rejecting the message as it doesn't give me an ID for the POST request containing the response. Is there something I'm missing when creating the Activity?

async def handle(turn_context):

    invoke_response = await message_handler.on_invoke_activity(turn_context)

    # invoke_response is an instance of botbuilder.schema._models_py3.InvokeResponse

    result = await turn_context.send_activity(Activity(type=ActivityTypes.invoke_response, value=invoke_response))

    self.logger.info(result)


Solution 1:[1]

I was totally unaware that message extensions followed a standard request response model rather than a request request model as the bot does. Here is a full working example using flask - ms example here :

import asyncio
import json

from botbuilder.core.teams import TeamsActivityHandler
from botbuilder.schema import Activity, CardAction, HeroCard

from botbuilder.core import (
    BotFrameworkAdapter,
    BotFrameworkAdapterSettings, TurnContext, CardFactory, MessageFactory,
)

from botbuilder.schema.teams import (
    MessagingExtensionAttachment,
    MessagingExtensionQuery,
    MessagingExtensionResult,
    MessagingExtensionResponse,

)

from flask import request, Response
from flask_restful import Resource


class ActivityHandler(TeamsActivityHandler):

    async def on_teams_messaging_extension_query(self, turn_context: TurnContext, query: MessagingExtensionQuery):
        search_query = str(query.parameters[0].value).strip()
        if search_query == '':
            await turn_context.send_activity(
                MessageFactory.text('You cannot enter a blank string for the search')
            )
            return
       
        # TODO: Implement a search returning objects
        search_results = self._get_search_results(search_query)
   
        attachments = []
        for obj in search_results:
            hero_card = HeroCard(
                title=obj['name'], tap=CardAction(type='invoke', value=obj)
            )
   
            attachment = MessagingExtensionAttachment(
                content_type=CardFactory.content_types.hero_card,
                content=HeroCard(title=obj['name']),
                preview=CardFactory.hero_card(hero_card),
            )
            attachments.append(attachment)
        return MessagingExtensionResponse(
            compose_extension=MessagingExtensionResult(
                type='result', attachment_layout='list', attachments=attachments
            )
        )


class TeamsMessageExtensionsBot(Resource):

    def __init__(self):

        self.event_loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.event_loop)

        app_config = BotFrameworkAdapterSettings(app_id='YOUR-BOT-ID',
                                                 app_password='YOUR-BOT-PASSWORD')
        self.bot_framework_adaptor = BotFrameworkAdapter(app_config)

    def post(self):

        message_handler = ActivityHandler()

        if 'application/json' in request.headers['Content-Type']:
            body = request.json
        else:
            return Response(status=415)

        activity = Activity().deserialize(body)
        auth_header = (
            request.headers['Authorization'] if 'Authorization' in request.headers else ''
        )

        try:
            task = self.event_loop.create_task(
                self.bot_framework_adaptor.process_activity(activity, auth_header, message_handler.on_turn)
            )

            invoke_response = self.event_loop.run_until_complete(asyncio.gather(task))[0]

            if invoke_response:
                self.logger.info(invoke_response.body)
                return Response(response=json.dumps(invoke_response.body),
                                status=invoke_response.status, mimetype='application/json')

            return Response(status=201)
        except Exception as exception:
            raise exception

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 James MV