예제 #1
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)
예제 #2
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)
예제 #3
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)
예제 #4
0
    def form_valid(self, form, *args, **kwargs):
        data = form.cleaned_data
        auth_id = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_ID, None)
        auth_token = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_TOKEN,
                                              None)

        try:
            params = dict(country_iso=data["country"],
                          pattern=data.get("pattern"))
            url = f"https://api.plivo.com/v1/Account/{auth_id}/PhoneNumber/?{urlencode(params)}"

            headers = http_headers(extra={"Content-Type": "application/json"})
            response = requests.get(url,
                                    headers=headers,
                                    auth=(auth_id, auth_token))

            if response.status_code == 200:
                response_data = response.json()
                results_numbers = [
                    "+" + number_dict["number"]
                    for number_dict in response_data["objects"]
                ]
            else:
                return JsonResponse({"error": response.text})

            numbers = []
            for number in results_numbers:
                numbers.append(
                    phonenumbers.format_number(
                        phonenumbers.parse(number, None),
                        phonenumbers.PhoneNumberFormat.INTERNATIONAL))

            return JsonResponse(numbers, safe=False)
        except Exception as e:
            return JsonResponse({"error": str(e)})
예제 #5
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)
예제 #6
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)
예제 #7
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)
예제 #8
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)
예제 #9
0
    def get_existing_numbers(self, org):
        auth_id = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_ID, None)
        auth_token = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_TOKEN,
                                              None)

        headers = http_headers(extra={"Content-Type": "application/json"})
        response = requests.get("https://api.plivo.com/v1/Account/%s/Number/" %
                                auth_id,
                                headers=headers,
                                auth=(auth_id, auth_token))

        account_numbers = []
        if response.status_code == 200:
            data = response.json()
            for number_dict in data["objects"]:
                region = number_dict["region"]
                country_name = region.split(",")[-1].strip().title()
                country = pycountry.countries.get(name=country_name).alpha_2
                if len(number_dict["number"]) <= 6:
                    phone_number = number_dict["number"]
                else:
                    parsed = phonenumbers.parse("+" + number_dict["number"],
                                                None)
                    phone_number = phonenumbers.format_number(
                        parsed, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
                account_numbers.append(
                    dict(number=phone_number, country=country))

        return account_numbers
예제 #10
0
파일: type.py 프로젝트: teehamaral/rapidpro
 def deactivate(self, channel):
     config = channel.config
     requests.delete(
         "https://api.plivo.com/v1/Account/%s/Application/%s/"
         % (config[Channel.CONFIG_PLIVO_AUTH_ID], config[Channel.CONFIG_PLIVO_APP_ID]),
         auth=(config[Channel.CONFIG_PLIVO_AUTH_ID], config[Channel.CONFIG_PLIVO_AUTH_TOKEN]),
         headers=http_headers(extra={"Content-Type": "application/json"}),
     )
예제 #11
0
 def deactivate(self, channel):
     config = channel.config
     requests.delete(
         "https://api.plivo.com/v1/Account/%s/Application/%s/"
         % (config[Channel.CONFIG_PLIVO_AUTH_ID], config[Channel.CONFIG_PLIVO_APP_ID]),
         auth=(config[Channel.CONFIG_PLIVO_AUTH_ID], config[Channel.CONFIG_PLIVO_AUTH_TOKEN]),
         headers=http_headers(extra={"Content-Type": "application/json"}),
     )
예제 #12
0
    def claim_number(self, user, phone_number, country, role):

        auth_id = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_ID, None)
        auth_token = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_TOKEN, None)

        org = user.get_org()

        plivo_uuid = generate_uuid()
        callback_domain = org.get_brand_domain()
        app_name = "%s/%s" % (callback_domain.lower(), plivo_uuid)

        message_url = "https://" + callback_domain + "%s" % reverse('handlers.plivo_handler', args=['receive', plivo_uuid])
        answer_url = "https://" + settings.AWS_BUCKET_DOMAIN + "/plivo_voice_unavailable.xml"

        headers = http_headers(extra={'Content-Type': "application/json"})
        create_app_url = "https://api.plivo.com/v1/Account/%s/Application/" % auth_id

        response = requests.post(create_app_url, json=dict(app_name=app_name, answer_url=answer_url, message_url=message_url),
                                 headers=headers, auth=(auth_id, auth_token))

        if response.status_code in [201, 200, 202]:
            plivo_app_id = response.json()['app_id']
        else:  # pragma: no cover
            plivo_app_id = None

        plivo_config = {Channel.CONFIG_PLIVO_AUTH_ID: auth_id,
                        Channel.CONFIG_PLIVO_AUTH_TOKEN: auth_token,
                        Channel.CONFIG_PLIVO_APP_ID: plivo_app_id,
                        Channel.CONFIG_CALLBACK_DOMAIN: org.get_brand_domain()}

        plivo_number = phone_number.strip('+ ').replace(' ', '')
        response = requests.get("https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number), headers=headers, auth=(auth_id, auth_token))

        if response.status_code != 200:
            response = requests.post("https://api.plivo.com/v1/Account/%s/PhoneNumber/%s/" % (auth_id, plivo_number), headers=headers, auth=(auth_id, auth_token))

            if response.status_code != 201:  # pragma: no cover
                raise Exception(_("There was a problem claiming that number, please check the balance on your account."))

            response = requests.get("https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number), headers=headers, auth=(auth_id, auth_token))

        if response.status_code == 200:
            response = requests.post("https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number),
                                     json=dict(app_id=plivo_app_id), headers=headers, auth=(auth_id, auth_token))

            if response.status_code != 202:  # pragma: no cover
                raise Exception(_("There was a problem updating that number, please try again."))

        phone_number = '+' + plivo_number
        phone = phonenumbers.format_number(phonenumbers.parse(phone_number, None),
                                           phonenumbers.PhoneNumberFormat.NATIONAL)

        channel = Channel.create(org, user, country, 'PL', name=phone, address=phone_number,
                                 config=plivo_config, uuid=plivo_uuid)

        analytics.track(user.username, 'temba.channel_claim_plivo', dict(number=phone_number))

        return channel
예제 #13
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)
예제 #14
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)
예제 #15
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)
예제 #16
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 = http_headers(
            dict(Accept='application/json', apikey=channel.config['api_key']))
        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)
