From f496beae3505b841fe4ae36f9b2292d9e682a87f Mon Sep 17 00:00:00 2001 From: sgiraldo Date: Tue, 15 Feb 2022 12:09:17 -0500 Subject: [PATCH 1/3] doc: format file with black --- messagebird/client.py | 356 +++++++++++++++++++++--------------------- 1 file changed, 180 insertions(+), 176 deletions(-) diff --git a/messagebird/client.py b/messagebird/client.py index 04b7aa4..c165aff 100644 --- a/messagebird/client.py +++ b/messagebird/client.py @@ -24,36 +24,36 @@ from messagebird.call_flow import CallFlow, CallFlowList, CallFlowNumberList from messagebird.number import Number, NumberList -ENDPOINT = 'https://rest.messagebird.com' -CLIENT_VERSION = '2.0.0' -PYTHON_VERSION = '%d.%d.%d' % (sys.version_info[0], sys.version_info[1], sys.version_info[2]) -USER_AGENT = 'MessageBird/ApiClient/%s Python/%s' % (CLIENT_VERSION, PYTHON_VERSION) -REST_TYPE = 'rest' - -CONVERSATION_API_ROOT = 'https://conversations.messagebird.com/v1/' -CONVERSATION_PATH = 'conversations' -CONVERSATION_MESSAGES_PATH = 'messages' -CONVERSATION_WEB_HOOKS_PATH = 'webhooks' -CONVERSATION_TYPE = 'conversation' - -VOICE_API_ROOT = 'https://voice.messagebird.com' -VOICE_TYPE = 'voice' -VOICE_PATH = 'calls' -VOICE_LEGS_PATH = 'legs' -VOICE_RECORDINGS_PATH = 'recordings' -VOICE_TRANSCRIPTIONS_PATH = 'transcriptions' -VOICE_WEB_HOOKS_PATH = 'webhooks' - -NUMBER_TYPE = 'number' -NUMBER_API_ROOT = 'https://numbers.messagebird.com/v1/' -NUMBER_PATH = 'phone-numbers' -NUMBER_AVAILABLE_PATH = 'available-phone-numbers' +ENDPOINT = "https://rest.messagebird.com" +CLIENT_VERSION = "2.0.0" +PYTHON_VERSION = "%d.%d.%d" % (sys.version_info[0], sys.version_info[1], sys.version_info[2]) +USER_AGENT = "MessageBird/ApiClient/%s Python/%s" % (CLIENT_VERSION, PYTHON_VERSION) +REST_TYPE = "rest" + +CONVERSATION_API_ROOT = "https://conversations.messagebird.com/v1/" +CONVERSATION_PATH = "conversations" +CONVERSATION_MESSAGES_PATH = "messages" +CONVERSATION_WEB_HOOKS_PATH = "webhooks" +CONVERSATION_TYPE = "conversation" + +VOICE_API_ROOT = "https://voice.messagebird.com" +VOICE_TYPE = "voice" +VOICE_PATH = "calls" +VOICE_LEGS_PATH = "legs" +VOICE_RECORDINGS_PATH = "recordings" +VOICE_TRANSCRIPTIONS_PATH = "transcriptions" +VOICE_WEB_HOOKS_PATH = "webhooks" + +NUMBER_TYPE = "number" +NUMBER_API_ROOT = "https://numbers.messagebird.com/v1/" +NUMBER_PATH = "phone-numbers" +NUMBER_AVAILABLE_PATH = "available-phone-numbers" class ErrorException(Exception): def __init__(self, errors): self.errors = errors - message = ' '.join([str(e) for e in self.errors]) + message = " ".join([str(e) for e in self.errors]) super(ErrorException, self).__init__(message) @@ -62,7 +62,6 @@ def __init__(self, errorMessage): super(SignleErrorException, self).__init__(errorMessage) - class Client(object): def __init__(self, access_key, http_client=None): self.access_key = access_key @@ -83,7 +82,7 @@ def _get_http_client(self, type=REST_TYPE): return HttpClient(ENDPOINT, self.access_key, USER_AGENT) - def request(self, path, method='GET', params=None, type=REST_TYPE): + def request(self, path, method="GET", params=None, type=REST_TYPE): """Builds a request, gets a response and decodes it.""" response_text = self._get_http_client(type).request(path, method, params) if not response_text: @@ -91,12 +90,12 @@ def request(self, path, method='GET', params=None, type=REST_TYPE): response_json = json.loads(response_text) - if 'errors' in response_json: - raise (ErrorException([Error().load(e) for e in response_json['errors']])) + if "errors" in response_json: + raise (ErrorException([Error().load(e) for e in response_json["errors"]])) return response_json - def request_plain_text(self, path, method='GET', params=None, type=REST_TYPE): + def request_plain_text(self, path, method="GET", params=None, type=REST_TYPE): """Builds a request, gets a response and returns the body.""" response_text = self._get_http_client(type).request(path, method, params) @@ -105,8 +104,8 @@ def request_plain_text(self, path, method='GET', params=None, type=REST_TYPE): # errors. response_json = json.loads(response_text) - if 'errors' in response_json: - raise (ErrorException([Error().load(e) for e in response_json['errors']])) + if "errors" in response_json: + raise (ErrorException([Error().load(e) for e in response_json["errors"]])) except ValueError: # Do nothing: json.loads throws if the input string is not valid JSON, # which is expected. We'll just return the response body below. @@ -114,25 +113,25 @@ def request_plain_text(self, path, method='GET', params=None, type=REST_TYPE): return response_text - def request_store_as_file(self, path, filepath, method='GET', params=None, type=REST_TYPE): + def request_store_as_file(self, path, filepath, method="GET", params=None, type=REST_TYPE): """Builds a request, gets a response and decodes it.""" response_binary = self._get_http_client(type).request(path, method, params, ResponseFormat.binary) if not response_binary: return response_binary - with io.open(filepath, 'wb') as f: + with io.open(filepath, "wb") as f: f.write(response_binary) return filepath def balance(self): """Retrieve your balance.""" - return Balance().load(self.request('balance')) + return Balance().load(self.request("balance")) def call(self, id): """Retrieve the information of a specific call""" - return Call().load(self.request('calls/' + str(id), 'GET', None, VOICE_TYPE)) + return Call().load(self.request("calls/" + str(id), "GET", None, VOICE_TYPE)) def call_list(self, page=1): """Listing calls @@ -144,7 +143,7 @@ def call_list(self, page=1): Returns: CallList(object) : The list of calls requested & their status.""" - return CallList().load(self.request('calls/?page=' + str(page), 'GET', None, VOICE_TYPE)) + return CallList().load(self.request("calls/?page=" + str(page), "GET", None, VOICE_TYPE)) def call_create(self, source, destination, callFlow, webhook): """Creating a call @@ -161,12 +160,12 @@ def call_create(self, source, destination, callFlow, webhook): Call(object) : The Call object just created.""" params = locals() - del (params['self']) - return Call().load(self.request('calls/', 'POST', params, VOICE_TYPE)) + del params["self"] + return Call().load(self.request("calls/", "POST", params, VOICE_TYPE)) def call_delete(self, id): """Delete an existing call object.""" - response = self.request_plain_text('calls/' + str(id), 'DELETE', None, VOICE_TYPE) + response = self.request_plain_text("calls/" + str(id), "DELETE", None, VOICE_TYPE) # successful delete should be empty if len(response) > 0: @@ -174,15 +173,15 @@ def call_delete(self, id): def hlr(self, id): """Retrieve the information of a specific HLR lookup.""" - return HLR().load(self.request('hlr/' + str(id))) + return HLR().load(self.request("hlr/" + str(id))) def hlr_create(self, msisdn, reference): """Perform a new HLR lookup.""" - return HLR().load(self.request('hlr', 'POST', {'msisdn': msisdn, 'reference': reference})) + return HLR().load(self.request("hlr", "POST", {"msisdn": msisdn, "reference": reference})) def message(self, id): """Retrieve the information of a specific message.""" - return Message().load(self.request('messages/' + str(id))) + return Message().load(self.request("messages/" + str(id))) def message_list(self, limit=20, offset=0, status=None): """Retrieve a list of the most recent messages. @@ -196,24 +195,26 @@ def message_list(self, limit=20, offset=0, status=None): query = self._format_query(limit, offset) if status: query = query + "&status=" + status - return MessageList().load(self.request('messages?' + query)) + return MessageList().load(self.request("messages?" + query)) def message_create(self, originator, recipients, body, params=None): """Create a new message.""" if params is None: params = {} if type(recipients) == list: - recipients = ','.join(recipients) + recipients = ",".join(recipients) - params.update({'originator': originator, 'body': body, 'recipients': recipients}) - return Message().load(self.request('messages', 'POST', params)) + params.update({"originator": originator, "body": body, "recipients": recipients}) + return Message().load(self.request("messages", "POST", params)) def message_delete(self, id): """Delete a message from the dashboard.""" - self.request_plain_text('messages/' + str(id), 'DELETE') + self.request_plain_text("messages/" + str(id), "DELETE") - def mms_create(self, originator, recipients, body, mediaUrls, subject=None, reference=None, scheduledDatetime=None): - """ Send bulk mms. + def mms_create( + self, originator, recipients, body, mediaUrls, subject=None, reference=None, scheduledDatetime=None + ): + """Send bulk mms. Args: originator(str): name of the originator @@ -230,168 +231,165 @@ def mms_create(self, originator, recipients, body, mediaUrls, subject=None, refe MMS: On success an MMS instance instantiated with success response """ if isinstance(recipients, list): - recipients = ','.join(recipients) + recipients = ",".join(recipients) if isinstance(mediaUrls, str): mediaUrls = [mediaUrls] params = locals() - del (params['self']) - return MMS().load(self.request('mms', 'POST', params)) + del params["self"] + return MMS().load(self.request("mms", "POST", params)) def voice_message(self, id): "Retrieve the information of a specific voice message." - return VoiceMessage().load(self.request('voicemessages/' + str(id))) + return VoiceMessage().load(self.request("voicemessages/" + str(id))) def voice_message_list(self, limit=10, offset=0): "Retrieve the information of a list of voice messages." query = self._format_query(limit, offset) - return VoiceMessagesList().load(self.request('voicemessages?' + query, 'GET', None)) + return VoiceMessagesList().load(self.request("voicemessages?" + query, "GET", None)) def voice_message_create(self, recipients, body, params=None): """Create a new voice message.""" if params is None: params = {} if type(recipients) == list: - recipients = ','.join(recipients) + recipients = ",".join(recipients) - params.update({'recipients': recipients, 'body': body}) - return VoiceMessage().load(self.request('voicemessages', 'POST', params)) + params.update({"recipients": recipients, "body": body}) + return VoiceMessage().load(self.request("voicemessages", "POST", params)) def lookup(self, phonenumber, params=None): """Do a new lookup.""" if params is None: params = {} - return Lookup().load(self.request('lookup/' + str(phonenumber), 'GET', params)) + return Lookup().load(self.request("lookup/" + str(phonenumber), "GET", params)) def lookup_hlr(self, phonenumber, params=None): """Retrieve the information of a specific HLR lookup.""" if params is None: params = {} - return HLR().load(self.request('lookup/' + str(phonenumber) + '/hlr', 'GET', params)) + return HLR().load(self.request("lookup/" + str(phonenumber) + "/hlr", "GET", params)) def lookup_hlr_create(self, phonenumber, params=None): """Perform a new HLR lookup.""" if params is None: params = {} - return HLR().load(self.request('lookup/' + str(phonenumber) + '/hlr', 'POST', params)) + return HLR().load(self.request("lookup/" + str(phonenumber) + "/hlr", "POST", params)) def verify(self, id): """Retrieve the information of a specific verification.""" - return Verify().load(self.request('verify/' + str(id))) + return Verify().load(self.request("verify/" + str(id))) def verify_create(self, recipient, params=None): """Create a new verification.""" if params is None: params = {} - params.update({'recipient': recipient}) - return Verify().load(self.request('verify', 'POST', params)) + params.update({"recipient": recipient}) + return Verify().load(self.request("verify", "POST", params)) def verify_create_email(self, recipient, originator, params=None): """Create a new email verification.""" if params is None: params = {} - params.update({ - 'type' : 'email', - 'recipient': recipient, - 'originator': originator - }) - return Verify().load(self.request('verify', 'POST', params)) + params.update({"type": "email", "recipient": recipient, "originator": originator}) + return Verify().load(self.request("verify", "POST", params)) def verify_verify(self, id, token): """Verify the token of a specific verification.""" - return Verify().load(self.request('verify/' + str(id), params={'token': token})) + return Verify().load(self.request("verify/" + str(id), params={"token": token})) def verify_delete(self, id): """Delete an existing verification object.""" - self.request_plain_text('verify/' + str(id), 'DELETE') + self.request_plain_text("verify/" + str(id), "DELETE") def contact(self, id): """Retrieve the information of a specific contact.""" - return Contact().load(self.request('contacts/' + str(id))) + return Contact().load(self.request("contacts/" + str(id))) def contact_create(self, phonenumber, params=None): if params is None: params = {} - params.update({'msisdn': phonenumber}) - return Contact().load(self.request('contacts', 'POST', params)) + params.update({"msisdn": phonenumber}) + return Contact().load(self.request("contacts", "POST", params)) def contact_delete(self, id): - self.request_plain_text('contacts/' + str(id), 'DELETE') + self.request_plain_text("contacts/" + str(id), "DELETE") def contact_update(self, id, params=None): - self.request_plain_text('contacts/' + str(id), 'PATCH', params) + self.request_plain_text("contacts/" + str(id), "PATCH", params) def contact_list(self, limit=10, offset=0): query = self._format_query(limit, offset) - return ContactList().load(self.request('contacts?' + query, 'GET', None)) + return ContactList().load(self.request("contacts?" + query, "GET", None)) def group(self, id): - return Group().load(self.request('groups/' + str(id), 'GET', None)) + return Group().load(self.request("groups/" + str(id), "GET", None)) def group_create(self, name, params=None): if params is None: params = {} - params.update({'name': name}) - return Group().load(self.request('groups', 'POST', params)) + params.update({"name": name}) + return Group().load(self.request("groups", "POST", params)) def group_delete(self, id): - self.request_plain_text('groups/' + str(id), 'DELETE', None) + self.request_plain_text("groups/" + str(id), "DELETE", None) def group_list(self, limit=10, offset=0): query = self._format_query(limit, offset) - return GroupList().load(self.request('groups?' + query, 'GET', None)) + return GroupList().load(self.request("groups?" + query, "GET", None)) def group_update(self, id, name, params=None): if params is None: params = {} - params.update({'name': name}) - self.request_plain_text('groups/' + str(id), 'PATCH', params) + params.update({"name": name}) + self.request_plain_text("groups/" + str(id), "PATCH", params) def group_add_contacts(self, groupId, contactIds): query = self.__group_add_contacts_query(contactIds) - self.request_plain_text('groups/' + str(groupId) + '?' + query, 'PUT', None) + self.request_plain_text("groups/" + str(groupId) + "?" + query, "PUT", None) def __group_add_contacts_query(self, contactIds): # __group_add_contacts_query gets a query string to add contacts to a # group. The expected format is ids[]=first-contact&ids[]=second-contact. # See: https://developers.messagebird.com/docs/groups#add-contact-to-group. - return '&'.join('ids[]=' + str(id) for id in contactIds) + return "&".join("ids[]=" + str(id) for id in contactIds) def group_remove_contact(self, groupId, contactId): - self.request_plain_text('groups/' + str(groupId) + '/contacts/' + str(contactId), 'DELETE', None) + self.request_plain_text("groups/" + str(groupId) + "/contacts/" + str(contactId), "DELETE", None) def conversation_list(self, limit=10, offset=0): - uri = CONVERSATION_PATH + '?' + self._format_query(limit, offset) - return ConversationList().load(self.request(uri, 'GET', None, CONVERSATION_TYPE)) + uri = CONVERSATION_PATH + "?" + self._format_query(limit, offset) + return ConversationList().load(self.request(uri, "GET", None, CONVERSATION_TYPE)) def conversation_start(self, start_request): - uri = CONVERSATION_PATH + '/start' - return Conversation().load(self.request(uri, 'POST', start_request, CONVERSATION_TYPE)) + uri = CONVERSATION_PATH + "/start" + return Conversation().load(self.request(uri, "POST", start_request, CONVERSATION_TYPE)) def conversation_update(self, id, update_request): - uri = CONVERSATION_PATH + '/' + str(id) - return Conversation().load(self.request(uri, 'PATCH', update_request, CONVERSATION_TYPE)) + uri = CONVERSATION_PATH + "/" + str(id) + return Conversation().load(self.request(uri, "PATCH", update_request, CONVERSATION_TYPE)) def conversation_read(self, id): - uri = CONVERSATION_PATH + '/' + str(id) - return Conversation().load(self.request(uri, 'GET', None, CONVERSATION_TYPE)) + uri = CONVERSATION_PATH + "/" + str(id) + return Conversation().load(self.request(uri, "GET", None, CONVERSATION_TYPE)) def conversation_list_messages(self, conversation_id, limit=10, offset=0): - uri = CONVERSATION_PATH + '/' + str(conversation_id) + '/' + CONVERSATION_MESSAGES_PATH - uri += '?' + self._format_query(limit, offset) + uri = CONVERSATION_PATH + "/" + str(conversation_id) + "/" + CONVERSATION_MESSAGES_PATH + uri += "?" + self._format_query(limit, offset) - return ConversationMessageList().load(self.request(uri, 'GET', None, CONVERSATION_TYPE)) + return ConversationMessageList().load(self.request(uri, "GET", None, CONVERSATION_TYPE)) def conversation_create_message(self, conversation_id, message_create_request): - uri = CONVERSATION_PATH + '/' + str(conversation_id) + '/' + CONVERSATION_MESSAGES_PATH - return ConversationMessage().load(self.request(uri, 'POST', message_create_request, CONVERSATION_TYPE)) + uri = CONVERSATION_PATH + "/" + str(conversation_id) + "/" + CONVERSATION_MESSAGES_PATH + return ConversationMessage().load(self.request(uri, "POST", message_create_request, CONVERSATION_TYPE)) def conversation_read_message(self, message_id): - uri = CONVERSATION_MESSAGES_PATH + '/' + str(message_id) - return ConversationMessage().load(self.request(uri, 'GET', None, CONVERSATION_TYPE)) + uri = CONVERSATION_MESSAGES_PATH + "/" + str(message_id) + return ConversationMessage().load(self.request(uri, "GET", None, CONVERSATION_TYPE)) def conversation_create_webhook(self, webhook_create_request): return ConversationWebhook().load( - self.request(CONVERSATION_WEB_HOOKS_PATH, 'POST', webhook_create_request, CONVERSATION_TYPE)) + self.request(CONVERSATION_WEB_HOOKS_PATH, "POST", webhook_create_request, CONVERSATION_TYPE) + ) def conversation_update_webhook(self, id, update_request): """ @@ -399,68 +397,68 @@ def conversation_update_webhook(self, id, update_request): API Reference: https://developers.messagebird.com/api/conversations/#webhooks """ - uri = CONVERSATION_WEB_HOOKS_PATH + '/' + str(id) - web_hook = self.request(uri, 'PATCH', update_request, CONVERSATION_TYPE) + uri = CONVERSATION_WEB_HOOKS_PATH + "/" + str(id) + web_hook = self.request(uri, "PATCH", update_request, CONVERSATION_TYPE) return ConversationWebhook().load(web_hook) def conversation_delete_webhook(self, id): - uri = CONVERSATION_WEB_HOOKS_PATH + '/' + str(id) - self.request(uri, 'DELETE', None, CONVERSATION_TYPE) + uri = CONVERSATION_WEB_HOOKS_PATH + "/" + str(id) + self.request(uri, "DELETE", None, CONVERSATION_TYPE) def conversation_list_webhooks(self, limit=10, offset=0): - uri = CONVERSATION_WEB_HOOKS_PATH + '?' + self._format_query(limit, offset) + uri = CONVERSATION_WEB_HOOKS_PATH + "?" + self._format_query(limit, offset) - return ConversationWebhookList().load(self.request(uri, 'GET', None, CONVERSATION_TYPE)) + return ConversationWebhookList().load(self.request(uri, "GET", None, CONVERSATION_TYPE)) def conversation_read_webhook(self, id): - uri = CONVERSATION_WEB_HOOKS_PATH + '/' + str(id) - return ConversationWebhook().load(self.request(uri, 'GET', None, CONVERSATION_TYPE)) + uri = CONVERSATION_WEB_HOOKS_PATH + "/" + str(id) + return ConversationWebhook().load(self.request(uri, "GET", None, CONVERSATION_TYPE)) def voice_recording_list_recordings(self, call_id, leg_id): uri = self.generate_voice_calls_url(call_id=call_id, leg_id=leg_id) - return VoiceRecordingsList().load(self.request(uri, 'GET')) + return VoiceRecordingsList().load(self.request(uri, "GET")) def voice_transcription_list(self, call_id, leg_id, recording_id): """List voice transcriptions.""" uri = self.generate_voice_calls_url(call_id, leg_id, recording_id) - return VoiceTranscriptionsList().load(self.request(uri, 'GET')) + return VoiceTranscriptionsList().load(self.request(uri, "GET")) def voice_transcription_download(self, call_id, leg_id, recording_id, transcriptions_file): """Download voice transcription file.""" - uri = self.generate_voice_calls_url(call_id, leg_id, recording_id) + '/' + str(transcriptions_file) - return self.request(uri, 'GET') + uri = self.generate_voice_calls_url(call_id, leg_id, recording_id) + "/" + str(transcriptions_file) + return self.request(uri, "GET") def voice_transcription_view(self, call_id, leg_id, recording_id, transcriptions_id): """Get voice transcription data.""" - uri = self.generate_voice_calls_url(call_id, leg_id, recording_id) + '/' + str(transcriptions_id) - return VoiceTranscriptionsView().load(self.request(uri, 'GET')) + uri = self.generate_voice_calls_url(call_id, leg_id, recording_id) + "/" + str(transcriptions_id) + return VoiceTranscriptionsView().load(self.request(uri, "GET")) def voice_transcription_create(self, call_id, leg_id, recording_id, language): """Create a voice transcription.""" uri = self.generate_voice_calls_url(call_id, leg_id, recording_id) - params = {'language': str(language)} - return VoiceTranscriptionsView().load(self.request(uri, 'POST', params, VOICE_TYPE)) + params = {"language": str(language)} + return VoiceTranscriptionsView().load(self.request(uri, "POST", params, VOICE_TYPE)) def voice_recording_view(self, call_id, leg_id, recording_id): - uri = self.generate_voice_calls_url(call_id=call_id, leg_id=leg_id) + '/' + str(recording_id) - recording_response = self.request(uri, 'GET') - recording_links = recording_response.get('_links') + uri = self.generate_voice_calls_url(call_id=call_id, leg_id=leg_id) + "/" + str(recording_id) + recording_response = self.request(uri, "GET") + recording_links = recording_response.get("_links") if recording_links is not None: - recording_response['data'][0]['_links'] = recording_links - return VoiceRecording().load(recording_response['data'][0]) + recording_response["data"][0]["_links"] = recording_links + return VoiceRecording().load(recording_response["data"][0]) def voice_recording_delete(self, call_id, leg_id, recording_id): - uri = self.generate_voice_calls_url(call_id=call_id, leg_id=leg_id) + '/' + str(recording_id) - recording_response = self.request(uri, 'DELETE', None, VOICE_TYPE) + uri = self.generate_voice_calls_url(call_id=call_id, leg_id=leg_id) + "/" + str(recording_id) + recording_response = self.request(uri, "DELETE", None, VOICE_TYPE) def voice_recording_download(self, call_id, leg_id, recording_id): - uri = self.generate_voice_calls_url(call_id=call_id, leg_id=leg_id) + '/' + str(recording_id) - recording_response = self.request(uri, 'GET') - recording_links = recording_response.get('_links') - if recording_links is None or recording_links.get('file') is None: - raise (ErrorException('There is no recording available')) - recording_file = recording_links.get('file') - recording_file = self.request_store_as_file(VOICE_API_ROOT + recording_file, recording_id + '.wav') + uri = self.generate_voice_calls_url(call_id=call_id, leg_id=leg_id) + "/" + str(recording_id) + recording_response = self.request(uri, "GET") + recording_links = recording_response.get("_links") + if recording_links is None or recording_links.get("file") is None: + raise (ErrorException("There is no recording available")) + recording_file = recording_links.get("file") + recording_file = self.request_store_as_file(VOICE_API_ROOT + recording_file, recording_id + ".wav") return VOICE_API_ROOT + recording_file def voice_read_webhook(self, id): @@ -468,94 +466,100 @@ def voice_read_webhook(self, id): Retrieve a voice webhook API Reference: https://developers.messagebird.com/api/voice-calling/#webhooks """ - uri = VOICE_API_ROOT + '/' + VOICE_WEB_HOOKS_PATH + '/' + str(id) - return VoiceWebhook().load(self.request(uri, 'GET', None, VOICE_TYPE)) + uri = VOICE_API_ROOT + "/" + VOICE_WEB_HOOKS_PATH + "/" + str(id) + return VoiceWebhook().load(self.request(uri, "GET", None, VOICE_TYPE)) def voice_list_webhooks(self, limit=10, offset=0): - """ Retrieve a list of voice webhooks. """ - uri = VOICE_API_ROOT + '/' + VOICE_WEB_HOOKS_PATH + '?' + self._format_query(limit, offset) - return VoiceWebhookList().load(self.request(uri, 'GET', None, VOICE_TYPE)) + """Retrieve a list of voice webhooks.""" + uri = VOICE_API_ROOT + "/" + VOICE_WEB_HOOKS_PATH + "?" + self._format_query(limit, offset) + return VoiceWebhookList().load(self.request(uri, "GET", None, VOICE_TYPE)) def voice_create_webhook(self, create_webhook_request): - """ Create a voice webhook. """ + """Create a voice webhook.""" if create_webhook_request is None: - raise ValidationError('Create request is empty') + raise ValidationError("Create request is empty") - uri = VOICE_API_ROOT + '/' + VOICE_WEB_HOOKS_PATH - return VoiceWebhook().load(self.request(uri, 'POST', create_webhook_request.__dict__(), VOICE_TYPE)) + uri = VOICE_API_ROOT + "/" + VOICE_WEB_HOOKS_PATH + return VoiceWebhook().load(self.request(uri, "POST", create_webhook_request.__dict__(), VOICE_TYPE)) def voice_update_webhook(self, id, update_webhook_request): - """ Update a voice webhook. """ + """Update a voice webhook.""" if update_webhook_request is None: - raise ValidationError('Update request is empty') + raise ValidationError("Update request is empty") - uri = VOICE_API_ROOT + '/' + VOICE_WEB_HOOKS_PATH + '/' + str(id) - return VoiceWebhook().load(self.request(uri, 'PUT', update_webhook_request.__dict__(), VOICE_TYPE)) + uri = VOICE_API_ROOT + "/" + VOICE_WEB_HOOKS_PATH + "/" + str(id) + return VoiceWebhook().load(self.request(uri, "PUT", update_webhook_request.__dict__(), VOICE_TYPE)) def voice_delete_webhook(self, id): - """ Delete a voice webhook. """ - uri = VOICE_API_ROOT + '/' + VOICE_WEB_HOOKS_PATH + '/' + str(id) - self.request(uri, 'DELETE', None, VOICE_TYPE) + """Delete a voice webhook.""" + uri = VOICE_API_ROOT + "/" + VOICE_WEB_HOOKS_PATH + "/" + str(id) + self.request(uri, "DELETE", None, VOICE_TYPE) def call_flow(self, id): - return CallFlow().load(self.request('call-flows/' + str(id), 'GET', None, VOICE_TYPE)) + return CallFlow().load(self.request("call-flows/" + str(id), "GET", None, VOICE_TYPE)) def call_flow_list(self, limit=10, offset=0): query = self._format_query(limit, offset) - return CallFlowList().load(self.request('call-flows?' + query, 'GET', None, VOICE_TYPE)) + return CallFlowList().load(self.request("call-flows?" + query, "GET", None, VOICE_TYPE)) def call_flow_create(self, title, steps, default=False, record=False): - params = {'title': title, 'steps': steps, 'default': default, 'record': record} - return CallFlow().load(self.request('call-flows', 'POST', params, VOICE_TYPE)) + params = {"title": title, "steps": steps, "default": default, "record": record} + return CallFlow().load(self.request("call-flows", "POST", params, VOICE_TYPE)) def call_flow_update(self, id, title, steps, default, record): - params = {'title': title, 'steps': steps, 'default': default, 'record': record} - return CallFlow().load(self.request('call-flows/' + str(id), 'PUT', params, VOICE_TYPE)) + params = {"title": title, "steps": steps, "default": default, "record": record} + return CallFlow().load(self.request("call-flows/" + str(id), "PUT", params, VOICE_TYPE)) def call_flow_delete(self, id): - self.request_plain_text('call-flows/' + str(id), 'DELETE', None, VOICE_TYPE) + self.request_plain_text("call-flows/" + str(id), "DELETE", None, VOICE_TYPE) def call_flow_numbers_list(self, call_flow_id): return CallFlowNumberList().load( - self.request('call-flows/' + str(call_flow_id) + '/numbers', 'GET', None, VOICE_TYPE)) + self.request("call-flows/" + str(call_flow_id) + "/numbers", "GET", None, VOICE_TYPE) + ) def call_flow_numbers_add(self, call_flow_id, numbers=()): - params = {'numbers': numbers} + params = {"numbers": numbers} return CallFlowNumberList().load( - self.request('call-flows/' + str(call_flow_id) + '/numbers', 'POST', params, VOICE_TYPE)) + self.request("call-flows/" + str(call_flow_id) + "/numbers", "POST", params, VOICE_TYPE) + ) def _format_query(self, limit, offset): - return 'limit=' + str(limit) + '&offset=' + str(offset) + return "limit=" + str(limit) + "&offset=" + str(offset) def available_numbers_list(self, country, params={}, limit=20, offset=0): """Retrieve a list of phone numbers available for purchase.""" - params['limit'] = limit - params['offset'] = offset - return NumberList().load(self.request(NUMBER_AVAILABLE_PATH + '/' + str(country), 'GET', params, NUMBER_TYPE)) + params["limit"] = limit + params["offset"] = offset + return NumberList().load(self.request(NUMBER_AVAILABLE_PATH + "/" + str(country), "GET", params, NUMBER_TYPE)) def purchase_number(self, number, country, billingIntervalMonths=1): - params = {'number': str(number), 'countryCode': str(country), 'billingIntervalMonths': int(billingIntervalMonths)} - return Number().load(self.request(NUMBER_PATH, 'POST', params, NUMBER_TYPE)) + params = { + "number": str(number), + "countryCode": str(country), + "billingIntervalMonths": int(billingIntervalMonths), + } + return Number().load(self.request(NUMBER_PATH, "POST", params, NUMBER_TYPE)) def update_number(self, number, tags): - params = {'tags': tags} - return Number().load(self.request(NUMBER_PATH + '/' + str(number), 'PATCH', params, NUMBER_TYPE)) + params = {"tags": tags} + return Number().load(self.request(NUMBER_PATH + "/" + str(number), "PATCH", params, NUMBER_TYPE)) def delete_number(self, number): - self.request(NUMBER_PATH + '/' + str(number), 'DELETE', None, NUMBER_TYPE) + self.request(NUMBER_PATH + "/" + str(number), "DELETE", None, NUMBER_TYPE) def purchased_numbers_list(self, params={}, limit=20, offset=0): - params['limit'] = limit - params['offset'] = offset - return NumberList().load(self.request(NUMBER_PATH, 'GET', params, NUMBER_TYPE)) + params["limit"] = limit + params["offset"] = offset + return NumberList().load(self.request(NUMBER_PATH, "GET", params, NUMBER_TYPE)) def purchased_number(self, number): - return Number().load(self.request(NUMBER_PATH + '/' + number, 'GET', None, NUMBER_TYPE)) + return Number().load(self.request(NUMBER_PATH + "/" + number, "GET", None, NUMBER_TYPE)) @staticmethod def generate_voice_calls_url(call_id=None, leg_id=None, recording_id=None): - uri = VOICE_API_ROOT + '/' + VOICE_PATH + '/' - uri += str(call_id) + '/' + VOICE_LEGS_PATH + '/' + str(leg_id) + '/' + VOICE_RECORDINGS_PATH + uri = VOICE_API_ROOT + "/" + VOICE_PATH + "/" + uri += str(call_id) + "/" + VOICE_LEGS_PATH + "/" + str(leg_id) + "/" + VOICE_RECORDINGS_PATH if recording_id: - uri += '/' + str(recording_id) + '/' + VOICE_TRANSCRIPTIONS_PATH + uri += "/" + str(recording_id) + "/" + VOICE_TRANSCRIPTIONS_PATH return uri From fdb4e3175fcd611b8ad42cce27b341d61fb96c88 Mon Sep 17 00:00:00 2001 From: sgiraldo Date: Tue, 15 Feb 2022 14:49:19 -0500 Subject: [PATCH 2/3] feat: whatsapp template list object mapping and conversation send method --- messagebird/client.py | 24 ++++++++++++++++++++++ messagebird/whatsapp_template.py | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 messagebird/whatsapp_template.py diff --git a/messagebird/client.py b/messagebird/client.py index c165aff..3ecfedf 100644 --- a/messagebird/client.py +++ b/messagebird/client.py @@ -23,6 +23,7 @@ from messagebird.voice_transcription import VoiceTranscriptionsList, VoiceTranscriptionsView from messagebird.call_flow import CallFlow, CallFlowList, CallFlowNumberList from messagebird.number import Number, NumberList +from messagebird.whatsapp_template import WhatsAppTemplateList ENDPOINT = "https://rest.messagebird.com" CLIENT_VERSION = "2.0.0" @@ -34,6 +35,7 @@ CONVERSATION_PATH = "conversations" CONVERSATION_MESSAGES_PATH = "messages" CONVERSATION_WEB_HOOKS_PATH = "webhooks" +CONVERSATION_SEND_PATH = "send" CONVERSATION_TYPE = "conversation" VOICE_API_ROOT = "https://voice.messagebird.com" @@ -49,6 +51,10 @@ NUMBER_PATH = "phone-numbers" NUMBER_AVAILABLE_PATH = "available-phone-numbers" +INTEGRATION_TYPE = "integration" +INTEGRATION_API_ROOT = "https://integrations.messagebird.com/v3/" +WHATSAPP_TEMPLATE_PATH = "platforms/whatsapp/templates" + class ErrorException(Exception): def __init__(self, errors): @@ -80,6 +86,9 @@ def _get_http_client(self, type=REST_TYPE): if type == NUMBER_TYPE: return HttpClient(NUMBER_API_ROOT, self.access_key, USER_AGENT) + if type == INTEGRATION_TYPE: + return HttpClient(INTEGRATION_API_ROOT, self.access_key, USER_AGENT) + return HttpClient(ENDPOINT, self.access_key, USER_AGENT) def request(self, path, method="GET", params=None, type=REST_TYPE): @@ -386,6 +395,18 @@ def conversation_read_message(self, message_id): uri = CONVERSATION_MESSAGES_PATH + "/" + str(message_id) return ConversationMessage().load(self.request(uri, "GET", None, CONVERSATION_TYPE)) + def conversation_send(self, params={}): + """ + This method sends a message via /conversations/send endpoint. + + Reference: https://developers.messagebird.com/api/conversations/#send-message + """ + assert "to" in params, "to is required" + assert "from" in params, "from is required" + assert "type" in params, "type is required" + assert "content" in params, "content is required" + return self.request(CONVERSATION_SEND_PATH, "POST", params, CONVERSATION_TYPE) + def conversation_create_webhook(self, webhook_create_request): return ConversationWebhook().load( self.request(CONVERSATION_WEB_HOOKS_PATH, "POST", webhook_create_request, CONVERSATION_TYPE) @@ -556,6 +577,9 @@ def purchased_numbers_list(self, params={}, limit=20, offset=0): def purchased_number(self, number): return Number().load(self.request(NUMBER_PATH + "/" + number, "GET", None, NUMBER_TYPE)) + def whatsapp_template_list(self): + return WhatsAppTemplateList().load(self.request(WHATSAPP_TEMPLATE_PATH, "GET", None, INTEGRATION_TYPE)) + @staticmethod def generate_voice_calls_url(call_id=None, leg_id=None, recording_id=None): uri = VOICE_API_ROOT + "/" + VOICE_PATH + "/" diff --git a/messagebird/whatsapp_template.py b/messagebird/whatsapp_template.py new file mode 100644 index 0000000..7a87578 --- /dev/null +++ b/messagebird/whatsapp_template.py @@ -0,0 +1,35 @@ +from messagebird.base import Base +from messagebird.base_list import BaseList + + +class WhatsAppTemplateList(BaseList): + def __init__(self): + # We're expecting items of type WhatsAppTemplate + super(WhatsAppTemplateList, self).__init__(WhatsAppTemplate) + + +class WhatsAppTemplate(Base): + def __init__(self): + self.name: str = None + self.language: str = None # TODO: HSMLanguage object + self.category: str = None # TODO: HSMCategory object + self.components: list = None # TODO: list of HSMComponent objects + self.status: str = None # TODO: HSMStatus object + self._createdDatetime = None + self._updatedDatetime = None + + @property + def createdDatetime(self): + return self._createdDatetime + + @createdDatetime.setter + def createdDatetime(self, value): + self._createdDatetime = self.value_to_time(value) + + @property + def updatedDatetime(self): + return self._updatedDatetime + + @updatedDatetime.setter + def updatedDatetime(self, value): + self._updatedDatetime = self.value_to_time(value) From 04b3c30d4169942185be65c25512f5edbae1de45 Mon Sep 17 00:00:00 2001 From: sgiraldo Date: Tue, 15 Feb 2022 17:53:33 -0500 Subject: [PATCH 3/3] fix: integration type to support v2 and v3 fix: if api returns a list enclose it in dict with items key feat: add list whatsapp template by name feat: add fetch whatsapp template by name and language --- messagebird/client.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/messagebird/client.py b/messagebird/client.py index 3ecfedf..4735960 100644 --- a/messagebird/client.py +++ b/messagebird/client.py @@ -23,7 +23,7 @@ from messagebird.voice_transcription import VoiceTranscriptionsList, VoiceTranscriptionsView from messagebird.call_flow import CallFlow, CallFlowList, CallFlowNumberList from messagebird.number import Number, NumberList -from messagebird.whatsapp_template import WhatsAppTemplateList +from messagebird.whatsapp_template import WhatsAppTemplate, WhatsAppTemplateList ENDPOINT = "https://rest.messagebird.com" CLIENT_VERSION = "2.0.0" @@ -51,8 +51,11 @@ NUMBER_PATH = "phone-numbers" NUMBER_AVAILABLE_PATH = "available-phone-numbers" -INTEGRATION_TYPE = "integration" -INTEGRATION_API_ROOT = "https://integrations.messagebird.com/v3/" +INTEGRATION_TYPE_V2 = "integration_v2" +INTEGRATION_TYPE_V3 = "integration_v3" +INTEGRATION_API_ROOT = "https://integrations.messagebird.com" +INTEGRATION_API_ROOT_V2 = f"{INTEGRATION_API_ROOT}/v2/" +INTEGRATION_API_ROOT_V3 = f"{INTEGRATION_API_ROOT}/v3/" WHATSAPP_TEMPLATE_PATH = "platforms/whatsapp/templates" @@ -86,8 +89,11 @@ def _get_http_client(self, type=REST_TYPE): if type == NUMBER_TYPE: return HttpClient(NUMBER_API_ROOT, self.access_key, USER_AGENT) - if type == INTEGRATION_TYPE: - return HttpClient(INTEGRATION_API_ROOT, self.access_key, USER_AGENT) + if type == INTEGRATION_TYPE_V2: + return HttpClient(INTEGRATION_API_ROOT_V2, self.access_key, USER_AGENT) + + if type == INTEGRATION_TYPE_V3: + return HttpClient(INTEGRATION_API_ROOT_V3, self.access_key, USER_AGENT) return HttpClient(ENDPOINT, self.access_key, USER_AGENT) @@ -99,6 +105,9 @@ def request(self, path, method="GET", params=None, type=REST_TYPE): response_json = json.loads(response_text) + if isinstance(response_json, list): + response_json = dict(items=response_json) + if "errors" in response_json: raise (ErrorException([Error().load(e) for e in response_json["errors"]])) @@ -578,7 +587,15 @@ def purchased_number(self, number): return Number().load(self.request(NUMBER_PATH + "/" + number, "GET", None, NUMBER_TYPE)) def whatsapp_template_list(self): - return WhatsAppTemplateList().load(self.request(WHATSAPP_TEMPLATE_PATH, "GET", None, INTEGRATION_TYPE)) + return WhatsAppTemplateList().load(self.request(WHATSAPP_TEMPLATE_PATH, "GET", None, INTEGRATION_TYPE_V3)) + + def whatsapp_template_list_by_name(self, name): + endpoint = WHATSAPP_TEMPLATE_PATH + "/" + name + return WhatsAppTemplateList().load(self.request(endpoint, "GET", None, INTEGRATION_TYPE_V2)) + + def whatsapp_template(self, name, language): + endpoint = WHATSAPP_TEMPLATE_PATH + "/" + name + "/" + language + return WhatsAppTemplate().load(self.request(endpoint, "GET", None, INTEGRATION_TYPE_V2)) @staticmethod def generate_voice_calls_url(call_id=None, leg_id=None, recording_id=None):