Beispiel #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)
Beispiel #2
0
    def send(self, channel, msg, text):
        callback_domain = channel.callback_domain

        payload = {
            'accountid': channel.config[Channel.CONFIG_USERNAME],
            'password': channel.config[Channel.CONFIG_PASSWORD],
            'text': text,
            'to': msg.urn_path,
            'ret_id': msg.id,
            'datacoding': 8,
            'userdata': 'textit',
            'ret_url': 'https://%s%s' % (callback_domain, reverse('handlers.hcnx_handler', args=['status', channel.uuid])),
            'ret_mo_url': 'https://%s%s' % (callback_domain, reverse('handlers.hcnx_handler', args=['receive', channel.uuid]))
        }

        # build our send URL
        url = 'https://highpushfastapi-v2.hcnx.eu/api' + '?' + urlencode(payload)
        log_payload = urlencode(payload)
        start = time.time()

        event = HttpEvent('GET', url, log_payload)

        try:
            response = requests.get(url, headers=http_headers(), timeout=30)
            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 != 200 and response.status_code != 201 and response.status_code != 202:
            raise SendException("Got non-200 response [%d] from API" % response.status_code,
                                event=event, start=start)

        Channel.success(channel, msg, WIRED, start, event=event)
Beispiel #3
0
    def refresh_access_token(self, channel_id):
        r = get_redis_connection()
        lock_name = self.TOKEN_REFRESH_LOCK % self.channel_uuid

        if not r.get(lock_name):
            with r.lock(lock_name, timeout=30):
                key = self.TOKEN_STORE_KEY % self.channel_uuid

                post_data = dict(grant_type="client_credentials", client_id=self.app_id, client_secret=self.app_secret)
                url = self.TOKEN_URL

                event = HttpEvent("POST", url, json.dumps(post_data))
                start = time.time()

                response = self._request(url, post_data, access_token=None)
                event.status_code = response.status_code

                if response.status_code != 200:
                    event.response_body = response.content
                    ChannelLog.log_channel_request(
                        channel_id, "Got non-200 response from %s" % self.API_NAME, event, start, True
                    )
                    return

                response_json = response.json()
                event.response_body = json.dumps(response_json)
                ChannelLog.log_channel_request(
                    channel_id, "Successfully fetched access token from %s" % self.API_NAME, event, start
                )

                access_token = response_json["access_token"]
                expires = response_json.get("expires_in", 7200)
                r.set(key, access_token, ex=int(expires))
                return access_token
Beispiel #4
0
    def send(self, channel, msg, text):
        # determine our encoding
        encoding, text = Channel.determine_encoding(text, replace=True)

        # if this looks like unicode, ask macrokiosk to send as unicode
        if encoding == Encoding.UNICODE:
            message_type = 5
        else:
            message_type = 0

        # strip a leading +
        recipient = msg.urn_path[1:] if msg.urn_path.startswith(
            '+') else msg.urn_path

        data = {
            'user': channel.config[Channel.CONFIG_USERNAME],
            'pass': channel.config[Channel.CONFIG_PASSWORD],
            'to': recipient,
            'text': text,
            'from': channel.config[Channel.CONFIG_MACROKIOSK_SENDER_ID],
            'servid': channel.config[Channel.CONFIG_MACROKIOSK_SERVICE_ID],
            'type': message_type
        }

        url = 'https://www.etracker.cc/bulksms/send'
        payload = json.dumps(data)
        headers = http_headers(extra={
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        })

        event = HttpEvent('POST', url, payload)

        start = time.time()

        try:
            response = requests.post(url,
                                     json=data,
                                     headers=headers,
                                     timeout=30)
            event.status_code = response.status_code
            event.response_body = response.text

            external_id = response.json().get('msgid', None)

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

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

        Channel.success(channel,
                        msg,
                        WIRED,
                        start,
                        event=event,
                        external_id=external_id)
Beispiel #5
0
    def request(self, method, uri, **kwargs):
        data = kwargs.get("data")
        if data is not None:
            udata = {}
            for k, v in data.items():
                key = k.encode("utf-8")
                if isinstance(v, (list, tuple, set)):
                    udata[key] = [encode_atom(x) for x in v]
                elif isinstance(v, (int, bytes, str)):
                    udata[key] = encode_atom(v)
                else:
                    raise ValueError("data should be an integer, " "binary, or string, or sequence ")
            data = urlencode(udata, doseq=True)

        del kwargs["auth"]
        event = HttpEvent(method, uri, data)
        if "/messages" in uri.lower() or "/calls" in uri.lower():
            self.events.append(event)
        resp = super().request(method, uri, auth=self.auth, **kwargs)

        event.url = uri
        event.status_code = resp.status_code
        event.response_body = force_text(resp.content)

        return resp