예제 #17
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)
예제 #18
0
    def pre_process(self, *args, **kwargs):
        auth_id = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_ID, None)
        auth_token = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_TOKEN, None)

        headers = http_headers(extra={'Content-Type': "application/json"})
        response = requests.get("https://api.plivo.com/v1/Account/%s/" % auth_id, headers=headers, auth=(auth_id, auth_token))

        if response.status_code == 200:
            return None
        else:
            return HttpResponseRedirect(reverse('orgs.org_plivo_connect'))
예제 #19
0
    def _request(self, url, method='GET', params=None, access_token=None):
        headers = http_headers(extra={'Authorization': 'Bearer ' + access_token} if access_token else {})

        if method == 'POST_JSON':
            response = requests.post(url, json=params, headers=headers, timeout=15)
        elif method == 'POST':
            response = requests.post(url, data=params, headers=headers, timeout=15)
        else:
            response = requests.get(url, params=params, headers=headers, timeout=15)

        return response
예제 #20
0
파일: views.py 프로젝트: mxabierto/rapidpro
    def pre_process(self, *args, **kwargs):
        auth_id = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_ID, None)
        auth_token = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_TOKEN, None)

        headers = http_headers(extra={"Content-Type": "application/json"})
        response = requests.get(
            "https://api.plivo.com/v1/Account/%s/" % auth_id, headers=headers, auth=(auth_id, auth_token)
        )

        if response.status_code == 200:
            return None
        else:
            return HttpResponseRedirect(reverse("orgs.org_plivo_connect"))
예제 #21
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)
예제 #22
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)
예제 #23
0
        def clean(self):
            access_token = self.cleaned_data.get('access_token')
            secret = self.cleaned_data.get('secret')

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

            response = requests.get('https://api.line.me/v1/oauth/verify',
                                    headers=headers)
            content = response.json()

            if response.status_code != 200:
                raise ValidationError(content.get('error_desciption'))
            else:
                channel_id = content.get('channelId')
                channel_mid = content.get('mid')

                credentials = {
                    'channel_id': channel_id,
                    'channel_mid': channel_mid,
                    'channel_access_token': access_token,
                    'channel_secret': secret
                }

                existing = Channel.objects.filter(
                    Q(config__contains=channel_id) | Q(config__contains=secret)
                    | Q(config__contains=access_token),
                    channel_type=self.channel_type.code,
                    address=channel_mid,
                    is_active=True).first()
                if existing:
                    raise ValidationError(
                        _("A channel with this configuration already exists."))

                headers.pop('Content-Type')
                response_profile = requests.get(
                    'https://api.line.me/v1/profile', headers=headers)
                content_profile = json.loads(response_profile.content)

                credentials['profile'] = {
                    'picture_url': content_profile.get('pictureUrl'),
                    'display_name': content_profile.get('displayName')
                }

                return credentials
예제 #24
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 = http_headers(extra={'Accept': 'application/json'})

        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)
