def send_url(request): try: if "CONTENT_TYPE" in request.META: content_type = "CONTENT_TYPE" else: content_type = "HTTP_CONTENT_TYPE" if request.META[content_type] == RP_RESPONSE_CONTENT_TYPES[ "URL_ENCODED"]: data = request.data content = data.dict() else: content = request.data # decrement key1 to = content['to_no_plus'] key1 = f"MO_MT_KEY_{to}" key2 = f"MSG_KEY_{to}" key1_val = int(r.decr(key1)) if key1_val >= 0: r.lpush(key2, str(content)) else: action = "end" # code here is currently temporary if content["session_status"] == RP_RESPONSE_STATUSES["waiting"]: action = "request" msg_extras = dict(msg_type="Outgoing", status="Received", action=action) content.update(msg_extras) push = push_ussd(content, request) # reset key to 1 r.set(key1, 0, ex=10) return Response({"message": "success"}, status=200) except Exception as err: error_logger.exception(err)
def post(self, request, *args, **kwargs): access_logger.info(request) try: if "channel_id" in kwargs: channel_id = kwargs['channel_id'] channel = USSDChannel.objects.get(pk=channel_id) form = ChannelConfForm(request.POST, instance=channel) else: form = ChannelConfForm(request.POST) if form.is_valid(): form.save() self.msg = 'Channel configurations successfully saved.' self.success = True return redirect(reverse("channel_list")) else: self.msg = 'Form is not valid' return render( request, self.template_name, { "form": form, "msg": self.msg, "success": self.success, "hostname": self.hostname }) except Exception as err: error_logger.exception(err)
def post(self, request, handler_id=None): access_logger.info(request) try: if handler_id: handler = Handler.objects.get(pk=handler_id) self.auth_scheme = handler.auth_scheme self.aggregator = handler.aggregator self.get_auth_token() self.isNewHandler = False form = HandlerForm(request.POST, instance=handler) else: form = HandlerForm(request.POST) if form.is_valid(): form.save() # create new non-staff user and assign them an auth token if self.isNewHandler: aggregator = form.cleaned_data["aggregator"] self.create_auth_user(aggregator) self.msg = 'Saved' self.success = True else: self.msg = 'Form is invalid' self.success = False return render(request, self.template_name, {"form": form, "msg": self.msg, "token": self.token, "success": self.success, "auth_scheme": self.auth_scheme}) except Exception as err: self.success = False error_logger.exception(err) self.msg = str(err) return render(request, self.template_name, {"form": form, "msg": self.msg, "token": self.token, "success": self.success, "auth_scheme": self.auth_scheme})
def push_ussd(payload, request): try: host = request.get_host() ws = create_connection(f"ws://{host}/ws/demo") ws.send(json.dumps(payload)) ws.close() return True except Exception as err: error_logger.exception(err) return False
def clear_sessions(request): try: # delete all sessions USSDSession.objects.all().delete() response = dict(status="success") return Response(response, status=200) except Exception as error: error_logger.exception(error) response = dict(status="error", message=f"{str(error)}") return Response(response, status=500)
def process_handler(self): try: # determine service_code self.determine_service_code() self.handler = Handler.objects.get(short_code=self.service_code) request_format = self.handler.request_format # use defined template self.rapidpro_keys, self.handler_keys = separate_keys( request_format) # generate standard request_string to send to rapidPro self.generate_standard_request() # save contact if it doesn't exist self.store_contact() return self.standard_request except Exception as err: error_logger.exception(err)
def post(self, request): access_logger.info(request.META) try: channels = USSDChannel.objects.all() if len(channels) > 0: channel = channels[0] form = ChannelConfForm(request.POST, instance=channel) else: form = ChannelConfForm(request.POST) if form.is_valid(): form.save() self.msg = 'Channel configurations successfully saved.' self.success = True return redirect("/") else: self.msg = 'Form is not valid' return render(request, self.template_name, {"form": form, "msg": self.msg, "success": self.success, "hostname": self.hostname}) except Exception as err: error_logger.exception(err)
def get(self, request, handler_id=None): access_logger.info(str(request)) try: if handler_id: if Handler.objects.filter(id=handler_id).exists(): handler = Handler.objects.get(pk=handler_id) self.aggregator = handler.aggregator form = HandlerForm(instance=handler) self.get_auth_token() else: form = HandlerForm() redirect('add_handler', permanent=True) else: form = HandlerForm() return render(request, self.template_name, { "form": form, "token": self.token }) except Exception as err: error_logger.exception(err)
def get(self, request, handler_id=None): access_logger.info(str(request)) try: if handler_id: if Handler.objects.filter(id=handler_id).exists(): handler = Handler.objects.get(pk=handler_id) self.callback_url = f"{handler.channel.send_url}/adaptor/call-back" self.auth_scheme = handler.auth_scheme self.aggregator = handler.aggregator form = HandlerForm(instance=handler) self.get_auth_token() else: form = HandlerForm() redirect('add_handler', permanent=True) else: form = HandlerForm() return render(request, self.template_name, {"form": form, "token": self.token, "auth_scheme": self.auth_scheme, "callback_url": self.callback_url}) except Exception as err: error_logger.exception(err)
def process_handler(self): try: ''' Lets first run the clear_timedout_sessions(), to create a task that deletes timed-out sessions ''' if PeriodicTask.objects.filter( name="Clear_Timedout_sessions").exists(): pass else: # only if the task and its scheduler have not been create yet clear_timedout_sessions() # determine service_code self.determine_service_code() request_format = self.handler.request_format # use defined template self.rapidpro_keys, self.handler_keys = separate_keys( request_format) # generate standard request_string to send to rapidPro self.generate_standard_request() # save contact if it doesn't exist self.store_contact() return self.standard_request except Exception as err: error_logger.exception(err)
def call_back(request): # CHECK METHOD USED if request.META["REQUEST_METHOD"] == "GET": data = request.GET request_data = data.dict() else: request_data = request.data try: sr = ProcessAggregatorRequest(request_data) standard_request_string = sr.process_handler() current_session = sr.log_session() is_new_session = sr.is_new_session still_in_flow = sr.is_in_flow handler = sr.get_handler end_action = handler.signal_end_string reply_action = handler.signal_reply_string urn = standard_request_string['from'] channel = get_channel() if channel is None: raise Exception( "Could not continue without a channel, configure one first and try again" ) if is_new_session: text = " " if still_in_flow else channel.trigger_word else: text = standard_request_string["text"] allowed_urn = standard_urn(urn) rapid_pro_request = {"from": allowed_urn, "text": text} # create redis keys key1 = f"MO_MT_KEY_{allowed_urn}" r.set(key1, 1) # proven necessary or else key2 = f"MSG_KEY_{allowed_urn}" """""Channel details """ "" # receive_url is used to send msgs to rapidPro receive_url = channel.rapidpro_receive_url # req = requests.post(receive_url, json.dumps(rapid_pro_request), headers=HEADERS) req = requests.post(receive_url, rapid_pro_request, headers=HEADERS) if req.status_code == 200: # increment key1 r.incr(key1) r.expire(key1, 30) # expire key1 after 30s data = r.blpop(key2, channel.timeout_after ) # wait for configured time for rapidPro instance if data: feedback = literal_eval( data[1].decode("utf-8")) # from RapidPro text = feedback[RP_RESPONSE_FORMAT['text']] status = feedback[RP_RESPONSE_FORMAT['session_status']] if status == RP_RESPONSE_STATUSES['waiting']: action = reply_action else: # mark session complete and give it a green badge changeSessionStatus(current_session, SESSION_STATUSES['COMPLETED'], 'success') action = end_action new_format = dict(text=text, action=action) response = sr.get_expected_response(new_format) else: # mark session timed out and give it a red badge changeSessionStatus(current_session, SESSION_STATUSES['TIMED_OUT'], 'danger') error_logger.debug(f"Response timed out for redis key {key2}") res_format = dict(text="Response timed out", action=end_action) response = sr.get_expected_response(res_format) else: changeSessionStatus(current_session, SESSION_STATUSES['TIMED_OUT'], 'danger') res_format = dict(text="External Application unreachable", action=end_action) error_logger.exception(req.content) response = sr.get_expected_response(res_format) return Response(response, status=200) except Exception as err: error_logger.exception(err) response = { "responseString": "External Application unreachable", "action": "end" } return Response(response, status=500)
def process_request(self): try: current_session = self.request_factory.log_session() is_new_session = self.request_factory.is_new_session still_in_flow = self.request_factory.is_in_flow handler = self.request_factory.get_handler end_action = handler.signal_end_string reply_action = handler.signal_reply_string urn = self.standard_request_string['from'] channel = handler.channel if is_new_session: text = handler.repeat_trigger if still_in_flow else handler.trigger_word else: text = self.standard_request_string["text"].strip() allowed_urn = standard_urn(urn, handler) rapid_pro_request = {"from": allowed_urn, "text": text} access_logger.info( f"Is new session: {is_new_session}, Still in flow: {still_in_flow}" ) access_logger.info(rapid_pro_request) # create redis keys key1 = f"MO_MT_KEY_{allowed_urn}" r.set(key1, 1) # proven necessary or else key2 = f"MSG_KEY_{allowed_urn}" """""Channel details """ "" # receive_url is used to send msgs to rapidPro receive_url = channel.rapidpro_receive_url req = requests.post(receive_url, rapid_pro_request, headers=HEADERS) if req.status_code == 200: # increment key1 r.incr(key1) r.expire(key1, 30) # expire key1 after 30s data = r.blpop( key2, channel.timeout_after ) # wait for configured time for rapidPro instance if data: feedback = literal_eval( data[1].decode("utf-8")) # from RapidPro text = feedback[RP_RESPONSE_FORMAT['text']] status = feedback[RP_RESPONSE_FORMAT['session_status']] access_logger.info(f"From redis key: {data}") if status == RP_RESPONSE_STATUSES['waiting']: action = reply_action else: # mark session complete and give it a green badge changeSessionStatus(current_session, SESSION_STATUSES['COMPLETED'], 'success') action = end_action new_format = dict(text=text, action=action) response = self.request_factory.get_expected_response( new_format) else: # mark session timed out and give it a red badge changeSessionStatus(current_session, SESSION_STATUSES['TIMED_OUT'], 'danger') error_logger.debug( f"Response timed out for redis key {key2}") res_format = dict(text="Response timed out", action=end_action) ''' We need to delete this contact as it will delete-cascade(which ever way they say it) sessions attached to it. Next time user comes back, they will submit a trigger word ''' contact = Contact.objects.get(urn=urn) contact.delete() response = self.request_factory.get_expected_response( res_format) r.delete(key2) # lets delete the key else: changeSessionStatus(current_session, SESSION_STATUSES['TIMED_OUT'], 'danger') res_format = dict(text="External Application error", action=end_action) contact = Contact.objects.get(urn=urn) contact.delete() error_logger.exception(req.content) response = self.request_factory.get_expected_response( res_format) return Response(response, status=200) except Exception as err: error_logger.exception(err) response = { "responseString": "External Application error", "action": "end" } return Response(response, status=500)