Beispiel #6
0
    def request(self, method, uri, **kwargs):
        """
        Send an HTTP request to the resource.

        :raises: a :exc:`~twilio.TwilioRestException`
        """
        if 'timeout' not in kwargs and self.timeout is not UNSET_TIMEOUT:
            kwargs['timeout'] = self.timeout

        data = kwargs.get('data')
        if data is not None:
            udata = {}
            for k, v in six.iteritems(data):
                key = k.encode('utf-8')
                if isinstance(v, (list, tuple, set)):
                    udata[key] = [encode_atom(x) for x in v]
                elif isinstance(v, (six.integer_types, six.binary_type, six.string_types)):
                    udata[key] = encode_atom(v)
                else:
                    raise ValueError('data should be an integer, '
                                     'binary, or string, or sequence ')
            data = urlencode(udata, doseq=True)

        event = HttpEvent(method, uri, data)
        self.events.append(event)
        resp = make_twilio_request(method, uri, auth=self.auth, **kwargs)

        event.url = resp.url
        event.status_code = resp.status_code
        event.response_body = six.text_type(resp.content)

        if method == "DELETE":
            return resp, {}
        else:
            return resp, json.loads(resp.content)
Beispiel #7
0
    def request(self, method, uri, **kwargs):
        data = kwargs.get("data")
        if data is not None:
            udata = {}
            for k, v in data.items():
                key = k.encode("utf-8")
                if isinstance(v, (list, tuple, set)):
                    udata[key] = [encode_atom(x) for x in v]
                elif isinstance(v, (int, bytes, str)):
                    udata[key] = encode_atom(v)
                else:
                    raise ValueError("data should be an integer, "
                                     "binary, or string, or sequence ")
            data = urlencode(udata, doseq=True)

        del kwargs["auth"]
        event = HttpEvent(method, uri, data)
        if "/messages" in uri.lower() or "/calls" in uri.lower():
            self.events.append(event)
        resp = super().request(method, uri, auth=self.auth, **kwargs)

        event.url = uri
        event.status_code = resp.status_code
        event.response_body = force_text(resp.content)

        return resp
Beispiel #8
0
    def get_user_detail(self, open_id, channel_id):
        access_token = self.get_access_token()

        url = 'https://channels.jiochat.com/user/info.action?'

        payload = dict(openid=open_id)
        event = HttpEvent('GET', url, json.dumps(payload))
        start = time.time()

        response = self._request(url, 'GET', payload, access_token)

        event.status_code = response.status_code

        if response.status_code != 200:
            event.response_body = response.content
            ChannelLog.log_channel_request(
                channel_id, "Got non-200 response from Jiochat", event, start,
                True)
            return dict()

        data = response.json()

        event.response_body = json.dumps(data)
        ChannelLog.log_channel_request(
            channel_id, "Successfully fetched user detail from Jiochat", event,
            start)

        return data
Beispiel #9
0
    def send(self, channel, msg, text):
        # requests are signed with a key built as follows:
        # signing_key = md5(username|password|from|to|msg|key|current_date)
        # where current_date is in the format: d/m/y H
        payload = {'from': channel.address.lstrip('+'), 'to': msg.urn_path.lstrip('+'),
                   'username': channel.config[Channel.CONFIG_USERNAME], 'password': channel.config[Channel.CONFIG_PASSWORD],
                   'msg': text}

        # build our send URL
        url = channel.config[Channel.CONFIG_SEND_URL] + "?" + urlencode(payload)
        start = time.time()

        event = HttpEvent('GET', url)

        try:
            # these guys use a self signed certificate
            response = requests.get(url, headers=http_headers(), timeout=15, verify=False)
            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 != 200 and response.status_code != 201 and response.status_code != 202:
            raise SendException("Got non-200 response [%d] from API" % response.status_code,
                                event=event, start=start)

        Channel.success(channel, msg, WIRED, start, event=event)
Beispiel #10
0
    def refresh_access_token(self, channel_id):
        r = get_redis_connection()
        lock_name = JIOCHAT_ACCESS_TOKEN_REFRESH_LOCK % self.channel_uuid

        if not r.get(lock_name):
            with r.lock(lock_name, timeout=30):
                key = JIOCHAT_ACCESS_TOKEN_KEY % self.channel_uuid

                post_data = dict(grant_type="client_credentials", client_id=self.app_id, client_secret=self.app_secret)
                url = "https://channels.jiochat.com/auth/token.action"

                event = HttpEvent("POST", url, json.dumps(post_data))
                start = time.time()

                response = self._request(url, post_data, access_token=None)
                event.status_code = response.status_code

                if response.status_code != 200:
                    event.response_body = response.content
                    ChannelLog.log_channel_request(channel_id, "Got non-200 response from Jiochat", event, start, True)
                    return

                response_json = response.json()
                event.response_body = json.dumps(response_json)
                ChannelLog.log_channel_request(
                    channel_id, "Successfully fetched access token from Jiochat", event, start
                )

                access_token = response_json["access_token"]
                r.set(key, access_token, ex=7200)
                return access_token