예제 #25
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)
예제 #26
0
    def send(self, channel, msg, text):
        payload = {
            'id': str(msg.id),
            'text': text,
            'to': msg.urn_path,
            'to_no_plus': msg.urn_path.lstrip('+'),
            'from': channel.address,
            'from_no_plus': channel.address.lstrip('+'),
            'channel': str(channel.id)
        }

        # build our send URL
        url = Channel.replace_variables(channel.config[Channel.CONFIG_SEND_URL], payload)
        start = time.time()

        method = channel.config.get(Channel.CONFIG_SEND_METHOD, 'POST')

        content_type = channel.config.get(Channel.CONFIG_CONTENT_TYPE, Channel.CONTENT_TYPE_URLENCODED)
        headers = http_headers(extra={'Content-Type': Channel.CONTENT_TYPES[content_type]})

        event = HttpEvent(method, url)

        if method in ('POST', 'PUT'):
            body = channel.config.get(Channel.CONFIG_SEND_BODY, Channel.CONFIG_DEFAULT_SEND_BODY)
            body = Channel.replace_variables(body, payload, content_type)
            event.request_body = body

        try:
            if method == 'POST':
                response = requests.post(url, data=body.encode('utf8'), headers=headers, timeout=5)
            elif method == 'PUT':
                response = requests.put(url, data=body.encode('utf8'), headers=headers, timeout=5)
            else:
                response = requests.get(url, headers=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)

        Channel.success(channel, msg, WIRED, start, event=event)
예제 #27
0
    def send(self, channel, msg, text):
        channel_access_token = channel.config.get(Channel.CONFIG_AUTH_TOKEN)

        data = json.dumps({
            'to': msg.urn_path,
            'messages': [{
                'type': 'text',
                'text': text
            }]
        })

        start = time.time()
        headers = http_headers(
            extra={
                'Content-Type': 'application/json',
                'Authorization': 'Bearer %s' % channel_access_token
            })
        send_url = 'https://api.line.me/v2/bot/message/push'

        event = HttpEvent('POST', send_url, data)

        try:
            response = requests.post(send_url, data=data, headers=headers)
            response.json()

            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, 202]:  # pragma: needs cover
            raise SendException("Got non-200 response [%d] from Line" %
                                response.status_code,
                                event=event,
                                start=start)

        Channel.success(channel, msg, WIRED, start, event=event)
예제 #28
0
파일: views.py 프로젝트: mxabierto/rapidpro
    def get_existing_numbers(self, org):
        auth_id = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_ID, None)
        auth_token = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_TOKEN, None)

        headers = http_headers(extra={"Content-Type": "application/json"})
        response = requests.get(
            "https://api.plivo.com/v1/Account/%s/Number/" % auth_id, headers=headers, auth=(auth_id, auth_token)
        )

        account_numbers = []
        if response.status_code == 200:
            data = response.json()
            for number_dict in data["objects"]:
                region = number_dict["region"]
                country_name = region.split(",")[-1].strip().title()
                country = pycountry.countries.get(name=country_name).alpha_2
                if len(number_dict["number"]) <= 6:
                    phone_number = number_dict["number"]
                else:
                    parsed = phonenumbers.parse("+" + number_dict["number"], None)
                    phone_number = phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
                account_numbers.append(dict(number=phone_number, country=country))

        return account_numbers
예제 #29
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 = {
            'LoginName': channel.config[Channel.CONFIG_USERNAME],
            'Password': channel.config[Channel.CONFIG_PASSWORD],
            'Tracking': 1,
            'Mobtyp': 1,
            'MessageRecipients': msg.urn_path.lstrip('+'),
            'MessageBody': text,
            '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=http_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)
예제 #30
0
파일: views.py 프로젝트: mxabierto/rapidpro
    def claim_number(self, user, phone_number, country, role):

        auth_id = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_ID, None)
        auth_token = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_TOKEN, None)

        org = user.get_org()

        plivo_uuid = generate_uuid()
        callback_domain = org.get_brand_domain()
        app_name = "%s/%s" % (callback_domain.lower(), plivo_uuid)

        message_url = "https://" + callback_domain + "%s" % reverse("courier.pl", args=[plivo_uuid, "receive"])
        answer_url = "https://" + settings.TEMBA_HOST+settings.MEDIA_URL+ "plivo_voice_unavailable.xml"

        headers = http_headers(extra={"Content-Type": "application/json"})
        create_app_url = "https://api.plivo.com/v1/Account/%s/Application/" % auth_id

        response = requests.post(
            create_app_url,
            json=dict(app_name=app_name, answer_url=answer_url, message_url=message_url),
            headers=headers,
            auth=(auth_id, auth_token),
        )

        if response.status_code in [201, 200, 202]:
            plivo_app_id = response.json()["app_id"]
        else:  # pragma: no cover
            plivo_app_id = None

        plivo_config = {
            Channel.CONFIG_PLIVO_AUTH_ID: auth_id,
            Channel.CONFIG_PLIVO_AUTH_TOKEN: auth_token,
            Channel.CONFIG_PLIVO_APP_ID: plivo_app_id,
            Channel.CONFIG_CALLBACK_DOMAIN: org.get_brand_domain(),
        }

        plivo_number = phone_number.strip("+ ").replace(" ", "")
        response = requests.get(
            "https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number),
            headers=headers,
            auth=(auth_id, auth_token),
        )

        if response.status_code != 200:
            response = requests.post(
                "https://api.plivo.com/v1/Account/%s/PhoneNumber/%s/" % (auth_id, plivo_number),
                headers=headers,
                auth=(auth_id, auth_token),
            )

            if response.status_code != 201:  # pragma: no cover
                raise Exception(
                    _("There was a problem claiming that number, please check the balance on your account.")
                )

            response = requests.get(
                "https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number),
                headers=headers,
                auth=(auth_id, auth_token),
            )

        if response.status_code == 200:
            response = requests.post(
                "https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number),
                json=dict(app_id=plivo_app_id),
                headers=headers,
                auth=(auth_id, auth_token),
            )

            if response.status_code != 202:  # pragma: no cover
                raise Exception(_("There was a problem updating that number, please try again."))

        phone_number = "+" + plivo_number
        phone = phonenumbers.format_number(
            phonenumbers.parse(phone_number, None), phonenumbers.PhoneNumberFormat.NATIONAL
        )

        channel = Channel.create(
            org, user, country, "PL", name=phone, address=phone_number, config=plivo_config, uuid=plivo_uuid
        )

        analytics.track(user.username, "temba.channel_claim_plivo", dict(number=phone_number))

        return channel
