Example #1
0
    def send(self, channel, msg, text):
        # url used for logs and exceptions
        url = 'https://api.plivo.com/v1/Account/%s/Message/' % channel.config[Channel.CONFIG_PLIVO_AUTH_ID]

        client = plivo.RestAPI(channel.config[Channel.CONFIG_PLIVO_AUTH_ID], channel.config[Channel.CONFIG_PLIVO_AUTH_TOKEN])
        status_url = "https://" + settings.TEMBA_HOST + "%s" % reverse('handlers.plivo_handler',
                                                                       args=['status', channel.uuid])

        payload = {'src': channel.address.lstrip('+'),
                   'dst': msg.urn_path.lstrip('+'),
                   'text': text,
                   'url': status_url,
                   'method': 'POST'}

        event = HttpEvent('POST', url, json.dumps(payload))

        start = time.time()

        try:
            # TODO: Grab real request and response here
            plivo_response_status, plivo_response = client.send_message(params=payload)
            event.status_code = plivo_response_status
            event.response_body = plivo_response

        except Exception as e:  # pragma: no cover
            raise SendException(six.text_type(e), event=event, start=start)

        if plivo_response_status != 200 and plivo_response_status != 201 and plivo_response_status != 202:
            raise SendException("Got non-200 response [%d] from API" % plivo_response_status,
                                event=event, start=start)

        external_id = plivo_response['message_uuid'][0]
        Channel.success(channel, msg, WIRED, start, event=event, external_id=external_id)
Example #2
0
    def send(self, channel, msg, text):

        client = NexmoClient(channel.org_config[NEXMO_KEY],
                             channel.org_config[NEXMO_SECRET],
                             channel.org_config[NEXMO_APP_ID],
                             channel.org_config[NEXMO_APP_PRIVATE_KEY])
        start = time.time()

        event = None
        attempts = 0
        while not event:
            try:
                (message_id, event) = client.send_message_via_nexmo(
                    channel.address, msg.urn_path, text)
            except SendException as e:
                match = regex.match(
                    r'.*Throughput Rate Exceeded - please wait \[ (\d+) \] and retry.*',
                    e.events[0].response_body)

                # this is a throughput failure, attempt to wait up to three times
                if match and attempts < 3:
                    sleep(float(match.group(1)) / 1000)
                    attempts += 1
                else:
                    raise e

        Channel.success(channel,
                        msg,
                        SENT,
                        start,
                        event=event,
                        external_id=message_id)
Example #3
0
    def send_whatsapp(self, channel_struct, msg, payload, attachments=None):
        url = ('%s/messages/' % (self.wassup_url(), ))
        headers = self.api_request_headers(channel_struct)
        event = HttpEvent('POST', url, json.dumps(payload))
        start = time.time()

        # Grab the first attachment if it exists
        attachments = Attachment.parse_all(msg.attachments)
        attachment = attachments[0] if attachments else None

        try:
            if attachment:
                files = self.fetch_attachment(attachment)
                data = payload
            else:
                headers.update({'Content-Type': 'application/json'})
                data = json.dumps(payload)
                files = {}

            response = requests.post(url,
                                     data=data,
                                     files=files,
                                     headers=headers)
            response.raise_for_status()
            event.status_code = response.status_code
            event.response_body = response.text
        except (requests.RequestException, ) as e:
            raise SendException(
                'error: %s, request: %r, response: %r' %
                (six.text_type(e), e.request.body, e.response.content),
                event=event,
                start=start)

        data = response.json()
        try:
            message_id = data['uuid']
            Channel.success(channel_struct,
                            msg,
                            WIRED,
                            start,
                            event=event,
                            external_id=message_id)
        except (KeyError, ) as e:
            raise SendException(
                "Unable to read external message_id: %r" % (e, ),
                event=HttpEvent('POST',
                                url,
                                request_body=json.dumps(json.dumps(payload)),
                                response_body=json.dumps(data)),
                start=start)
