We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[PORT] Update telemetry logger to include attachments
> Port this change from botbuilder-dotnet/master branch:
https://github.com/microsoft/botbuilder-dotnet/pull/3737

Addresses #1574 in JS repo (https://github.com/microsoft/botbuilder-js/issues/1574)

Telemetry logger will now include the collection of any attachments sent with the activity, if the LogPersonalInformation flag is set to true.


# Changed projects
* Microsoft.Bot.Builder





--- 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_constants.py`
Content:
```
1 # Copyright (c) Microsoft Corporation. All rights reserved.
2 # Licensed under the MIT License
3 
4 
5 class TelemetryConstants:
6     """Telemetry logger property names."""
7 
8     CHANNEL_ID_PROPERTY: str = "channelId"
9     CONVERSATION_ID_PROPERTY: str = "conversationId"
10     CONVERSATION_NAME_PROPERTY: str = "conversationName"
11     DIALOG_ID_PROPERTY: str = "dialogId"
12     FROM_ID_PROPERTY: str = "fromId"
13     FROM_NAME_PROPERTY: str = "fromName"
14     LOCALE_PROPERTY: str = "locale"
15     RECIPIENT_ID_PROPERTY: str = "recipientId"
16     RECIPIENT_NAME_PROPERTY: str = "recipientName"
17     REPLY_ACTIVITY_ID_PROPERTY: str = "replyActivityId"
18     TEXT_PROPERTY: str = "text"
19     SPEAK_PROPERTY: str = "speak"
20     USER_ID_PROPERTY: str = "userId"
21
```

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.from_property.name and activity.from_property.name.strip():
215                 properties[
216                     TelemetryConstants.FROM_NAME_PROPERTY
217                 ] = activity.from_property.name
218             if activity.text and activity.text.strip():
219                 properties[TelemetryConstants.TEXT_PROPERTY] = activity.text
220             if activity.speak and activity.speak.strip():
221                 properties[TelemetryConstants.SPEAK_PROPERTY] = activity.speak
222 
223         # Additional properties can override "stock" properties
224         if additional_properties:
225             for prop in additional_properties:
226                 properties[prop.key] = prop.value
227 
228         return properties
229 
230     async def fill_update_event_properties(
231         self, activity: Activity, additional_properties: Dict[str, str] = None
232     ) -> Dict[str, str]:
233         """Fills the event properties for the BotMessageUpdate.
234         These properties are logged when an activity message is updated by the Bot.
235         For example, if a card is interacted with by the use, and the card needs
236         to be updated to reflect some interaction.
237         :param activity: activity sent from user.
238         :param additional_properties: Additional properties to add to the event.
239         Additional properties can override "stock" properties.
240 
241         :return: A dictionary that is sent as "Properties" to the
242         BotTelemetryClient.track_event method for the BotMessageUpdate event.
243         """
244         properties = {
245             TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,
246             TelemetryConstants.CONVERSATION_ID_PROPERTY: activity.conversation.id,
247             TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,
248             TelemetryConstants.LOCALE_PROPERTY: activity.locale,
249         }
250 
251         # Use the LogPersonalInformation flag to toggle logging PII data, text is a common examples
252         if self.log_personal_information:
253             if activity.text and activity.text.strip():
254                 properties[TelemetryConstants.TEXT_PROPERTY] = activity.text
255 
256         # Additional properties can override "stock" properties
257         if additional_properties:
258             for prop in additional_properties:
259                 properties[prop.key] = prop.value
260 
261         return properties
262 
263     async def fill_delete_event_properties(
264         self, activity: Activity, additional_properties: Dict[str, str] = None
265     ) -> Dict[str, str]:
266         """Fills the event properties for the BotMessageDelete.
267         These properties are logged when an activity message is deleted by the Bot.
268         :param activity: activity sent from user.
269         :param additional_properties: Additional properties to add to the event.
270         Additional properties can override "stock" properties.
271 
272         :return: A dictionary that is sent as "Properties" to the
273         BotTelemetryClient.track_event method for the BotMessageUpdate event.
274         """
275         properties = {
276             TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,
277             TelemetryConstants.CONVERSATION_ID_PROPERTY: activity.conversation.id,
278             TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,
279         }
280 
281         # Additional properties can override "stock" properties
282         if additional_properties:
283             for prop in additional_properties:
284                 properties[prop.key] = prop.value
285 
286         return properties
287
```
--- 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_constants.py b/libraries/botbuilder-core/botbuilder/core/telemetry_constants.py
--- a/libraries/botbuilder-core/botbuilder/core/telemetry_constants.py
+++ b/libraries/botbuilder-core/botbuilder/core/telemetry_constants.py
@@ -5,6 +5,7 @@
 class TelemetryConstants:
     """Telemetry logger property names."""
 