예제 #31
0
def call_webhook(run,
                 webhook_url,
                 ruleset,
                 msg,
                 action="POST",
                 resthook=None,
                 headers=None):
    from temba.api.models import WebHookEvent, WebHookResult
    from temba.flows.models import Flow

    flow = run.flow
    contact = run.contact
    org = flow.org
    channel = msg.channel if msg else None
    contact_urn = msg.contact_urn if (
        msg and msg.contact_urn) else contact.get_urn()

    contact_dict = dict(uuid=contact.uuid, name=contact.name)
    if contact_urn:
        contact_dict["urn"] = contact_urn.urn

    post_data = {
        "contact":
        contact_dict,
        "flow":
        dict(name=flow.name,
             uuid=flow.uuid,
             revision=flow.revisions.order_by("revision").last().revision),
        "path":
        run.path,
        "results":
        run.results,
        "run":
        dict(uuid=str(run.uuid), created_on=run.created_on.isoformat()),
    }

    if msg and msg.id > 0:
        post_data["input"] = dict(
            urn=msg.contact_urn.urn if msg.contact_urn else None,
            text=msg.text,
            attachments=(msg.attachments or []))

    if channel:
        post_data["channel"] = dict(name=channel.name, uuid=channel.uuid)

    if not action:  # pragma: needs cover
        action = "POST"

    if resthook:
        WebHookEvent.objects.create(org=org,
                                    data=post_data,
                                    action=action,
                                    resthook=resthook)

    status_code = -1
    message = "None"
    body = None
    request = ""

    start = time.time()

    # webhook events fire immediately since we need the results back
    try:
        # no url, bail!
        if not webhook_url:
            raise ValueError("No webhook_url specified, skipping send")

        # only send webhooks when we are configured to, otherwise fail
        if settings.SEND_WEBHOOKS:
            requests_headers = http_headers(extra=headers)

            s = requests.Session()

            # some hosts deny generic user agents, use Temba as our user agent
            if action == "GET":
                prepped = requests.Request("GET",
                                           webhook_url,
                                           headers=requests_headers).prepare()
            else:
                requests_headers["Content-type"] = "application/json"
                prepped = requests.Request("POST",
                                           webhook_url,
                                           data=json.dumps(post_data),
                                           headers=requests_headers).prepare()

            request = prepped_request_to_str(prepped)
            response = s.send(prepped, timeout=10)
            body = response.text
            if body:
                body = body.strip()
            status_code = response.status_code

        else:
            print("!! Skipping WebHook send, SEND_WEBHOOKS set to False")
            body = "Skipped actual send"
            status_code = 200

        if ruleset:
            run.update_fields({Flow.label_to_slug(ruleset.label): body},
                              do_save=False)
        new_extra = {}

        # process the webhook response
        try:
            response_json = json.loads(body)

            # only update if we got a valid JSON dictionary or list
            if not isinstance(response_json, dict) and not isinstance(
                    response_json, list):
                raise ValueError(
                    "Response must be a JSON dictionary or list, ignoring response."
                )

            new_extra = response_json
            message = "Webhook called successfully."
        except ValueError:
            message = "Response must be a JSON dictionary, ignoring response."

        run.update_fields(new_extra)

        if not (200 <= status_code < 300):
            message = "Got non 200 response (%d) from webhook." % response.status_code
            raise ValueError("Got non 200 response (%d) from webhook." %
                             response.status_code)

    except (requests.ReadTimeout, ValueError) as e:
        message = f"Error calling webhook: {str(e)}"

    except Exception as e:
        logger.error(f"Could not trigger flow webhook: {str(e)}",
                     exc_info=True)

        message = "Error calling webhook: %s" % str(e)

    finally:
        # make sure our message isn't too long
        if message:
            message = message[:255]

        if body is None:
            body = message

        request_time = (time.time() - start) * 1000

        contact = None
        if run:
            contact = run.contact

        result = WebHookResult.objects.create(
            contact=contact,
            url=webhook_url,
            status_code=status_code,
            response=body,
            request=request,
            request_time=request_time,
            org=run.org,
        )

    return result