Example #4
0
    def send(self, channel, msg, text):
        auth_id = channel.config[Channel.CONFIG_PLIVO_AUTH_ID]
        auth_token = channel.config[Channel.CONFIG_PLIVO_AUTH_TOKEN]

        url = 'https://api.plivo.com/v1/Account/%s/Message/' % auth_id
        status_url = "https://%s%s" % (channel.callback_domain,
                                       reverse('handlers.plivo_handler',
                                               args=['status', channel.uuid]))

        payload = {
            'src': channel.address.lstrip('+'),
            'dst': msg.urn_path.lstrip('+'),
            'text': text,
            'url': status_url,
            'method': 'POST'
        }

        event = HttpEvent('POST', url, json.dumps(payload))
        headers = http_headers(extra={'Content-Type': "application/json"})

        start = time.time()

        try:
            # TODO: Grab real request and response here
            response = requests.post(url,
                                     json=payload,
                                     headers=headers,
                                     auth=(auth_id, auth_token))
            event.status_code = response.status_code
            event.response_body = response.json()

        except Exception as e:  # pragma: no cover
            raise SendException(six.text_type(e), event=event, start=start)

        if response.status_code not in [200, 201, 202]:  # pragma: no cover
            raise SendException("Got non-200 response [%d] from API" %
                                response.status_code,
                                event=event,
                                start=start)

        external_id = response.json()['message_uuid'][0]
        Channel.success(channel,
                        msg,
                        WIRED,
                        start,
                        event=event,
                        external_id=external_id)
Example #5
0
    def send(self, channel, msg, text):

        config = channel.config

        client = NexmoClient(config[Channel.CONFIG_NEXMO_API_KEY],
                             config[Channel.CONFIG_NEXMO_API_SECRET],
                             config[Channel.CONFIG_NEXMO_APP_ID],
                             config[Channel.CONFIG_NEXMO_APP_PRIVATE_KEY])
        start = time.time()

        callback_url = "https://" + channel.callback_domain + reverse(
            'courier.nx', args=[channel.uuid, 'receive'])

        event = None
        attempts = 0
        while not event:
            try:
                (message_id, event) = client.send_message_via_nexmo(
                    channel.address, msg.urn_path, text, callback_url)
            except SendException as e:
                match = regex.match(
                    r'.*Throughput Rate Exceeded - please wait \[ (\d+) \] and retry.*',
                    e.events[0].response_body)

                # this is a throughput failure, attempt to wait up to three times
                if match and attempts < 3:
                    sleep(float(match.group(1)) / 1000)
                    attempts += 1
                else:
                    raise e

        Channel.success(channel,
                        msg,
                        SENT,
                        start,
                        event=event,
                        external_id=message_id)
Example #6
0
    def send(self, channel, msg, text):
        connection = None

        # if the channel config has specified and override hostname use that, otherwise use settings
        callback_domain = channel.config.get(Channel.CONFIG_RP_HOSTNAME_OVERRIDE, None)
        if not callback_domain:
            callback_domain = channel.callback_domain

        # the event url Junebug will relay events to
        event_url = "http://%s%s" % (
            callback_domain,
            reverse("handlers.junebug_handler", args=["event", channel.uuid]),
        )

        is_ussd = Channel.get_type_from_code(channel.channel_type).category == ChannelType.Category.USSD

        # build our payload
        payload = {"event_url": event_url, "content": text}

        secret = channel.config.get(Channel.CONFIG_SECRET)
        if secret is not None:
            payload["event_auth_token"] = secret

        connection = USSDSession.objects.get_with_status_only(msg.connection_id)

        # make sure USSD responses are only valid for a short window
        response_expiration = timezone.now() - timedelta(seconds=180)
        external_id = None
        if msg.response_to_id and msg.created_on > response_expiration:
            external_id = Msg.objects.values_list("external_id", flat=True).filter(pk=msg.response_to_id).first()
        # NOTE: Only one of `to` or `reply_to` may be specified, use external_id if we have it.
        if external_id:
            payload["reply_to"] = external_id
        else:
            payload["to"] = msg.urn_path
        payload["channel_data"] = {"continue_session": connection and not connection.should_end or False}

        log_url = channel.config[Channel.CONFIG_SEND_URL]
        start = time.time()

        event = HttpEvent("POST", log_url, json.dumps(payload))
        headers = http_headers(extra={"Content-Type": "application/json"})

        try:
            response = requests.post(
                channel.config[Channel.CONFIG_SEND_URL],
                verify=True,
                json=payload,
                timeout=15,
                headers=headers,
                auth=(channel.config[Channel.CONFIG_USERNAME], channel.config[Channel.CONFIG_PASSWORD]),
            )

            event.status_code = response.status_code
            event.response_body = response.text

        except Exception as e:
            raise SendException(str(e), event=event, start=start)

        if not (200 <= response.status_code < 300):
            raise SendException(
                "Received a non 200 response %d from Junebug" % response.status_code, event=event, start=start
            )

        data = response.json()

        if is_ussd and connection and connection.should_end:
            connection.close()

        try:
            message_id = data["result"]["message_id"]
            Channel.success(channel, msg, WIRED, start, event=event, external_id=message_id)
        except KeyError as e:
            raise SendException(
                "Unable to read external message_id: %r" % (e,),
                event=HttpEvent(
                    "POST", log_url, request_body=json.dumps(json.dumps(payload)), response_body=json.dumps(data)
                ),
                start=start,
            )
