diff --git a/examples/conversation_create_message.py b/examples/conversation_create_message.py new file mode 100644 index 0000000..38b0dc3 --- /dev/null +++ b/examples/conversation_create_message.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +import argparse +import messagebird +from messagebird.conversation_message import MESSAGE_TYPE_TEXT + +parser = argparse.ArgumentParser() +parser.add_argument('--accessKey', help='access key for MessageBird API', type=str, required=True) +parser.add_argument('--conversationId', help='conversation ID that you want to create a message for', type=str, + required=True) +parser.add_argument('--channelId', help='channel ID that you want to create a message for', type=str, required=True) +parser.add_argument('--message', help='message that you want to send', type=str, required=True) +args = vars(parser.parse_args()) + +try: + client = messagebird.Client(args['accessKey']) + + msg = client.conversation_create_message(args['conversationId'], + {'channelId': args['channelId'], 'type': MESSAGE_TYPE_TEXT, + 'content': {'text': args['message']}}) + + # Print the object information. + print('The following information was returned as a Conversation List object:') + print(msg) + +except messagebird.client.ErrorException as e: + print('An error occured while requesting a Message object:') + + for error in e.errors: + print(' code : %d' % error.code) + print(' description : %s' % error.description) + print(' parameter : %s\n' % error.parameter) diff --git a/examples/conversation_create_webhook.py b/examples/conversation_create_webhook.py new file mode 100644 index 0000000..abc459b --- /dev/null +++ b/examples/conversation_create_webhook.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +import argparse +import messagebird +from messagebird.conversation_webhook import \ + CONVERSATION_WEBHOOK_EVENT_CONVERSATION_CREATED, \ + CONVERSATION_WEBHOOK_EVENT_CONVERSATION_UPDATED + +parser = argparse.ArgumentParser() +parser.add_argument('--accessKey', help='access key for MessageBird API', type=str, required=True) +parser.add_argument('--channelId', help='channel that you want create the webhook', type=str, required=True) +parser.add_argument('--url', help='url for the webhook', type=str) +args = vars(parser.parse_args()) + +try: + client = messagebird.Client(args['accessKey']) + + webhook = client.conversation_create_webhook({ + 'channelId': args['channelId'], + 'events': [CONVERSATION_WEBHOOK_EVENT_CONVERSATION_CREATED, CONVERSATION_WEBHOOK_EVENT_CONVERSATION_UPDATED], + 'url': args['url'] + }) + + # Print the object information. + print('The following information was returned as a Webhook object:') + print(webhook) + +except messagebird.client.ErrorException as e: + print('An error occured while requesting a Webhook object:') + + for error in e.errors: + print(' code : %d' % error.code) + print(' description : %s' % error.description) + print(' parameter : %s\n' % error.parameter) diff --git a/examples/conversation_delete_webhook.py b/examples/conversation_delete_webhook.py new file mode 100644 index 0000000..4f4a21d --- /dev/null +++ b/examples/conversation_delete_webhook.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +import argparse +import messagebird + +parser = argparse.ArgumentParser() +parser.add_argument('--accessKey', help='access key for MessageBird API', type=str, required=True) +parser.add_argument('--webhookId', help='webhook that you want to delete', type=str, required=True) +args = vars(parser.parse_args()) + +try: + client = messagebird.Client(args['accessKey']) + + client.conversation_delete_webhook(args['webhookId']) + + # Print the object information. + print('Webhook has been deleted') + +except messagebird.client.ErrorException as e: + print('An error occured while requesting a Webhook object:') + + for error in e.errors: + print(' code : %d' % error.code) + print(' description : %s' % error.description) + print(' parameter : %s\n' % error.parameter) diff --git a/examples/conversation_list.py b/examples/conversation_list.py new file mode 100644 index 0000000..991d0d2 --- /dev/null +++ b/examples/conversation_list.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +import messagebird +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument('--accessKey', help='access key for MessageBird API', type=str, required=True) +args = vars(parser.parse_args()) + +try: + client = messagebird.Client(args['accessKey']) + + conversationList = client.conversation_list() + + # Print the object information. + print('The following information was returned as a Conversation List object:') + print(conversationList) + +except messagebird.client.ErrorException as e: + print('An error occured while requesting a Message object:') + + for error in e.errors: + print(' code : %d' % error.code) + print(' description : %s' % error.description) + print(' parameter : %s\n' % error.parameter) diff --git a/examples/conversation_list_messages.py b/examples/conversation_list_messages.py new file mode 100644 index 0000000..7c58d3a --- /dev/null +++ b/examples/conversation_list_messages.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +import argparse +import messagebird + +parser = argparse.ArgumentParser() +parser.add_argument('--accessKey', help='access key for MessageBird API', type=str, required=True) +parser.add_argument('--conversationId', help='conversation that you want the list of messages', type=str, required=True) +args = vars(parser.parse_args()) + +try: + client = messagebird.Client(args['accessKey']) + + msg = client.conversation_list_messages(args['conversationId']) + + # Print the object information. + print('The following information was returned as a Conversation Message List object:') + print(msg) + +except messagebird.client.ErrorException as e: + print('An error occured while requesting a Conversation Message List object:') + + for error in e.errors: + print(' code : %d' % error.code) + print(' description : %s' % error.description) + print(' parameter : %s\n' % error.parameter) diff --git a/examples/conversation_list_webhook.py b/examples/conversation_list_webhook.py new file mode 100644 index 0000000..12a2e72 --- /dev/null +++ b/examples/conversation_list_webhook.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +import argparse +import messagebird + +parser = argparse.ArgumentParser() +parser.add_argument('--accessKey', help='access key for MessageBird API', type=str, required=True) +args = vars(parser.parse_args()) + +try: + client = messagebird.Client(args['accessKey']) + + webhook_list = client.conversation_list_webhooks() + + # Print the object information. + print('The following information was returned as a Conversation Webhook List object:') + print(webhook_list) + +except messagebird.client.ErrorException as e: + print('An error occured while requesting a Conversation Webhook List object:') + + for error in e.errors: + print(' code : %d' % error.code) + print(' description : %s' % error.description) + print(' parameter : %s\n' % error.parameter) diff --git a/examples/conversation_read.py b/examples/conversation_read.py new file mode 100644 index 0000000..71260d6 --- /dev/null +++ b/examples/conversation_read.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +import messagebird +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument('--accessKey', help='access key for MessageBird API', type=str, required=True) +parser.add_argument('--conversationId', help='conversation that you want the message list', type=str, required=True) +args = vars(parser.parse_args()) + +try: + client = messagebird.Client(args['accessKey']) + + conversation = client.conversation_read(args['conversationId']) + + # Print the object information. + print('The following information was returned as a Conversation object:') + print(conversation) + +except messagebird.client.ErrorException as e: + print('An error occured while requesting a Conversation object:') + + for error in e.errors: + print(' code : %d' % error.code) + print(' description : %s' % error.description) + print(' parameter : %s\n' % error.parameter) diff --git a/examples/conversation_read_message.py b/examples/conversation_read_message.py new file mode 100644 index 0000000..afe1841 --- /dev/null +++ b/examples/conversation_read_message.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +import argparse +import messagebird + +parser = argparse.ArgumentParser() +parser.add_argument('--accessKey', help='access key for MessageBird API', type=str, required=True) +parser.add_argument('--messageId', help='message that you want to read', type=str, required=True) +args = vars(parser.parse_args()) + +try: + client = messagebird.Client(args['accessKey']) + + msg = client.conversation_read_message(args['messageId']) + + # Print the object information. + print('The following information was returned as a Conversation List object:') + print(msg) + +except messagebird.client.ErrorException as e: + print('An error occured while requesting a Message object:') + + for error in e.errors: + print(' code : %d' % error.code) + print(' description : %s' % error.description) + print(' parameter : %s\n' % error.parameter) diff --git a/examples/conversation_read_webhook.py b/examples/conversation_read_webhook.py new file mode 100644 index 0000000..3282787 --- /dev/null +++ b/examples/conversation_read_webhook.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +import messagebird +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument('--accessKey', help='access key for MessageBird API', type=str, required=True) +parser.add_argument('--webhookId', help='webhook that you want to read', type=str, required=True) +args = vars(parser.parse_args()) + +try: + client = messagebird.Client(args['accessKey']) + + webhook = client.conversation_read_webhook(args['webhookId']) + + # Print the object information. + print('The following information was returned as a Webhook object:') + print(webhook) + +except messagebird.client.ErrorException as e: + print('An error occured while requesting a Webhook object:') + + for error in e.errors: + print(' code : %d' % error.code) + print(' description : %s' % error.description) + print(' parameter : %s\n' % error.parameter) diff --git a/examples/conversation_start.py b/examples/conversation_start.py new file mode 100644 index 0000000..b4973da --- /dev/null +++ b/examples/conversation_start.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +import argparse +import messagebird +from messagebird.conversation_message import MESSAGE_TYPE_TEXT + +parser = argparse.ArgumentParser() +parser.add_argument('--accessKey', help='access key for MessageBird API', type=str, required=True) +parser.add_argument('--channelId', help='channel that you want to start a conversation', type=str, required=True) +parser.add_argument('--phoneNumber', help='phone number that you want to send a message', type=str, required=True) +parser.add_argument('--textMessage', help='text that you want to send', type=str, required=True) +args = vars(parser.parse_args()) + +try: + client = messagebird.Client(args['accessKey']) + + msg = client.conversation_start( + {'channelId': args['channelId'], 'to': args['phoneNumber'], 'type': MESSAGE_TYPE_TEXT, + 'content': {'text': args['textMessage']}}) + + # Print the object information. + print('The following information was returned as a Conversation object:') + print(msg) + +except messagebird.client.ErrorException as e: + print('An error occured while requesting a Message object:') + + for error in e.errors: + print(' code : %d' % error.code) + print(' description : %s' % error.description) + print(' parameter : %s\n' % error.parameter) diff --git a/examples/conversation_update.py b/examples/conversation_update.py new file mode 100644 index 0000000..ffbca12 --- /dev/null +++ b/examples/conversation_update.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +import argparse +import messagebird + +parser = argparse.ArgumentParser() +parser.add_argument('--accessKey', help='access key for MessageBird API', type=str, required=True) +parser.add_argument('--conversationId', help='conversation ID that you want to update', type=str, required=True) +args = vars(parser.parse_args()) + +try: + client = messagebird.Client(args['accessKey']) + + conversation = client.conversation_update(args['conversationId'], {'status': 'active'}) + + # Print the object information. + print('The following information was returned as a Conversation object:') + print(conversation) + +except messagebird.client.ErrorException as e: + print('An error occured while requesting a Conversation object:') + + for error in e.errors: + print(' code : %d' % error.code) + print(' description : %s' % error.description) + print(' parameter : %s\n' % error.parameter) diff --git a/messagebird/base.py b/messagebird/base.py index 32a1906..ee88e8e 100644 --- a/messagebird/base.py +++ b/messagebird/base.py @@ -8,6 +8,13 @@ def load(self, data): return self - def value_to_time(self, value): + def strip_nanoseconds_from_date(self, value): + if str(value).find(".") != -1: + return value[:-11] + value[-1:] + + return value + + def value_to_time(self, value, format='%Y-%m-%dT%H:%M:%S+00:00'): if value != None: - return datetime.strptime(value, '%Y-%m-%dT%H:%M:%S+00:00') + value = self.strip_nanoseconds_from_date(value) + return datetime.strptime(value, format) diff --git a/messagebird/client.py b/messagebird/client.py index 9803e54..5b8b9c8 100644 --- a/messagebird/client.py +++ b/messagebird/client.py @@ -1,187 +1,255 @@ import sys import json -from messagebird.base import Base -from messagebird.balance import Balance -from messagebird.contact import Contact, ContactList -from messagebird.error import Error -from messagebird.group import ContactReference, Group, GroupList -from messagebird.hlr import HLR -from messagebird.http_client import HttpClient -from messagebird.message import Message +from messagebird.balance import Balance +from messagebird.contact import Contact, ContactList +from messagebird.error import Error +from messagebird.group import Group, GroupList +from messagebird.hlr import HLR +from messagebird.message import Message from messagebird.voicemessage import VoiceMessage -from messagebird.lookup import Lookup -from messagebird.verify import Verify - -ENDPOINT = 'https://rest.messagebird.com' +from messagebird.lookup import Lookup +from messagebird.verify import Verify +from messagebird.http_client import HttpClient +from messagebird.conversation_message import ConversationMessage, ConversationMessageList +from messagebird.conversation import Conversation, ConversationList +from messagebird.conversation_webhook import ConversationWebhook, ConversationWebhookList + +ENDPOINT = 'https://rest.messagebird.com' CLIENT_VERSION = '1.3.1' 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' class ErrorException(Exception): - def __init__(self, errors): - self.errors = errors - message = ' '.join([str(e) for e in self.errors]) - super(ErrorException, self).__init__(message) + def __init__(self, errors): + self.errors = errors + message = ' '.join([str(e) for e in self.errors]) + super(ErrorException, self).__init__(message) class Client(object): - def __init__(self, access_key, http_client=None): - self.access_key = access_key - - if http_client is None: - self.http_client = HttpClient(ENDPOINT, access_key, USER_AGENT) - else: - self.http_client = http_client - - def request(self, path, method='GET', params=None): - """Builds a request, gets a response and decodes it.""" - response_text = self.http_client.request(path, method, params) - response_json = json.loads(response_text) - - 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): - """Builds a request, gets a response and returns the body.""" - response_text = self.http_client.request(path, method, params) - - try: - # Try to decode the response to JSON to see if the API returned any - # errors. - response_json = json.loads(response_text) - - 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. - pass - - return response_text - - def balance(self): - """Retrieve your balance.""" - return Balance().load(self.request('balance')) - - def hlr(self, id): - """Retrieve the information of a specific HLR lookup.""" - 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 })) - - def message(self, id): - """Retrieve the information of a specific message.""" - return Message().load(self.request('messages/' + str(id))) - - 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) - - 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') - - def voice_message(self, id): - "Retrieve the information of a specific voice message." - return VoiceMessage().load(self.request('voicemessages/' + str(id))) - - 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) - - 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)) - - 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)) - - 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)) - - def verify(self, id): - """Retrieve the information of a specific verification.""" - 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)) - - def verify_verify(self, id, token): - """Verify the token of a specific verification.""" - return Verify().load(self.request('verify/' + str(id), params={'token': token})) - - def contact(self, id): - """Retrieve the information of a specific contact.""" - 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)) - - def contact_delete(self, id): - self.request_plain_text('contacts/' + str(id), 'DELETE') - - def contact_update(self, id, params=None): - self.request_plain_text('contacts/' + str(id), 'PATCH', params) - - def contact_list(self, limit=10, offset=0): - query = 'limit='+str(limit)+'&offset='+str(offset) - return ContactList().load(self.request('contacts?'+query, 'GET', None)) - - def group(self, id): - 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)) - - def group_delete(self, id): - self.request_plain_text('groups/' + str(id), 'DELETE', None) - - def group_list(self, limit=10, offset=0): - query = 'limit=' + str(limit) + '&offset=' + str(offset) - 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) - - def group_add_contacts(self, groupId, contactIds): - query = self.__group_add_contacts_query(contactIds) - 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) - - def group_remove_contact(self, groupId, contactId): - self.request_plain_text('groups/' + str(groupId) + '/contacts/' + str(contactId), 'DELETE', None) + def __init__(self, access_key, http_client=None): + self.access_key = access_key + self.http_client = http_client + + def _get_http_client(self, type=REST_TYPE): + if self.http_client: + return self.http_client + + if type == REST_TYPE: + return HttpClient(ENDPOINT, self.access_key, USER_AGENT) + + return HttpClient(CONVERSATION_API_ROOT, self.access_key, USER_AGENT) + + 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: + return response_text + + response_json = json.loads(response_text) + + 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): + """Builds a request, gets a response and returns the body.""" + response_text = self._get_http_client(type).request(path, method, params) + + try: + # Try to decode the response to JSON to see if the API returned any + # errors. + response_json = json.loads(response_text) + + 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. + pass + + return response_text + + def balance(self): + """Retrieve your balance.""" + return Balance().load(self.request('balance')) + + def hlr(self, id): + """Retrieve the information of a specific HLR lookup.""" + 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})) + + def message(self, id): + """Retrieve the information of a specific message.""" + return Message().load(self.request('messages/' + str(id))) + + 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) + + 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') + + def voice_message(self, id): + "Retrieve the information of a specific voice message." + return VoiceMessage().load(self.request('voicemessages/' + str(id))) + + 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) + + 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)) + + 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)) + + 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)) + + def verify(self, id): + """Retrieve the information of a specific verification.""" + 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)) + + def verify_verify(self, id, token): + """Verify the token of a specific verification.""" + return Verify().load(self.request('verify/' + str(id), params={'token': token})) + + def contact(self, id): + """Retrieve the information of a specific contact.""" + 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)) + + def contact_delete(self, id): + self.request_plain_text('contacts/' + str(id), 'DELETE') + + def contact_update(self, id, params=None): + 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)) + + def group(self, id): + 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)) + + def group_delete(self, id): + 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)) + + 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) + + def group_add_contacts(self, groupId, contactIds): + query = self.__group_add_contacts_query(contactIds) + 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) + + def group_remove_contact(self, groupId, contactId): + 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)) + + def conversation_start(self, start_request): + 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)) + + def conversation_read(self, id): + 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) + + 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)) + + 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_create_webhook(self, webhook_create_request): + return ConversationWebhook().load( + self.request(CONVERSATION_WEB_HOOKS_PATH, 'POST', webhook_create_request, CONVERSATION_TYPE)) + + def conversation_delete_webhook(self, id): + 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) + + 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)) + + def _format_query(self, limit, offset): + return 'limit=' + str(limit) + '&offset=' + str(offset) diff --git a/messagebird/conversation.py b/messagebird/conversation.py new file mode 100644 index 0000000..d62dce6 --- /dev/null +++ b/messagebird/conversation.py @@ -0,0 +1,120 @@ +from messagebird.base import Base +from messagebird.conversation_contact import ConversationContact +from messagebird.conversation_channel import Channel +from messagebird.conversation_message import ConversationMessageReference + +CONVERSATION_STATUS_ACTIVE = "active" +CONVERSATION_STATUS_ARCHIVED = "archived" + + +class Conversation(Base): + + def __init__(self): + self.id = None + self.contactId = None + self._contact = None + self.lastUsedChannelId = None + self._channels = None + self._messages = None + self.status = None + self._createdDatetime = None + self._updatedDatetime = None + self._lastReceivedDatetime = None + + @property + def contact(self): + return self._contact + + @contact.setter + def contact(self, value): + self._contact = ConversationContact().load(value) + + @property + def messages(self): + return self._messages + + @messages.setter + def messages(self, value): + self._messages = ConversationMessageReference().load(value) + + @property + def channels(self): + return self._channels + + @channels.setter + def channels(self, value): + if isinstance(value, list): + self._channels = [] + for channelData in value: + self._channels.append(Channel().load(channelData)) + else: + self._channels = value + + @property + def createdDatetime(self): + return self._createdDatetime + + @createdDatetime.setter + def createdDatetime(self, value): + self._createdDatetime = self.value_to_time(value, '%Y-%m-%dT%H:%M:%SZ') + + @property + def updatedDatetime(self): + return self._updatedDatetime + + @updatedDatetime.setter + def updatedDatetime(self, value): + self._updatedDatetime = self.value_to_time(value, '%Y-%m-%dT%H:%M:%SZ') + + @property + def lastReceivedDatetime(self): + return self._lastReceivedDatetime + + @lastReceivedDatetime.setter + def lastReceivedDatetime(self, value): + self._lastReceivedDatetime = self.value_to_time(value, '%Y-%m-%dT%H:%M:%SZ') + + def __str__(self): + return "\n".join([ + 'id : %s' % self.id, + 'contact id : %s' % self.contactId, + 'last used channel id : %s' % self.lastUsedChannelId, + 'message total count : %s' % self.messages.totalCount, + 'status : %s' % self.status, + 'created date time : %s' % self.createdDatetime, + 'updated date time : %s' % self.updatedDatetime, + 'last received date : %s' % self.lastReceivedDatetime, + ]) + + +class ConversationList(Base): + def __init__(self): + self.offset = None + self.limit = None + self.count = None + self.totalCount = None + self._items = None + + @property + def items(self): + return self._items + + @items.setter + def items(self, value): + if isinstance(value, list): + self._items = [] + for item in value: + self._items.append(Conversation().load(item)) + + def __str__(self): + item_ids = [] + for msg_item in self._items: + item_ids.append(msg_item.id) + + return "\n".join([ + 'items IDs : %s' % item_ids, + 'offset : %s' % self.offset, + 'limit : %s' % self.limit, + 'count : %s' % self.count, + 'totalCount : %s' % self.totalCount, + ]) diff --git a/messagebird/conversation_channel.py b/messagebird/conversation_channel.py new file mode 100644 index 0000000..58fe0c8 --- /dev/null +++ b/messagebird/conversation_channel.py @@ -0,0 +1,27 @@ +from messagebird.base import Base + + +class Channel(Base): + def __init__(self): + self.id = None + self.name = None + self.platformId = None + self.status = None + 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) diff --git a/messagebird/conversation_contact.py b/messagebird/conversation_contact.py new file mode 100644 index 0000000..5addd4b --- /dev/null +++ b/messagebird/conversation_contact.py @@ -0,0 +1,39 @@ +from messagebird.base import Base +from messagebird.contact import CustomDetails + + +class ConversationContact(Base): + + def __init__(self): + self.id = None + self.href = None + self.msisdn = None + self.firstName = None + self.lastName = None + self._customDetails = None + self._createdDatetime = None + self._updatedDatetime = None + + @property + def customDetails(self): + return self._customDetails + + @customDetails.setter + def customDetails(self, value): + self._customDetails = CustomDetails().load(value) + + @property + def createdDatetime(self): + return self._createdDatetime + + @createdDatetime.setter + def createdDatetime(self, value): + self._createdDatetime = self.value_to_time(value, '%Y-%m-%dT%H:%M:%SZ') + + @property + def updatedDatetime(self): + return self._updatedDatetime + + @updatedDatetime.setter + def updatedDatetime(self, value): + self._updatedDatetime = self.value_to_time(value, '%Y-%m-%dT%H:%M:%SZ') diff --git a/messagebird/conversation_message.py b/messagebird/conversation_message.py new file mode 100644 index 0000000..f9d1032 --- /dev/null +++ b/messagebird/conversation_message.py @@ -0,0 +1,95 @@ +from messagebird.base import Base + +MESSAGE_TYPE_AUDIO = "audio" +MESSAGE_TYPE_FILE = "file" +MESSAGE_TYPE_HSM = "hsm" +MESSAGE_TYPE_IMAGE = "image" +MESSAGE_TYPE_LOCATION = "location" +MESSAGE_TYPE_TEXT = "text" +MESSAGE_TYPE_VIDEO = "video" + + +class ConversationMessage(Base): + + def __init__(self): + self.id = None + self.conversationId = None + self.channelId = None + self.direction = None + self.status = None + self.type = None + self.content = None + self._createdDatetime = None + self._updatedDatetime = None + + @property + def createdDatetime(self): + return self._createdDatetime + + @createdDatetime.setter + def createdDatetime(self, value): + if value is not None: + self._createdDatetime = self.value_to_time(value, '%Y-%m-%dT%H:%M:%SZ') + + @property + def updatedDatetime(self): + return self._updatedDatetime + + @updatedDatetime.setter + def updatedDatetime(self, value): + if value is not None: + self._updatedDatetime = self.value_to_time(value, '%Y-%m-%dT%H:%M:%SZ') + + def __str__(self): + return "\n".join([ + 'message id : %s' % self.id, + 'channel id : %s' % self.channelId, + 'direction : %s' % self.direction, + 'status : %s' % self.status, + 'type : %s' % self.type, + 'content : %s' % self.content, + 'created date time : %s' % self._createdDatetime, + 'updated date time : %s' % self._updatedDatetime + ]) + + +class ConversationMessageReference(Base): + def __init__(self): + self.totalCount = None + self.href = None + + +class ConversationMessageList(Base): + + def __init__(self): + self.offset = None + self.limit = None + self.count = None + self.totalCount = None + self._items = None + + @property + def items(self): + return self._items + + @items.setter + def items(self, value): + items = [] + if isinstance(value, list): + for item in value: + items.append(ConversationMessage().load(item)) + + self._items = items + + def __str__(self): + item_ids = [] + for msg_item in self._items: + item_ids.append(msg_item.id) + + return "\n".join([ + 'items IDs : %s' % item_ids, + 'offset : %s' % self.offset, + 'limit : %s' % self.limit, + 'count : %s' % self.count, + 'totalCount : %s' % self.totalCount, + ]) diff --git a/messagebird/conversation_webhook.py b/messagebird/conversation_webhook.py new file mode 100644 index 0000000..9729743 --- /dev/null +++ b/messagebird/conversation_webhook.py @@ -0,0 +1,77 @@ +from messagebird.base import Base + +CONVERSATION_WEBHOOK_EVENT_CONVERSATION_CREATED = 'conversation.created' +CONVERSATION_WEBHOOK_EVENT_CONVERSATION_UPDATED = 'conversation.updated' +CONVERSATION_WEBHOOK_EVENT_MESSAGE_CREATED = 'message.created' +CONVERSATION_WEBHOOK_EVENT_MESSAGE_UPDATED = 'message.updated' + + +class ConversationWebhook(Base): + + def __init__(self): + self.id = None + self.channelId = None + self.url = None + self.events = None + 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, '%Y-%m-%dT%H:%M:%SZ') + + @property + def updatedDatetime(self): + return self._updatedDatetime + + @updatedDatetime.setter + def updatedDatetime(self, value): + self._updatedDatetime = self.value_to_time(value, '%Y-%m-%dT%H:%M:%SZ') + + def __str__(self): + return "\n".join([ + 'id : %s' % self.id, + 'events : %s' % self.events, + 'channel id : %s' % self.channelId, + 'created date time : %s' % self.createdDatetime, + 'updated date time : %s' % self.updatedDatetime + ]) + + +class ConversationWebhookList(Base): + def __init__(self): + self.offset = None + self.limit = None + self.count = None + self.totalCount = None + self._items = None + + @property + def items(self): + return self._items + + @items.setter + def items(self, value): + items = [] + if isinstance(value, list): + for item in value: + items.append(ConversationWebhook().load(item)) + + self._items = items + + def __str__(self): + item_ids = [] + for item in self._items: + item_ids.append(item.id) + + return "\n".join([ + 'items IDs : %s' % item_ids, + 'offset : %s' % self.offset, + 'limit : %s' % self.limit, + 'count : %s' % self.count, + 'totalCount : %s' % self.totalCount, + ]) diff --git a/tests/test_conversation.py b/tests/test_conversation.py new file mode 100644 index 0000000..dc27e64 --- /dev/null +++ b/tests/test_conversation.py @@ -0,0 +1,71 @@ +import unittest +from datetime import datetime +from messagebird import Client + +try: + from unittest.mock import Mock +except ImportError: + # mock was added to unittest in Python 3.3, but was an external library + # before. + from mock import Mock + + +class TestConversation(unittest.TestCase): + + def test_conversation_start(self): + http_client = Mock() + http_client.request.return_value = '{"id":"1234","contactId":"1234","contact":{"id":"1234","href":"https://contacts.messagebird.com/v2/contacts/1234","msisdn":99999999999,"displayName":"99999999999","firstName":"","lastName":"","customDetails":{},"attributes":{},"createdDatetime":"2019-04-02T08:19:37Z","updatedDatetime":"2019-04-02T08:19:38Z"},"channels":[{"id":"1234","name":"channel-name","platformId":"sms","status":"active","createdDatetime":"2019-04-01T15:25:12Z","updatedDatetime":"0001-01-01T00:00:00Z"}],"status":"active","createdDatetime":"2019-04-02T08:19:37Z","updatedDatetime":"2019-04-02T08:54:42.497114599Z","lastReceivedDatetime":"2019-04-02T08:54:42.464955904Z","lastUsedChannelId":"1234","messages":{"totalCount":1,"href":"https://conversations.messagebird.com/v1/conversations/1234/messages"}}' + + data = { + 'channelId': '1234', + 'to': '+99999999999', + 'type': "text", + 'content': { + 'text': 'Message Example' + }, + } + + msg = Client('', http_client).conversation_start(data) + + http_client.request.assert_called_once_with('conversations/start', 'POST', data) + + self.assertEqual('1234', msg.id) + self.assertEqual(99999999999, msg.contact.msisdn) + self.assertEqual(datetime(2019, 4, 2, 8, 19, 37), msg.contact.createdDatetime) + self.assertEqual(datetime(2019, 4, 2, 8, 19, 38), msg.contact.updatedDatetime) + self.assertEqual('channel-name', msg.channels[0].name) + + def test_conversation_list(self): + http_client = Mock() + http_client.request.return_value = '{}' + + Client('', http_client).conversation_list() + + http_client.request.assert_called_once_with('conversations?limit=10&offset=0', 'GET', None) + + def test_conversation_read(self): + http_client = Mock() + http_client.request.return_value = '{"id":"57b96dbe0fda40f0a814f5e3268c30a9","contactId":"8846d44229094c20813cf9eea596e680","contact":{"id":"8846d44229094c20813cf9eea596e680","href":"https://contacts.messagebird.com/v2/contacts/8846d44229094c20813cf9eea596e680","msisdn":31617110163,"displayName":"31617110163","firstName":"","lastName":"","customDetails":{},"attributes":{},"createdDatetime":"2019-04-02T08:54:39Z","updatedDatetime":"2019-04-02T08:54:40Z"},"channels":[{"id":"c0dae31e440145e094c4708b7d908842","name":"test","platformId":"sms","status":"active","createdDatetime":"2019-04-01T15:25:12Z","updatedDatetime":"0001-01-01T00:00:00Z"}],"status":"active","createdDatetime":"2019-04-02T08:54:38Z","updatedDatetime":"2019-04-02T14:24:09.192202886Z","lastReceivedDatetime":"2019-04-02T14:24:09.14826339Z","lastUsedChannelId":"c0dae31e440145e094c4708b7d908842","messages":{"totalCount":2,"href":"https://conversations.messagebird.com/v1/conversations/57b96dbe0fda40f0a814f5e3268c30a9/messages"}}' + + conversation = Client('', http_client).conversation_read('conversation-id') + + http_client.request.assert_called_once_with('conversations/conversation-id', 'GET', None) + + self.assertEqual('57b96dbe0fda40f0a814f5e3268c30a9', conversation.id) + self.assertEqual(datetime(2019, 4, 2, 8, 54, 38), conversation.createdDatetime) + self.assertEqual(datetime(2019, 4, 2, 14, 24, 9), conversation.updatedDatetime) + self.assertEqual(datetime(2019, 4, 2, 14, 24), conversation.lastReceivedDatetime) + self.assertEqual('8846d44229094c20813cf9eea596e680', conversation.contact.id) + self.assertEqual('c0dae31e440145e094c4708b7d908842', conversation.channels[0].id) + self.assertEqual(2, conversation.messages.totalCount) + + def test_conversation_update(self): + http_client = Mock() + http_client.request.return_value = '{"id":"07e823fdb36a462fb5e187d6d7b96493","contactId":"459a35432b0c4195abbdae353eb19359","status":"archived","createdDatetime":"2019-04-02T08:19:37Z","updatedDatetime":"2019-04-03T07:22:58.965421128Z","lastReceivedDatetime":"2019-04-02T12:02:22.707634424Z","lastUsedChannelId":"c0dae31e440145e094c4708b7d908842","messages":{"totalCount":16,"href":"https://conversations.messagebird.com/v1/conversations/07e823fdb36a462fb5e187d6d7b96493/messages"}}' + + updated_request_data = {'status': 'archived'} + + conversation = Client('', http_client).conversation_update('conversation-id', updated_request_data) + + http_client.request.assert_called_once_with('conversations/conversation-id', 'PATCH', updated_request_data) + self.assertEqual('archived', conversation.status) diff --git a/tests/test_conversation_message.py b/tests/test_conversation_message.py new file mode 100644 index 0000000..4c6836b --- /dev/null +++ b/tests/test_conversation_message.py @@ -0,0 +1,51 @@ +import unittest +from datetime import datetime +from messagebird import Client + +try: + from unittest.mock import Mock +except ImportError: + # mock was added to unittest in Python 3.3, but was an external library + # before. + from mock import Mock + + +class TestConversationMessage(unittest.TestCase): + + def test_conversation_list_messages(self): + http_client = Mock() + http_client.request.return_value = '{"count":1,"items":[{"id":"54445534","conversationId":"54345543543","channelId":"4535434354","type":"text","content":{"text":"Hello"},"direction":"sent","status":"delivered","createdDatetime":"2019-04-02T08:54:54.608157775Z","updatedDatetime":"2019-04-02T08:54:54.63910221Z"}],"limit":10,"offset":0,"totalCount":1}' + + msg = Client('', http_client).conversation_list_messages(54567) + + self.assertEqual(1, msg.count) + self.assertEqual('54445534', msg.items[0].id) + + http_client.request.assert_called_once_with('conversations/54567/messages?limit=10&offset=0', 'GET', None) + + def test_conversation_read_message(self): + http_client = Mock() + http_client.request.return_value = '{}' + + Client('', http_client).conversation_read_message('message-id') + + http_client.request.assert_called_once_with('messages/message-id', 'GET', None) + + def test_create_message(self): + http_client = Mock() + http_client.request.return_value = '{"id":"id","conversationId":"conversation-id","channelId":"channel-id","type":"text","content":{"text":"Example Text Message"},"direction":"sent","status":"pending","createdDatetime":"2019-04-02T11:57:52.142641447Z","updatedDatetime":"2019-04-02T11:57:53.142641447Z"}' + + data = { + 'channelId': 1234, + 'type': 'text', + 'content': { + 'text': 'this is a message' + }, + } + + msg = Client('', http_client).conversation_create_message('conversation-id', data) + + self.assertEqual(datetime(2019, 4, 2, 11, 57, 53), msg.updatedDatetime) + self.assertEqual(datetime(2019, 4, 2, 11, 57, 52), msg.createdDatetime) + + http_client.request.assert_called_once_with('conversations/conversation-id/messages', 'POST', data) diff --git a/tests/test_conversation_webhook.py b/tests/test_conversation_webhook.py new file mode 100644 index 0000000..ec009e8 --- /dev/null +++ b/tests/test_conversation_webhook.py @@ -0,0 +1,64 @@ +import unittest +from datetime import datetime +from messagebird import Client +from messagebird.conversation_webhook import \ + CONVERSATION_WEBHOOK_EVENT_CONVERSATION_CREATED, \ + CONVERSATION_WEBHOOK_EVENT_CONVERSATION_UPDATED + +try: + from unittest.mock import Mock +except ImportError: + # mock was added to unittest in Python 3.3, but was an external library + # before. + from mock import Mock + + +class TestConversationWebhook(unittest.TestCase): + + def test_conversation_webhook_create(self): + http_client = Mock() + http_client.request.return_value = '{"id":"20c308852190485bbb658e43baffc5fa","url":"https://example.com","channelId":"c0dae31e440145e094c4708b7d908842","events":["conversation.created","conversation.updated"],"status":"enabled","createdDatetime":"2019-04-03T07:46:37.984026573Z","updatedDatetime":null}' + + webhookRequestData = { + 'channelId': '20c308852190485bbb658e43baffc5fa', + 'events': [CONVERSATION_WEBHOOK_EVENT_CONVERSATION_CREATED, + CONVERSATION_WEBHOOK_EVENT_CONVERSATION_UPDATED], + 'url': 'https://example.com' + } + + Client('', http_client).conversation_create_webhook(webhookRequestData) + + http_client.request.assert_called_once_with('webhooks', 'POST', webhookRequestData) + + def test_conversation_webhook_delete(self): + http_client = Mock() + http_client.request.return_value = '' + + Client('', http_client).conversation_delete_webhook('webhook-id') + + http_client.request.assert_called_once_with('webhooks/webhook-id', 'DELETE', None) + + def test_conversation_webhook_list(self): + http_client = Mock() + http_client.request.return_value = '{"offset":0,"limit":10,"count":2,"totalCount":2,"items":[{"id":"57b96dbe0fda40f0a814f5e3268c30a9","contactId":"8846d44229094c20813cf9eea596e680","contact":{"id":"8846d44229094c20813cf9eea596e680","href":"https://contacts.messagebird.com/v2/contacts/8846d44229094c20813cf9eea596e680","msisdn":31617110163,"displayName":"31617110163","firstName":"","lastName":"","customDetails":{},"attributes":{},"createdDatetime":"2019-04-02T08:54:39Z","updatedDatetime":"2019-04-02T08:54:40Z"},"channels":[{"id":"c0dae31e440145e094c4708b7d908842","name":"test","platformId":"sms","status":"active","createdDatetime":"2019-04-01T15:25:12Z","updatedDatetime":"0001-01-01T00:00:00Z"}],"status":"active","createdDatetime":"2019-04-02T08:54:38Z","updatedDatetime":"2019-04-02T14:24:09.192202886Z","lastReceivedDatetime":"2019-04-02T14:24:09.14826339Z","lastUsedChannelId":"c0dae31e440145e094c4708b7d908842","messages":{"totalCount":2,"href":"https://conversations.messagebird.com/v1/conversations/57b96dbe0fda40f0a814f5e3268c30a9/messages"}},{"id":"07e823fdb36a462fb5e187d6d7b96493","contactId":"459a35432b0c4195abbdae353eb19359","contact":{"id":"459a35432b0c4195abbdae353eb19359","href":"https://contacts.messagebird.com/v2/contacts/459a35432b0c4195abbdae353eb19359","msisdn":31615164888,"displayName":"31615164888","firstName":"","lastName":"","customDetails":{},"attributes":{},"createdDatetime":"2019-04-02T08:19:37Z","updatedDatetime":"2019-04-02T08:19:38Z"},"channels":[{"id":"c0dae31e440145e094c4708b7d908842","name":"test","platformId":"sms","status":"active","createdDatetime":"2019-04-01T15:25:12Z","updatedDatetime":"0001-01-01T00:00:00Z"}],"status":"active","createdDatetime":"2019-04-02T08:19:37Z","updatedDatetime":"2019-04-03T07:35:47.35395356Z","lastReceivedDatetime":"2019-04-02T12:02:22.707634424Z","lastUsedChannelId":"c0dae31e440145e094c4708b7d908842","messages":{"totalCount":16,"href":"https://conversations.messagebird.com/v1/conversations/07e823fdb36a462fb5e187d6d7b96493/messages"}}]}' + + Client('', http_client).conversation_list_webhooks() + http_client.request.assert_called_once_with('webhooks?limit=10&offset=0', 'GET', None) + + def test_conversation_webhook_list_pagination(self): + http_client = Mock() + http_client.request.return_value = '{"offset":0,"limit":10,"count":2,"totalCount":2,"items":[{"id":"57b96dbe0fda40f0a814f5e3268c30a9","contactId":"8846d44229094c20813cf9eea596e680","contact":{"id":"8846d44229094c20813cf9eea596e680","href":"https://contacts.messagebird.com/v2/contacts/8846d44229094c20813cf9eea596e680","msisdn":31617110163,"displayName":"31617110163","firstName":"","lastName":"","customDetails":{},"attributes":{},"createdDatetime":"2019-04-02T08:54:39Z","updatedDatetime":"2019-04-02T08:54:40Z"},"channels":[{"id":"c0dae31e440145e094c4708b7d908842","name":"test","platformId":"sms","status":"active","createdDatetime":"2019-04-01T15:25:12Z","updatedDatetime":"0001-01-01T00:00:00Z"}],"status":"active","createdDatetime":"2019-04-02T08:54:38Z","updatedDatetime":"2019-04-02T14:24:09.192202886Z","lastReceivedDatetime":"2019-04-02T14:24:09.14826339Z","lastUsedChannelId":"c0dae31e440145e094c4708b7d908842","messages":{"totalCount":2,"href":"https://conversations.messagebird.com/v1/conversations/57b96dbe0fda40f0a814f5e3268c30a9/messages"}},{"id":"07e823fdb36a462fb5e187d6d7b96493","contactId":"459a35432b0c4195abbdae353eb19359","contact":{"id":"459a35432b0c4195abbdae353eb19359","href":"https://contacts.messagebird.com/v2/contacts/459a35432b0c4195abbdae353eb19359","msisdn":31615164888,"displayName":"31615164888","firstName":"","lastName":"","customDetails":{},"attributes":{},"createdDatetime":"2019-04-02T08:19:37Z","updatedDatetime":"2019-04-02T08:19:38Z"},"channels":[{"id":"c0dae31e440145e094c4708b7d908842","name":"test","platformId":"sms","status":"active","createdDatetime":"2019-04-01T15:25:12Z","updatedDatetime":"0001-01-01T00:00:00Z"}],"status":"active","createdDatetime":"2019-04-02T08:19:37Z","updatedDatetime":"2019-04-03T07:35:47.35395356Z","lastReceivedDatetime":"2019-04-02T12:02:22.707634424Z","lastUsedChannelId":"c0dae31e440145e094c4708b7d908842","messages":{"totalCount":16,"href":"https://conversations.messagebird.com/v1/conversations/07e823fdb36a462fb5e187d6d7b96493/messages"}}]}' + + Client('', http_client).conversation_list_webhooks(2, 1) + http_client.request.assert_called_once_with('webhooks?limit=2&offset=1', 'GET', None) + + def test_conversation_webhook_read(self): + http_client = Mock() + http_client.request.return_value = '{"id":"5031e2da142d401c93fbc38518ebb604","url":"https://example.com","channelId":"c0dae31e440145e094c4708b7d908842","events":["conversation.created","conversation.updated"],"status":"enabled","createdDatetime":"2019-04-03T08:41:37Z","updatedDatetime":null}' + + web_hook = Client('', http_client).conversation_read_webhook('webhook-id') + + http_client.request.assert_called_once_with('webhooks/webhook-id', 'GET', None) + self.assertEqual(datetime(2019, 4, 3, 8, 41, 37), web_hook.createdDatetime) + self.assertEqual(None, web_hook.updatedDatetime) + self.assertEqual(['conversation.created', 'conversation.updated'], web_hook.events)