예제 #32
0
    def claim_number(self, user, phone_number, country, role):

        auth_id = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_ID, None)
        auth_token = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_TOKEN, None)

        org = user.get_org()

        plivo_uuid = generate_uuid()
        callback_domain = org.get_brand_domain()
        app_name = "%s/%s" % (callback_domain.lower(), plivo_uuid)

        message_url = f"https://{callback_domain}{reverse('courier.pl', args=[plivo_uuid, 'receive'])}"
        answer_url = f"{settings.STORAGE_URL}/plivo_voice_unavailable.xml"

        headers = http_headers(extra={"Content-Type": "application/json"})
        create_app_url = "https://api.plivo.com/v1/Account/%s/Application/" % auth_id

        response = requests.post(
            create_app_url,
            json=dict(app_name=app_name, answer_url=answer_url, message_url=message_url),
            headers=headers,
            auth=(auth_id, auth_token),
        )

        if response.status_code in [201, 200, 202]:
            plivo_app_id = response.json()["app_id"]
        else:  # pragma: no cover
            plivo_app_id = None

        plivo_config = {
            Channel.CONFIG_PLIVO_AUTH_ID: auth_id,
            Channel.CONFIG_PLIVO_AUTH_TOKEN: auth_token,
            Channel.CONFIG_PLIVO_APP_ID: plivo_app_id,
            Channel.CONFIG_CALLBACK_DOMAIN: org.get_brand_domain(),
        }

        plivo_number = phone_number.strip("+ ").replace(" ", "")
        response = requests.get(
            "https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number),
            headers=headers,
            auth=(auth_id, auth_token),
        )

        if response.status_code != 200:
            response = requests.post(
                "https://api.plivo.com/v1/Account/%s/PhoneNumber/%s/" % (auth_id, plivo_number),
                headers=headers,
                auth=(auth_id, auth_token),
            )

            if response.status_code != 201:  # pragma: no cover
                raise Exception(
                    _("There was a problem claiming that number, please check the balance on your account.")
                )

            response = requests.get(
                "https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number),
                headers=headers,
                auth=(auth_id, auth_token),
            )

        if response.status_code == 200:
            response = requests.post(
                "https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number),
                json=dict(app_id=plivo_app_id),
                headers=headers,
                auth=(auth_id, auth_token),
            )

            if response.status_code != 202:  # pragma: no cover
                raise Exception(_("There was a problem updating that number, please try again."))

        phone_number = "+" + plivo_number
        phone = phonenumbers.format_number(
            phonenumbers.parse(phone_number, None), phonenumbers.PhoneNumberFormat.NATIONAL
        )

        channel = Channel.create(
            org, user, country, "PL", name=phone, address=phone_number, config=plivo_config, uuid=plivo_uuid
        )

        return channel
예제 #33
0
 def _request(self, url, params=None, access_token=None):
     headers = http_headers(extra={"Authorization": "Bearer " + access_token} if access_token else {})
     response = requests.post(url, data=params, headers=headers, timeout=15)
     return response