Beispiel #11
0
    def send(self, channel, msg, text):

        # build our message dict
        params = dict(origin=channel.address.lstrip('+'),
                      sms_content=text,
                      destinations=msg.urn_path.lstrip('+'),
                      ybsacctno=channel.config['username'],
                      password=channel.config['password'])
        log_params = params.copy()
        log_params['password'] = '******' * len(log_params['password'])

        start = time.time()
        failed = False
        fatal = False
        events = []

        for send_url in [YO_API_URL_1, YO_API_URL_2, YO_API_URL_3]:
            url = send_url + '?' + urlencode(params)
            log_url = send_url + '?' + urlencode(log_params)

            event = HttpEvent('GET', log_url)
            events.append(event)

            failed = False
            try:
                response = requests.get(url, headers=http_headers(), timeout=5)
                event.status_code = response.status_code
                event.response_body = response.text

                response_qs = urlparse.parse_qs(response.text)
            except Exception:
                failed = True

            if not failed and response.status_code != 200 and response.status_code != 201:
                failed = True

            # if it wasn't successfully delivered, throw
            if not failed and response_qs.get('ybs_autocreate_status',
                                              [''])[0] != 'OK':
                failed = True

            # check if we failed permanently (they blocked us)
            if failed and response_qs.get('ybs_autocreate_message',
                                          [''])[0].find('BLACKLISTED') >= 0:
                contact = Contact.objects.get(id=msg.contact)
                contact.stop(contact.modified_by)
                fatal = True
                break

            # if we sent the message, then move on
            if not failed:
                break

        if failed:
            raise SendException("Received error from Yo! API",
                                events=events,
                                fatal=fatal,
                                start=start)

        Channel.success(channel, msg, SENT, start, events=events)
Beispiel #12
0
    def send(self, channel, msg, text):

        # strip a leading +
        mobile = msg.urn_path[1:] if msg.urn_path.startswith('+') else msg.urn_path

        payload = {
            'user': channel.config[Channel.CONFIG_USERNAME], 'pass': channel.config[Channel.CONFIG_PASSWORD], 'mobile': mobile, 'content': text,
        }

        url = 'http://smail.smscentral.com.np/bp/ApiSms.php'
        log_payload = urlencode(payload)

        event = HttpEvent('POST', url, log_payload)

        start = time.time()

        try:
            response = requests.post(url, data=payload, headers=http_headers(), timeout=30)
            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 != 200 and response.status_code != 201 and response.status_code != 202:
            raise SendException("Got non-200 response [%d] from API" % response.status_code,
                                event=event, start=start)

        Channel.success(channel, msg, WIRED, start, event=event)
Beispiel #13
0
    def refresh_access_token(self, channel_id):
        r = get_redis_connection()
        lock_name = self.TOKEN_REFRESH_LOCK % self.channel_uuid

        if not r.get(lock_name):
            with r.lock(lock_name, timeout=30):
                key = self.TOKEN_STORE_KEY % self.channel_uuid

                post_data = dict(grant_type="client_credentials", client_id=self.app_id, client_secret=self.app_secret)
                url = self.TOKEN_URL

                event = HttpEvent("POST", url, json.dumps(post_data))
                start = time.time()

                response = self._request(url, post_data, access_token=None)
                event.status_code = response.status_code

                if response.status_code != 200:
                    event.response_body = response.content
                    ChannelLog.log_channel_request(
                        channel_id, "Got non-200 response from %s" % self.API_NAME, event, start, True
                    )
                    return

                response_json = response.json()
                event.response_body = json.dumps(response_json)
                ChannelLog.log_channel_request(
                    channel_id, "Successfully fetched access token from %s" % self.API_NAME, event, start
                )

                access_token = response_json["access_token"]
                expires = response_json.get("expires_in", 7200)
                r.set(key, access_token, ex=int(expires))
                return access_token
    def request_media(self, media_id, channel_id):
        access_token = self.get_access_token()

        url = 'https://channels.jiochat.com/media/download.action'

        payload = dict(media_id=media_id)

        event = HttpEvent('GET', url, json.dumps(payload))
        start = time.time()

        response = None

        attempts = 0
        while attempts < 4:
            response = self._request(url, 'GET', payload, access_token)

            # If we fail sleep for a bit then try again up to 4 times
            if response.status_code == 200:
                break
            else:
                attempts += 1
                time.sleep(.250)

        if response:
            event.status_code = response.status_code

        if not event.status_code or event.status_code != 200:
            ChannelLog.log_channel_request(channel_id, "Failed to get media from Jiochat", event, start, True)
        else:
            ChannelLog.log_channel_request(channel_id, "Successfully fetched media from Jiochat", event, start)

        return response
Beispiel #15
0
    def send(self, channel, msg, text):

        # http://175.103.48.29:28078/testing/smsmt.php?
        #   userid=xxx
        #   &password=xxxx
        #   &original=6282881134567
        #   &sendto=628159152565
        #   &messagetype=0
        #   &messageid=1897869768
        #   &message=Test+Normal+Single+Message&dcs=0
        #   &udhl=0&charset=utf-8
        #
        url = HUB9_ENDPOINT
        payload = dict(userid=channel.config['username'], password=channel.config['password'],
                       original=channel.address.lstrip('+'), sendto=msg.urn_path.lstrip('+'),
                       messageid=msg.id, message=text, dcs=0, udhl=0)

        # build up our querystring and send it as a get
        send_url = "%s?%s" % (url, urlencode(payload))
        payload['password'] = '******' * len(payload['password'])
        masked_url = "%s?%s" % (url, urlencode(payload))

        event = HttpEvent('GET', masked_url)

        start = time.time()

        try:
            response = requests.get(send_url, headers=http_headers(), timeout=15)
            event.status_code = response.status_code
            event.response_body = response.text
            if not response:  # pragma: no cover
                raise SendException("Unable to send message",
                                    event=event, start=start)

            if response.status_code != 200 and response.status_code != 201:
                raise SendException("Received non 200 status: %d" % response.status_code,
                                    event=event, start=start)

            # if it wasn't successfully delivered, throw
            if response.text != "000":  # pragma: no cover
                error = "Unknown error"
                if response.text == "001":
                    error = "Error 001: Authentication Error"
                elif response.text == "101":
                    error = "Error 101: Account expired or invalid parameters"

                raise SendException(error, event=event, start=start)

            Channel.success(channel, msg, SENT, start, event=event)

        except SendException as e:
            raise e
        except Exception as e:  # pragma: no cover
            reason = "Unknown error"
            try:
                if e.message and e.message.reason:
                    reason = e.message.reason
            except Exception:
                pass
            raise SendException(u"Unable to send message: %s" % six.text_type(reason)[:64], event=event, start=start)
