We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[PORT] EoC should only be handled when coming from parent in RunAsync
> Port this change from botbuilder-dotnet/master branch:
https://github.com/microsoft/botbuilder-dotnet/pull/3540

Fixes #3541 

Added check to only cancel dialogs in RunAsync when EoC comes from a parent bot (root or skill).

The issue was that this affected part of the code was being triggered when a skill responds with an EoC to a caller (EoCs are routed back to the pipeline in SkillHandler). 

This PR also applies the urn format to CallerId as per [OBI Spec](https://github.com/microsoft/botframework-obi/blob/master/protocols/botframework-activity/botframework-activity.md#bot-calling-skill)


# Changed projects
* Microsoft.Bot.Builder.Dialogs
* integration





--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_extensions.py`
Content:
```
1 # Copyright (c) Microsoft Corporation. All rights reserved.
2 # Licensed under the MIT License.
3 
4 from botbuilder.core import BotAdapter, StatePropertyAccessor, TurnContext
5 from botbuilder.dialogs import (
6     Dialog,
7     DialogEvents,
8     DialogSet,
9     DialogTurnStatus,
10 )
11 from botbuilder.schema import Activity, ActivityTypes
12 from botframework.connector.auth import ClaimsIdentity, SkillValidation
13 
14 
15 class DialogExtensions:
16     @staticmethod
17     async def run_dialog(
18         dialog: Dialog, turn_context: TurnContext, accessor: StatePropertyAccessor
19     ):
20         dialog_set = DialogSet(accessor)
21         dialog_set.add(dialog)
22 
23         dialog_context = await dialog_set.create_context(turn_context)
24 
25         claims = turn_context.turn_state.get(BotAdapter.BOT_IDENTITY_KEY)
26         if isinstance(claims, ClaimsIdentity) and SkillValidation.is_skill_claim(
27             claims.claims
28         ):
29             # The bot is running as a skill.
30             if (
31                 turn_context.activity.type == ActivityTypes.end_of_conversation
32                 and dialog_context.stack
33             ):
34                 await dialog_context.cancel_all_dialogs()
35             else:
36                 # Process a reprompt event sent from the parent.
37                 if (
38                     turn_context.activity.type == ActivityTypes.event
39                     and turn_context.activity.name == DialogEvents.reprompt_dialog
40                     and dialog_context.stack
41                 ):
42                     await dialog_context.reprompt_dialog()
43                     return
44 
45                 # Run the Dialog with the new message Activity and capture the results
46                 # so we can send end of conversation if needed.
47                 result = await dialog_context.continue_dialog()
48                 if result.status == DialogTurnStatus.Empty:
49                     start_message_text = f"Starting {dialog.id}"
50                     await turn_context.send_trace_activity(
51                         f"Extension {Dialog.__name__}.run_dialog",
52                         label=start_message_text,
53                     )
54                     result = await dialog_context.begin_dialog(dialog.id)
55 
56                 # Send end of conversation if it is completed or cancelled.
57                 if (
58                     result.status == DialogTurnStatus.Complete
59                     or result.status == DialogTurnStatus.Cancelled
60                 ):
61                     end_message_text = f"Dialog {dialog.id} has **completed**. Sending EndOfConversation."
62                     await turn_context.send_trace_activity(
63                         f"Extension {Dialog.__name__}.run_dialog",
64                         label=end_message_text,
65                         value=result.result,
66                     )
67 
68                     activity = Activity(
69                         type=ActivityTypes.end_of_conversation, value=result.result
70                     )
71                     await turn_context.send_activity(activity)
72 
73         else:
74             # The bot is running as a standard bot.
75             results = await dialog_context.continue_dialog()
76             if results.status == DialogTurnStatus.Empty:
77                 await dialog_context.begin_dialog(dialog.id)
78
```

Path: `libraries/botbuilder-integration-aiohttp/botbuilder/integration/aiohttp/bot_framework_http_client.py`
Content:
```
1 # Copyright (c) Microsoft Corporation. All rights reserved.
2 # Licensed under the MIT License.
3 # pylint: disable=no-member
4 
5 import json
6 from typing import Dict
7 from logging import Logger
8 
9 import aiohttp
10 from botbuilder.core import InvokeResponse
11 from botbuilder.core.skills import BotFrameworkClient
12 from botbuilder.schema import (
13     Activity,
14     ExpectedReplies,
15     ConversationReference,
16     ConversationAccount,
17 )
18 from botframework.connector.auth import (
19     ChannelProvider,
20     CredentialProvider,
21     GovernmentConstants,
22     MicrosoftAppCredentials,
23 )
24 
25 
26 class BotFrameworkHttpClient(BotFrameworkClient):
27 
28     """
29     A skill host adapter implements API to forward activity to a skill and
30     implements routing ChannelAPI calls from the Skill up through the bot/adapter.
31     """
32 
33     INVOKE_ACTIVITY_NAME = "SkillEvents.ChannelApiInvoke"
34     _BOT_IDENTITY_KEY = "BotIdentity"
35     _APP_CREDENTIALS_CACHE: Dict[str, MicrosoftAppCredentials] = {}
36 
37     def __init__(
38         self,
39         credential_provider: CredentialProvider,
40         channel_provider: ChannelProvider = None,
41         logger: Logger = None,
42     ):
43         if not credential_provider:
44             raise TypeError("credential_provider can't be None")
45 
46         self._credential_provider = credential_provider
47         self._channel_provider = channel_provider
48         self._logger = logger
49         self._session = aiohttp.ClientSession()
50 
51     async def post_activity(
52         self,
53         from_bot_id: str,
54         to_bot_id: str,
55         to_url: str,
56         service_url: str,
57         conversation_id: str,
58         activity: Activity,
59     ) -> InvokeResponse:
60         app_credentials = await self._get_app_credentials(from_bot_id, to_bot_id)
61 
62         if not app_credentials:
63             raise KeyError("Unable to get appCredentials to connect to the skill")
64 
65         # Get token for the skill call
66         token = (
67             app_credentials.get_access_token()
68             if app_credentials.microsoft_app_id
69             else None
70         )
71 
72         # Capture current activity settings before changing them.
73         # TODO: DO we need to set the activity ID? (events that are created manually don't have it).
74         original_conversation_id = activity.conversation.id
75         original_service_url = activity.service_url
76         original_caller_id = activity.caller_id
77         original_relates_to = activity.relates_to
78 
79         try:
80             # TODO: The relato has to be ported to the adapter in the new integration library when
81             #  resolving conflicts in merge
82             activity.relates_to = ConversationReference(
83                 service_url=activity.service_url,
84                 activity_id=activity.id,
85                 channel_id=activity.channel_id,
86                 conversation=ConversationAccount(
87                     id=activity.conversation.id,
88                     name=activity.conversation.name,
89                     conversation_type=activity.conversation.conversation_type,
90                     aad_object_id=activity.conversation.aad_object_id,
91                     is_group=activity.conversation.is_group,
92                     role=activity.conversation.role,
93                     tenant_id=activity.conversation.tenant_id,
94                     properties=activity.conversation.properties,
95                 ),
96                 bot=None,
97             )
98             activity.conversation.id = conversation_id
99             activity.service_url = service_url
100             activity.caller_id = from_bot_id
101 
102             headers_dict = {
103                 "Content-type": "application/json; charset=utf-8",
104             }
105             if token:
106                 headers_dict.update(
107                     {"Authorization": f"Bearer {token}",}
108                 )
109 
110             json_content = json.dumps(activity.serialize())
111             resp = await self._session.post(
112                 to_url, data=json_content.encode("utf-8"), headers=headers_dict,
113             )
114             resp.raise_for_status()
115             data = (await resp.read()).decode()
116             content = json.loads(data) if data else None
117 
118             if content:
119                 return InvokeResponse(status=resp.status, body=content)
120 
121         finally:
122             # Restore activity properties.
123             activity.conversation.id = original_conversation_id
124             activity.service_url = original_service_url
125             activity.caller_id = original_caller_id
126             activity.relates_to = original_relates_to
127 
128     async def post_buffered_activity(
129         self,
130         from_bot_id: str,
131         to_bot_id: str,
132         to_url: str,
133         service_url: str,
134         conversation_id: str,
135         activity: Activity,
136     ) -> [Activity]:
137         """
138         Helper method to return a list of activities when an Activity is being
139         sent with DeliveryMode == expectReplies.
140         """
141         response = await self.post_activity(
142             from_bot_id, to_bot_id, to_url, service_url, conversation_id, activity
143         )
144         if not response or (response.status / 100) != 2:
145             return []
146         return ExpectedReplies().deserialize(response.body).activities
147 
148     async def _get_app_credentials(
149         self, app_id: str, oauth_scope: str
150     ) -> MicrosoftAppCredentials:
151         if not app_id:
152             return MicrosoftAppCredentials(None, None)
153 
154         cache_key = f"{app_id}{oauth_scope}"
155         app_credentials = BotFrameworkHttpClient._APP_CREDENTIALS_CACHE.get(cache_key)
156 
157         if app_credentials:
158             return app_credentials
159 
160         app_password = await self._credential_provider.get_app_password(app_id)
161         app_credentials = MicrosoftAppCredentials(
162             app_id, app_password, oauth_scope=oauth_scope
163         )
164         if self._channel_provider and self._channel_provider.is_government():
165             app_credentials.oauth_endpoint = (
166                 GovernmentConstants.TO_CHANNEL_FROM_BOT_LOGIN_URL
167             )
168             app_credentials.oauth_scope = (
169                 GovernmentConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE
170             )
171 
172         BotFrameworkHttpClient._APP_CREDENTIALS_CACHE[cache_key] = app_credentials
173         return app_credentials
174
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_extensions.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_extensions.py
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_extensions.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_extensions.py
@@ -30,7 +30,13 @@
             if (
                 turn_context.activity.type == ActivityTypes.end_of_conversation
                 and dialog_context.stack
+                and DialogExtensions.__is_eoc_coming_from_parent(turn_context)
             ):
+                remote_cancel_text = "Skill was canceled through an EndOfConversation activity from the parent."
+                await turn_context.send_trace_activity(
+                    f"Extension {Dialog.__name__}.run_dialog", label=remote_cancel_text,
+                )
+
                 await dialog_context.cancel_all_dialogs()
             else:
                 # Process a reprompt event sent from the parent.
@@ -75,3 +81,9 @@
             results = await dialog_context.continue_dialog()
             if results.status == DialogTurnStatus.Empty:
                 await dialog_context.begin_dialog(dialog.id)
+
+    @staticmethod
+    def __is_eoc_coming_from_parent(turn_context: TurnContext) -> bool:
+        # To determine the direction we check callerId property which is set to the parent bot
+        # by the BotFrameworkHttpClient on outgoing requests.
+        return bool(turn_context.activity.caller_id)
diff --git a/libraries/botbuilder-integration-aiohttp/botbuilder/integration/aiohttp/bot_framework_http_client.py b/libraries/botbuilder-integration-aiohttp/botbuilder/integration/aiohttp/bot_framework_http_client.py
--- a/libraries/botbuilder-integration-aiohttp/botbuilder/integration/aiohttp/bot_framework_http_client.py
+++ b/libraries/botbuilder-integration-aiohttp/botbuilder/integration/aiohttp/bot_framework_http_client.py
@@ -97,7 +97,7 @@
             )
             activity.conversation.id = conversation_id
             activity.service_url = service_url