예제 #34
0
    def trigger_flow_webhook(cls,
                             run,
                             webhook_url,
                             node_uuid,
                             msg,
                             action='POST',
                             resthook=None,
                             headers=None):

        flow = run.flow
        contact = run.contact
        org = flow.org
        channel = msg.channel if msg else None
        contact_urn = msg.contact_urn if msg else contact.get_urn()

        contact_dict = dict(uuid=contact.uuid, name=contact.name)
        if contact_urn:
            contact_dict['urn'] = contact_urn.urn

        post_data = {
            'contact':
            contact_dict,
            'flow':
            dict(name=flow.name, uuid=flow.uuid),
            'path':
            run.get_path(),
            'results':
            run.get_results(),
            'run':
            dict(uuid=six.text_type(run.uuid),
                 created_on=run.created_on.isoformat())
        }

        if msg and msg.id > 0:
            post_data['input'] = dict(
                urn=msg.contact_urn.urn if msg.contact_urn else None,
                text=msg.text,
                attachments=(msg.attachments or []))

        if channel:
            post_data['channel'] = dict(name=channel.name, uuid=channel.uuid)

        api_user = get_api_user()
        if not action:  # pragma: needs cover
            action = 'POST'

        webhook_event = cls.objects.create(org=org,
                                           event=cls.TYPE_FLOW,
                                           channel=channel,
                                           data=json.dumps(post_data),
                                           run=run,
                                           try_count=1,
                                           action=action,
                                           resthook=resthook,
                                           created_by=api_user,
                                           modified_by=api_user)

        status_code = -1
        message = "None"
        body = None

        start = time.time()

        # webhook events fire immediately since we need the results back
        try:
            # no url, bail!
            if not webhook_url:
                raise ValueError("No webhook_url specified, skipping send")

            # only send webhooks when we are configured to, otherwise fail
            if settings.SEND_WEBHOOKS:
                requests_headers = http_headers(extra=headers)

                # some hosts deny generic user agents, use Temba as our user agent
                if action == 'GET':
                    response = requests.get(webhook_url,
                                            headers=requests_headers,
                                            timeout=10)
                else:
                    requests_headers['Content-type'] = 'application/json'
                    response = requests.post(webhook_url,
                                             data=json.dumps(post_data),
                                             headers=requests_headers,
                                             timeout=10)

                body = response.text
                if body:
                    body = body.strip()
                status_code = response.status_code
            else:
                print("!! Skipping WebHook send, SEND_WEBHOOKS set to False")
                body = 'Skipped actual send'
                status_code = 200

            # process the webhook response
            try:
                response_json = json.loads(body, object_pairs_hook=OrderedDict)

                # only update if we got a valid JSON dictionary or list
                if not isinstance(response_json, dict) and not isinstance(
                        response_json, list):
                    raise ValueError(
                        "Response must be a JSON dictionary or list, ignoring response."
                    )

                run.update_fields(response_json)
                message = "Webhook called successfully."
            except ValueError:
                message = "Response must be a JSON dictionary, ignoring response."

            if 200 <= status_code < 300:
                webhook_event.status = cls.STATUS_COMPLETE
            else:
                webhook_event.status = cls.STATUS_FAILED
                message = "Got non 200 response (%d) from webhook." % response.status_code
                raise Exception("Got non 200 response (%d) from webhook." %
                                response.status_code)

        except Exception as e:
            import traceback
            traceback.print_exc()

            webhook_event.status = cls.STATUS_FAILED
            message = "Error calling webhook: %s" % six.text_type(e)

        finally:
            webhook_event.save(update_fields=('status', ))

            # make sure our message isn't too long
            if message:
                message = message[:255]

            request_time = (time.time() - start) * 1000

            contact = None
            if webhook_event.run:
                contact = webhook_event.run.contact

            result = WebHookResult.objects.create(event=webhook_event,
                                                  contact=contact,
                                                  url=webhook_url,
                                                  status_code=status_code,
                                                  body=body,
                                                  message=message,
                                                  data=json.dumps(post_data),
                                                  request_time=request_time,
                                                  created_by=api_user,
                                                  modified_by=api_user)

            # if this is a test contact, add an entry to our action log
            if run.contact.is_test:
                log_txt = "Triggered <a href='%s' target='_log'>webhook event</a> - %d" % (
                    reverse('api.log_read', args=[webhook_event.pk
                                                  ]), status_code)
                ActionLog.create(run, log_txt, safe=True)

        return result