Example #7
0
    def send(self, channel, msg, text):
        connection = None

        # if the channel config has specified and override hostname use that, otherwise use settings
        event_hostname = channel.config.get(Channel.CONFIG_RP_HOSTNAME_OVERRIDE, settings.HOSTNAME)

        # the event url Junebug will relay events to
        event_url = 'http://%s%s' % (event_hostname, reverse('courier.jn', args=[channel.uuid, 'event']))

        is_ussd = Channel.get_type_from_code(channel.channel_type).category == ChannelType.Category.USSD

        # build our payload
        payload = {'event_url': event_url, 'content': text}

        if channel.secret is not None:
            payload['event_auth_token'] = channel.secret

        if is_ussd:
            connection = USSDSession.objects.get_with_status_only(msg.connection_id)
            # make sure USSD responses are only valid for a short window
            response_expiration = timezone.now() - timedelta(seconds=180)
            external_id = None
            if msg.response_to_id and msg.created_on > response_expiration:
                external_id = Msg.objects.values_list('external_id', flat=True).filter(pk=msg.response_to_id).first()
            # NOTE: Only one of `to` or `reply_to` may be specified, use external_id if we have it.
            if external_id:
                payload['reply_to'] = external_id
            else:
                payload['to'] = msg.urn_path
            payload['channel_data'] = {
                'continue_session': connection and not connection.should_end or False,
            }
        else:
            payload['from'] = channel.address
            payload['to'] = msg.urn_path

        log_url = channel.config[Channel.CONFIG_SEND_URL]
        start = time.time()

        event = HttpEvent('POST', log_url, json.dumps(payload))
        headers = {'Content-Type': 'application/json'}
        headers.update(TEMBA_HEADERS)

        try:
            response = requests.post(
                channel.config[Channel.CONFIG_SEND_URL], verify=True,
                json=payload, timeout=15, headers=headers,
                auth=(channel.config[Channel.CONFIG_USERNAME],
                      channel.config[Channel.CONFIG_PASSWORD]))

            event.status_code = response.status_code
            event.response_body = response.text

        except Exception as e:
            raise SendException(unicode(e), event=event, start=start)

        if not (200 <= response.status_code < 300):
            raise SendException("Received a non 200 response %d from Junebug" % response.status_code,
                                event=event, start=start)

        data = response.json()

        if is_ussd and connection and connection.should_end:
            connection.close()

        try:
            message_id = data['result']['message_id']
            Channel.success(channel, msg, WIRED, start, event=event, external_id=message_id)
        except KeyError, e:
            raise SendException("Unable to read external message_id: %r" % (e,),
                                event=HttpEvent('POST', log_url,
                                                request_body=json.dumps(json.dumps(payload)),
                                                response_body=json.dumps(data)),
                                start=start)