-            activity.caller_id = from_bot_id
+            activity.caller_id = f"urn:botframework:aadappid:{from_bot_id}"
 
             headers_dict = {
                 "Content-type": "application/json; charset=utf-8",

{"golden_diff": "diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_extensions.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_extensions.py\n--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_extensions.py\n+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_extensions.py\n@@ -30,7 +30,13 @@\n             if (\n                 turn_context.activity.type == ActivityTypes.end_of_conversation\n                 and dialog_context.stack\n+                and DialogExtensions.__is_eoc_coming_from_parent(turn_context)\n             ):\n+                remote_cancel_text = \"Skill was canceled through an EndOfConversation activity from the parent.\"\n+                await turn_context.send_trace_activity(\n+                    f\"Extension {Dialog.__name__}.run_dialog\", label=remote_cancel_text,\n+                )\n+\n                 await dialog_context.cancel_all_dialogs()\n             else:\n                 # Process a reprompt event sent from the parent.\n@@ -75,3 +81,9 @@\n             results = await dialog_context.continue_dialog()\n             if results.status == DialogTurnStatus.Empty:\n                 await dialog_context.begin_dialog(dialog.id)\n+\n+    @staticmethod\n+    def __is_eoc_coming_from_parent(turn_context: TurnContext) -> bool:\n+        # To determine the direction we check callerId property which is set to the parent bot\n+        # by the BotFrameworkHttpClient on outgoing requests.\n+        return bool(turn_context.activity.caller_id)\ndiff --git a/libraries/botbuilder-integration-aiohttp/botbuilder/integration/aiohttp/bot_framework_http_client.py b/libraries/botbuilder-integration-aiohttp/botbuilder/integration/aiohttp/bot_framework_http_client.py\n--- a/libraries/botbuilder-integration-aiohttp/botbuilder/integration/aiohttp/bot_framework_http_client.py\n+++ b/libraries/botbuilder-integration-aiohttp/botbuilder/integration/aiohttp/bot_framework_http_client.py\n@@ -97,7 +97,7 @@\n             )\n             activity.conversation.id = conversation_id\n             activity.service_url = service_url\n-            activity.caller_id = from_bot_id\n+            activity.caller_id = f\"urn:botframework:aadappid:{from_bot_id}\"\n \n             headers_dict = {\n                 \"Content-type\": \"application/json; charset=utf-8\",\n", "issue": "[PORT] EoC should only be handled when coming from parent in RunAsync\n> Port this change from botbuilder-dotnet/master branch:\nhttps://github.com/microsoft/botbuilder-dotnet/pull/3540\n\nFixes #3541 \r\n\r\nAdded check to only cancel dialogs in RunAsync when EoC comes from a parent bot (root or skill).\r\n\r\nThe issue was that this affected part of the code was being triggered when a skill responds with an EoC to a caller (EoCs are routed back to the pipeline in SkillHandler). \r\n\r\nThis PR also applies the urn format to CallerId as per [OBI Spec](https://github.com/microsoft/botframework-obi/blob/master/protocols/botframework-activity/botframework-activity.md#bot-calling-skill)\n\n\r\n# Changed projects\r\n* Microsoft.Bot.Builder.Dialogs\r\n* integration\r\n\r\n\r\n\n\n", "before_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n\nfrom botbuilder.core import BotAdapter, StatePropertyAccessor, TurnContext\nfrom botbuilder.dialogs import (\n    Dialog,\n    DialogEvents,\n    DialogSet,\n    DialogTurnStatus,\n)\nfrom botbuilder.schema import Activity, ActivityTypes\nfrom botframework.connector.auth import ClaimsIdentity, SkillValidation\n\n\nclass DialogExtensions:\n    @staticmethod\n    async def run_dialog(\n        dialog: Dialog, turn_context: TurnContext, accessor: StatePropertyAccessor\n    ):\n        dialog_set = DialogSet(accessor)\n        dialog_set.add(dialog)\n\n        dialog_context = await dialog_set.create_context(turn_context)\n\n        claims = turn_context.turn_state.get(BotAdapter.BOT_IDENTITY_KEY)\n        if isinstance(claims, ClaimsIdentity) and SkillValidation.is_skill_claim(\n            claims.claims\n        ):\n            # The bot is running as a skill.\n            if (\n                turn_context.activity.type == ActivityTypes.end_of_conversation\n                and dialog_context.stack\n            ):\n                await dialog_context.cancel_all_dialogs()\n            else:\n                # Process a reprompt event sent from the parent.\n                if (\n                    turn_context.activity.type == ActivityTypes.event\n                    and turn_context.activity.name == DialogEvents.reprompt_dialog\n                    and dialog_context.stack\n                ):\n                    await dialog_context.reprompt_dialog()\n                    return\n\n                # Run the Dialog with the new message Activity and capture the results\n                # so we can send end of conversation if needed.\n                result = await dialog_context.continue_dialog()\n                if result.status == DialogTurnStatus.Empty:\n                    start_message_text = f\"Starting {dialog.id}\"\n                    await turn_context.send_trace_activity(\n                        f\"Extension {Dialog.__name__}.run_dialog\",\n                        label=start_message_text,\n                    )\n                    result = await dialog_context.begin_dialog(dialog.id)\n\n                # Send end of conversation if it is completed or cancelled.\n                if (\n                    result.status == DialogTurnStatus.Complete\n                    or result.status == DialogTurnStatus.Cancelled\n                ):\n                    end_message_text = f\"Dialog {dialog.id} has **completed**. Sending EndOfConversation.\"\n                    await turn_context.send_trace_activity(\n                        f\"Extension {Dialog.__name__}.run_dialog\",\n                        label=end_message_text,\n                        value=result.result,\n                    )\n\n                    activity = Activity(\n                        type=ActivityTypes.end_of_conversation, value=result.result\n                    )\n                    await turn_context.send_activity(activity)\n\n        else:\n            # The bot is running as a standard bot.\n            results = await dialog_context.continue_dialog()\n            if results.status == DialogTurnStatus.Empty:\n                await dialog_context.begin_dialog(dialog.id)\n", "path": "libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_extensions.py"}, {"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n# pylint: disable=no-member\n\nimport json\nfrom typing import Dict\nfrom logging import Logger\n\nimport aiohttp\nfrom botbuilder.core import InvokeResponse\nfrom botbuilder.core.skills import BotFrameworkClient\nfrom botbuilder.schema import (\n    Activity,\n    ExpectedReplies,\n    ConversationReference,\n    ConversationAccount,\n)\nfrom botframework.connector.auth import (\n    ChannelProvider,\n    CredentialProvider,\n    GovernmentConstants,\n    MicrosoftAppCredentials,\n)\n\n\nclass BotFrameworkHttpClient(BotFrameworkClient):\n\n    \"\"\"\n    A skill host adapter implements API to forward activity to a skill and\n    implements routing ChannelAPI calls from the Skill up through the bot/adapter.\n    \"\"\"\n\n    INVOKE_ACTIVITY_NAME = \"SkillEvents.ChannelApiInvoke\"\n    _BOT_IDENTITY_KEY = \"BotIdentity\"\n    _APP_CREDENTIALS_CACHE: Dict[str, MicrosoftAppCredentials] = {}\n\n    def __init__(\n        self,\n        credential_provider: CredentialProvider,\n        channel_provider: ChannelProvider = None,\n        logger: Logger = None,\n    ):\n        if not credential_provider:\n            raise TypeError(\"credential_provider can't be None\")\n\n        self._credential_provider = credential_provider\n        self._channel_provider = channel_provider\n        self._logger = logger\n        self._session = aiohttp.ClientSession()\n\n    async def post_activity(\n        self,\n        from_bot_id: str,\n        to_bot_id: str,\n        to_url: str,\n        service_url: str,\n        conversation_id: str,\n        activity: Activity,\n    ) -> InvokeResponse:\n        app_credentials = await self._get_app_credentials(from_bot_id, to_bot_id)\n\n        if not app_credentials:\n            raise KeyError(\"Unable to get appCredentials to connect to the skill\")\n\n        # Get token for the skill call\n        token = (\n            app_credentials.get_access_token()\n            if app_credentials.microsoft_app_id\n            else None\n        )\n\n        # Capture current activity settings before changing them.\n        # TODO: DO we need to set the activity ID? (events that are created manually don't have it).\n        original_conversation_id = activity.conversation.id\n        original_service_url = activity.service_url\n        original_caller_id = activity.caller_id\n        original_relates_to = activity.relates_to\n\n        try:\n            # TODO: The relato has to be ported to the adapter in the new integration library when\n            #  resolving conflicts in merge\n            activity.relates_to = ConversationReference(\n                service_url=activity.service_url,\n                activity_id=activity.id,\n                channel_id=activity.channel_id,\n                conversation=ConversationAccount(\n                    id=activity.conversation.id,\n                    name=activity.conversation.name,\n                    conversation_type=activity.conversation.conversation_type,\n                    aad_object_id=activity.conversation.aad_object_id,\n                    is_group=activity.conversation.is_group,\n                    role=activity.conversation.role,\n                    tenant_id=activity.conversation.tenant_id,\n                    properties=activity.conversation.properties,\n                ),\n                bot=None,\n            )\n            activity.conversation.id = conversation_id\n            activity.service_url = service_url\n            activity.caller_id = from_bot_id\n\n            headers_dict = {\n                \"Content-type\": \"application/json; charset=utf-8\",\n            }\n            if token:\n                headers_dict.update(\n                    {\"Authorization\": f\"Bearer {token}\",}\n                )\n\n            json_content = json.dumps(activity.serialize())\n            resp = await self._session.post(\n                to_url, data=json_content.encode(\"utf-8\"), headers=headers_dict,\n            )\n            resp.raise_for_status()\n            data = (await resp.read()).decode()\n            content = json.loads(data) if data else None\n\n            if content:\n                return InvokeResponse(status=resp.status, body=content)\n\n        finally:\n            # Restore activity properties.\n            activity.conversation.id = original_conversation_id\n            activity.service_url = original_service_url\n            activity.caller_id = original_caller_id\n            activity.relates_to = original_relates_to\n\n    async def post_buffered_activity(\n        self,\n        from_bot_id: str,\n        to_bot_id: str,\n        to_url: str,\n        service_url: str,\n        conversation_id: str,\n        activity: Activity,\n    ) -> [Activity]:\n        \"\"\"\n        Helper method to return a list of activities when an Activity is being\n        sent with DeliveryMode == expectReplies.\n        \"\"\"\n        response = await self.post_activity(\n            from_bot_id, to_bot_id, to_url, service_url, conversation_id, activity\n        )\n        if not response or (response.status / 100) != 2:\n            return []\n        return ExpectedReplies().deserialize(response.body).activities\n\n    async def _get_app_credentials(\n        self, app_id: str, oauth_scope: str\n    ) -> MicrosoftAppCredentials:\n        if not app_id:\n            return MicrosoftAppCredentials(None, None)\n\n        cache_key = f\"{app_id}{oauth_scope}\"\n        app_credentials = BotFrameworkHttpClient._APP_CREDENTIALS_CACHE.get(cache_key)\n\n        if app_credentials:\n            return app_credentials\n\n        app_password = await self._credential_provider.get_app_password(app_id)\n        app_credentials = MicrosoftAppCredentials(\n            app_id, app_password, oauth_scope=oauth_scope\n        )\n        if self._channel_provider and self._channel_provider.is_government():\n            app_credentials.oauth_endpoint = (\n                GovernmentConstants.TO_CHANNEL_FROM_BOT_LOGIN_URL\n            )\n            app_credentials.oauth_scope = (\n                GovernmentConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE\n            )\n\n        BotFrameworkHttpClient._APP_CREDENTIALS_CACHE[cache_key] = app_credentials\n        return app_credentials\n", "path": "libraries/botbuilder-integration-aiohttp/botbuilder/integration/aiohttp/bot_framework_http_client.py"}], "after_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n\nfrom botbuilder.core import BotAdapter, StatePropertyAccessor, TurnContext\nfrom botbuilder.dialogs import (\n    Dialog,\n    DialogEvents,\n    DialogSet,\n    DialogTurnStatus,\n)\nfrom botbuilder.schema import Activity, ActivityTypes\nfrom botframework.connector.auth import ClaimsIdentity, SkillValidation\n\n\nclass DialogExtensions:\n    @staticmethod\n    async def run_dialog(\n        dialog: Dialog, turn_context: TurnContext, accessor: StatePropertyAccessor\n    ):\n        dialog_set = DialogSet(accessor)\n        dialog_set.add(dialog)\n\n        dialog_context = await dialog_set.create_context(turn_context)\n\n        claims = turn_context.turn_state.get(BotAdapter.BOT_IDENTITY_KEY)\n        if isinstance(claims, ClaimsIdentity) and SkillValidation.is_skill_claim(\n            claims.claims\n        ):\n            # The bot is running as a skill.\n            if (\n                turn_context.activity.type == ActivityTypes.end_of_conversation\n                and dialog_context.stack\n                and DialogExtensions.__is_eoc_coming_from_parent(turn_context)\n            ):\n                remote_cancel_text = \"Skill was canceled through an EndOfConversation activity from the parent.\"\n                await turn_context.send_trace_activity(\n                    f\"Extension {Dialog.__name__}.run_dialog\", label=remote_cancel_text,\n                )\n\n                await dialog_context.cancel_all_dialogs()\n            else:\n                # Process a reprompt event sent from the parent.\n                if (\n                    turn_context.activity.type == ActivityTypes.event\n                    and turn_context.activity.name == DialogEvents.reprompt_dialog\n                    and dialog_context.stack\n                ):\n                    await dialog_context.reprompt_dialog()\n                    return\n\n                # Run the Dialog with the new message Activity and capture the results\n                # so we can send end of conversation if needed.\n                result = await dialog_context.continue_dialog()\n                if result.status == DialogTurnStatus.Empty:\n                    start_message_text = f\"Starting {dialog.id}\"\n                    await turn_context.send_trace_activity(\n                        f\"Extension {Dialog.__name__}.run_dialog\",\n                        label=start_message_text,\n                    )\n                    result = await dialog_context.begin_dialog(dialog.id)\n\n                # Send end of conversation if it is completed or cancelled.\n                if (\n                    result.status == DialogTurnStatus.Complete\n                    or result.status == DialogTurnStatus.Cancelled\n                ):\n                    end_message_text = f\"Dialog {dialog.id} has **completed**. Sending EndOfConversation.\"\n                    await turn_context.send_trace_activity(\n                        f\"Extension {Dialog.__name__}.run_dialog\",\n                        label=end_message_text,\n                        value=result.result,\n                    )\n\n                    activity = Activity(\n                        type=ActivityTypes.end_of_conversation, value=result.result\n                    )\n                    await turn_context.send_activity(activity)\n\n        else:\n            # The bot is running as a standard bot.\n            results = await dialog_context.continue_dialog()\n            if results.status == DialogTurnStatus.Empty:\n                await dialog_context.begin_dialog(dialog.id)\n\n    @staticmethod\n    def __is_eoc_coming_from_parent(turn_context: TurnContext) -> bool:\n        # To determine the direction we check callerId property which is set to the parent bot\n        # by the BotFrameworkHttpClient on outgoing requests.\n        return bool(turn_context.activity.caller_id)\n", "path": "libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_extensions.py"}, {"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n# pylint: disable=no-member\n\nimport json\nfrom typing import Dict\nfrom logging import Logger\n\nimport aiohttp\nfrom botbuilder.core import InvokeResponse\nfrom botbuilder.core.skills import BotFrameworkClient\nfrom botbuilder.schema import (\n    Activity,\n    ExpectedReplies,\n    ConversationReference,\n    ConversationAccount,\n)\nfrom botframework.connector.auth import (\n    ChannelProvider,\n    CredentialProvider,\n    GovernmentConstants,\n    MicrosoftAppCredentials,\n)\n\n\nclass BotFrameworkHttpClient(BotFrameworkClient):\n\n    \"\"\"\n    A skill host adapter implements API to forward activity to a skill and\n    implements routing ChannelAPI calls from the Skill up through the bot/adapter.\n    \"\"\"\n\n    INVOKE_ACTIVITY_NAME = \"SkillEvents.ChannelApiInvoke\"\n    _BOT_IDENTITY_KEY = \"BotIdentity\"\n    _APP_CREDENTIALS_CACHE: Dict[str, MicrosoftAppCredentials] = {}\n\n    def __init__(\n        self,\n        credential_provider: CredentialProvider,\n        channel_provider: ChannelProvider = None,\n        logger: Logger = None,\n    ):\n        if not credential_provider:\n            raise TypeError(\"credential_provider can't be None\")\n\n        self._credential_provider = credential_provider\n        self._channel_provider = channel_provider\n        self._logger = logger\n        self._session = aiohttp.ClientSession()\n\n    async def post_activity(\n        self,\n        from_bot_id: str,\n        to_bot_id: str,\n        to_url: str,\n        service_url: str,\n        conversation_id: str,\n        activity: Activity,\n    ) -> InvokeResponse:\n        app_credentials = await self._get_app_credentials(from_bot_id, to_bot_id)\n\n        if not app_credentials:\n            raise KeyError(\"Unable to get appCredentials to connect to the skill\")\n\n        # Get token for the skill call\n        token = (\n            app_credentials.get_access_token()\n            if app_credentials.microsoft_app_id\n            else None\n        )\n\n        # Capture current activity settings before changing them.\n        # TODO: DO we need to set the activity ID? (events that are created manually don't have it).\n        original_conversation_id = activity.conversation.id\n        original_service_url = activity.service_url\n        original_caller_id = activity.caller_id\n        original_relates_to = activity.relates_to\n\n        try:\n            # TODO: The relato has to be ported to the adapter in the new integration library when\n            #  resolving conflicts in merge\n            activity.relates_to = ConversationReference(\n                service_url=activity.service_url,\n                activity_id=activity.id,\n                channel_id=activity.channel_id,\n                conversation=ConversationAccount(\n                    id=activity.conversation.id,\n                    name=activity.conversation.name,\n                    conversation_type=activity.conversation.conversation_type,\n                    aad_object_id=activity.conversation.aad_object_id,\n                    is_group=activity.conversation.is_group,\n                    role=activity.conversation.role,\n                    tenant_id=activity.conversation.tenant_id,\n                    properties=activity.conversation.properties,\n                ),\n                bot=None,\n            )\n            activity.conversation.id = conversation_id\n            activity.service_url = service_url\n            activity.caller_id = f\"urn:botframework:aadappid:{from_bot_id}\"\n\n            headers_dict = {\n                \"Content-type\": \"application/json; charset=utf-8\",\n            }\n            if token:\n                headers_dict.update(\n                    {\"Authorization\": f\"Bearer {token}\",}\n                )\n\n            json_content = json.dumps(activity.serialize())\n            resp = await self._session.post(\n                to_url, data=json_content.encode(\"utf-8\"), headers=headers_dict,\n            )\n            resp.raise_for_status()\n            data = (await resp.read()).decode()\n            content = json.loads(data) if data else None\n\n            if content:\n                return InvokeResponse(status=resp.status, body=content)\n\n        finally:\n            # Restore activity properties.\n            activity.conversation.id = original_conversation_id\n            activity.service_url = original_service_url\n            activity.caller_id = original_caller_id\n            activity.relates_to = original_relates_to\n\n    async def post_buffered_activity(\n        self,\n        from_bot_id: str,\n        to_bot_id: str,\n        to_url: str,\n        service_url: str,\n        conversation_id: str,\n        activity: Activity,\n    ) -> [Activity]:\n        \"\"\"\n        Helper method to return a list of activities when an Activity is being\n        sent with DeliveryMode == expectReplies.\n        \"\"\"\n        response = await self.post_activity(\n            from_bot_id, to_bot_id, to_url, service_url, conversation_id, activity\n        )\n        if not response or (response.status / 100) != 2:\n            return []\n        return ExpectedReplies().deserialize(response.body).activities\n\n    async def _get_app_credentials(\n        self, app_id: str, oauth_scope: str\n    ) -> MicrosoftAppCredentials:\n        if not app_id:\n            return MicrosoftAppCredentials(None, None)\n\n        cache_key = f\"{app_id}{oauth_scope}\"\n        app_credentials = BotFrameworkHttpClient._APP_CREDENTIALS_CACHE.get(cache_key)\n\n        if app_credentials:\n            return app_credentials\n\n        app_password = await self._credential_provider.get_app_password(app_id)\n        app_credentials = MicrosoftAppCredentials(\n            app_id, app_password, oauth_scope=oauth_scope\n        )\n        if self._channel_provider and self._channel_provider.is_government():\n            app_credentials.oauth_endpoint = (\n                GovernmentConstants.TO_CHANNEL_FROM_BOT_LOGIN_URL\n            )\n            app_credentials.oauth_scope = (\n                GovernmentConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE\n            )\n\n        BotFrameworkHttpClient._APP_CREDENTIALS_CACHE[cache_key] = app_credentials\n        return app_credentials\n", "path": "libraries/botbuilder-integration-aiohttp/botbuilder/integration/aiohttp/bot_framework_http_client.py"}]}