Beispiel #1
0
    def deliver(self):
        from .v1.serializers import MsgCreateSerializer

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

        # 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 = 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 = 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")

            # some hosts deny generic user agents, use Temba as our user agent
            headers = TEMBA_HEADERS.copy()

            # also include any user-defined headers
            headers.update(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 = COMPLETE
            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
                        obj = serializer.object
                        result[
                            'message'] = "SMS message to %d recipient(s) with text: '%s'" % (
                                len(obj.contacts), obj.text)
                    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 Exception 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" % unicode(
                            e)

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

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

        if 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
Beispiel #2
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
Beispiel #3
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
Beispiel #4
0
    def deliver(self):
        from .serializers import MsgCreateSerializer

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

        # 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 = 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 = 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")
            # some hosts deny generic user agents, use Temba as our user agent
            headers = TEMBA_HEADERS

            # also include any user-defined headers
            headers.update(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)

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

            r.raise_for_status()

            # any 200 code is ok by us
            self.status = COMPLETE
            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
                        obj = serializer.object
                        result["message"] = "SMS message to %d recipient(s) with text: '%s'" % (
                            len(obj.contacts),
                            obj.text,
                        )
                    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 Exception 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" % unicode(
                        e
                    )

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

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

        if 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
Beispiel #5
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