We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[PORT] Add Teams specific telemetry properties 
> Port this change from botbuilder-dotnet/master branch:
https://github.com/microsoft/botbuilder-dotnet/pull/4256

Add Teams specific telemetry properties when activity received via the Teams channel.

See also https://github.com/microsoft/botframework-sdk/issues/5855



# Changed projects
* Microsoft.Bot.Builder
* Microsoft.Bot.Builder.Tests





--- 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-core/botbuilder/core/telemetry_logger_middleware.py`
Content:
```
1 # Copyright (c) Microsoft Corporation. All rights reserved.
2 # Licensed under the MIT License.
3 """Middleware Component for logging Activity messages."""
4 
5 from typing import Awaitable, Callable, List, Dict
6 from botbuilder.schema import Activity, ConversationReference, ActivityTypes
7 from .bot_telemetry_client import BotTelemetryClient
8 from .bot_assert import BotAssert
9 from .middleware_set import Middleware
10 from .null_telemetry_client import NullTelemetryClient
11 from .turn_context import TurnContext
12 from .telemetry_constants import TelemetryConstants
13 from .telemetry_logger_constants import TelemetryLoggerConstants
14 
15 
16 # pylint: disable=line-too-long
17 class TelemetryLoggerMiddleware(Middleware):
18     """Middleware for logging incoming, outgoing, updated or deleted Activity messages."""
19 
20     def __init__(
21         self, telemetry_client: BotTelemetryClient, log_personal_information: bool
22     ) -> None:
23         super(TelemetryLoggerMiddleware, self).__init__()
24         self._telemetry_client = telemetry_client or NullTelemetryClient()
25         self._log_personal_information = log_personal_information
26 
27     @property
28     def telemetry_client(self) -> BotTelemetryClient:
29         """Gets the currently configured BotTelemetryClient."""
30         return self._telemetry_client
31 
32     @property
33     def log_personal_information(self) -> bool:
34         """ Gets a value indicating whether determines whether to log personal
35         information that came from the user."""
36         return self._log_personal_information
37 
38     # pylint: disable=arguments-differ
39     async def on_turn(
40         self, context: TurnContext, logic_fn: Callable[[TurnContext], Awaitable]
41     ) -> None:
42         """Logs events based on incoming and outgoing activities using
43         BotTelemetryClient base class
44 
45         :param turn_context: The context object for this turn.
46         :param logic: Callable to continue the bot middleware pipeline
47 
48         :return:  None
49         """
50         BotAssert.context_not_none(context)
51 
52         # Log incoming activity at beginning of turn
53         if context.activity:
54             activity = context.activity
55             # Log Bot Message Received
56             await self.on_receive_activity(activity)
57 
58         # hook up onSend pipeline
59         # pylint: disable=unused-argument
60         async def send_activities_handler(
61             ctx: TurnContext,
62             activities: List[Activity],
63             next_send: Callable[[], Awaitable[None]],
64         ):
65             # Run full pipeline
66             responses = await next_send()
67             for activity in activities:
68                 await self.on_send_activity(activity)
69             return responses
70 
71         context.on_send_activities(send_activities_handler)
72 
73         # hook up update activity pipeline
74         async def update_activity_handler(
75             ctx: TurnContext, activity: Activity, next_update: Callable[[], Awaitable]
76         ):
77             # Run full pipeline
78             response = await next_update()
79             await self.on_update_activity(activity)
80             return response
81 
82         context.on_update_activity(update_activity_handler)
83 
84         # hook up delete activity pipeline
85         async def delete_activity_handler(
86             ctx: TurnContext,
87             reference: ConversationReference,
88             next_delete: Callable[[], Awaitable],
89         ):
90             # Run full pipeline
91             await next_delete()
92 
93             delete_msg = Activity(
94                 type=ActivityTypes.message_delete, id=reference.activity_id
95             )
96             deleted_activity: Activity = TurnContext.apply_conversation_reference(
97                 delete_msg, reference, False
98             )
99             await self.on_delete_activity(deleted_activity)
100 
101         context.on_delete_activity(delete_activity_handler)
102 
103         if logic_fn:
104             await logic_fn()
105 
106     async def on_receive_activity(self, activity: Activity) -> None:
107         """Invoked when a message is received from the user.
108         Performs logging of telemetry data using the BotTelemetryClient.track_event() method.
109         This event name used is "BotMessageReceived".
110         :param activity: Current activity sent from user.
111         """
112         self.telemetry_client.track_event(
113             TelemetryLoggerConstants.BOT_MSG_RECEIVE_EVENT,
114             await self.fill_receive_event_properties(activity),
115         )
116 
117     async def on_send_activity(self, activity: Activity) -> None:
118         """Invoked when the bot sends a message to the user.
119         Performs logging of telemetry data using the BotTelemetryClient.track_event() method.
120         This event name used is "BotMessageSend".
121         :param activity: Current activity sent from bot.
122         """
123         self.telemetry_client.track_event(
124             TelemetryLoggerConstants.BOT_MSG_SEND_EVENT,
125             await self.fill_send_event_properties(activity),
126         )
127 
128     async def on_update_activity(self, activity: Activity) -> None:
129         """Invoked when the bot updates a message.
130         Performs logging of telemetry data using the BotTelemetryClient.track_event() method.
131         This event name used is "BotMessageUpdate".
132         :param activity: Current activity sent from user.
133         """
134         self.telemetry_client.track_event(
135             TelemetryLoggerConstants.BOT_MSG_UPDATE_EVENT,
136             await self.fill_update_event_properties(activity),
137         )
138 
139     async def on_delete_activity(self, activity: Activity) -> None:
140         """Invoked when the bot deletes a message.
141         Performs logging of telemetry data using the BotTelemetryClient.track_event() method.
142         This event name used is "BotMessageDelete".
143         :param activity: Current activity sent from user.
144         """
145         self.telemetry_client.track_event(
146             TelemetryLoggerConstants.BOT_MSG_DELETE_EVENT,
147             await self.fill_delete_event_properties(activity),
148         )
149 
150     async def fill_receive_event_properties(
151         self, activity: Activity, additional_properties: Dict[str, str] = None
152     ) -> Dict[str, str]:
153         """Fills the event properties for the BotMessageReceived.
154         Adheres to the LogPersonalInformation flag to filter Name, Text and Speak properties.
155         :param activity: activity sent from user.
156         :param additional_properties: Additional properties to add to the event.
157         Additional properties can override "stock" properties.
158 
159         :return: A dictionary that is sent as "Properties" to
160         BotTelemetryClient.track_event method for the BotMessageReceived event.
161         """
162         properties = {
163             TelemetryConstants.FROM_ID_PROPERTY: activity.from_property.id
164             if activity.from_property
165             else None,
166             TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,
167             TelemetryConstants.LOCALE_PROPERTY: activity.locale,
168             TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,
169             TelemetryConstants.RECIPIENT_NAME_PROPERTY: activity.recipient.name,
170         }
171 
172         if self.log_personal_information:
173             if (
174                 activity.from_property
175                 and activity.from_property.name
176                 and activity.from_property.name.strip()
177             ):
178                 properties[
179                     TelemetryConstants.FROM_NAME_PROPERTY
180                 ] = activity.from_property.name
181             if activity.text and activity.text.strip():
182                 properties[TelemetryConstants.TEXT_PROPERTY] = activity.text
183             if activity.speak and activity.speak.strip():
184                 properties[TelemetryConstants.SPEAK_PROPERTY] = activity.speak
185 
186         # Additional properties can override "stock" properties
187         if additional_properties:
188             for prop in additional_properties:
189                 properties[prop.key] = prop.value
190 
191         return properties
192 
193     async def fill_send_event_properties(
194         self, activity: Activity, additional_properties: Dict[str, str] = None
195     ) -> Dict[str, str]:
196         """Fills the event properties for the BotMessageSend.
197         These properties are logged when an activity message is sent by the Bot to the user.
198         :param activity: activity sent from user.
199         :param additional_properties: Additional properties to add to the event.
200         Additional properties can override "stock" properties.
201 
202         :return: A dictionary that is sent as "Properties" to the
203         BotTelemetryClient.track_event method for the BotMessageSend event.
204         """
205         properties = {
206             TelemetryConstants.REPLY_ACTIVITY_ID_PROPERTY: activity.reply_to_id,
207             TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,
208             TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,
209             TelemetryConstants.LOCALE_PROPERTY: activity.locale,
210         }
211 
212         # Use the LogPersonalInformation flag to toggle logging PII data, text and user name are common examples
213         if self.log_personal_information:
214             if activity.attachments and activity.attachments.strip():
215                 properties[
216                     TelemetryConstants.ATTACHMENTS_PROPERTY
217                 ] = activity.attachments
218             if activity.from_property.name and activity.from_property.name.strip():
219                 properties[
220                     TelemetryConstants.FROM_NAME_PROPERTY
221                 ] = activity.from_property.name
222             if activity.text and activity.text.strip():
223                 properties[TelemetryConstants.TEXT_PROPERTY] = activity.text
224             if activity.speak and activity.speak.strip():
225                 properties[TelemetryConstants.SPEAK_PROPERTY] = activity.speak
226 
227         # Additional properties can override "stock" properties
228         if additional_properties:
229             for prop in additional_properties:
230                 properties[prop.key] = prop.value
231 
232         return properties
233 
234     async def fill_update_event_properties(
235         self, activity: Activity, additional_properties: Dict[str, str] = None
236     ) -> Dict[str, str]:
237         """Fills the event properties for the BotMessageUpdate.
238         These properties are logged when an activity message is updated by the Bot.
239         For example, if a card is interacted with by the use, and the card needs
240         to be updated to reflect some interaction.
241         :param activity: activity sent from user.
242         :param additional_properties: Additional properties to add to the event.
243         Additional properties can override "stock" properties.
244 
245         :return: A dictionary that is sent as "Properties" to the
246         BotTelemetryClient.track_event method for the BotMessageUpdate event.
247         """
248         properties = {
249             TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,
250             TelemetryConstants.CONVERSATION_ID_PROPERTY: activity.conversation.id,
251             TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,
252             TelemetryConstants.LOCALE_PROPERTY: activity.locale,
253         }
254 
255         # Use the LogPersonalInformation flag to toggle logging PII data, text is a common examples
256         if self.log_personal_information:
257             if activity.text and activity.text.strip():
258                 properties[TelemetryConstants.TEXT_PROPERTY] = activity.text
259 
260         # Additional properties can override "stock" properties
261         if additional_properties:
262             for prop in additional_properties:
263                 properties[prop.key] = prop.value
264 
265         return properties
266 
267     async def fill_delete_event_properties(
268         self, activity: Activity, additional_properties: Dict[str, str] = None
269     ) -> Dict[str, str]:
270         """Fills the event properties for the BotMessageDelete.
271         These properties are logged when an activity message is deleted by the Bot.
272         :param activity: activity sent from user.
273         :param additional_properties: Additional properties to add to the event.
274         Additional properties can override "stock" properties.
275 
276         :return: A dictionary that is sent as "Properties" to the
277         BotTelemetryClient.track_event method for the BotMessageUpdate event.
278         """
279         properties = {
280             TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,
281             TelemetryConstants.CONVERSATION_ID_PROPERTY: activity.conversation.id,
282             TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,
283         }
284 
285         # Additional properties can override "stock" properties
286         if additional_properties:
287             for prop in additional_properties:
288                 properties[prop.key] = prop.value
289 
290         return properties
291
```
--- 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-core/botbuilder/core/telemetry_logger_middleware.py b/libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py
--- a/libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py
+++ b/libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py
@@ -1,9 +1,11 @@
 # Copyright (c) Microsoft Corporation. All rights reserved.
 # Licensed under the MIT License.
 """Middleware Component for logging Activity messages."""
-
 from typing import Awaitable, Callable, List, Dict
 from botbuilder.schema import Activity, ConversationReference, ActivityTypes
+from botbuilder.schema.teams import TeamsChannelData, TeamInfo
+from botframework.connector import Channels
+
 from .bot_telemetry_client import BotTelemetryClient
 from .bot_assert import BotAssert
 from .middleware_set import Middleware
@@ -183,6 +185,10 @@
             if activity.speak and activity.speak.strip():
                 properties[TelemetryConstants.SPEAK_PROPERTY] = activity.speak
 
+        TelemetryLoggerMiddleware.__populate_additional_channel_properties(
+            activity, properties
+        )
+
         # Additional properties can override "stock" properties
         if additional_properties:
             for prop in additional_properties:
@@ -288,3 +294,25 @@
                 properties[prop.key] = prop.value
 
         return properties
+
+    @staticmethod
+    def __populate_additional_channel_properties(
+        activity: Activity, properties: dict,
+    ):
+        if activity.channel_id == Channels.ms_teams:
+            teams_channel_data: TeamsChannelData = activity.channel_data
+
+            properties["TeamsTenantId"] = (
+                teams_channel_data.tenant
+                if teams_channel_data and teams_channel_data.tenant
+                else ""
+            )
+
+            properties["TeamsUserAadObjectId"] = (
+                activity.from_property.aad_object_id if activity.from_property else ""
+            )
+
+            if teams_channel_data and teams_channel_data.team:
+                properties["TeamsTeamInfo"] = TeamInfo.serialize(
+                    teams_channel_data.team
+                )

{"golden_diff": "diff --git a/libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py b/libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py\n--- a/libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py\n+++ b/libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py\n@@ -1,9 +1,11 @@\n # Copyright (c) Microsoft Corporation. All rights reserved.\n # Licensed under the MIT License.\n \"\"\"Middleware Component for logging Activity messages.\"\"\"\n-\n from typing import Awaitable, Callable, List, Dict\n from botbuilder.schema import Activity, ConversationReference, ActivityTypes\n+from botbuilder.schema.teams import TeamsChannelData, TeamInfo\n+from botframework.connector import Channels\n+\n from .bot_telemetry_client import BotTelemetryClient\n from .bot_assert import BotAssert\n from .middleware_set import Middleware\n@@ -183,6 +185,10 @@\n             if activity.speak and activity.speak.strip():\n                 properties[TelemetryConstants.SPEAK_PROPERTY] = activity.speak\n \n+        TelemetryLoggerMiddleware.__populate_additional_channel_properties(\n+            activity, properties\n+        )\n+\n         # Additional properties can override \"stock\" properties\n         if additional_properties:\n             for prop in additional_properties:\n@@ -288,3 +294,25 @@\n                 properties[prop.key] = prop.value\n \n         return properties\n+\n+    @staticmethod\n+    def __populate_additional_channel_properties(\n+        activity: Activity, properties: dict,\n+    ):\n+        if activity.channel_id == Channels.ms_teams:\n+            teams_channel_data: TeamsChannelData = activity.channel_data\n+\n+            properties[\"TeamsTenantId\"] = (\n+                teams_channel_data.tenant\n+                if teams_channel_data and teams_channel_data.tenant\n+                else \"\"\n+            )\n+\n+            properties[\"TeamsUserAadObjectId\"] = (\n+                activity.from_property.aad_object_id if activity.from_property else \"\"\n+            )\n+\n+            if teams_channel_data and teams_channel_data.team:\n+                properties[\"TeamsTeamInfo\"] = TeamInfo.serialize(\n+                    teams_channel_data.team\n+                )\n", "issue": "[PORT] Add Teams specific telemetry properties \n> Port this change from botbuilder-dotnet/master branch:\r\nhttps://github.com/microsoft/botbuilder-dotnet/pull/4256\r\n\r\nAdd Teams specific telemetry properties when activity received via the Teams channel.\r\n\r\nSee also https://github.com/microsoft/botframework-sdk/issues/5855\r\n\r\n\r\n\r\n# Changed projects\r\n* Microsoft.Bot.Builder\r\n* Microsoft.Bot.Builder.Tests\r\n\r\n\r\n\r\n\n", "before_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n\"\"\"Middleware Component for logging Activity messages.\"\"\"\n\nfrom typing import Awaitable, Callable, List, Dict\nfrom botbuilder.schema import Activity, ConversationReference, ActivityTypes\nfrom .bot_telemetry_client import BotTelemetryClient\nfrom .bot_assert import BotAssert\nfrom .middleware_set import Middleware\nfrom .null_telemetry_client import NullTelemetryClient\nfrom .turn_context import TurnContext\nfrom .telemetry_constants import TelemetryConstants\nfrom .telemetry_logger_constants import TelemetryLoggerConstants\n\n\n# pylint: disable=line-too-long\nclass TelemetryLoggerMiddleware(Middleware):\n    \"\"\"Middleware for logging incoming, outgoing, updated or deleted Activity messages.\"\"\"\n\n    def __init__(\n        self, telemetry_client: BotTelemetryClient, log_personal_information: bool\n    ) -> None:\n        super(TelemetryLoggerMiddleware, self).__init__()\n        self._telemetry_client = telemetry_client or NullTelemetryClient()\n        self._log_personal_information = log_personal_information\n\n    @property\n    def telemetry_client(self) -> BotTelemetryClient:\n        \"\"\"Gets the currently configured BotTelemetryClient.\"\"\"\n        return self._telemetry_client\n\n    @property\n    def log_personal_information(self) -> bool:\n        \"\"\" Gets a value indicating whether determines whether to log personal\n        information that came from the user.\"\"\"\n        return self._log_personal_information\n\n    # pylint: disable=arguments-differ\n    async def on_turn(\n        self, context: TurnContext, logic_fn: Callable[[TurnContext], Awaitable]\n    ) -> None:\n        \"\"\"Logs events based on incoming and outgoing activities using\n        BotTelemetryClient base class\n\n        :param turn_context: The context object for this turn.\n        :param logic: Callable to continue the bot middleware pipeline\n\n        :return:  None\n        \"\"\"\n        BotAssert.context_not_none(context)\n\n        # Log incoming activity at beginning of turn\n        if context.activity:\n            activity = context.activity\n            # Log Bot Message Received\n            await self.on_receive_activity(activity)\n\n        # hook up onSend pipeline\n        # pylint: disable=unused-argument\n        async def send_activities_handler(\n            ctx: TurnContext,\n            activities: List[Activity],\n            next_send: Callable[[], Awaitable[None]],\n        ):\n            # Run full pipeline\n            responses = await next_send()\n            for activity in activities:\n                await self.on_send_activity(activity)\n            return responses\n\n        context.on_send_activities(send_activities_handler)\n\n        # hook up update activity pipeline\n        async def update_activity_handler(\n            ctx: TurnContext, activity: Activity, next_update: Callable[[], Awaitable]\n        ):\n            # Run full pipeline\n            response = await next_update()\n            await self.on_update_activity(activity)\n            return response\n\n        context.on_update_activity(update_activity_handler)\n\n        # hook up delete activity pipeline\n        async def delete_activity_handler(\n            ctx: TurnContext,\n            reference: ConversationReference,\n            next_delete: Callable[[], Awaitable],\n        ):\n            # Run full pipeline\n            await next_delete()\n\n            delete_msg = Activity(\n                type=ActivityTypes.message_delete, id=reference.activity_id\n            )\n            deleted_activity: Activity = TurnContext.apply_conversation_reference(\n                delete_msg, reference, False\n            )\n            await self.on_delete_activity(deleted_activity)\n\n        context.on_delete_activity(delete_activity_handler)\n\n        if logic_fn:\n            await logic_fn()\n\n    async def on_receive_activity(self, activity: Activity) -> None:\n        \"\"\"Invoked when a message is received from the user.\n        Performs logging of telemetry data using the BotTelemetryClient.track_event() method.\n        This event name used is \"BotMessageReceived\".\n        :param activity: Current activity sent from user.\n        \"\"\"\n        self.telemetry_client.track_event(\n            TelemetryLoggerConstants.BOT_MSG_RECEIVE_EVENT,\n            await self.fill_receive_event_properties(activity),\n        )\n\n    async def on_send_activity(self, activity: Activity) -> None:\n        \"\"\"Invoked when the bot sends a message to the user.\n        Performs logging of telemetry data using the BotTelemetryClient.track_event() method.\n        This event name used is \"BotMessageSend\".\n        :param activity: Current activity sent from bot.\n        \"\"\"\n        self.telemetry_client.track_event(\n            TelemetryLoggerConstants.BOT_MSG_SEND_EVENT,\n            await self.fill_send_event_properties(activity),\n        )\n\n    async def on_update_activity(self, activity: Activity) -> None:\n        \"\"\"Invoked when the bot updates a message.\n        Performs logging of telemetry data using the BotTelemetryClient.track_event() method.\n        This event name used is \"BotMessageUpdate\".\n        :param activity: Current activity sent from user.\n        \"\"\"\n        self.telemetry_client.track_event(\n            TelemetryLoggerConstants.BOT_MSG_UPDATE_EVENT,\n            await self.fill_update_event_properties(activity),\n        )\n\n    async def on_delete_activity(self, activity: Activity) -> None:\n        \"\"\"Invoked when the bot deletes a message.\n        Performs logging of telemetry data using the BotTelemetryClient.track_event() method.\n        This event name used is \"BotMessageDelete\".\n        :param activity: Current activity sent from user.\n        \"\"\"\n        self.telemetry_client.track_event(\n            TelemetryLoggerConstants.BOT_MSG_DELETE_EVENT,\n            await self.fill_delete_event_properties(activity),\n        )\n\n    async def fill_receive_event_properties(\n        self, activity: Activity, additional_properties: Dict[str, str] = None\n    ) -> Dict[str, str]:\n        \"\"\"Fills the event properties for the BotMessageReceived.\n        Adheres to the LogPersonalInformation flag to filter Name, Text and Speak properties.\n        :param activity: activity sent from user.\n        :param additional_properties: Additional properties to add to the event.\n        Additional properties can override \"stock\" properties.\n\n        :return: A dictionary that is sent as \"Properties\" to\n        BotTelemetryClient.track_event method for the BotMessageReceived event.\n        \"\"\"\n        properties = {\n            TelemetryConstants.FROM_ID_PROPERTY: activity.from_property.id\n            if activity.from_property\n            else None,\n            TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,\n            TelemetryConstants.LOCALE_PROPERTY: activity.locale,\n            TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,\n            TelemetryConstants.RECIPIENT_NAME_PROPERTY: activity.recipient.name,\n        }\n\n        if self.log_personal_information:\n            if (\n                activity.from_property\n                and activity.from_property.name\n                and activity.from_property.name.strip()\n            ):\n                properties[\n                    TelemetryConstants.FROM_NAME_PROPERTY\n                ] = activity.from_property.name\n            if activity.text and activity.text.strip():\n                properties[TelemetryConstants.TEXT_PROPERTY] = activity.text\n            if activity.speak and activity.speak.strip():\n                properties[TelemetryConstants.SPEAK_PROPERTY] = activity.speak\n\n        # Additional properties can override \"stock\" properties\n        if additional_properties:\n            for prop in additional_properties:\n                properties[prop.key] = prop.value\n\n        return properties\n\n    async def fill_send_event_properties(\n        self, activity: Activity, additional_properties: Dict[str, str] = None\n    ) -> Dict[str, str]:\n        \"\"\"Fills the event properties for the BotMessageSend.\n        These properties are logged when an activity message is sent by the Bot to the user.\n        :param activity: activity sent from user.\n        :param additional_properties: Additional properties to add to the event.\n        Additional properties can override \"stock\" properties.\n\n        :return: A dictionary that is sent as \"Properties\" to the\n        BotTelemetryClient.track_event method for the BotMessageSend event.\n        \"\"\"\n        properties = {\n            TelemetryConstants.REPLY_ACTIVITY_ID_PROPERTY: activity.reply_to_id,\n            TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,\n            TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,\n            TelemetryConstants.LOCALE_PROPERTY: activity.locale,\n        }\n\n        # Use the LogPersonalInformation flag to toggle logging PII data, text and user name are common examples\n        if self.log_personal_information:\n            if activity.attachments and activity.attachments.strip():\n                properties[\n                    TelemetryConstants.ATTACHMENTS_PROPERTY\n                ] = activity.attachments\n            if activity.from_property.name and activity.from_property.name.strip():\n                properties[\n                    TelemetryConstants.FROM_NAME_PROPERTY\n                ] = activity.from_property.name\n            if activity.text and activity.text.strip():\n                properties[TelemetryConstants.TEXT_PROPERTY] = activity.text\n            if activity.speak and activity.speak.strip():\n                properties[TelemetryConstants.SPEAK_PROPERTY] = activity.speak\n\n        # Additional properties can override \"stock\" properties\n        if additional_properties:\n            for prop in additional_properties:\n                properties[prop.key] = prop.value\n\n        return properties\n\n    async def fill_update_event_properties(\n        self, activity: Activity, additional_properties: Dict[str, str] = None\n    ) -> Dict[str, str]:\n        \"\"\"Fills the event properties for the BotMessageUpdate.\n        These properties are logged when an activity message is updated by the Bot.\n        For example, if a card is interacted with by the use, and the card needs\n        to be updated to reflect some interaction.\n        :param activity: activity sent from user.\n        :param additional_properties: Additional properties to add to the event.\n        Additional properties can override \"stock\" properties.\n\n        :return: A dictionary that is sent as \"Properties\" to the\n        BotTelemetryClient.track_event method for the BotMessageUpdate event.\n        \"\"\"\n        properties = {\n            TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,\n            TelemetryConstants.CONVERSATION_ID_PROPERTY: activity.conversation.id,\n            TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,\n            TelemetryConstants.LOCALE_PROPERTY: activity.locale,\n        }\n\n        # Use the LogPersonalInformation flag to toggle logging PII data, text is a common examples\n        if self.log_personal_information:\n            if activity.text and activity.text.strip():\n                properties[TelemetryConstants.TEXT_PROPERTY] = activity.text\n\n        # Additional properties can override \"stock\" properties\n        if additional_properties:\n            for prop in additional_properties:\n                properties[prop.key] = prop.value\n\n        return properties\n\n    async def fill_delete_event_properties(\n        self, activity: Activity, additional_properties: Dict[str, str] = None\n    ) -> Dict[str, str]:\n        \"\"\"Fills the event properties for the BotMessageDelete.\n        These properties are logged when an activity message is deleted by the Bot.\n        :param activity: activity sent from user.\n        :param additional_properties: Additional properties to add to the event.\n        Additional properties can override \"stock\" properties.\n\n        :return: A dictionary that is sent as \"Properties\" to the\n        BotTelemetryClient.track_event method for the BotMessageUpdate event.\n        \"\"\"\n        properties = {\n            TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,\n            TelemetryConstants.CONVERSATION_ID_PROPERTY: activity.conversation.id,\n            TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,\n        }\n\n        # Additional properties can override \"stock\" properties\n        if additional_properties:\n            for prop in additional_properties:\n                properties[prop.key] = prop.value\n\n        return properties\n", "path": "libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py"}], "after_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n\"\"\"Middleware Component for logging Activity messages.\"\"\"\nfrom typing import Awaitable, Callable, List, Dict\nfrom botbuilder.schema import Activity, ConversationReference, ActivityTypes\nfrom botbuilder.schema.teams import TeamsChannelData, TeamInfo\nfrom botframework.connector import Channels\n\nfrom .bot_telemetry_client import BotTelemetryClient\nfrom .bot_assert import BotAssert\nfrom .middleware_set import Middleware\nfrom .null_telemetry_client import NullTelemetryClient\nfrom .turn_context import TurnContext\nfrom .telemetry_constants import TelemetryConstants\nfrom .telemetry_logger_constants import TelemetryLoggerConstants\n\n\n# pylint: disable=line-too-long\nclass TelemetryLoggerMiddleware(Middleware):\n    \"\"\"Middleware for logging incoming, outgoing, updated or deleted Activity messages.\"\"\"\n\n    def __init__(\n        self, telemetry_client: BotTelemetryClient, log_personal_information: bool\n    ) -> None:\n        super(TelemetryLoggerMiddleware, self).__init__()\n        self._telemetry_client = telemetry_client or NullTelemetryClient()\n        self._log_personal_information = log_personal_information\n\n    @property\n    def telemetry_client(self) -> BotTelemetryClient:\n        \"\"\"Gets the currently configured BotTelemetryClient.\"\"\"\n        return self._telemetry_client\n\n    @property\n    def log_personal_information(self) -> bool:\n        \"\"\" Gets a value indicating whether determines whether to log personal\n        information that came from the user.\"\"\"\n        return self._log_personal_information\n\n    # pylint: disable=arguments-differ\n    async def on_turn(\n        self, context: TurnContext, logic_fn: Callable[[TurnContext], Awaitable]\n    ) -> None:\n        \"\"\"Logs events based on incoming and outgoing activities using\n        BotTelemetryClient base class\n\n        :param turn_context: The context object for this turn.\n        :param logic: Callable to continue the bot middleware pipeline\n\n        :return:  None\n        \"\"\"\n        BotAssert.context_not_none(context)\n\n        # Log incoming activity at beginning of turn\n        if context.activity:\n            activity = context.activity\n            # Log Bot Message Received\n            await self.on_receive_activity(activity)\n\n        # hook up onSend pipeline\n        # pylint: disable=unused-argument\n        async def send_activities_handler(\n            ctx: TurnContext,\n            activities: List[Activity],\n            next_send: Callable[[], Awaitable[None]],\n        ):\n            # Run full pipeline\n            responses = await next_send()\n            for activity in activities:\n                await self.on_send_activity(activity)\n            return responses\n\n        context.on_send_activities(send_activities_handler)\n\n        # hook up update activity pipeline\n        async def update_activity_handler(\n            ctx: TurnContext, activity: Activity, next_update: Callable[[], Awaitable]\n        ):\n            # Run full pipeline\n            response = await next_update()\n            await self.on_update_activity(activity)\n            return response\n\n        context.on_update_activity(update_activity_handler)\n\n        # hook up delete activity pipeline\n        async def delete_activity_handler(\n            ctx: TurnContext,\n            reference: ConversationReference,\n            next_delete: Callable[[], Awaitable],\n        ):\n            # Run full pipeline\n            await next_delete()\n\n            delete_msg = Activity(\n                type=ActivityTypes.message_delete, id=reference.activity_id\n            )\n            deleted_activity: Activity = TurnContext.apply_conversation_reference(\n                delete_msg, reference, False\n            )\n            await self.on_delete_activity(deleted_activity)\n\n        context.on_delete_activity(delete_activity_handler)\n\n        if logic_fn:\n            await logic_fn()\n\n    async def on_receive_activity(self, activity: Activity) -> None:\n        \"\"\"Invoked when a message is received from the user.\n        Performs logging of telemetry data using the BotTelemetryClient.track_event() method.\n        This event name used is \"BotMessageReceived\".\n        :param activity: Current activity sent from user.\n        \"\"\"\n        self.telemetry_client.track_event(\n            TelemetryLoggerConstants.BOT_MSG_RECEIVE_EVENT,\n            await self.fill_receive_event_properties(activity),\n        )\n\n    async def on_send_activity(self, activity: Activity) -> None:\n        \"\"\"Invoked when the bot sends a message to the user.\n        Performs logging of telemetry data using the BotTelemetryClient.track_event() method.\n        This event name used is \"BotMessageSend\".\n        :param activity: Current activity sent from bot.\n        \"\"\"\n        self.telemetry_client.track_event(\n            TelemetryLoggerConstants.BOT_MSG_SEND_EVENT,\n            await self.fill_send_event_properties(activity),\n        )\n\n    async def on_update_activity(self, activity: Activity) -> None:\n        \"\"\"Invoked when the bot updates a message.\n        Performs logging of telemetry data using the BotTelemetryClient.track_event() method.\n        This event name used is \"BotMessageUpdate\".\n        :param activity: Current activity sent from user.\n        \"\"\"\n        self.telemetry_client.track_event(\n            TelemetryLoggerConstants.BOT_MSG_UPDATE_EVENT,\n            await self.fill_update_event_properties(activity),\n        )\n\n    async def on_delete_activity(self, activity: Activity) -> None:\n        \"\"\"Invoked when the bot deletes a message.\n        Performs logging of telemetry data using the BotTelemetryClient.track_event() method.\n        This event name used is \"BotMessageDelete\".\n        :param activity: Current activity sent from user.\n        \"\"\"\n        self.telemetry_client.track_event(\n            TelemetryLoggerConstants.BOT_MSG_DELETE_EVENT,\n            await self.fill_delete_event_properties(activity),\n        )\n\n    async def fill_receive_event_properties(\n        self, activity: Activity, additional_properties: Dict[str, str] = None\n    ) -> Dict[str, str]:\n        \"\"\"Fills the event properties for the BotMessageReceived.\n        Adheres to the LogPersonalInformation flag to filter Name, Text and Speak properties.\n        :param activity: activity sent from user.\n        :param additional_properties: Additional properties to add to the event.\n        Additional properties can override \"stock\" properties.\n\n        :return: A dictionary that is sent as \"Properties\" to\n        BotTelemetryClient.track_event method for the BotMessageReceived event.\n        \"\"\"\n        properties = {\n            TelemetryConstants.FROM_ID_PROPERTY: activity.from_property.id\n            if activity.from_property\n            else None,\n            TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,\n            TelemetryConstants.LOCALE_PROPERTY: activity.locale,\n            TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,\n            TelemetryConstants.RECIPIENT_NAME_PROPERTY: activity.recipient.name,\n        }\n\n        if self.log_personal_information:\n            if (\n                activity.from_property\n                and activity.from_property.name\n                and activity.from_property.name.strip()\n            ):\n                properties[\n                    TelemetryConstants.FROM_NAME_PROPERTY\n                ] = activity.from_property.name\n            if activity.text and activity.text.strip():\n                properties[TelemetryConstants.TEXT_PROPERTY] = activity.text\n            if activity.speak and activity.speak.strip():\n                properties[TelemetryConstants.SPEAK_PROPERTY] = activity.speak\n\n        TelemetryLoggerMiddleware.__populate_additional_channel_properties(\n            activity, properties\n        )\n\n        # Additional properties can override \"stock\" properties\n        if additional_properties:\n            for prop in additional_properties:\n                properties[prop.key] = prop.value\n\n        return properties\n\n    async def fill_send_event_properties(\n        self, activity: Activity, additional_properties: Dict[str, str] = None\n    ) -> Dict[str, str]:\n        \"\"\"Fills the event properties for the BotMessageSend.\n        These properties are logged when an activity message is sent by the Bot to the user.\n        :param activity: activity sent from user.\n        :param additional_properties: Additional properties to add to the event.\n        Additional properties can override \"stock\" properties.\n\n        :return: A dictionary that is sent as \"Properties\" to the\n        BotTelemetryClient.track_event method for the BotMessageSend event.\n        \"\"\"\n        properties = {\n            TelemetryConstants.REPLY_ACTIVITY_ID_PROPERTY: activity.reply_to_id,\n            TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,\n            TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,\n            TelemetryConstants.LOCALE_PROPERTY: activity.locale,\n        }\n\n        # Use the LogPersonalInformation flag to toggle logging PII data, text and user name are common examples\n        if self.log_personal_information:\n            if activity.attachments and activity.attachments.strip():\n                properties[\n                    TelemetryConstants.ATTACHMENTS_PROPERTY\n                ] = activity.attachments\n            if activity.from_property.name and activity.from_property.name.strip():\n                properties[\n                    TelemetryConstants.FROM_NAME_PROPERTY\n                ] = activity.from_property.name\n            if activity.text and activity.text.strip():\n                properties[TelemetryConstants.TEXT_PROPERTY] = activity.text\n            if activity.speak and activity.speak.strip():\n                properties[TelemetryConstants.SPEAK_PROPERTY] = activity.speak\n\n        # Additional properties can override \"stock\" properties\n        if additional_properties:\n            for prop in additional_properties:\n                properties[prop.key] = prop.value\n\n        return properties\n\n    async def fill_update_event_properties(\n        self, activity: Activity, additional_properties: Dict[str, str] = None\n    ) -> Dict[str, str]:\n        \"\"\"Fills the event properties for the BotMessageUpdate.\n        These properties are logged when an activity message is updated by the Bot.\n        For example, if a card is interacted with by the use, and the card needs\n        to be updated to reflect some interaction.\n        :param activity: activity sent from user.\n        :param additional_properties: Additional properties to add to the event.\n        Additional properties can override \"stock\" properties.\n\n        :return: A dictionary that is sent as \"Properties\" to the\n        BotTelemetryClient.track_event method for the BotMessageUpdate event.\n        \"\"\"\n        properties = {\n            TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,\n            TelemetryConstants.CONVERSATION_ID_PROPERTY: activity.conversation.id,\n            TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,\n            TelemetryConstants.LOCALE_PROPERTY: activity.locale,\n        }\n\n        # Use the LogPersonalInformation flag to toggle logging PII data, text is a common examples\n        if self.log_personal_information:\n            if activity.text and activity.text.strip():\n                properties[TelemetryConstants.TEXT_PROPERTY] = activity.text\n\n        # Additional properties can override \"stock\" properties\n        if additional_properties:\n            for prop in additional_properties:\n                properties[prop.key] = prop.value\n\n        return properties\n\n    async def fill_delete_event_properties(\n        self, activity: Activity, additional_properties: Dict[str, str] = None\n    ) -> Dict[str, str]:\n        \"\"\"Fills the event properties for the BotMessageDelete.\n        These properties are logged when an activity message is deleted by the Bot.\n        :param activity: activity sent from user.\n        :param additional_properties: Additional properties to add to the event.\n        Additional properties can override \"stock\" properties.\n\n        :return: A dictionary that is sent as \"Properties\" to the\n        BotTelemetryClient.track_event method for the BotMessageUpdate event.\n        \"\"\"\n        properties = {\n            TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,\n            TelemetryConstants.CONVERSATION_ID_PROPERTY: activity.conversation.id,\n            TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,\n        }\n\n        # Additional properties can override \"stock\" properties\n        if additional_properties:\n            for prop in additional_properties:\n                properties[prop.key] = prop.value\n\n        return properties\n\n    @staticmethod\n    def __populate_additional_channel_properties(\n        activity: Activity, properties: dict,\n    ):\n        if activity.channel_id == Channels.ms_teams:\n            teams_channel_data: TeamsChannelData = activity.channel_data\n\n            properties[\"TeamsTenantId\"] = (\n                teams_channel_data.tenant\n                if teams_channel_data and teams_channel_data.tenant\n                else \"\"\n            )\n\n            properties[\"TeamsUserAadObjectId\"] = (\n                activity.from_property.aad_object_id if activity.from_property else \"\"\n            )\n\n            if teams_channel_data and teams_channel_data.team:\n                properties[\"TeamsTeamInfo\"] = TeamInfo.serialize(\n                    teams_channel_data.team\n                )\n", "path": "libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py"}]}