diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index bdb063b1..00000000 Binary files a/.DS_Store and /dev/null differ diff --git a/bandwidth/messaging/controllers/api_controller.py b/bandwidth/messaging/controllers/api_controller.py index 18fb2bc4..9fa5158d 100644 --- a/bandwidth/messaging/controllers/api_controller.py +++ b/bandwidth/messaging/controllers/api_controller.py @@ -31,7 +31,7 @@ def list_media(self, continuation_token=None): """Does a GET request to /users/{accountId}/media. - listMedia + Gets a list of your media files. No query parameters are supported. Args: account_id (string): User's account ID @@ -95,7 +95,7 @@ def get_media(self, media_id): """Does a GET request to /users/{accountId}/media/{mediaId}. - getMedia + Downloads a media file you previously uploaded. Args: account_id (string): User's account ID @@ -156,7 +156,8 @@ def upload_media(self, cache_control=None): """Does a PUT request to /users/{accountId}/media/{mediaId}. - uploadMedia + Uploads a file the normal HTTP way. You may add headers to the request + in order to provide some control to your media-file. Args: account_id (string): User's account ID @@ -231,7 +232,10 @@ def delete_media(self, media_id): """Does a DELETE request to /users/{accountId}/media/{mediaId}. - deleteMedia + Deletes a media file from Bandwidth API server. Make sure you don't + have any application scripts still using the media before you delete. + If you accidentally delete a media file, you can immediately upload a + new file with the same name. Args: account_id (string): User's account ID @@ -295,7 +299,7 @@ def get_messages(self, limit=None): """Does a GET request to /users/{accountId}/messages. - getMessages + Gets a list of messages based on query parameters. Args: account_id (string): User's account ID @@ -392,7 +396,8 @@ def create_message(self, body): """Does a POST request to /users/{accountId}/messages. - createMessage + Endpoint for sending text messages and picture messages using V2 + messaging. Args: account_id (string): User's account ID diff --git a/bandwidth/messaging/models/bandwidth_message.py b/bandwidth/messaging/models/bandwidth_message.py index 3388bd3f..39eb70e2 100644 --- a/bandwidth/messaging/models/bandwidth_message.py +++ b/bandwidth/messaging/models/bandwidth_message.py @@ -27,7 +27,11 @@ class BandwidthMessage(object): Bandwidth. Can be in or out to (list of string): The phone number recipients of the message mfrom (string): The phone number the message was sent from - media (list of string): The list of media URLs sent in the message + media (list of string): The list of media URLs sent in the message. + Including a `filename` field in the `Content-Disposition` header + of the media linked with a URL will set the displayed file name. + This is a best practice to ensure that your media has a readable + file name. text (string): The contents of the message tag (string): The custom string set by the user priority (string): The priority specified by the user diff --git a/bandwidth/multifactorauth/controllers/mfa_controller.py b/bandwidth/multifactorauth/controllers/mfa_controller.py index 8e20ebd2..dc4b6be0 100644 --- a/bandwidth/multifactorauth/controllers/mfa_controller.py +++ b/bandwidth/multifactorauth/controllers/mfa_controller.py @@ -32,7 +32,8 @@ def create_voice_two_factor(self, body): """Does a POST request to /accounts/{accountId}/code/voice. - Allows a user to send a MFA code through a phone call + Multi-Factor authentication with Bandwidth Voice services. Allows for + a user to send an MFA code via a phone call. Args: account_id (string): Bandwidth Account ID with Voice service @@ -92,7 +93,8 @@ def create_messaging_two_factor(self, body): """Does a POST request to /accounts/{accountId}/code/messaging. - Allows a user to send a MFA code through a text message (SMS) + Multi-Factor authentication with Bandwidth Messaging services. Allows + a user to send an MFA code via a text message (SMS). Args: account_id (string): Bandwidth Account ID with Messaging service @@ -152,7 +154,7 @@ def create_verify_two_factor(self, body): """Does a POST request to /accounts/{accountId}/code/verify. - Allows a user to verify an MFA code + Allows a user to verify an MFA code. Args: account_id (string): Bandwidth Account ID with Two-Factor enabled diff --git a/bandwidth/phonenumberlookup/controllers/api_controller.py b/bandwidth/phonenumberlookup/controllers/api_controller.py index c1056d0c..9914dacc 100644 --- a/bandwidth/phonenumberlookup/controllers/api_controller.py +++ b/bandwidth/phonenumberlookup/controllers/api_controller.py @@ -30,7 +30,7 @@ def create_lookup_request(self, body): """Does a POST request to /accounts/{accountId}/tnlookup. - Create a TN Lookup Order + Create a TN Lookup Order. Args: account_id (string): The ID of the Bandwidth account that the user @@ -292,7 +292,7 @@ def get_lookup_request_status(self, request_id): """Does a GET request to /accounts/{accountId}/tnlookup/{requestId}. - Query an existing TN Lookup Order + Query an existing TN Lookup Order. Args: account_id (string): The ID of the Bandwidth account that the user diff --git a/bandwidth/voice/controllers/api_controller.py b/bandwidth/voice/controllers/api_controller.py index c5481340..5b616835 100644 --- a/bandwidth/voice/controllers/api_controller.py +++ b/bandwidth/voice/controllers/api_controller.py @@ -35,7 +35,7 @@ def create_call(self, body): """Does a POST request to /api/v2/accounts/{accountId}/calls. - Creates an outbound call. + Creates an outbound phone call. Args: account_id (string): TODO: type description here. @@ -298,7 +298,7 @@ def get_call_recordings(self, """Does a GET request to /api/v2/accounts/{accountId}/calls/{callId}/recordings. Returns a (potentially empty) list of metadata for the recordings that - took place during the specified call + took place during the specified call. Args: account_id (string): TODO: type description here. diff --git a/bandwidth/voice/models/__init__.py b/bandwidth/voice/models/__init__.py index dbe2d27e..049d0371 100644 --- a/bandwidth/voice/models/__init__.py +++ b/bandwidth/voice/models/__init__.py @@ -9,17 +9,24 @@ 'conference_state', 'conference_member_state', 'conference_recording_metadata', + 'machine_detection_request', 'transcribe_recording_request', 'transcription_response', 'transcription_metadata', 'transcript', + 'call_callback', + 'transcription', + 'diversion', + 'conference_callback', 'answer_fallback_method_enum', 'answer_method_enum', 'callback_method_enum', 'conference_event_method_enum', 'direction_enum', 'disconnect_method_enum', + 'fallback_method_enum', 'file_format_enum', + 'mode_enum', 'redirect_fallback_method_enum', 'redirect_method_enum', 'state_enum', diff --git a/bandwidth/voice/models/call_callback.py b/bandwidth/voice/models/call_callback.py new file mode 100644 index 00000000..36ad4762 --- /dev/null +++ b/bandwidth/voice/models/call_callback.py @@ -0,0 +1,233 @@ +# -*- coding: utf-8 -*- + +""" +bandwidth + +This file was automatically generated by APIMATIC v3.0 ( + https://www.apimatic.io ). +""" +from bandwidth.voice.models.diversion import Diversion +from bandwidth.voice.models.transcription import Transcription + + +class CallCallback(object): + + """Implementation of the 'CallCallback' model. + + This object represents all possible fields that may be included in + callbacks related to call events, including events that come from BXML + verbs + + Attributes: + event_type (string): TODO: type description here. + event_time (string): TODO: type description here. + account_id (string): TODO: type description here. + application_id (string): TODO: type description here. + mfrom (string): TODO: type description here. + to (string): TODO: type description here. + direction (string): TODO: type description here. + call_id (string): TODO: type description here. + call_url (string): TODO: type description here. + start_time (string): TODO: type description here. + answer_time (string): TODO: type description here. + transfer_caller_id (string): TODO: type description here. + transfer_to (string): TODO: type description here. + cause (string): TODO: type description here. + error_message (string): TODO: type description here. + error_id (string): TODO: type description here. + end_time (string): TODO: type description here. + digit (string): TODO: type description here. + parent_call_id (string): TODO: type description here. + recording_id (string): TODO: type description here. + duration (string): TODO: type description here. + file_format (string): TODO: type description here. + media_url (string): TODO: type description here. + tag (string): TODO: type description here. + channels (int): TODO: type description here. + status (string): TODO: type description here. + digits (string): TODO: type description here. + terminating_digit (string): TODO: type description here. + transcription (Transcription): TODO: type description here. + diversion (Diversion): TODO: type description here. + + """ + + # Create a mapping from Model property names to API property names + _names = { + "event_type": 'eventType', + "event_time": 'eventTime', + "account_id": 'accountId', + "application_id": 'applicationId', + "mfrom": 'from', + "to": 'to', + "direction": 'direction', + "call_id": 'callId', + "call_url": 'callUrl', + "start_time": 'startTime', + "answer_time": 'answerTime', + "transfer_caller_id": 'transferCallerId', + "transfer_to": 'transferTo', + "cause": 'cause', + "error_message": 'errorMessage', + "error_id": 'errorId', + "end_time": 'endTime', + "digit": 'digit', + "parent_call_id": 'parentCallId', + "recording_id": 'recordingId', + "duration": 'duration', + "file_format": 'fileFormat', + "media_url": 'mediaUrl', + "tag": 'tag', + "channels": 'channels', + "status": 'status', + "digits": 'digits', + "terminating_digit": 'terminatingDigit', + "transcription": 'transcription', + "diversion": 'diversion' + } + + def __init__(self, + event_type=None, + event_time=None, + account_id=None, + application_id=None, + mfrom=None, + to=None, + direction=None, + call_id=None, + call_url=None, + start_time=None, + answer_time=None, + transfer_caller_id=None, + transfer_to=None, + cause=None, + error_message=None, + error_id=None, + end_time=None, + digit=None, + parent_call_id=None, + recording_id=None, + duration=None, + file_format=None, + media_url=None, + tag=None, + channels=None, + status=None, + digits=None, + terminating_digit=None, + transcription=None, + diversion=None): + """Constructor for the CallCallback class""" + + # Initialize members of the class + self.event_type = event_type + self.event_time = event_time + self.account_id = account_id + self.application_id = application_id + self.mfrom = mfrom + self.to = to + self.direction = direction + self.call_id = call_id + self.call_url = call_url + self.start_time = start_time + self.answer_time = answer_time + self.transfer_caller_id = transfer_caller_id + self.transfer_to = transfer_to + self.cause = cause + self.error_message = error_message + self.error_id = error_id + self.end_time = end_time + self.digit = digit + self.parent_call_id = parent_call_id + self.recording_id = recording_id + self.duration = duration + self.file_format = file_format + self.media_url = media_url + self.tag = tag + self.channels = channels + self.status = status + self.digits = digits + self.terminating_digit = terminating_digit + self.transcription = transcription + self.diversion = diversion + + @classmethod + def from_dictionary(cls, + dictionary): + """Creates an instance of this model from a dictionary + + Args: + dictionary (dictionary): A dictionary representation of the object + as obtained from the deserialization of the server's response. The + keys MUST match property names in the API description. + + Returns: + object: An instance of this structure class. + + """ + if dictionary is None: + return None + + # Extract variables from the dictionary + event_type = dictionary.get('eventType') + event_time = dictionary.get('eventTime') + account_id = dictionary.get('accountId') + application_id = dictionary.get('applicationId') + mfrom = dictionary.get('from') + to = dictionary.get('to') + direction = dictionary.get('direction') + call_id = dictionary.get('callId') + call_url = dictionary.get('callUrl') + start_time = dictionary.get('startTime') + answer_time = dictionary.get('answerTime') + transfer_caller_id = dictionary.get('transferCallerId') + transfer_to = dictionary.get('transferTo') + cause = dictionary.get('cause') + error_message = dictionary.get('errorMessage') + error_id = dictionary.get('errorId') + end_time = dictionary.get('endTime') + digit = dictionary.get('digit') + parent_call_id = dictionary.get('parentCallId') + recording_id = dictionary.get('recordingId') + duration = dictionary.get('duration') + file_format = dictionary.get('fileFormat') + media_url = dictionary.get('mediaUrl') + tag = dictionary.get('tag') + channels = dictionary.get('channels') + status = dictionary.get('status') + digits = dictionary.get('digits') + terminating_digit = dictionary.get('terminatingDigit') + transcription = Transcription.from_dictionary(dictionary.get('transcription')) if dictionary.get('transcription') else None + diversion = Diversion.from_dictionary(dictionary.get('diversion')) if dictionary.get('diversion') else None + + # Return an object of this model + return cls(event_type, + event_time, + account_id, + application_id, + mfrom, + to, + direction, + call_id, + call_url, + start_time, + answer_time, + transfer_caller_id, + transfer_to, + cause, + error_message, + error_id, + end_time, + digit, + parent_call_id, + recording_id, + duration, + file_format, + media_url, + tag, + channels, + status, + digits, + terminating_digit, + transcription, + diversion) diff --git a/bandwidth/voice/models/call_state.py b/bandwidth/voice/models/call_state.py index 00bbb2fe..bc09aa2c 100644 --- a/bandwidth/voice/models/call_state.py +++ b/bandwidth/voice/models/call_state.py @@ -28,7 +28,7 @@ class CallState(object): may be added in the future, so your application must be tolerant of unknown values. identity (string): TODO: type description here. - pai (dict): TODO: type description here. + stir_shaken (dict): TODO: type description here. start_time (datetime): TODO: type description here. answer_time (datetime): TODO: type description here. end_time (datetime): TODO: type description here. @@ -56,7 +56,7 @@ class CallState(object): "direction": 'direction', "state": 'state', "identity": 'identity', - "pai": 'pai', + "stir_shaken": 'stirShaken', "start_time": 'startTime', "answer_time": 'answerTime', "end_time": 'endTime', @@ -76,7 +76,7 @@ def __init__(self, direction=None, state=None, identity=None, - pai=None, + stir_shaken=None, start_time=None, answer_time=None, end_time=None, @@ -96,7 +96,7 @@ def __init__(self, self.direction = direction self.state = state self.identity = identity - self.pai = pai + self.stir_shaken = stir_shaken self.start_time = APIHelper.RFC3339DateTime(start_time) if start_time else None self.answer_time = APIHelper.RFC3339DateTime(answer_time) if answer_time else None self.end_time = APIHelper.RFC3339DateTime(end_time) if end_time else None @@ -132,7 +132,7 @@ def from_dictionary(cls, direction = dictionary.get('direction') state = dictionary.get('state') identity = dictionary.get('identity') - pai = dictionary.get('pai') + stir_shaken = dictionary.get('stirShaken') start_time = APIHelper.RFC3339DateTime.from_value(dictionary.get("startTime")).datetime if dictionary.get("startTime") else None answer_time = APIHelper.RFC3339DateTime.from_value(dictionary.get("answerTime")).datetime if dictionary.get("answerTime") else None end_time = APIHelper.RFC3339DateTime.from_value(dictionary.get("endTime")).datetime if dictionary.get("endTime") else None @@ -151,7 +151,7 @@ def from_dictionary(cls, direction, state, identity, - pai, + stir_shaken, start_time, answer_time, end_time, diff --git a/bandwidth/voice/models/conference_callback.py b/bandwidth/voice/models/conference_callback.py new file mode 100644 index 00000000..3ab33016 --- /dev/null +++ b/bandwidth/voice/models/conference_callback.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- + +""" +bandwidth + +This file was automatically generated by APIMATIC v3.0 ( + https://www.apimatic.io ). +""" + + +class ConferenceCallback(object): + + """Implementation of the 'ConferenceCallback' model. + + This object represents all possible fields that may be included in + callbacks related to conference events + + Attributes: + conference_id (string): TODO: type description here. + name (string): TODO: type description here. + event_type (string): TODO: type description here. + event_time (string): TODO: type description here. + tag (string): TODO: type description here. + call_id (string): TODO: type description here. + to (string): TODO: type description here. + mfrom (string): TODO: type description here. + account_id (string): TODO: type description here. + recording_id (string): TODO: type description here. + channels (int): TODO: type description here. + start_time (string): TODO: type description here. + end_time (string): TODO: type description here. + duration (string): TODO: type description here. + file_format (string): TODO: type description here. + media_url (string): TODO: type description here. + status (string): TODO: type description here. + + """ + + # Create a mapping from Model property names to API property names + _names = { + "conference_id": 'conferenceId', + "name": 'name', + "event_type": 'eventType', + "event_time": 'eventTime', + "tag": 'tag', + "call_id": 'callId', + "to": 'to', + "mfrom": 'from', + "account_id": 'accountId', + "recording_id": 'recordingId', + "channels": 'channels', + "start_time": 'startTime', + "end_time": 'endTime', + "duration": 'duration', + "file_format": 'fileFormat', + "media_url": 'mediaUrl', + "status": 'status' + } + + def __init__(self, + conference_id=None, + name=None, + event_type=None, + event_time=None, + tag=None, + call_id=None, + to=None, + mfrom=None, + account_id=None, + recording_id=None, + channels=None, + start_time=None, + end_time=None, + duration=None, + file_format=None, + media_url=None, + status=None): + """Constructor for the ConferenceCallback class""" + + # Initialize members of the class + self.conference_id = conference_id + self.name = name + self.event_type = event_type + self.event_time = event_time + self.tag = tag + self.call_id = call_id + self.to = to + self.mfrom = mfrom + self.account_id = account_id + self.recording_id = recording_id + self.channels = channels + self.start_time = start_time + self.end_time = end_time + self.duration = duration + self.file_format = file_format + self.media_url = media_url + self.status = status + + @classmethod + def from_dictionary(cls, + dictionary): + """Creates an instance of this model from a dictionary + + Args: + dictionary (dictionary): A dictionary representation of the object + as obtained from the deserialization of the server's response. The + keys MUST match property names in the API description. + + Returns: + object: An instance of this structure class. + + """ + if dictionary is None: + return None + + # Extract variables from the dictionary + conference_id = dictionary.get('conferenceId') + name = dictionary.get('name') + event_type = dictionary.get('eventType') + event_time = dictionary.get('eventTime') + tag = dictionary.get('tag') + call_id = dictionary.get('callId') + to = dictionary.get('to') + mfrom = dictionary.get('from') + account_id = dictionary.get('accountId') + recording_id = dictionary.get('recordingId') + channels = dictionary.get('channels') + start_time = dictionary.get('startTime') + end_time = dictionary.get('endTime') + duration = dictionary.get('duration') + file_format = dictionary.get('fileFormat') + media_url = dictionary.get('mediaUrl') + status = dictionary.get('status') + + # Return an object of this model + return cls(conference_id, + name, + event_type, + event_time, + tag, + call_id, + to, + mfrom, + account_id, + recording_id, + channels, + start_time, + end_time, + duration, + file_format, + media_url, + status) diff --git a/bandwidth/voice/models/create_call_request.py b/bandwidth/voice/models/create_call_request.py index e6e9b950..0f18391f 100644 --- a/bandwidth/voice/models/create_call_request.py +++ b/bandwidth/voice/models/create_call_request.py @@ -6,6 +6,7 @@ This file was automatically generated by APIMATIC v3.0 ( https://www.apimatic.io ). """ +from bandwidth.voice.models.machine_detection_request import MachineDetectionRequest class CreateCallRequest(object): @@ -17,11 +18,12 @@ class CreateCallRequest(object): Attributes: mfrom (string): Format is E164 to (string): Format is E164 or SIP URI - uui (string): When calling a SIP URI, this will be sent as the - 'User-To-User' header within the initial INVITE. It must end with - an 'encoding' parameter as described in - https://tools.ietf.org/html/rfc7433. This header cannot exceed 256 - characters, including the encoding parameter. + uui (string): A comma-separated list of 'User-To-User' headers to be + sent in the INVITE when calling a SIP URI. Each value must end + with an 'encoding' parameter as described in + https://tools.ietf.org/html/rfc7433. Only 'jwt' and 'base64' + encodings are allowed. The entire value cannot exceed 350 + characters, including parameters and separators. call_timeout (float): TODO: type description here. callback_timeout (float): TODO: type description here. answer_url (string): TODO: type description here. @@ -38,6 +40,8 @@ class CreateCallRequest(object): here. tag (string): TODO: type description here. application_id (string): TODO: type description here. + machine_detection (MachineDetectionRequest): TODO: type description + here. """ @@ -59,7 +63,8 @@ class CreateCallRequest(object): "answer_fallback_method": 'answerFallbackMethod', "disconnect_url": 'disconnectUrl', "disconnect_method": 'disconnectMethod', - "tag": 'tag' + "tag": 'tag', + "machine_detection": 'machineDetection' } def __init__(self, @@ -79,7 +84,8 @@ def __init__(self, answer_fallback_method=None, disconnect_url=None, disconnect_method=None, - tag=None): + tag=None, + machine_detection=None): """Constructor for the CreateCallRequest class""" # Initialize members of the class @@ -100,6 +106,7 @@ def __init__(self, self.disconnect_method = disconnect_method self.tag = tag self.application_id = application_id + self.machine_detection = machine_detection @classmethod def from_dictionary(cls, @@ -136,6 +143,7 @@ def from_dictionary(cls, disconnect_url = dictionary.get('disconnectUrl') disconnect_method = dictionary.get('disconnectMethod') tag = dictionary.get('tag') + machine_detection = MachineDetectionRequest.from_dictionary(dictionary.get('machineDetection')) if dictionary.get('machineDetection') else None # Return an object of this model return cls(mfrom, @@ -154,4 +162,5 @@ def from_dictionary(cls, answer_fallback_method, disconnect_url, disconnect_method, - tag) + tag, + machine_detection) diff --git a/bandwidth/voice/models/diversion.py b/bandwidth/voice/models/diversion.py new file mode 100644 index 00000000..1a99140e --- /dev/null +++ b/bandwidth/voice/models/diversion.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +""" +bandwidth + +This file was automatically generated by APIMATIC v3.0 ( + https://www.apimatic.io ). +""" + + +class Diversion(object): + + """Implementation of the 'Diversion' model. + + TODO: type model description here. + + Attributes: + reason (string): TODO: type description here. + privacy (string): TODO: type description here. + unknown (string): TODO: type description here. + orig_to (string): TODO: type description here. + + """ + + # Create a mapping from Model property names to API property names + _names = { + "reason": 'reason', + "privacy": 'privacy', + "unknown": 'unknown', + "orig_to": 'origTo' + } + + def __init__(self, + reason=None, + privacy=None, + unknown=None, + orig_to=None): + """Constructor for the Diversion class""" + + # Initialize members of the class + self.reason = reason + self.privacy = privacy + self.unknown = unknown + self.orig_to = orig_to + + @classmethod + def from_dictionary(cls, + dictionary): + """Creates an instance of this model from a dictionary + + Args: + dictionary (dictionary): A dictionary representation of the object + as obtained from the deserialization of the server's response. The + keys MUST match property names in the API description. + + Returns: + object: An instance of this structure class. + + """ + if dictionary is None: + return None + + # Extract variables from the dictionary + reason = dictionary.get('reason') + privacy = dictionary.get('privacy') + unknown = dictionary.get('unknown') + orig_to = dictionary.get('origTo') + + # Return an object of this model + return cls(reason, + privacy, + unknown, + orig_to) diff --git a/bandwidth/voice/models/fallback_method_enum.py b/bandwidth/voice/models/fallback_method_enum.py new file mode 100644 index 00000000..0b5e0ed6 --- /dev/null +++ b/bandwidth/voice/models/fallback_method_enum.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +""" +bandwidth + +This file was automatically generated by APIMATIC v3.0 ( + https://www.apimatic.io ). +""" + + +class FallbackMethodEnum(object): + + """Implementation of the 'FallbackMethod' enum. + + TODO: type enum description here. + + Attributes: + POST: TODO: type description here. + GET: TODO: type description here. + + """ + + POST = 'POST' + + GET = 'GET' diff --git a/bandwidth/voice/models/machine_detection_request.py b/bandwidth/voice/models/machine_detection_request.py new file mode 100644 index 00000000..448dc4a7 --- /dev/null +++ b/bandwidth/voice/models/machine_detection_request.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- + +""" +bandwidth + +This file was automatically generated by APIMATIC v3.0 ( + https://www.apimatic.io ). +""" + + +class MachineDetectionRequest(object): + + """Implementation of the 'MachineDetectionRequest' model. + + TODO: type model description here. + + Attributes: + mode (ModeEnum): The machine detection mode. If set to 'async', the + detection result will be sent in a 'machineDetectionComplete' + callback. If set to 'sync', the 'answer' callback will wait for + the machine detection to complete and will include its result. + Default is 'async'. + detection_timeout (float): Total amount of time (in seconds) before + giving up. + silence_timeout (float): If no speech is detected in this period, a + callback with a 'silence' result is sent. Default is 10 seconds. + speech_threshold (float): When speech has ended and a result couldn't + be determined based on the audio content itself, this value is + used to determine if the speaker is a machine based on the speech + duration. If the length of the speech detected is greater than or + equal to this threshold, the result will be 'answering-machine'. + If the length of speech detected is below this threshold, the + result will be 'human'. Default is 10 seconds. + speech_end_threshold (float): Amount of silence (in seconds) before + assuming the callee has finished speaking. + delay_result (bool): If set to 'true' and if an answering machine is + detected, the 'answering-machine' callback will be delayed until + the machine is done speaking or until the 'detectionTimeout' is + exceeded. If false, the 'answering-machine' result is sent + immediately. Default is 'false'. + callback_url (string): The URL to send the 'machineDetectionComplete' + callback when the detection is completed. Only for 'async' mode. + callback_method (CallbackMethodEnum): TODO: type description here. + fallback_url (string): TODO: type description here. + fallback_method (FallbackMethodEnum): TODO: type description here. + username (string): TODO: type description here. + password (string): TODO: type description here. + fallback_username (string): TODO: type description here. + fallback_password (string): TODO: type description here. + + """ + + # Create a mapping from Model property names to API property names + _names = { + "mode": 'mode', + "detection_timeout": 'detectionTimeout', + "silence_timeout": 'silenceTimeout', + "speech_threshold": 'speechThreshold', + "speech_end_threshold": 'speechEndThreshold', + "delay_result": 'delayResult', + "callback_url": 'callbackUrl', + "callback_method": 'callbackMethod', + "fallback_url": 'fallbackUrl', + "fallback_method": 'fallbackMethod', + "username": 'username', + "password": 'password', + "fallback_username": 'fallbackUsername', + "fallback_password": 'fallbackPassword' + } + + def __init__(self, + mode=None, + detection_timeout=None, + silence_timeout=None, + speech_threshold=None, + speech_end_threshold=None, + delay_result=None, + callback_url=None, + callback_method=None, + fallback_url=None, + fallback_method=None, + username=None, + password=None, + fallback_username=None, + fallback_password=None): + """Constructor for the MachineDetectionRequest class""" + + # Initialize members of the class + self.mode = mode + self.detection_timeout = detection_timeout + self.silence_timeout = silence_timeout + self.speech_threshold = speech_threshold + self.speech_end_threshold = speech_end_threshold + self.delay_result = delay_result + self.callback_url = callback_url + self.callback_method = callback_method + self.fallback_url = fallback_url + self.fallback_method = fallback_method + self.username = username + self.password = password + self.fallback_username = fallback_username + self.fallback_password = fallback_password + + @classmethod + def from_dictionary(cls, + dictionary): + """Creates an instance of this model from a dictionary + + Args: + dictionary (dictionary): A dictionary representation of the object + as obtained from the deserialization of the server's response. The + keys MUST match property names in the API description. + + Returns: + object: An instance of this structure class. + + """ + if dictionary is None: + return None + + # Extract variables from the dictionary + mode = dictionary.get('mode') + detection_timeout = dictionary.get('detectionTimeout') + silence_timeout = dictionary.get('silenceTimeout') + speech_threshold = dictionary.get('speechThreshold') + speech_end_threshold = dictionary.get('speechEndThreshold') + delay_result = dictionary.get('delayResult') + callback_url = dictionary.get('callbackUrl') + callback_method = dictionary.get('callbackMethod') + fallback_url = dictionary.get('fallbackUrl') + fallback_method = dictionary.get('fallbackMethod') + username = dictionary.get('username') + password = dictionary.get('password') + fallback_username = dictionary.get('fallbackUsername') + fallback_password = dictionary.get('fallbackPassword') + + # Return an object of this model + return cls(mode, + detection_timeout, + silence_timeout, + speech_threshold, + speech_end_threshold, + delay_result, + callback_url, + callback_method, + fallback_url, + fallback_method, + username, + password, + fallback_username, + fallback_password) diff --git a/bandwidth/voice/models/mode_enum.py b/bandwidth/voice/models/mode_enum.py new file mode 100644 index 00000000..85c80e43 --- /dev/null +++ b/bandwidth/voice/models/mode_enum.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +""" +bandwidth + +This file was automatically generated by APIMATIC v3.0 ( + https://www.apimatic.io ). +""" + + +class ModeEnum(object): + + """Implementation of the 'Mode' enum. + + The machine detection mode. If set to 'async', the detection result will + be sent in a 'machineDetectionComplete' callback. If set to 'sync', the + 'answer' callback will wait for the machine detection to complete and will + include its result. Default is 'async'. + + Attributes: + SYNC: TODO: type description here. + ASYNC: TODO: type description here. + + """ + + SYNC = 'sync' + + ASYNC = 'async' diff --git a/bandwidth/voice/models/transcription.py b/bandwidth/voice/models/transcription.py new file mode 100644 index 00000000..b0d10fc9 --- /dev/null +++ b/bandwidth/voice/models/transcription.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +""" +bandwidth + +This file was automatically generated by APIMATIC v3.0 ( + https://www.apimatic.io ). +""" + + +class Transcription(object): + + """Implementation of the 'Transcription' model. + + TODO: type model description here. + + Attributes: + id (string): TODO: type description here. + url (string): TODO: type description here. + status (string): TODO: type description here. + completed_time (string): TODO: type description here. + + """ + + # Create a mapping from Model property names to API property names + _names = { + "id": 'id', + "url": 'url', + "status": 'status', + "completed_time": 'completedTime' + } + + def __init__(self, + id=None, + url=None, + status=None, + completed_time=None): + """Constructor for the Transcription class""" + + # Initialize members of the class + self.id = id + self.url = url + self.status = status + self.completed_time = completed_time + + @classmethod + def from_dictionary(cls, + dictionary): + """Creates an instance of this model from a dictionary + + Args: + dictionary (dictionary): A dictionary representation of the object + as obtained from the deserialization of the server's response. The + keys MUST match property names in the API description. + + Returns: + object: An instance of this structure class. + + """ + if dictionary is None: + return None + + # Extract variables from the dictionary + id = dictionary.get('id') + url = dictionary.get('url') + status = dictionary.get('status') + completed_time = dictionary.get('completedTime') + + # Return an object of this model + return cls(id, + url, + status, + completed_time) diff --git a/bandwidth/webrtc/controllers/api_controller.py b/bandwidth/webrtc/controllers/api_controller.py index 46e6ccf5..f4bd04bb 100644 --- a/bandwidth/webrtc/controllers/api_controller.py +++ b/bandwidth/webrtc/controllers/api_controller.py @@ -32,9 +32,9 @@ def create_participant(self, body=None): """Does a POST request to /accounts/{accountId}/participants. - Create a new participant under this account + Create a new participant under this account. Participants are idempotent, so relevant parameters must be set in - this function if desired + this function if desired. Args: account_id (string): Account ID @@ -92,7 +92,7 @@ def get_participant(self, participant_id): """Does a GET request to /accounts/{accountId}/participants/{participantId}. - Get participant by ID + Get participant by ID. Args: account_id (string): Account ID @@ -150,7 +150,7 @@ def delete_participant(self, participant_id): """Does a DELETE request to /accounts/{accountId}/participants/{participantId}. - Delete participant by ID + Delete participant by ID. Args: account_id (string): Account ID @@ -203,9 +203,9 @@ def create_session(self, body=None): """Does a POST request to /accounts/{accountId}/sessions. - Create a new session + Create a new session. Sessions are idempotent, so relevant parameters must be set in this - function if desired + function if desired. Args: account_id (string): Account ID @@ -263,7 +263,7 @@ def get_session(self, session_id): """Does a GET request to /accounts/{accountId}/sessions/{sessionId}. - Get session by ID + Get session by ID. Args: account_id (string): Account ID @@ -321,7 +321,7 @@ def delete_session(self, session_id): """Does a DELETE request to /accounts/{accountId}/sessions/{sessionId}. - Delete session by ID + Delete session by ID. Args: account_id (string): Account ID @@ -374,7 +374,7 @@ def list_session_participants(self, session_id): """Does a GET request to /accounts/{accountId}/sessions/{sessionId}/participants. - List participants in a session + List participants in a session. Args: account_id (string): Account ID @@ -434,8 +434,8 @@ def add_participant_to_session(self, body=None): """Does a PUT request to /accounts/{accountId}/sessions/{sessionId}/participants/{participantId}. - Add a participant to a session - Subscriptions can optionally be provided as part of this call + Add a participant to a session. + Subscriptions can optionally be provided as part of this call. Args: account_id (string): Account ID @@ -498,9 +498,9 @@ def remove_participant_from_session(self, participant_id): """Does a DELETE request to /accounts/{accountId}/sessions/{sessionId}/participants/{participantId}. - Remove a participant from a session + Remove a participant from a session. This will automatically remove any subscriptions the participant has - associated with this session + associated with this session. Args: account_id (string): Account ID @@ -556,7 +556,7 @@ def get_participant_subscriptions(self, participant_id): """Does a GET request to /accounts/{accountId}/sessions/{sessionId}/participants/{participantId}/subscriptions. - Get a participant's subscriptions + Get a participant's subscriptions. Args: account_id (string): Account ID @@ -618,11 +618,11 @@ def update_participant_subscriptions(self, body=None): """Does a PUT request to /accounts/{accountId}/sessions/{sessionId}/participants/{participantId}/subscriptions. - Update a participant's subscriptions + Update a participant's subscriptions. This is a full update that will replace the participant's subscriptions. First call `getParticipantSubscriptions` if you need the current subscriptions. Call this function with no `Subscriptions` - object to remove all subscriptions + object to remove all subscriptions. Args: account_id (string): Account ID diff --git a/setup.py b/setup.py index 8f46cf53..cb14f7fe 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='bandwidth-sdk', - version='11.0.0', + version='12.0.0', description='Bandwidth\'s set of APIs', long_description=long_description, long_description_content_type="text/markdown", diff --git a/tests/controllers/__init__.py b/tests/controllers/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/controllers/controller_test_base.py b/tests/controllers/controller_test_base.py deleted file mode 100644 index c6e02469..00000000 --- a/tests/controllers/controller_test_base.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -bandwidth - -This file was automatically generated by APIMATIC v3.0 ( - https://www.apimatic.io ). -""" - -import unittest - -from bandwidth.configuration import Configuration -from bandwidth.configuration import Environment -from bandwidth.bandwidth_client import BandwidthClient - - -class ControllerTestBase(unittest.TestCase): - - """All test classes inherit from this base class. It abstracts out - common functionality and configuration variables set up.""" - - @classmethod - def setUpClass(cls): - """Class method called once before running tests in a test class.""" - cls.request_timeout = 30 - cls.assert_precision = 0.01 - cls.config = ControllerTestBase.create_configuration() - cls.client = BandwidthClient() - - @staticmethod - def create_configuration(): - return Configuration() diff --git a/tests/http_response_catcher.py b/tests/http_response_catcher.py deleted file mode 100644 index 8621d915..00000000 --- a/tests/http_response_catcher.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -bandwidth - -This file was automatically generated by APIMATIC v3.0 ( - https://www.apimatic.io ). -""" - -from bandwidth.http.http_call_back import HttpCallBack - -class HttpResponseCatcher(HttpCallBack): - - """A class used for catching the HttpResponse object from controllers. - - This class inherits HttpCallBack and implements the on_after_response - method to catch the HttpResponse object as returned by the HttpClient - after a request is executed. - - """ - def on_before_request(self, request): - pass - - def on_after_response(self, response): - self.response = response - - - diff --git a/tests/integration/api_tests.py b/tests/integration/api_tests.py index 22658339..f7170962 100644 --- a/tests/integration/api_tests.py +++ b/tests/integration/api_tests.py @@ -7,9 +7,10 @@ """ from bandwidth.bandwidth_client import BandwidthClient from bandwidth.messaging.exceptions.messaging_exception import MessagingException -from bandwidth.voice.exceptions.api_error_exception import ApiErrorException +from bandwidth.voice.exceptions.api_exception import APIException from bandwidth.messaging.models.message_request import MessageRequest from bandwidth.voice.models.create_call_request import CreateCallRequest +from bandwidth.voice.models.machine_detection_request import MachineDetectionRequest from bandwidth.multifactorauth.models.two_factor_code_request_schema import TwoFactorCodeRequestSchema from bandwidth.multifactorauth.models.two_factor_verify_request_schema import TwoFactorVerifyRequestSchema from bandwidth.phonenumberlookup.models.order_request import OrderRequest @@ -33,6 +34,7 @@ except: raise Exception("Environmental variables not found") + class MonitorTest(unittest.TestCase): """ Class that holds basic monitoring tests for the Python SDK. Makes requests to cover JSON call and response, @@ -57,6 +59,7 @@ def setUp(self): self.auth_client = self.bandwidth_client.multi_factor_auth_client.mfa self.tn_lookup_client = self.bandwidth_client.phone_number_lookup_client.client + def test_create_message(self): body = MessageRequest() body.application_id = MESSAGING_APPLICATION_ID @@ -66,6 +69,7 @@ def test_create_message(self): response = self.messaging_client.create_message(ACCOUNT_ID, body) self.assertTrue(len(response.body.id) > 0) #validate that _some_ id was returned + def test_create_message_invalid_phone_number(self): body = MessageRequest() body.application_id = MESSAGING_APPLICATION_ID @@ -80,6 +84,7 @@ def test_create_message_invalid_phone_number(self): except: self.assertTrue(False) + def test_upload_download_media(self): #define constants for upload media and download media media_file_name = 'python_monitoring' #future update to add special symbols @@ -94,6 +99,7 @@ def test_upload_download_media(self): #validate that the response is the same as the upload self.assertEqual(media_file, downloaded_media_file) + def test_create_call_and_get_call(self): body = CreateCallRequest() body.mfrom = PHONE_NUMBER_OUTBOUND @@ -104,11 +110,10 @@ def test_create_call_and_get_call(self): self.assertTrue(len(response.body.call_id) > 1) #get phone call information - import time - time.sleep(1) #No guarantee that the info will be immediately available response = self.voice_client.get_call(ACCOUNT_ID, response.body.call_id) self.assertTrue(len(response.body.state) > 1) + def test_create_call_invalid_phone_number(self): body = CreateCallRequest() body.mfrom = PHONE_NUMBER_OUTBOUND @@ -117,12 +122,54 @@ def test_create_call_invalid_phone_number(self): body.answer_url = CALLBACK_URL try: self.voice_client.create_call(ACCOUNT_ID, body) - self.assertTrue(False) - except ApiErrorException as e: + except APIException as e: self.assertTrue(len(e.description) > 0) except: self.assertTrue(False); + + def create_call_amd_and_get_call(self): + machine_detection_parameters = MachineDetectionRequest() + machine_detection_parameters.mode = "async" + machine_detection_parameters.callback_url = CALLBACK_URL + machine_detection_parameters.callback_method = "POST" + machine_detection_parameters.detection_timeout = 5.0 + machine_detection_parameters.silence_timeout = 5.0 + machine_detection_parameters.speech_threshold = 5.0 + machine_detection_parameters.speech_end_threshold = 5.0 + machine_detection_parameters.delay_result = True + + body = CreateCallRequest() + body.mfrom = PHONE_NUMBER_OUTBOUND + body.to = PHONE_NUMBER_INBOUND + body.application_id = VOICE_APPLICATION_ID + body.answer_url = CALLBACK_URL + body.machine_detection = machine_detection_parameters + create_response = self.voice_client.create_call(ACCOUNT_ID, body) + self.assertTrue(len(create_response.body.call_id) > 1) + + #get phone call information + get_response = self.voice_client.get_call(ACCOUNT_ID, create_response.body.call_id) + self.assertTrue(len(get_response.body.state) > 1) + self.assertEqual(get_response.body.callId, create_response.body.call_id) + self.assertIs(get_response.body.call_id, str) + self.assertIs(get_response.body.application_id, str) + self.assertIs(get_response.body.account_id, str) + self.assertIs(get_response.body.to, str) + self.assertIs(get_response.body.mfrom, str) + self.assertIs(get_response.body.direction, str) + self.assertIs(get_response.body.state, str) + self.assertIs(get_response.body.start_time, str) + self.assertIs(get_response.body.last_update, str) + + if get_response.body.disconnect_cause: + self.assertIs(get_response.body.disconnect_cause, str) + if get_response.body.error_message: + self.assertIs(get_response.body.error_message, str) + if get_response.body.error_id: + self.assertIs(get_response.body.error_id, str) + + def test_mfa_messaging(self): body = TwoFactorCodeRequestSchema( mfrom = PHONE_NUMBER_MFA, @@ -135,6 +182,7 @@ def test_mfa_messaging(self): response = self.auth_client.create_messaging_two_factor(ACCOUNT_ID, body) self.assertTrue(len(response.body.message_id) > 0) + def test_mfa_voice(self): body = TwoFactorCodeRequestSchema( mfrom = PHONE_NUMBER_MFA, @@ -147,6 +195,7 @@ def test_mfa_voice(self): response = self.auth_client.create_voice_two_factor(ACCOUNT_ID, body) self.assertTrue(len(response.body.call_id) > 0) + def test_mfa_verify(self): body = TwoFactorVerifyRequestSchema( to = PHONE_NUMBER_INBOUND, @@ -158,6 +207,7 @@ def test_mfa_verify(self): response = self.auth_client.create_verify_two_factor(ACCOUNT_ID, body) self.assertTrue(isinstance(response.body.valid, bool)) + def test_tn_lookup(self): body = OrderRequest() body.tns = [PHONE_NUMBER_OUTBOUND] @@ -169,5 +219,6 @@ def test_tn_lookup(self): get_response = self.tn_lookup_client.get_lookup_request_status(ACCOUNT_ID, request_id) self.assertTrue(get_response.status_code == 200) + if __name__ == '__main__': unittest.main() diff --git a/tests/test_helper.py b/tests/test_helper.py deleted file mode 100644 index 9eb9468d..00000000 --- a/tests/test_helper.py +++ /dev/null @@ -1,127 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -bandwidth - -This file was automatically generated by APIMATIC v3.0 ( - https://www.apimatic.io ). -""" - -import os, tempfile, requests - -class TestHelper(object): - - """A Helper Class for various functions associated with API testing. - - This class contains static and class methods for operations that need to be - performed during API testing. All of the methods inside this class are - static or class methods, there is no need to ever initialise an instance of this - class. - - Attributes: - cache (Set): Class variable which stores hashes of file URLs so we don't - download the same file twice in a test session. - - """ - - cache = {} - - @staticmethod - def match_headers(expected_headers, - received_headers, - allow_extra=True): - """Static method to compare the received headers with the expected headers. - - Args: - expected_headers (dict): A dictionary of expected headers (keys in lower case). - received_headers (dict): A dictionary of headers received. - allow_extra (Boolean, optional): A flag which determines if we - allow extra headers. - Returns: - Boolean: True if headers match, False otherwise. - - """ - if ((len(received_headers) < len(expected_headers)) or - ((allow_extra == False) and (len(expected_headers) != len(received_headers)))): - return False - - received_headers = {k.lower(): v for k, v in received_headers.items()} - for e_key in expected_headers: - if e_key not in received_headers: - return False - if((expected_headers[e_key] != None) and - (expected_headers[e_key] != received_headers[e_key])): - return False - - return True - - @staticmethod - def match_body(expected_body, - received_body, - check_values=False, - check_order=False, - check_count=False): - """Static method to compare the received body with the expected body. - - Args: - expected_body (dynamic): The expected body. - received_body (dynamic): The received body. - check_values (Boolean, optional): A flag which determines if we - check values in dictionaries. - check_order (Boolean, optional): A flag which determines if we - check the order of array elements. - check_count (Boolean, optional): A flag which determines if we - check the count of array elements. - Returns: - Boolean: True if bodies match, False otherwise. - - """ - if type(expected_body) == dict: - if type(received_body) != dict: - return False - for key in expected_body: - if key not in received_body: - return False - if check_values or type(expected_body[key]) == dict: - if TestHelper.match_body(expected_body[key], received_body[key], - check_values, check_order, check_count) == False: - return False - elif type(expected_body) == list: - if type(received_body) != list: - return False - if check_count == True and (len(expected_body) != len(received_body)): - return False - else: - previous_matches = [] - for i, expected_element in enumerate(expected_body): - matches = [j for j, received_element - in enumerate(received_body) - if TestHelper.match_body(expected_element, received_element, - check_values, check_order, check_count)] - if len(matches) == 0: - return False - if check_order == True: - if i != 0 and all([all(y > x for y in previous_matches) for x in matches]): - return False - previous_matches = matches - elif expected_body != received_body: - return False - return True - - @classmethod - def get_file(cls, url): - """Class method which takes a URL, downloads the file (if not - already downloaded for this test session) and returns a file object for - the file in read-binary mode. - - Args: - url (string): The URL of the required file. - Returns: - FileObject: The file object of the required file (opened with "rb"). - - """ - if url not in cls.cache: - cls.cache[url] = tempfile.TemporaryFile() - cls.cache[url].write(requests.get(url).content) - cls.cache[url].seek(0) - return cls.cache[url] \ No newline at end of file