Example #8
0
    def send(self, channel, msg, text):
        connection = None

        # if the channel config has specified and override hostname use that, otherwise use settings
        callback_domain = channel.config.get(
            Channel.CONFIG_RP_HOSTNAME_OVERRIDE, None)
        if not callback_domain:
            callback_domain = channel.callback_domain

        # the event url Junebug will relay events to
        event_url = "http://%s%s" % (
            callback_domain,
            reverse("handlers.junebug_handler", args=["event", channel.uuid]),
        )

        is_ussd = Channel.get_type_from_code(
            channel.channel_type).category == ChannelType.Category.USSD

        # build our payload
        payload = {"event_url": event_url, "content": text}

        secret = channel.config.get(Channel.CONFIG_SECRET)
        if secret is not None:
            payload["event_auth_token"] = secret

        connection = USSDSession.objects.get_with_status_only(
            msg.connection_id)

        # make sure USSD responses are only valid for a short window
        response_expiration = timezone.now() - timedelta(seconds=180)
        external_id = None
        if msg.response_to_id and msg.created_on > response_expiration:
            external_id = Msg.objects.values_list(
                "external_id",
                flat=True).filter(pk=msg.response_to_id).first()
        # NOTE: Only one of `to` or `reply_to` may be specified, use external_id if we have it.
        if external_id:
            payload["reply_to"] = external_id
        else:
            payload["to"] = msg.urn_path
        payload["channel_data"] = {
            "continue_session": connection and not connection.should_end
            or False
        }

        log_url = channel.config[Channel.CONFIG_SEND_URL]
        start = time.time()

        event = HttpEvent("POST", log_url, json.dumps(payload))
        headers = http_headers(extra={"Content-Type": "application/json"})

        try:
            response = requests.post(
                channel.config[Channel.CONFIG_SEND_URL],
                verify=True,
                json=payload,
                timeout=15,
                headers=headers,
                auth=(channel.config[Channel.CONFIG_USERNAME],
                      channel.config[Channel.CONFIG_PASSWORD]),
            )

            event.status_code = response.status_code
            event.response_body = response.text

        except Exception as e:
            raise SendException(str(e), event=event, start=start)

        if not (200 <= response.status_code < 300):
            raise SendException("Received a non 200 response %d from Junebug" %
                                response.status_code,
                                event=event,
                                start=start)

        data = response.json()

        if is_ussd and connection and connection.should_end:
            connection.close()

        try:
            message_id = data["result"]["message_id"]
            Channel.success(channel,
                            msg,
                            WIRED,
                            start,
                            event=event,
                            external_id=message_id)
        except KeyError as e:
            raise SendException(
                "Unable to read external message_id: %r" % (e, ),
                event=HttpEvent("POST",
                                log_url,
                                request_body=json.dumps(json.dumps(payload)),
                                response_body=json.dumps(data)),
                start=start,
            )
Example #9
0
    def send(self, channel, msg, text):

        is_ussd = Channel.get_type_from_code(
            channel.channel_type).category == ChannelType.Category.USSD
        channel.config[
            'transport_name'] = 'ussd_transport' if is_ussd else 'mtech_ng_smpp_transport'

        session = None
        session_event = None
        in_reply_to = None

        if is_ussd:
            session = USSDSession.objects.get_with_status_only(
                msg.connection_id)
            if session and session.should_end:
                session_event = "close"
            else:
                session_event = "resume"

        if msg.response_to_id:
            in_reply_to = Msg.objects.values_list(
                'external_id',
                flat=True).filter(pk=msg.response_to_id).first()

        payload = dict(message_id=msg.id,
                       in_reply_to=in_reply_to,
                       session_event=session_event,
                       to_addr=msg.urn_path,
                       from_addr=channel.address,
                       content=text,
                       transport_name=channel.config['transport_name'],
                       transport_type='ussd' if is_ussd else 'sms',
                       transport_metadata={},
                       helper_metadata={})

        payload = json.dumps(payload)

        headers = http_headers(extra={'Content-Type': 'application/json'})

        api_url_base = channel.config.get('api_url', Channel.VUMI_GO_API_URL)

        url = "%s/%s/messages.json" % (api_url_base,
                                       channel.config['conversation_key'])

        event = HttpEvent('PUT', url, json.dumps(payload))

        start = time.time()

        validator = URLValidator()
        validator(url)

        try:
            response = requests.put(url,
                                    data=payload,
                                    headers=headers,
                                    timeout=30,
                                    auth=(channel.config['account_key'],
                                          channel.config['access_token']))

            event.status_code = response.status_code
            event.response_body = response.text

        except Exception as e:
            raise SendException(six.text_type(e), event=event, start=start)

        if response.status_code not in (200, 201):
            # this is a fatal failure, don't retry
            fatal = response.status_code == 400

            # if this is fatal due to the user opting out, stop them
            if response.text and response.text.find('has opted out') >= 0:
                contact = Contact.objects.get(id=msg.contact)
                contact.stop(contact.modified_by)
                fatal = True

            raise SendException("Got non-200 response [%d] from API" %
                                response.status_code,
                                event=event,
                                fatal=fatal,
                                start=start)

        # parse our response
        body = response.json()
        external_id = body.get('message_id', '')

        if is_ussd and session and session.should_end:
            session.close()

        Channel.success(channel,
                        msg,
                        WIRED,
                        start,
                        event=event,
                        external_id=external_id)