We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
An activity with attachments raises an exception in telemetry logger
## Version
4.14.1

## Describe the bug

Activity.attachments is a list. Therefore, telemetry logger raises an exception (see below) if trying to call `strip()` on attachements.

```
  File "/Users/ivo/Projects/alvao-chatbot/bot/venv/lib/python3.8/site-packages/botbuilder/core/telemetry_logger_middleware.py", line 220, in fill_send_event_properties
    if activity.attachments and activity.attachments.strip():
AttributeError: 'list' object has no attribute 'strip'
```

This line produces the bug: https://github.com/microsoft/botbuilder-python/blob/main/libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py#L220

## To Reproduce
Steps to reproduce the behavior:
1. Use TelemetryLoggerMiddleware
2. Create a card message: `MessageFactory.attachment(Attachment(content_type="application/vnd.microsoft.card.adaptive", content=card))`
3. Send this activity from bot to user
4. When this activity is being logged via TelemetryLoggerMiddleware, it raises an exception

## Expected behavior
There should be another check than `strip()`, possible `len() > 0`. Function `strip` returns a string without leading and trailing whitespaces. There is no sense to do that a) in a condition, b) on a list.



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

{"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@@ -2,6 +2,8 @@\n # Licensed under the MIT License.\n \"\"\"Middleware Component for logging Activity messages.\"\"\"\n from typing import Awaitable, Callable, List, Dict\n+from jsonpickle import encode\n+\n from botbuilder.schema import Activity, ConversationReference, ActivityTypes\n from botbuilder.schema.teams import TeamsChannelData, TeamInfo\n from botframework.connector import Channels\n@@ -33,7 +35,7 @@\n \n     @property\n     def log_personal_information(self) -> bool:\n-        \"\"\" Gets a value indicating whether determines whether to log personal\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@@ -217,10 +219,10 @@\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.attachments and len(activity.attachments) > 0:\n+                properties[TelemetryConstants.ATTACHMENTS_PROPERTY] = encode(\n+                    activity.attachments\n+                )\n             if activity.from_property.name and activity.from_property.name.strip():\n                 properties[\n                     TelemetryConstants.FROM_NAME_PROPERTY\n", "issue": "An activity with attachments raises an exception in telemetry logger\n## Version\r\n4.14.1\r\n\r\n## Describe the bug\r\n\r\nActivity.attachments is a list. Therefore, telemetry logger raises an exception (see below) if trying to call `strip()` on attachements.\r\n\r\n```\r\n  File \"/Users/ivo/Projects/alvao-chatbot/bot/venv/lib/python3.8/site-packages/botbuilder/core/telemetry_logger_middleware.py\", line 220, in fill_send_event_properties\r\n    if activity.attachments and activity.attachments.strip():\r\nAttributeError: 'list' object has no attribute 'strip'\r\n```\r\n\r\nThis line produces the bug: https://github.com/microsoft/botbuilder-python/blob/main/libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py#L220\r\n\r\n## To Reproduce\r\nSteps to reproduce the behavior:\r\n1. Use TelemetryLoggerMiddleware\r\n2. Create a card message: `MessageFactory.attachment(Attachment(content_type=\"application/vnd.microsoft.card.adaptive\", content=card))`\r\n3. Send this activity from bot to user\r\n4. When this activity is being logged via TelemetryLoggerMiddleware, it raises an exception\r\n\r\n## Expected behavior\r\nThere should be another check than `strip()`, possible `len() > 0`. Function `strip` returns a string without leading and trailing whitespaces. There is no sense to do that a) in a condition, b) on a list.\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.\"\"\"\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"}], "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 jsonpickle import encode\n\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 len(activity.attachments) > 0:\n                properties[TelemetryConstants.ATTACHMENTS_PROPERTY] = encode(\n                    activity.attachments\n                )\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"}]}