Beispiel #16
0
    def send(self, channel, msg, text):
        encoding, text = Channel.determine_encoding(text, replace=True)

        # http://http1.javna.com/epicenter/gatewaysendG.asp?LoginName=xxxx&Password=xxxx&Tracking=1&Mobtyp=1&MessageRecipients=962796760057&MessageBody=hi&SenderName=Xxx
        params = dict()
        params['LoginName'] = channel.config[Channel.CONFIG_USERNAME]
        params['Password'] = channel.config[Channel.CONFIG_PASSWORD]
        params['Tracking'] = 1
        params['Mobtyp'] = 1
        params['MessageRecipients'] = msg.urn_path.lstrip('+')
        params['MessageBody'] = text
        params['SenderName'] = channel.address.lstrip('+')

        # we are unicode
        if encoding == Encoding.UNICODE:
            params['Msgtyp'] = 10 if len(text) >= 70 else 9
        elif len(text) > 160:
            params['Msgtyp'] = 5

        url = 'http://http1.javna.com/epicenter/GatewaySendG.asp'
        event = HttpEvent('GET', url + '?' + urlencode(params))
        start = time.time()

        try:
            response = requests.get(url,
                                    params=params,
                                    headers=TEMBA_HEADERS,
                                    timeout=15)
            event.status_code = response.status_code
            event.response_body = response.text

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

        Channel.success(channel, msg, WIRED, start, event=event)
Beispiel #17
0
    def send_message_via_nexmo(self, from_number, to_number, text):
        from temba.channels.models import SendException

        params = dict(api_key=self.api_key, api_secret=self.api_secret)
        params['from'] = from_number.strip('+')
        params['to'] = to_number.strip('+')
        params['text'] = text
        params['status-report-req'] = 1

        # if this isn't going to work as plaintext, send as unicode instead
        if not is_gsm7(text):
            params['type'] = 'unicode'

        log_params = params.copy()
        log_params['api_secret'] = 'x' * len(log_params['api_secret'])
        log_url = NexmoClient.SEND_URL + '?' + urlencode(log_params)

        event = HttpEvent('GET', log_url)

        try:
            response = requests.get(NexmoClient.SEND_URL, params=params)
            event.status_code = response.status_code
            event.response_body = response.text

            response_json = response.json()
            messages = response_json.get('messages', [])
        except Exception:
            raise SendException(u"Failed sending message: %s" % response.text, event=event)

        if not messages or int(messages[0]['status']) != 0:
            raise SendException(u"Failed sending message, received error status [%s]" % messages[0]['status'],
                                event=event)

        else:
            return messages[0]['message-id'], event
Beispiel #18
0
    def send(self, channel, msg, text):
        payload = {
            'address': msg.urn_path.lstrip('+'),
            'message': text,
            'passphrase': channel.config['passphrase'],
            'app_id': channel.config['app_id'],
            'app_secret': channel.config['app_secret']
        }

        url = 'https://devapi.globelabs.com.ph/smsmessaging/v1/outbound/%s/requests' % channel.address
        event = HttpEvent('POST', url, json.dumps(payload))
        start = time.time()

        try:
            response = requests.post(url,
                                     data=payload,
                                     headers=http_headers(),
                                     timeout=5)
            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 != 200 and response.status_code != 201:  # pragma: no cover
            raise SendException("Got non-200 response [%d] from API" %
                                response.status_code,
                                event=event,
                                start=start)

        # parse our response
        response.json()

        Channel.success(channel, msg, WIRED, start, event=event)
Beispiel #19
0
    def send(self, channel, msg, text):

        payload = dict(username=channel.config['username'],
                       to=msg.urn_path,
                       message=text)

        # if this isn't a shared shortcode, send the from address
        if not channel.config.get('is_shared', False):
            payload['from'] = channel.address

        headers = dict(Accept='application/json',
                       apikey=channel.config['api_key'])
        headers.update(TEMBA_HEADERS)

        api_url = "https://api.africastalking.com/version1/messaging"

        event = HttpEvent('POST', api_url, urlencode(payload))

        start = time.time()

        try:
            response = requests.post(api_url,
                                     data=payload,
                                     headers=headers,
                                     timeout=5)
            event.status_code = response.status_code
            event.response_body = response.text
        except Exception as e:
            raise SendException(u"Unable to send message: %s" %
                                six.text_type(e),
                                event=event,
                                start=start)

        if response.status_code != 200 and response.status_code != 201:
            raise SendException("Got non-200 response from API: %d" %
                                response.status_code,
                                event=event,
                                start=start)

        response_data = response.json()

        # grab the status out of our response
        status = response_data['SMSMessageData']['Recipients'][0]['status']
        if status != 'Success':
            raise SendException("Got non success status from API: %s" % status,
                                event=event,
                                start=start)

        # set our external id so we know when it is actually sent, this is missing in cases where
        # it wasn't sent, in which case we'll become an errored message
        external_id = response_data['SMSMessageData']['Recipients'][0][
            'messageId']

        Channel.success(channel,
                        msg,
                        SENT,
                        start,
                        event=event,
                        external_id=external_id)