+    ATTACHMENTS_PROPERTY: str = "attachments"
     CHANNEL_ID_PROPERTY: str = "channelId"
     CONVERSATION_ID_PROPERTY: str = "conversationId"
     CONVERSATION_NAME_PROPERTY: str = "conversationName"
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
@@ -211,6 +211,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.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_constants.py b/libraries/botbuilder-core/botbuilder/core/telemetry_constants.py\n--- a/libraries/botbuilder-core/botbuilder/core/telemetry_constants.py\n+++ b/libraries/botbuilder-core/botbuilder/core/telemetry_constants.py\n@@ -5,6 +5,7 @@\n class TelemetryConstants:\n     \"\"\"Telemetry logger property names.\"\"\"\n \n+    ATTACHMENTS_PROPERTY: str = \"attachments\"\n     CHANNEL_ID_PROPERTY: str = \"channelId\"\n     CONVERSATION_ID_PROPERTY: str = \"conversationId\"\n     CONVERSATION_NAME_PROPERTY: str = \"conversationName\"\ndiff --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@@ -211,6 +211,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.from_property.name and activity.from_property.name.strip():\n                 properties[\n                     TelemetryConstants.FROM_NAME_PROPERTY\n", "issue": "[PORT] Update telemetry logger to include attachments\n> Port this change from botbuilder-dotnet/master branch:\nhttps://github.com/microsoft/botbuilder-dotnet/pull/3737\n\nAddresses #1574 in JS repo (https://github.com/microsoft/botbuilder-js/issues/1574)\r\n\r\nTelemetry logger will now include the collection of any attachments sent with the activity, if the LogPersonalInformation flag is set to true.\n\n\r\n# Changed projects\r\n* Microsoft.Bot.Builder\r\n\r\n\r\n\n\n", "before_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License\n\n\nclass TelemetryConstants:\n    \"\"\"Telemetry logger property names.\"\"\"\n\n    CHANNEL_ID_PROPERTY: str = \"channelId\"\n    CONVERSATION_ID_PROPERTY: str = \"conversationId\"\n    CONVERSATION_NAME_PROPERTY: str = \"conversationName\"\n    DIALOG_ID_PROPERTY: str = \"dialogId\"\n    FROM_ID_PROPERTY: str = \"fromId\"\n    FROM_NAME_PROPERTY: str = \"fromName\"\n    LOCALE_PROPERTY: str = \"locale\"\n    RECIPIENT_ID_PROPERTY: str = \"recipientId\"\n    RECIPIENT_NAME_PROPERTY: str = \"recipientName\"\n    REPLY_ACTIVITY_ID_PROPERTY: str = \"replyActivityId\"\n    TEXT_PROPERTY: str = \"text\"\n    SPEAK_PROPERTY: str = \"speak\"\n    USER_ID_PROPERTY: str = \"userId\"\n", "path": "libraries/botbuilder-core/botbuilder/core/telemetry_constants.py"}, {"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.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\n\nclass TelemetryConstants:\n    \"\"\"Telemetry logger property names.\"\"\"\n\n    ATTACHMENTS_PROPERTY: str = \"attachments\"\n    CHANNEL_ID_PROPERTY: str = \"channelId\"\n    CONVERSATION_ID_PROPERTY: str = \"conversationId\"\n    CONVERSATION_NAME_PROPERTY: str = \"conversationName\"\n    DIALOG_ID_PROPERTY: str = \"dialogId\"\n    FROM_ID_PROPERTY: str = \"fromId\"\n    FROM_NAME_PROPERTY: str = \"fromName\"\n    LOCALE_PROPERTY: str = \"locale\"\n    RECIPIENT_ID_PROPERTY: str = \"recipientId\"\n    RECIPIENT_NAME_PROPERTY: str = \"recipientName\"\n    REPLY_ACTIVITY_ID_PROPERTY: str = \"replyActivityId\"\n    TEXT_PROPERTY: str = \"text\"\n    SPEAK_PROPERTY: str = \"speak\"\n    USER_ID_PROPERTY: str = \"userId\"\n", "path": "libraries/botbuilder-core/botbuilder/core/telemetry_constants.py"}, {"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"}]}