def handle_as207960_oauth_code(msg_id, msg_platform, msg_conv_id, metadata, content): platform = get_platform(msg_platform, msg_conv_id, metadata) token_response = oauth_client.authorization_code( code=content, redirect_uri="https://messaging.as207960.net/brand_oauth/redirect/") token_data = oauth_client.decode_token( token_response["id_token"], key=oauth_client.certs()["keys"][0], algorithms=oauth_client. well_known["id_token_signing_alg_values_supported"], issuer=oauth_client.well_known["issuer"], access_token=token_response["access_token"], ) platform.conversation.update_user_id(token_data["sub"]) message = Message( platform=platform, text="Login complete, thanks!", direction=Message.TO_CUSTOMER, ) message.save() operator_interface.tasks.process_message.delay(message.id)
def handle_azure_message(msg): conversation = event_to_conversation(msg) mid = msg["id"] timestamp = msg["timestamp"] text = msg.get("text") if not Message.message_exits(conversation, mid): message_m = Message( conversation=conversation, message_id=mid, direction=Message.FROM_CUSTOMER, state=Message.DELIVERED, timestamp=dateutil.parser.parse(timestamp), ) if text: message_m.text = text else: return message_m.save() operator_interface.tasks.process_message.delay(message_m.id)
def handle_as207960_chatstate(msg_id, msg_platform, msg_conv_id, metadata, content): platform = get_platform(msg_platform, msg_conv_id, metadata) platform.is_typing = False platform.save() state = content.get("state") if state == "composing": platform.is_typing = True platform.save() elif state == "paused": platform.is_typing = False platform.save() elif state == "request_live_agent": platform.is_typing = False platform.save() m = Message(platform=platform, platform_message_id=msg_id, request_live_agent=True, direction=Message.FROM_CUSTOMER, state=Message.DELIVERED) m.save() operator_interface.tasks.send_message_to_interface.delay(m.id)
def notif_webhook(request): try: data = json.loads(request.body) except json.JSONDecodeError: return HttpResponseBadRequest() logger.debug(f"Got event from ABC notification webhook: {data}") msg_id = data.get("id") from_id = data.get("from") msg_event = data.get("event") conv: ConversationPlatform = ConversationPlatform.objects.filter( platform=ConversationPlatform.ABC, platform_id=from_id).first() msg: Message = Message.objects.filter(message_id=msg_id).first() if msg_event == "failed": msg_fail_reason = data.get("reason", {}) msg_error_code = msg_fail_reason.get("code") if msg_error_code == 87 and conv: m = Message( platform=conv, platform_message_id=msg_id, end=True, direction=Message.FROM_CUSTOMER, state=Message.DELIVERED, ) m.save() operator_interface.tasks.process_message.delay(m.id) if msg: msg.state = Message.FAILED msg.save() elif msg_event == "accepted" and msg and msg.state != Message.READ: msg.state = Message.DELIVERED msg.save() elif msg_event == "consumed" and msg: msg.state = Message.READ msg.save() return HttpResponse("")
def account_linking(request): state = request.GET.get("state") try: state = models.AccountLinkingState.objects.get(id=state) except models.AccountLinkingState.DoesNotExist: return HttpResponseBadRequest() if state.timestamp + datetime.timedelta(minutes=5) < timezone.now(): return HttpResponseBadRequest() state.conversation.conversation.update_user_id(request.user.username) state.delete() message = Message( platform=state.conversation, text="Login complete, thanks!", direction=Message.TO_CUSTOMER, ) message.save() operator_interface.tasks.process_message.delay(message.id) return HttpResponse( '<script type="text/javascript">window.close();</script><h1>You can now close this window</h1>' )
def send_message(request, customer_id): if request.method != "POST": return HttpResponseNotAllowed(permitted_methods=["POST"]) auth = request.META.get("HTTP_AUTHORIZATION") if not auth or not auth.startswith("Bearer "): return HttpResponseForbidden() try: claims = django_keycloak_auth.clients.verify_token( auth[len("Bearer "):].strip() ) except keycloak.exceptions.KeycloakClientError: return HttpResponseForbidden() if "send-messages" not in claims.get("resource_access", {}).get( "bot-server", {} ).get("roles", []): return HttpResponseForbidden() try: customer = django_keycloak_auth.users.get_user_by_id(customer_id).user except keycloak.exceptions.KeycloakClientError: raise Http404() try: data = json.loads(request.body) except json.JSONDecodeError: return HttpResponseBadRequest() text = data.get("text") tag = data.get("tag") if not text: return HttpResponseBadRequest() platform = None conv = None try: conv = Conversation.objects.get(conversation_user_id=customer_id) platform = conv.last_usable_platform(tag, True, text) except Conversation.DoesNotExist: pass if platform is None: mobile_numbers = [] other_numbers = [] for n in customer.get("attributes", {}).get("phone", []): try: n = phonenumbers.parse(n, settings.PHONENUMBER_DEFAULT_REGION) except phonenumbers.phonenumberutil.NumberParseException: continue if phonenumbers.is_valid_number(n): if ( phonenumbers.phonenumberutil.number_type(n) == phonenumbers.PhoneNumberType.MOBILE ): mobile_numbers.append(n) else: other_numbers.append(n) if len(mobile_numbers) or len(other_numbers): for n in mobile_numbers: formatted_num = phonenumbers.format_number( n, phonenumbers.PhoneNumberFormat.E164 ) try: platform = ConversationPlatform.objects.get( platform=ConversationPlatform.WHATSAPP, platform_id=formatted_num, ) if not platform.can_message(tag, True, text): platform = None else: break except ConversationPlatform.DoesNotExist: pass try: platform = ConversationPlatform.objects.get( platform=ConversationPlatform.AS207960, platform_id=f"msisdn-messaging;{formatted_num}", ) break except ConversationPlatform.DoesNotExist: pass try: platform = ConversationPlatform.objects.get( platform=ConversationPlatform.SMS, platform_id=formatted_num, ) break except ConversationPlatform.DoesNotExist: pass if not platform: for n in other_numbers: formatted_num = phonenumbers.format_number( n, phonenumbers.PhoneNumberFormat.E164 ) try: platform = ConversationPlatform.objects.get( platform=ConversationPlatform.WHATSAPP, platform_id=formatted_num, ) if not platform.can_message(tag, True, text): platform = None else: break except ConversationPlatform.DoesNotExist: pass try: platform = ConversationPlatform.objects.get( platform=ConversationPlatform.AS207960, platform_id=f"msisdn-messaging;{formatted_num}", ) break except ConversationPlatform.DoesNotExist: pass try: platform = ConversationPlatform.objects.get( platform=ConversationPlatform.SMS, platform_id=formatted_num ) break except ConversationPlatform.DoesNotExist: pass if not platform: if not conv: conv = Conversation(conversation_user_id=customer_id) conv.save() formatted_num = phonenumbers.format_number( mobile_numbers[0], phonenumbers.PhoneNumberFormat.E164 ) if len(mobile_numbers) else phonenumbers.format_number( other_numbers[0], phonenumbers.PhoneNumberFormat.E164 ) if operator_interface.models.ConversationPlatform.is_whatsapp_template(text): platform_id = operator_interface.models.ConversationPlatform.WHATSAPP platform_rcpt_id = formatted_num else: platform_id = operator_interface.models.ConversationPlatform.AS207960 platform_rcpt_id = f"msisdn-messaging;{formatted_num}" platform = ConversationPlatform( conversation=conv, platform=platform_id, platform_id=platform_rcpt_id, additional_platform_data=json.dumps({ "try_others": [phonenumbers.format_number( n, phonenumbers.PhoneNumberFormat.E164 ) for n in mobile_numbers] + [phonenumbers.format_number( n, phonenumbers.PhoneNumberFormat.E164 ) for n in other_numbers] }) ) platform.save() else: platform.conversation.update_user_id(customer_id) else: return HttpResponse( json.dumps({"status": "no_platform_available"}), content_type="application/json", ) message = Message( platform=platform, text=text, direction=Message.TO_CUSTOMER, message_id=uuid.uuid4(), ) message.save() operator_interface.tasks.process_message.delay(message.id) return HttpResponse(json.dumps({"status": "ok"}), content_type="application/json")
def handle_abc_own(data: dict): msg_from: str = data.get("from") msg_id: str = data.get("id") device: str = data.get("device") capabilities: [str] = data.get("capabilities") msg_locale: str = data.get("locale") # msg_group: str = data.get("group") # msg_intent: str = data.get("intent") contents: dict = data.get("contents") platform = get_platform(msg_from) conversation = platform.conversation if not platform.additional_platform_data: info = {} else: try: info = json.loads(platform.additional_platform_data) except json.JSONDecodeError: info = {} info["current_capabilities"] = capabilities if conversation.conversation_user_id: django_keycloak_auth.users.update_user( str(conversation.conversation_user_id), locale=msg_locale, force_update=False, ) if not Message.message_exits(platform, msg_id): if contents.get("text"): platform.is_typing = False platform.save() message_m: Message = Message(platform=platform, platform_message_id=msg_id, text=html.conditional_escape( contents["text"]), direction=Message.FROM_CUSTOMER, state=Message.DELIVERED, device_data=device) message_m.save() operator_interface.tasks.process_message.delay(message_m.id) elif contents.get("action"): action: str = contents["action"] if action == "typing_start": platform.is_typing = True platform.save() elif action == "typing_end": platform.is_typing = False platform.save() elif action == "close": platform.is_typing = False platform.save() m = Message(platform=platform, platform_message_id=msg_id, end=True, direction=Message.FROM_CUSTOMER, state=Message.DELIVERED, device_data=device) m.save() operator_interface.tasks.send_message_to_interface.delay(m.id) platform.additional_platform_data = json.dumps(info) platform.save() for attachment in data.get("attachments", []): contents: str = attachment.get("contents") name: str = attachment.get("name") mime_type: str = attachment.get("mime-type") if contents: try: contents: bytes = base64.b64decode(contents) except ValueError: continue fs = DefaultStorage() file_name = fs.save(name, BytesIO(contents)) if mime_type.startswith("image"): m = Message(platform=platform, platform_message_id=msg_id, direction=Message.FROM_CUSTOMER, state=Message.DELIVERED, device_data=device, image=fs.base_url + file_name) else: m = Message( platform=platform, platform_message_id=msg_id, direction=Message.FROM_CUSTOMER, state=Message.DELIVERED, device_data=device, text= f"<a href=\"{fs.base_url + file_name}\" target=\"_blank\">{name}</a>", ) m.save() operator_interface.tasks.process_message.delay(m.id)
def handle_telegram_message(message): text = message.get("text") contact = message.get("contact") photo = message.get("photo") sticker = message.get("sticker") document = message.get("document") entities = message.get("entities") caption = message.get("caption") mid = message["message_id"] chat_id = message["chat"]["id"] timestamp = message["date"] conversation = Conversation.get_or_create_conversation( Conversation.TELEGRAM, chat_id, agent_responding=False) update_telegram_profile(chat_id, conversation.id) if not Message.message_exits(conversation, mid): message_m = Message( conversation=conversation, message_id=mid, direction=Message.FROM_CUSTOMER, state=Message.DELIVERED, timestamp=datetime.datetime.fromtimestamp(timestamp), ) if text: if entities: for entity in entities: if entity["type"] == "bot_command": command = text[entity["offset"] + 1:entity["offset"] + entity["length"]] if command == "start": operator_interface.tasks.process_event.delay( conversation.id, "WELCOME") else: operator_interface.tasks.process_event.delay( conversation.id, command) else: message_m.text = text else: message_m.text = text elif photo or sticker: if photo: photo = photo[-1] else: photo = sticker file = requests.get( f"https://api.telegram.org/bot{settings.TELEGRAM_TOKEN}/getFile", json={"file_id": photo["file_id"]}, ) file.raise_for_status() fs = DefaultStorage() file_name = fs.save(photo["file_id"], BytesIO(file.content)) message_m.image = fs.base_url + file_name if caption: message.text = caption elif document: file_name = document["file_name"] if document.get( "file_name") else "File" file = requests.get( f"https://api.telegram.org/bot{settings.TELEGRAM_TOKEN}/getFile", json={"file_id": document["file_id"]}, ) file.raise_for_status() fs = DefaultStorage() file_url = fs.save(photo["file_id"], BytesIO(file.content)) message_m.text = ( f'<a href="{fs.base_url + file_url}" target="_blank">{file_name}</a>' ) if caption: message.text = caption elif contact: message_m.text = contact["phone_number"] else: return message_m.save() operator_interface.tasks.process_message.delay(message_m.id)
def handle_facebook_message(psid: dict, message: dict, timestamp: int) -> None: text: typing.Text = message.get("text") reply_to = message.get("reply_to", {}) attachments: typing.List[typing.Dict] = message.get("attachments") mid: typing.Text = message["mid"] is_echo: bool = message.get("is_echo") psid: typing.Text = psid["sender"] if not is_echo else psid["recipient"] platform: ConversationPlatform = ConversationPlatform.exists( ConversationPlatform.FACEBOOK, psid ) if not platform: user_id = attempt_get_user_id(psid) platform = ConversationPlatform.create( ConversationPlatform.FACEBOOK, psid, customer_user_id=user_id ) message_m: typing.Optional[Message] = None if not is_echo: update_facebook_profile(psid, platform.conversation.id) if not Message.message_exits(platform, mid): handle_mark_facebook_message_read.delay(psid) if text: message_m = Message( platform=platform, platform_message_id=mid, text=html.conditional_escape(text), direction=Message.FROM_CUSTOMER, state=Message.DELIVERED, timestamp=datetime.datetime.fromtimestamp(timestamp / 1000), ) if attachments: for attachment in attachments: payload = attachment["payload"] att_type: typing.Text = attachment.get("type") if att_type == "image" or att_type == "file": url = payload.get("url") r = requests.get(url) if r.status_code == 200: orig_file_name = os.path.basename( urllib.parse.urlparse(url).path ) fs = DefaultStorage() file_name = fs.save(orig_file_name, BytesIO(r.content)) if att_type == "image": message_m = Message( platform=platform, platform_message_id=mid, image=fs.base_url + file_name, direction=Message.FROM_CUSTOMER, state=Message.DELIVERED, timestamp=datetime.datetime.fromtimestamp( timestamp / 1000 ), ) else: message_m = Message( platform=platform, platform_message_id=mid, direction=Message.FROM_CUSTOMER, state=Message.DELIVERED, timestamp=datetime.datetime.fromtimestamp( timestamp / 1000 ), text=f'<a href="{fs.base_url + file_name}" target="_blank">' f"{html.conditional_escape(orig_file_name)}" f"</a>", ) elif att_type == "location": message_m = Message( platform=platform, platform_message_id=mid, direction=Message.FROM_CUSTOMER, state=Message.DELIVERED, timestamp=datetime.datetime.fromtimestamp(timestamp / 1000), text=f"<a href=\"{attachment.get('url')}\" target=\"_blank\">Location</a>", ) elif att_type == "fallback": message_m = Message( platform=platform, platform_message_id=mid, direction=Message.FROM_CUSTOMER, state=Message.DELIVERED, timestamp=datetime.datetime.fromtimestamp(timestamp / 1000), text=f"<a href=\"{payload.get('url')}\" target=\"_blank\">{payload.get('title')}</a>", ) else: if not Message.message_exits(platform, mid): message_m: Message = Message( platform=platform, platform_message_id=mid, text=html.conditional_escape(text) if text else "", direction=Message.TO_CUSTOMER, state=operator_interface.models.Message.DELIVERED, ) if message_m: if reply_to: reply_to_mid = reply_to.get("mid") reply_to_m = Message.objects.filter(platform_message_id=reply_to_mid).first() if reply_to_m: message_m.reply_to = reply_to_m message_m.save() operator_interface.tasks.process_message.delay(message_m.id)
def handle_text(platform: ConversationPlatform, text: str): operator_interface.tasks.process_typing_on.delay(platform.id) r = requests.post( f"{settings.RASA_HTTP_URL}/webhooks/rest/webhook?stream=true", json={"sender": f"CONV:{platform.id}", "message": text}, stream=True, ) r.raise_for_status() items = r.iter_lines(None, True) out_data = [] for item in items: if item: try: data = json.loads(item) except json.JSONDecodeError: continue logger.debug(f"Got messages from rasa callback: {data}") if not data.get("recipient_id"): continue message = Message( platform=platform, direction=Message.TO_CUSTOMER, message_id=uuid.uuid4(), ) if data.get("text"): message.text = data["text"] elif data.get("image"): message.image = data["image"] elif data.get("custom"): custom = data["custom"] if not custom.get("type"): continue event_type = custom["type"] if event_type == "payment": # TODO: Integrate with new system # payment_id = custom.get("payment_id") # payment_o = payment.models.Payment.objects.get(id=payment_id) message.text = "To complete payment follow this link 💸" # message.payment_request = payment_o message.save() elif event_type == "request_human": platform.conversation.agent_responding = False platform.conversation.save() operator_interface.tasks.send_message_notifications.delay( { "type": "alert", "cid": platform.conversation.id, "name": platform.conversation.conversation_name, "text": "Human needed!", } ) continue elif event_type == "request": message.text = custom.get("text", "") message.request = custom.get("request") elif event_type == "card": message.card = json.dumps(custom.get("card")) elif event_type == "selection": message.selection = json.dumps(custom.get("selection")) elif event_type == "restart": message.end = True else: continue else: continue message.save() if data.get("buttons"): for button in data["buttons"]: suggestion = MessageSuggestion( message=message, suggested_response=button["payload"] ) suggestion.save() out_data.append(operator_interface.tasks.process_message(message.id)) return out_data