예제 #35
0
    def deliver(self):
        from .v1.serializers import MsgCreateSerializer
        start = time.time()

        # create our post parameters
        post_data = json.loads(self.data)
        post_data['event'] = self.event
        post_data['relayer'] = self.channel.pk if self.channel else ''
        post_data['channel'] = self.channel.pk if self.channel else ''
        post_data[
            'relayer_phone'] = self.channel.address if self.channel else ''

        # look up the endpoint for this channel
        result = dict(url=self.org.get_webhook_url(),
                      data=urlencode(post_data, doseq=True))

        if not self.org.get_webhook_url():  # pragma: no cover
            result['status_code'] = 0
            result[
                'message'] = "No webhook registered for this org, ignoring event"
            self.status = self.STATUS_FAILED
            self.next_attempt = None
            return result

        # get our org user
        user = self.org.get_user()

        # no user?  we shouldn't be doing webhooks shtuff
        if not user:
            result['status_code'] = 0
            result['message'] = "No active user for this org, ignoring event"
            self.status = self.STATUS_FAILED
            self.next_attempt = None
            return result

        # make the request
        try:
            if not settings.SEND_WEBHOOKS:
                raise Exception(
                    "!! Skipping WebHook send, SEND_WEBHOOKS set to False")

            headers = http_headers(extra=self.org.get_webhook_headers())

            s = requests.Session()
            prepped = requests.Request('POST',
                                       self.org.get_webhook_url(),
                                       data=post_data,
                                       headers=headers).prepare()
            result['url'] = prepped.url
            result['request'] = prepped_request_to_str(prepped)
            r = s.send(prepped, timeout=5)

            result['status_code'] = r.status_code
            result['body'] = r.text.strip()

            r.raise_for_status()

            # any 200 code is ok by us
            self.status = self.STATUS_COMPLETE
            result['request_time'] = (time.time() - start) * 1000
            result['message'] = "Event delivered successfully."

            # read our body if we have one
            if result['body']:
                try:
                    data = r.json()
                    serializer = MsgCreateSerializer(data=data,
                                                     user=user,
                                                     org=self.org)

                    if serializer.is_valid():
                        result['serializer'] = serializer
                        result[
                            'message'] = "Response body contains message which will be sent"
                    else:
                        errors = serializer.errors
                        result['message'] = "Event delivered successfully, ignoring response body, wrong format: %s" % \
                                            ",".join("%s: %s" % (_, ",".join(errors[_])) for _ in errors.keys())

                except ValueError as e:
                    # we were unable to make anything of the body, that's ok though because
                    # get a 200, so just save our error for posterity
                    result[
                        'message'] = "Event delivered successfully, ignoring response body, not JSON: %s" % six.text_type(
                            e)

        except Exception as e:
            # we had an error, log it
            self.status = self.STATUS_ERRORED
            result['request_time'] = time.time() - start
            result[
                'message'] = "Error when delivering event - %s" % six.text_type(
                    e)

        # if we had an error of some kind, schedule a retry for five minutes from now
        self.try_count += 1

        if self.status == self.STATUS_ERRORED:
            if self.try_count < 3:
                self.next_attempt = timezone.now() + timedelta(minutes=5)
            else:
                self.next_attempt = None
                self.status = 'F'
        else:
            self.next_attempt = None

        return result
예제 #36
0
    def trigger_flow_webhook(cls, run, webhook_url, ruleset, msg, action="POST", resthook=None, headers=None):

        flow = run.flow
        contact = run.contact
        org = flow.org
        channel = msg.channel if msg else None
        contact_urn = msg.contact_urn if (msg and msg.contact_urn) else contact.get_urn()

        contact_dict = dict(uuid=contact.uuid, name=contact.name)
        if contact_urn:
            contact_dict["urn"] = contact_urn.urn

        post_data = {
            "contact": contact_dict,
            "flow": dict(name=flow.name, uuid=flow.uuid, revision=flow.revisions.order_by("revision").last().revision),
            "path": run.path,
            "results": run.results,
            "run": dict(uuid=str(run.uuid), created_on=run.created_on.isoformat()),
        }

        if msg and msg.id > 0:
            post_data["input"] = dict(
                urn=msg.contact_urn.urn if msg.contact_urn else None,
                text=msg.text,
                attachments=(msg.attachments or []),
            )

        if channel:
            post_data["channel"] = dict(name=channel.name, uuid=channel.uuid)

        api_user = get_api_user()
        if not action:  # pragma: needs cover
            action = "POST"

        webhook_event = cls.objects.create(
            org=org,
            event=cls.TYPE_FLOW,
            channel=channel,
            data=post_data,
            run=run,
            try_count=1,
            action=action,
            resthook=resthook,
            created_by=api_user,
            modified_by=api_user,
        )

        status_code = -1
        message = "None"
        body = None

        start = time.time()

        # webhook events fire immediately since we need the results back
        try:
            # no url, bail!
            if not webhook_url:
                raise ValueError("No webhook_url specified, skipping send")

            # only send webhooks when we are configured to, otherwise fail
            if settings.SEND_WEBHOOKS:
                requests_headers = http_headers(extra=headers)

                # some hosts deny generic user agents, use Temba as our user agent
                if action == "GET":
                    response = requests.get(webhook_url, headers=requests_headers, timeout=10)
                else:
                    requests_headers["Content-type"] = "application/json"
                    response = requests.post(
                        webhook_url, data=json.dumps(post_data), headers=requests_headers, timeout=10
                    )

                body = response.text
                if body:
                    body = body.strip()
                status_code = response.status_code
            else:
                print("!! Skipping WebHook send, SEND_WEBHOOKS set to False")
                body = "Skipped actual send"
                status_code = 200

            if ruleset:
                run.update_fields({Flow.label_to_slug(ruleset.label): body}, do_save=False)
            new_extra = {}

            # process the webhook response
            try:
                response_json = json.loads(body)

                # only update if we got a valid JSON dictionary or list
                if not isinstance(response_json, dict) and not isinstance(response_json, list):
                    raise ValueError("Response must be a JSON dictionary or list, ignoring response.")

                new_extra = response_json
                message = "Webhook called successfully."
            except ValueError:
                message = "Response must be a JSON dictionary, ignoring response."

            run.update_fields(new_extra)

            if 200 <= status_code < 300:
                webhook_event.status = cls.STATUS_COMPLETE
            else:
                webhook_event.status = cls.STATUS_FAILED
                message = "Got non 200 response (%d) from webhook." % response.status_code
                raise Exception("Got non 200 response (%d) from webhook." % response.status_code)

        except Exception as e:
            import traceback

            traceback.print_exc()

            webhook_event.status = cls.STATUS_FAILED
            message = "Error calling webhook: %s" % str(e)

        finally:
            webhook_event.save(update_fields=("status",))

            # make sure our message isn't too long
            if message:
                message = message[:255]

            request_time = (time.time() - start) * 1000

            contact = None
            if webhook_event.run:
                contact = webhook_event.run.contact

            result = WebHookResult.objects.create(
                event=webhook_event,
                contact=contact,
                url=webhook_url,
                status_code=status_code,
                body=body,
                message=message,
                data=post_data,
                request_time=request_time,
                created_by=api_user,
                modified_by=api_user,
            )

            # if this is a test contact, add an entry to our action log
            if run.contact.is_test:  # pragma: no cover
                log_txt = "Triggered <a href='%s' target='_log'>webhook event</a> - %d" % (
                    reverse("api.log_read", args=[webhook_event.pk]),
                    status_code,
                )
                ActionLog.create(run, log_txt, safe=True)

        return result