Beispiel #20
0
    def send(self, channel, msg, text):
        auth_token = channel.config['auth_token']
        send_url = 'https://api.telegram.org/bot%s/sendMessage' % auth_token
        post_body = {'chat_id': msg.urn_path, 'text': text}

        metadata = msg.metadata if hasattr(msg, 'metadata') else {}
        quick_replies = metadata.get('quick_replies', [])
        formatted_replies = json.dumps(
            dict(resize_keyboard=True,
                 one_time_keyboard=True,
                 keyboard=[[dict(text=item[:self.quick_reply_text_size])]
                           for item in quick_replies]))

        if quick_replies:
            post_body['reply_markup'] = formatted_replies

        start = time.time()

        # for now we only support sending one attachment per message but this could change in future
        attachments = Attachment.parse_all(msg.attachments)
        attachment = attachments[0] if attachments else None

        if attachment:
            category = attachment.content_type.split('/')[0]
            if category == 'image':
                send_url = 'https://api.telegram.org/bot%s/sendPhoto' % auth_token
                post_body['photo'] = attachment.url
                post_body['caption'] = text
                del post_body['text']
            elif category == 'video':
                send_url = 'https://api.telegram.org/bot%s/sendVideo' % auth_token
                post_body['video'] = attachment.url
                post_body['caption'] = text
                del post_body['text']
            elif category == 'audio':
                send_url = 'https://api.telegram.org/bot%s/sendAudio' % auth_token
                post_body['audio'] = attachment.url
                post_body['caption'] = text
                del post_body['text']

        event = HttpEvent('POST', send_url, urlencode(post_body))

        try:
            response = requests.post(send_url, post_body)
            event.status_code = response.status_code
            event.response_body = response.text

            external_id = response.json()['result']['message_id']
        except Exception as e:
            raise SendException(str(e), event=event, start=start)

        Channel.success(channel,
                        msg,
                        WIRED,
                        start,
                        event=event,
                        external_id=external_id)
Beispiel #21
0
    def send(self, channel, msg, text):
        start = time.time()

        url = 'https://fcm.googleapis.com/fcm/send'
        title = channel.config.get('FCM_TITLE')
        data = {
            'data': {
                'type': 'rapidpro',
                'title': title,
                'message': text,
                'message_id': msg.id
            },
            'content_available': False,
            'to': msg.auth,
            'priority': 'high'
        }

        if channel.config.get('FCM_NOTIFICATION'):
            data['notification'] = {'title': title, 'body': text}
            data['content_available'] = True

        payload = json.dumps(data)
        headers = http_headers(
            extra={
                'Content-Type': 'application/json',
                'Authorization': 'key=%s' % channel.config.get('FCM_KEY')
            })

        event = HttpEvent('POST', url, payload)

        try:
            response = requests.post(url,
                                     data=payload,
                                     headers=headers,
                                     timeout=5)
            result = json.loads(
                response.text) if response.status_code == 200 else None

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

        if result and 'success' in result and result.get('success') == 1:
            external_id = result.get('multicast_id')
            Channel.success(channel,
                            msg,
                            WIRED,
                            start,
                            events=[event],
                            external_id=external_id)
        else:
            raise SendException(
                "Got non-200 response [%d] from Firebase Cloud Messaging" %
                response.status_code,
                event,
                start=start)
Beispiel #22
0
    def send(self, channel, msg, text):

        url = 'http://bulk.startmobile.com.ua/clients.php'
        post_body = u"""
          <message>
            <service id="single" source=$$FROM$$ validity=$$VALIDITY$$/>
            <to>$$TO$$</to>
            <body content-type="plain/text" encoding="plain">$$BODY$$</body>
          </message>
        """
        post_body = post_body.replace("$$FROM$$", quoteattr(channel.address))

        # tell Start to attempt to deliver this message for up to 12 hours
        post_body = post_body.replace("$$VALIDITY$$", quoteattr("+12 hours"))
        post_body = post_body.replace("$$TO$$", escape(msg.urn_path))
        post_body = post_body.replace("$$BODY$$", escape(text))
        event = HttpEvent('POST', url, post_body)
        post_body = post_body.encode('utf8')

        start = time.time()
        try:
            headers = http_headers(
                extra={'Content-Type': 'application/xml; charset=utf8'})

            response = requests.post(
                url,
                data=post_body,
                headers=headers,
                auth=(channel.config[Channel.CONFIG_USERNAME],
                      channel.config[Channel.CONFIG_PASSWORD]),
                timeout=30)

            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 != 200 and response.status_code != 201
            ) or response.text.find("error") >= 0:
            raise SendException("Error Sending Message",
                                event=event,
                                start=start)

        # parse out our id, this is XML but we only care about the id
        external_id = None
        start_idx = response.text.find("<id>")
        end_idx = response.text.find("</id>")
        if end_idx > start_idx > 0:
            external_id = response.text[start_idx + 4:end_idx]

        Channel.success(channel,
                        msg,
                        WIRED,
                        start,
                        event=event,
                        external_id=external_id)
