5
5
from enum import Enum
6
6
import warnings
7
7
from typing import Mapping , Optional , Union , Any , cast
8
- from typing_extensions import Literal , TypedDict , Protocol , runtime_checkable
8
+ from typing_extensions import Literal , TypedDict , Protocol , runtime_checkable , NotRequired
9
9
10
10
from azure .core import CaseInsensitiveEnumMeta
11
11
@@ -37,6 +37,7 @@ class CommunicationIdentifierKind(str, Enum, metaclass=DeprecatedEnumMeta):
37
37
PHONE_NUMBER = "phone_number"
38
38
MICROSOFT_TEAMS_USER = "microsoft_teams_user"
39
39
MICROSOFT_TEAMS_APP = "microsoft_teams_app"
40
+ TEAMS_EXTENSION_USER = "teams_extension_user"
40
41
41
42
42
43
class CommunicationCloudEnvironment (str , Enum , metaclass = CaseInsensitiveEnumMeta ):
@@ -86,6 +87,8 @@ def properties(self) -> Mapping[str, Any]:
86
87
ACS_USER_GCCH_CLOUD_PREFIX = "8:gcch-acs:"
87
88
SPOOL_USER_PREFIX = "8:spool:"
88
89
90
+ PHONE_NUMBER_ANONYMOUS_SUFFIX = "anonymous"
91
+
89
92
90
93
class CommunicationUserProperties (TypedDict ):
91
94
"""Dictionary of properties for a CommunicationUserIdentifier."""
@@ -127,6 +130,10 @@ class PhoneNumberProperties(TypedDict):
127
130
128
131
value : str
129
132
"""The phone number in E.164 format."""
133
+ asserted_id : NotRequired [str ]
134
+ """The asserted Id set on a phone number to distinguish from other connections made through the same number."""
135
+ is_anonymous : NotRequired [bool ]
136
+ """True if the phone number is anonymous, e.g. when used to represent a hidden caller Id."""
130
137
131
138
132
139
class PhoneNumberIdentifier :
@@ -145,8 +152,21 @@ def __init__(self, value: str, **kwargs: Any) -> None:
145
152
:keyword str raw_id: The raw ID of the identifier. If not specified, this will be constructed from
146
153
the 'value' parameter.
147
154
"""
148
- self . properties = PhoneNumberProperties ( value = value )
155
+
149
156
raw_id : Optional [str ] = kwargs .get ("raw_id" )
157
+ is_anonymous : bool
158
+
159
+ if raw_id is not None :
160
+ phone_number = raw_id [len (PHONE_NUMBER_PREFIX ):]
161
+ is_anonymous = phone_number == PHONE_NUMBER_ANONYMOUS_SUFFIX
162
+ asserted_id_index = - 1 if is_anonymous else phone_number .rfind ("_" ) + 1
163
+ has_asserted_id = 0 < asserted_id_index < len (phone_number )
164
+ props = {"value" : value , "is_anonymous" : is_anonymous }
165
+ if has_asserted_id :
166
+ props ["asserted_id" ] = phone_number [asserted_id_index :]
167
+ self .properties = PhoneNumberProperties (** props ) # type: ignore
168
+ else :
169
+ self .properties = PhoneNumberProperties (value = value )
150
170
self .raw_id = raw_id if raw_id is not None else self ._format_raw_id (self .properties )
151
171
152
172
def __eq__ (self , other ):
@@ -163,7 +183,6 @@ def _format_raw_id(self, properties: PhoneNumberProperties) -> str:
163
183
value = properties ["value" ]
164
184
return f"{ PHONE_NUMBER_PREFIX } { value } "
165
185
166
-
167
186
class UnknownIdentifier :
168
187
"""Represents an identifier of an unknown type.
169
188
@@ -223,7 +242,7 @@ def __init__(self, user_id: str, **kwargs: Any) -> None:
223
242
:param str user_id: Microsoft Teams user id.
224
243
:keyword bool is_anonymous: `True` if the identifier is anonymous. Default value is `False`.
225
244
:keyword cloud: Cloud environment that the user belongs to. Default value is `PUBLIC`.
226
- :paramtype cloud: str or ~azure.communication.chat .CommunicationCloudEnvironment
245
+ :paramtype cloud: str or ~azure.communication.callautomation .CommunicationCloudEnvironment
227
246
:keyword str raw_id: The raw ID of the identifier. If not specified, this value will be constructed from
228
247
the other properties.
229
248
"""
@@ -294,7 +313,7 @@ def __init__(self, app_id: str, **kwargs: Any) -> None:
294
313
"""
295
314
:param str app_id: Microsoft Teams application id.
296
315
:keyword cloud: Cloud environment that the application belongs to. Default value is `PUBLIC`.
297
- :paramtype cloud: str or ~azure.communication.chat .CommunicationCloudEnvironment
316
+ :paramtype cloud: str or ~azure.communication.callautomation .CommunicationCloudEnvironment
298
317
:keyword str raw_id: The raw ID of the identifier. If not specified, this value will be constructed
299
318
from the other properties.
300
319
"""
@@ -338,7 +357,7 @@ def __init__(self, bot_id, **kwargs):
338
357
:keyword bool is_resource_account_configured: `False` if the identifier is global.
339
358
Default value is `True` for tennantzed bots.
340
359
:keyword cloud: Cloud environment that the bot belongs to. Default value is `PUBLIC`.
341
- :paramtype cloud: str or ~azure.communication.chat .CommunicationCloudEnvironment
360
+ :paramtype cloud: str or ~azure.communication.callautomation .CommunicationCloudEnvironment
342
361
"""
343
362
warnings .warn (
344
363
"The MicrosoftBotIdentifier is deprecated and has been replaced by MicrosoftTeamsAppIdentifier." ,
@@ -347,6 +366,88 @@ def __init__(self, bot_id, **kwargs):
347
366
super ().__init__ (bot_id , ** kwargs )
348
367
349
368
369
+ class TeamsExtensionUserProperties (TypedDict ):
370
+ """Dictionary of properties for a TeamsExtensionUserIdentifier."""
371
+
372
+ user_id : str
373
+ """The id of the Teams extension user."""
374
+ tenant_id : str
375
+ """The tenant id associated with the user."""
376
+ resource_id : str
377
+ """The Communication Services resource id."""
378
+ cloud : Union [CommunicationCloudEnvironment , str ]
379
+ """Cloud environment that this identifier belongs to."""
380
+
381
+
382
+ class TeamsExtensionUserIdentifier :
383
+ """Represents an identifier for a Teams Extension user."""
384
+
385
+ kind : Literal [CommunicationIdentifierKind .TEAMS_EXTENSION_USER ] = CommunicationIdentifierKind .TEAMS_EXTENSION_USER
386
+ """The type of identifier."""
387
+ properties : TeamsExtensionUserProperties
388
+ """The properties of the identifier."""
389
+ raw_id : str
390
+ """The raw ID of the identifier."""
391
+
392
+ def __init__ (
393
+ self ,
394
+ user_id : str ,
395
+ tenant_id : str ,
396
+ resource_id : str ,
397
+ ** kwargs : Any
398
+ ) -> None :
399
+ """
400
+ :param str user_id: Teams extension user id.
401
+ :param str tenant_id: Tenant id associated with the user.
402
+ :param str resource_id: The Communication Services resource id.
403
+ :keyword cloud: Cloud environment that the user belongs to. Default value is `PUBLIC`.
404
+ :paramtype cloud: str or ~azure.communication.callautomation.CommunicationCloudEnvironment
405
+ :keyword str raw_id: The raw ID of the identifier.
406
+ If not specified, this value will be constructed from the other properties.
407
+ """
408
+ self .properties = TeamsExtensionUserProperties (
409
+ user_id = user_id ,
410
+ tenant_id = tenant_id ,
411
+ resource_id = resource_id ,
412
+ cloud = kwargs .get ("cloud" ) or CommunicationCloudEnvironment .PUBLIC ,
413
+ )
414
+ raw_id : Optional [str ] = kwargs .get ("raw_id" )
415
+ self .raw_id = raw_id if raw_id is not None else self ._format_raw_id (self .properties )
416
+
417
+ def __eq__ (self , other ):
418
+ try :
419
+ if other .raw_id :
420
+ return self .raw_id == other .raw_id
421
+ return self .raw_id == self ._format_raw_id (other .properties )
422
+ except Exception : # pylint: disable=broad-except
423
+ return False
424
+
425
+ def _format_raw_id (self , properties : TeamsExtensionUserProperties ) -> str :
426
+ # The prefix depends on the cloud
427
+ cloud = properties ["cloud" ]
428
+ if cloud == CommunicationCloudEnvironment .DOD :
429
+ prefix = ACS_USER_DOD_CLOUD_PREFIX
430
+ elif cloud == CommunicationCloudEnvironment .GCCH :
431
+ prefix = ACS_USER_GCCH_CLOUD_PREFIX
432
+ else :
433
+ prefix = ACS_USER_PREFIX
434
+ return f"{ prefix } { properties ['resource_id' ]} _{ properties ['tenant_id' ]} _{ properties ['user_id' ]} "
435
+
436
+ def try_create_teams_extension_user (prefix : str , suffix : str ) -> Optional [TeamsExtensionUserIdentifier ]:
437
+ segments = suffix .split ("_" )
438
+ if len (segments ) != 3 :
439
+ return None
440
+ resource_id , tenant_id , user_id = segments
441
+ if prefix == ACS_USER_PREFIX :
442
+ cloud = CommunicationCloudEnvironment .PUBLIC
443
+ elif prefix == ACS_USER_DOD_CLOUD_PREFIX :
444
+ cloud = CommunicationCloudEnvironment .DOD
445
+ elif prefix == ACS_USER_GCCH_CLOUD_PREFIX :
446
+ cloud = CommunicationCloudEnvironment .GCCH
447
+ else :
448
+ raise ValueError ("Invalid MRI" )
449
+ return TeamsExtensionUserIdentifier (user_id , tenant_id , resource_id , cloud = cloud )
450
+
350
451
def identifier_from_raw_id (raw_id : str ) -> CommunicationIdentifier : # pylint: disable=too-many-return-statements
351
452
"""
352
453
Creates a CommunicationIdentifier from a given raw ID.
@@ -407,11 +508,16 @@ def identifier_from_raw_id(raw_id: str) -> CommunicationIdentifier: # pylint: d
407
508
cloud = CommunicationCloudEnvironment .GCCH ,
408
509
raw_id = raw_id ,
409
510
)
511
+ if prefix == SPOOL_USER_PREFIX :
512
+ return CommunicationUserIdentifier (id = raw_id , raw_id = raw_id )
513
+
410
514
if prefix in [
411
515
ACS_USER_PREFIX ,
412
516
ACS_USER_DOD_CLOUD_PREFIX ,
413
517
ACS_USER_GCCH_CLOUD_PREFIX ,
414
- SPOOL_USER_PREFIX ,
415
518
]:
519
+ identifier = try_create_teams_extension_user (prefix , suffix )
520
+ if identifier is not None :
521
+ return identifier
416
522
return CommunicationUserIdentifier (id = raw_id , raw_id = raw_id )
417
523
return UnknownIdentifier (identifier = raw_id )
0 commit comments