예제 #37
0
파일: type.py 프로젝트: teehamaral/rapidpro
    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,
            )
예제 #38
0
    def deliver(self):
        from .v1.serializers import MsgCreateSerializer

        start = time.time()

        # create our post parameters
        post_data = self.data
        post_data["event"] = self.event
        post_data["relayer"] = self.channel.pk if self.channel else ""
        post_data["channel"] = self.channel.pk if self.channel else ""
        post_data["relayer_phone"] = self.channel.address if self.channel else ""

        # look up the endpoint for this channel
        result = dict(url=self.org.get_webhook_url(), data=urlencode(post_data, doseq=True))

        if not self.org.get_webhook_url():  # pragma: no cover
            result["status_code"] = 0
            result["message"] = "No webhook registered for this org, ignoring event"
            self.status = self.STATUS_FAILED
            self.next_attempt = None
            return result

        # get our org user
        user = self.org.get_user()

        # no user?  we shouldn't be doing webhooks shtuff
        if not user:
            result["status_code"] = 0
            result["message"] = "No active user for this org, ignoring event"
            self.status = self.STATUS_FAILED
            self.next_attempt = None
            return result

        # make the request
        try:
            if not settings.SEND_WEBHOOKS:  # pragma: no cover
                raise Exception("!! Skipping WebHook send, SEND_WEBHOOKS set to False")

            headers = http_headers(extra=self.org.get_webhook_headers())

            s = requests.Session()
            prepped = requests.Request("POST", self.org.get_webhook_url(), data=post_data, headers=headers).prepare()
            result["url"] = prepped.url
            result["request"] = prepped_request_to_str(prepped)
            r = s.send(prepped, timeout=5)

            result["status_code"] = r.status_code
            result["body"] = r.text.strip()

            r.raise_for_status()

            # any 200 code is ok by us
            self.status = self.STATUS_COMPLETE
            result["request_time"] = (time.time() - start) * 1000
            result["message"] = "Event delivered successfully."

            # read our body if we have one
            if result["body"]:
                try:
                    data = r.json()
                    serializer = MsgCreateSerializer(data=data, user=user, org=self.org)

                    if serializer.is_valid():
                        result["serializer"] = serializer
                        result["message"] = "Response body contains message which will be sent"
                    else:
                        errors = serializer.errors
                        result["message"] = (
                            "Event delivered successfully, ignoring response body, wrong format: %s"
                            % ",".join("%s: %s" % (_, ",".join(errors[_])) for _ in errors.keys())
                        )

                except ValueError as e:
                    # we were unable to make anything of the body, that's ok though because
                    # get a 200, so just save our error for posterity
                    result["message"] = "Event delivered successfully, ignoring response body, not JSON: %s" % str(e)

        except Exception as e:
            # we had an error, log it
            self.status = self.STATUS_ERRORED
            result["request_time"] = time.time() - start
            result["message"] = "Error when delivering event - %s" % str(e)

        # if we had an error of some kind, schedule a retry for five minutes from now
        self.try_count += 1

        if self.status == self.STATUS_ERRORED:
            if self.try_count < 3:
                self.next_attempt = timezone.now() + timedelta(minutes=5)
            else:
                self.next_attempt = None
                self.status = "F"
        else:
            self.next_attempt = None

        return result