Beispiel #23
0
    def send(self, channel, msg, text):
        # determine our encoding
        encoding, text = Channel.determine_encoding(text, replace=True)

        # if this looks like unicode, ask clickatell to send as unicode
        if encoding == Encoding.UNICODE:
            unicode_switch = 1
        else:
            unicode_switch = 0

        url = 'https://api.clickatell.com/http/sendmsg'
        payload = {
            'api_id': channel.config[Channel.CONFIG_API_ID],
            'user': channel.config[Channel.CONFIG_USERNAME],
            'password': channel.config[Channel.CONFIG_PASSWORD],
            'from': channel.address.lstrip('+'),
            'concat': 3,
            'callback': 7,
            'mo': 1,
            'unicode': unicode_switch,
            'to': msg.urn_path.lstrip('+'),
            'text': text
        }

        event = HttpEvent('GET', url + "?" + urlencode(payload))

        start = time.time()

        try:
            response = requests.get(url,
                                    params=payload,
                                    headers=http_headers(),
                                    timeout=5)
            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 != 200 and response.status_code != 201 and response.status_code != 202:
            raise SendException("Got non-200 response [%d] from API" %
                                response.status_code,
                                event=event,
                                start=start)

        # parse out the external id for the message, comes in the format: "ID: id12312312312"
        external_id = None
        if response.text.startswith("ID: "):
            external_id = response.text[4:]

        Channel.success(channel,
                        msg,
                        WIRED,
                        start,
                        event=event,
                        external_id=external_id)
Beispiel #24
0
    def send(self, channel, msg, text):
        url = "https://api.infobip.com/sms/1/text/advanced"

        username = channel.config['username']
        password = channel.config['password']
        encoded_auth = base64.b64encode(username + ":" + password)

        headers = http_headers(extra={
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'Authorization': 'Basic %s' % encoded_auth
        })

        # the event url InfoBip will forward delivery reports to
        status_url = 'https://%s%s' % (channel.callback_domain, reverse('courier.ib', args=[channel.uuid, 'delivered']))

        payload = {"messages": [
            {
                "from": channel.address.lstrip('+'),
                "destinations": [
                    {"to": msg.urn_path.lstrip('+'), "messageId": msg.id}
                ],
                "text": text,
                "notifyContentType": "application/json",
                "intermediateReport": True,
                "notifyUrl": status_url
            }
        ]}

        event = HttpEvent('POST', url, json.dumps(payload))
        events = [event]
        start = time.time()

        try:
            response = requests.post(url, json=payload, headers=headers, timeout=5)
            event.status_code = response.status_code
            event.response_body = response.text
        except Exception as e:
            raise SendException(u"Unable to send message: %s" % six.text_type(e),
                                events=events, start=start)

        if response.status_code != 200 and response.status_code != 201:
            raise SendException("Received non 200 status: %d" % response.status_code,
                                events=events, start=start)

        response_json = response.json()
        messages = response_json['messages']

        # if it wasn't successfully delivered, throw
        if int(messages[0]['status']['groupId']) not in [1, 3]:
            raise SendException("Received error status: %s" % messages[0]['status']['description'],
                                events=events, start=start)

        Channel.success(channel, msg, SENT, start, events=events)
Beispiel #25
0
    def send(self, channel, msg, text):

        # determine our encoding
        encoding, text = Channel.determine_encoding(text, replace=True)

        # if this looks like unicode, ask m3tech to send as unicode
        if encoding == Encoding.UNICODE:
            sms_type = '7'
        else:
            sms_type = '0'

        url = 'https://secure.m3techservice.com/GenericServiceRestAPI/api/SendSMS'
        payload = {'AuthKey': 'm3-Tech',
                   'UserId': channel.config[Channel.CONFIG_USERNAME],
                   'Password': channel.config[Channel.CONFIG_PASSWORD],
                   'MobileNo': msg.urn_path.lstrip('+'),
                   'MsgId': msg.id,
                   'SMS': text,
                   'MsgHeader': channel.address.lstrip('+'),
                   'SMSType': sms_type,
                   'HandsetPort': '0',
                   'SMSChannel': '0',
                   'Telco': '0'}

        event = HttpEvent('GET', url + "?" + urlencode(payload))

        start = time.time()

        try:
            response = requests.get(url, params=payload, headers=TEMBA_HEADERS, timeout=5)
            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 != 200 and response.status_code != 201 and response.status_code != 202:
            raise SendException("Got non-200 response [%d] from API" % response.status_code,
                                event=event, start=start)

        # our response is JSON and should contain a 0 as a status code:
        # [{"Response":"0"}]
        try:
            response_code = json.loads(response.text)[0]["Response"]
        except Exception as e:
            response_code = str(e)

        # <Response>0</Response>
        if response_code != "0":
            raise SendException("Received non-zero status from API: %s" % str(response_code),
                                event=event, start=start)

        Channel.success(channel, msg, WIRED, start, event=event)
