def inbound_sms()

in api/views/phones.py [0:0]


def inbound_sms(request):
    """
    Handle an inbound SMS message sent by Twilio.

    The return value is TwilML Response XML that reports the error or an empty success
    message.
    """
    incr_if_enabled("phones_inbound_sms")
    _validate_twilio_request(request)

    """
    TODO: delete the message from Twilio; how to do this AFTER this request? queue?
    E.g., with a django-celery task in phones.tasks:

    inbound_msg_sid = request.data.get("MessageSid", None)
    if inbound_msg_sid is None:
        raise exceptions.ValidationError("Request missing MessageSid")
    tasks._try_delete_from_twilio.delay(args=message, countdown=10)
    """

    inbound_body = request.data.get("Body", None)
    inbound_from = request.data.get("From", None)
    inbound_to = request.data.get("To", None)
    if inbound_body is None or inbound_from is None or inbound_to is None:
        raise exceptions.ValidationError("Request missing From, To, Or Body.")

    relay_number, real_phone = _get_phone_objects(inbound_to)
    if not real_phone.user.is_active:
        return response.Response(
            status=200,
            template_name="twiml_empty_response.xml",
        )

    glean_logger().log_text_received(user=real_phone.user)
    _check_remaining(relay_number, "texts")

    if inbound_from == real_phone.number:
        prepared = False
        try:
            relay_number, destination_number, body = _prepare_sms_reply(
                relay_number, inbound_body
            )
            prepared = True
        except RelaySMSException as sms_exception:
            _log_sms_exception("twilio", real_phone, sms_exception)
            user_error_message = _get_user_error_message(real_phone, sms_exception)
            twilio_client().messages.create(
                from_=relay_number.number, body=user_error_message, to=real_phone.number
            )
            if sms_exception.status_code >= 400:
                raise

        if prepared:
            client = twilio_client()
            incr_if_enabled("phones_send_sms_reply")
            success = False
            try:
                client.messages.create(
                    from_=relay_number.number, body=body, to=destination_number
                )
                success = True
            except TwilioRestException as e:
                logger.error(
                    "Twilio failed to send reply",
                    {"code": e.code, "http_status_code": e.status, "msg": e.msg},
                )
            if success:
                relay_number.remaining_texts -= 1
                relay_number.texts_forwarded += 1
                relay_number.save()

        return response.Response(
            status=200,
            template_name="twiml_empty_response.xml",
        )

    number_disabled = _check_disabled(relay_number, "texts")
    if number_disabled:
        return response.Response(
            status=200,
            template_name="twiml_empty_response.xml",
        )
    inbound_contact = _get_inbound_contact(relay_number, inbound_from)
    if inbound_contact:
        _check_and_update_contact(inbound_contact, "texts", relay_number)

    client = twilio_client()
    app = twiml_app()
    incr_if_enabled("phones_outbound_sms")
    body = message_body(inbound_from, inbound_body)
    result = "SUCCESS"
    try:
        client.messages.create(
            from_=relay_number.number,
            body=body,
            status_callback=app.sms_status_callback,
            to=real_phone.number,
        )
    except TwilioRestException as e:
        if e.code == 21610:
            # User has opted out with "STOP"
            # TODO: Mark RealPhone as unsubscribed?
            context = {"code": e.code, "http_status_code": e.status, "msg": e.msg}
            context["fxa_id"] = real_phone.user.profile.metrics_fxa_id
            info_logger.info("User has blocked their Relay number", context)
            result = "BLOCKED"
        else:
            result = "FAILED"
            logger.error(
                "Twilio failed to forward message",
                {"code": e.code, "http_status_code": e.status, "msg": e.msg},
            )
    if result == "SUCCESS":
        relay_number.remaining_texts -= 1
        relay_number.texts_forwarded += 1
        relay_number.save()
    elif result == "BLOCKED":
        relay_number.texts_blocked += 1
        relay_number.save()

    return response.Response(
        status=201,
        template_name="twiml_empty_response.xml",
    )