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
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
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
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
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