Beispiel #26
0
    def send(self, channel, msg, text):

        payload = {
            'address': msg.urn_path,
            'senderaddress': channel.address,
            'message': text,
        }

        url = 'http://api.blackmyna.com/2/smsmessaging/outbound'
        external_id = None
        start = time.time()

        event = HttpEvent('POST', url, payload)

        try:
            response = requests.post(
                url,
                data=payload,
                headers=http_headers(),
                timeout=30,
                auth=(channel.config[Channel.CONFIG_USERNAME],
                      channel.config[Channel.CONFIG_PASSWORD]))
            # parse our response, should be JSON that looks something like:
            # [{
            #   "recipient" : recipient_number_1,
            #   "id" : Unique_identifier (universally unique identifier UUID)
            # }]
            event.status_code = response.status_code
            event.response_body = response.text

            response_json = response.json()

            # we only care about the first piece
            if response_json and len(response_json) > 0:
                external_id = response_json[0].get('id', None)

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

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

        Channel.success(channel,
                        msg,
                        WIRED,
                        start,
                        event=event,
                        external_id=external_id)
Beispiel #27
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)
Beispiel #28
0
    def send(self, channel, msg, text):
        url = 'https://chatapi.viber.com/pa/send_message'
        payload = {
            'auth_token': channel.config['auth_token'],
            'receiver': msg.urn_path,
            'text': text,
            'type': 'text',
            'tracking_data': msg.id
        }

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

        start = time.time()

        headers = {'Accept': 'application/json'}
        headers.update(TEMBA_HEADERS)

        try:
            response = requests.post(url,
                                     json=payload,
                                     headers=headers,
                                     timeout=5)
            event.status_code = response.status_code
            event.response_body = response.text

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

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

        # success is 0, everything else is a failure
        if response_json['status'] != 0:
            raise SendException("Got non-0 status [%d] from API" %
                                response_json['status'],
                                event=event,
                                fatal=True,
                                start=start)

        external_id = response.json().get('message_token', None)
        Channel.success(channel,
                        msg,
                        WIRED,
                        start,
                        event=event,
                        external_id=external_id)
Beispiel #29
0
    def send(self, channel, msg, text):
        # Zenvia accepts messages via a GET
        # http://www.zenvia360.com.br/GatewayIntegration/msgSms.do?dispatch=send&account=temba&
        # code=abc123&to=5511996458779&msg=my message content&id=123&callbackOption=1
        payload = dict(dispatch='send',
                       account=channel.config['account'],
                       code=channel.config['code'],
                       msg=text,
                       to=msg.urn_path,
                       id=msg.id,
                       callbackOption=1)

        zenvia_url = "http://www.zenvia360.com.br/GatewayIntegration/msgSms.do"
        headers = http_headers(extra={
            'Content-Type': "text/html",
            'Accept-Charset': 'ISO-8859-1'
        })

        event = HttpEvent('POST', zenvia_url, urlencode(payload))

        start = time.time()

        try:
            response = requests.get(zenvia_url,
                                    params=payload,
                                    headers=headers,
                                    timeout=5)
            event.status_code = response.status_code
            event.response_body = response.text

        except Exception as e:
            raise SendException(u"Unable to send message: %s" %
                                six.text_type(e),
                                event=event,
                                start=start)

        if response.status_code != 200 and response.status_code != 201:
            raise SendException("Got non-200 response from API: %d" %
                                response.status_code,
                                event=event,
                                start=start)

        response_code = int(response.text[:3])

        if response_code != 0:
            raise Exception("Got non-zero response from Zenvia: %s" %
                            response.text)

        Channel.success(channel, msg, WIRED, start, event=event)
Beispiel #30
0
    def download_media(self, call, media_url):
        """
        Fetches the recording and stores it with the provided recording_id
        :param media_url: the url where the media lives
        :return: the url for our downloaded media with full content type prefix
        """
        attempts = 0
        response = None
        while attempts < 4:
            response = self.download_recording(media_url)

            # in some cases Twilio isn't ready for us to fetch the recording URL yet, if we get a 404
            # sleep for a bit then try again up to 4 times
            if response.status_code == 200:
                break
            else:
                attempts += 1
                time.sleep(0.250)

        content_type, downloaded = self.org.save_response_media(response)

        if content_type:
            # log that we downloaded it to our own url
            request = response.request
            event = HttpEvent(request.method, request.url, request.body, response.status_code, downloaded)
            ChannelLog.log_ivr_interaction(call, "Downloaded media", event)

            return "%s:%s" % (content_type, downloaded)

        return None
Beispiel #31
0
    def start_call(self, call, to, from_, status_callback):
        if not settings.SEND_CALLS:
            raise ValueError("SEND_CALLS set to False, skipping call start")

        url = "https://%s%s" % (self.org.get_brand_domain(), reverse("ivr.ivrcall_handle", args=[call.pk]))

        params = dict()
        params["answer_url"] = [url]
        params["answer_method"] = "POST"
        params["to"] = [dict(type="phone", number=to.strip("+"))]
        params["from"] = dict(type="phone", number=from_.strip("+"))
        params["event_url"] = ["%s?has_event=1" % url]
        params["event_method"] = "POST"

        try:
            response = self.create_call(params=params)
            call_uuid = response.get("uuid", None)
            call.external_id = str(call_uuid)

            # the call was successfully sent to the IVR provider
            call.status = IVRCall.WIRED
            call.save()

            for event in self.events:
                ChannelLog.log_ivr_interaction(call, "Started call", event)

        except Exception as e:
            event = HttpEvent("POST", "https://api.nexmo.com/v1/calls", json.dumps(params), response_body=str(e))
            ChannelLog.log_ivr_interaction(call, "Call start failed", event, is_error=True)

            call.status = IVRCall.FAILED
            call.save()

            raise IVRException(_("Nexmo call failed, with error %s") % str(e))
Beispiel #32
0
    def start_call(self, call, to, from_, status_callback):
        if not settings.SEND_CALLS:
            raise ValueError("SEND_CALLS set to False, skipping call start")

        params = dict(to=to, from_=call.channel.address, url=status_callback, status_callback=status_callback)

        try:
            twilio_call = self.api.calls.create(**params)
            call.external_id = str(twilio_call.sid)

            # the call was successfully sent to the IVR provider
            call.status = IVRCall.WIRED
            call.save()

            for event in self.events:
                ChannelLog.log_ivr_interaction(call, "Started call", event)

        except TwilioRestException as twilio_error:
            message = "Twilio Error: %s" % twilio_error.msg
            if twilio_error.code == 20003:
                message = _("Could not authenticate with your Twilio account. Check your token and try again.")

            event = HttpEvent("POST", "https://api.nexmo.com/v1/calls", json.dumps(params), response_body=str(message))
            ChannelLog.log_ivr_interaction(call, "Call start failed", event, is_error=True)

            call.status = IVRCall.FAILED
            call.save()

            raise IVRException(message)
Beispiel #33
0
    def refresh_access_token(self, channel_id):
        r = get_redis_connection()
        lock_name = self.TOKEN_REFRESH_LOCK % self.channel_uuid

        if not r.get(lock_name):
            with r.lock(lock_name, timeout=30):
                key = self.TOKEN_STORE_KEY % self.channel_uuid

                data = dict(grant_type="client_credential", appid=self.app_id, secret=self.app_secret)
                url = self.TOKEN_URL

                event = HttpEvent("GET", url + "?" + urlencode(data))
                start = time.time()

                response = requests.get(url, params=data, timeout=15)
                event.status_code = response.status_code

                if response.status_code != 200:
                    event.response_body = response.content
                    ChannelLog.log_channel_request(
                        channel_id, "Got non-200 response from %s" % self.API_NAME, event, start, True
                    )
                    return

                response_json = response.json()
                has_error = False
                if response_json.get("errcode", -1) != 0:
                    has_error = True

                event.response_body = json.dumps(response_json)
                ChannelLog.log_channel_request(
                    channel_id, "Successfully fetched access token from %s" % self.API_NAME, event, start, has_error
                )

                access_token = response_json.get("access_token", "")
                expires = response_json.get("expires_in", 7200)
                if access_token:
                    r.set(key, access_token, ex=int(expires))
                    return access_token
Beispiel #34
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,
            )
Beispiel #35
0
    def _request(self, url, method="GET", params=None, api_call=None):
        """Internal request method"""
        method = method.lower()
        params = params or {}

        func = getattr(self.client, method)
        params, files = (params, None) if "event" in params else _transparent_params(params)

        requests_args = {}
        for k, v in self.client_args.items():
            # Maybe this should be set as a class variable and only done once?
            if k in ("timeout", "allow_redirects", "stream", "verify"):
                requests_args[k] = v

        if method == "get":
            requests_args["params"] = params
        else:
            requests_args.update({"data": json.dumps(params) if "event" in params else params, "files": files})
        try:
            if method == "get":
                event = HttpEvent(method, url + "?" + urlencode(params))
            else:
                event = HttpEvent(method, url, urlencode(params))
            self.events.append(event)

            response = func(url, **requests_args)
            event.status_code = response.status_code
            event.response_body = response.text

        except requests.RequestException as e:
            raise TwythonError(str(e))
        content = response.content.decode("utf-8")

        # create stash for last function intel
        self._last_call = {
            "api_call": api_call,
            "api_error": None,
            "cookies": response.cookies,
            "headers": response.headers,
            "status_code": response.status_code,
            "url": response.url,
            "content": content,
        }

        #  Wrap the json loads in a try, and defer an error
        #  Twitter will return invalid json with an error code in the headers
        json_error = False
        if content:
            try:
                try:
                    # try to get json
                    content = content.json()
                except AttributeError:
                    # if unicode detected
                    content = json.loads(content)
            except ValueError:
                json_error = True
                content = {}

        if response.status_code > 304:
            # If there is no error message, use a default.
            errors = content.get("errors", [{"message": "An error occurred processing your request."}])
            if errors and isinstance(errors, list):
                error_message = errors[0]["message"]
            else:
                error_message = errors  # pragma: no cover
            self._last_call["api_error"] = error_message

            ExceptionType = TwythonError
            if response.status_code == 429:
                # Twitter API 1.1, always return 429 when rate limit is exceeded
                ExceptionType = TwythonRateLimitError  # pragma: no cover
            elif response.status_code == 401 or "Bad Authentication data" in error_message:
                # Twitter API 1.1, returns a 401 Unauthorized or
                # a 400 "Bad Authentication data" for invalid/expired app keys/user tokens
                ExceptionType = TwythonAuthError

            raise ExceptionType(
                error_message, error_code=response.status_code, retry_after=response.headers.get("retry-after")
            )

        # if we have a json error here, then it's not an official Twitter API error
        if json_error and response.status_code not in (200, 201, 202):  # pragma: no cover
            raise TwythonError("Response was not valid JSON, unable to decode.")

        return content