https://d226lax1qjow5r.cloudfront.net/blog/blogposts/sending-sms-templates-python-dr/Sending-SMS-templates-with-Jinja2-and-Python.png

Envío de plantillas de SMS con Jinja2 y Python

Publicado el May 11, 2021

Tiempo de lectura: 2 minutos

¿Necesitas enviar un mensaje a muchas personas a la vez? ¿Quieres personalizarlo para que no parezca tan impersonal? ¿No puedes enviar un mensaje en grupo porque no quieres que tu madre vea que has enviado el mismo mensaje a todos tus familiares y no sólo a ella? Vamos a ver cómo crear un cliente de SMS que admita mensajes masivos y plantillas de personalización.

Primeros pasos

Necesitarás un poco de experiencia con Python para ejecutar la CLI, y al menos Python 3.6 ya que vamos a utilizar cadenas-f.

  1. Clonar el código fuente de GitHub

  2. Instale las dependencias en su entorno virtual; recomiendo utilizar pipenv e instalar el paquete en modo edición: pipenv install -e .

  3. Cree las siguientes variables de entorno; NEXMO_KEY, NEXMO_SECRETy MY_NUMBER las dos primeras están disponibles en su Panel Nexmo. MY_NUMBER es su número de móvil en el formato internacional E.164.

Gestión de contactos SMS

Creating a new contact via the CLICreating a new contact via the CLI

Nuestra aplicación va a necesitar un CRM básico para mantener nuestros contactos. Vamos a utilizar tinydb para nuestra capa de persistencia, ya que no necesitamos nada demasiado complicado y, además, almacena los datos como un archivo JSON, lo que hace que las copias de seguridad y las ediciones manuales de los datos sean sencillas.

Además del nombre y el número de teléfono del contacto, recogemos algunos valores de opción múltiple.

@contact.command("create")
def contact_create():
    """Creates a new contact"""
    questions = [
        {"type": "input", "name": "name", "message": "Contact name"},
        {
            "type": "input",
            "name": "phonenumber",
            "message": "Contact phone number (E.164 international format)",
        },
        {
            "type": "checkbox",
            "qmark": "?",
            "message": "Select diminutives (select at least 1)",
            "name": "diminutive",
            "choices": [
                Separator("= The Bros ="),
                {"name": "Bro"},
                {"name": "Buddy"},
                {"name": "Dude"},
                {"name": "Matey"},
                {"name": "Pal"},
                Separator("= The Sweethearts ="),
                {"name": "Baby"},
                {"name": "Bae"},
                {"name": "Darling"},
                {"name": "Sweetheart"},
                {"name": "Sugar"},
                Separator("= The Scots/Irish/Aussies ="),
                {"name": "████"},
                {"name": "Eejit"},
                {"name": "█████████"},
                {"name": "Numpty"},
            ],
            "validate": lambda answer: "You must choose at least one diminutive."
            if len(answer) == 0
            else True,
        },
        {
            "type": "checkbox",
            "qmark": "?",
            "message": "Select greetings (select at least 1)",
            "name": "greeting",
            "choices": [
                {"name": "Alright"},
                {"name": "Greetings"},
                {"name": "Hello"},
                {"name": "Hey"},
                {"name": "Hi"},
                {"name": "Oi"},
                {"name": "Wasssssup"},
                {"name": "Yo"},
            ],
            "validate": lambda answer: "You must choose at least one greeting."
            if len(answer) == 0
            else True,
        },
        {
            "type": "checkbox",
            "qmark": "?",
            "message": "Select valediction (select at least 1)",
            "name": "valediction",
            "choices": [
                {"name": "Bye"},
                {"name": "Cya"},
                {"name": "Love you x"},
                {"name": "Peace"},
                {"name": "xox"},
            ],
            "validate": lambda answer: "You must choose at least one valediction."
            if len(answer) == 0
            else True,
        },
    ]

    answers = prompt(questions, style=questions_style)
    contacts_db.insert(answers)

    click.secho(
        f"New contact {answers['name']} created", fg="black", bg="cyan", bold=True
    )

Cuando generemos la plantilla de un mensaje, proporcionaremos un valor aleatorio de estas listas en el contexto, para dar a los mensajes alguna variación.

Gestión de nuestras plantillas de SMS

Nuestras plantillas van a ser renderizadas usando Jinja2 dándonos acceso a todos los filtros y características incorporadas de Jinja. También información sobre cada contacto en el contexto al renderizar la plantilla.

Creating a new template via the CLICreating a new template via the CLI

He hecho un uso liberal del filtro random para que haya cierta variación en mis mensajes, incluso si reenvío el mismo mensaje a la misma persona varias veces, debería parecer que me he tomado el tiempo de escribirlo personalmente cada vez. Para mayor autenticidad, puedes añadir alguna errata de vez en cuando.

{{ greeting }} {{ diminutive }} {{ ["gah", "sorry", "I suck", "soz"]|random }}, I'm {{ ["running", "runnin", "runing"]|random }} about {{ range(5,25)|random }} {{ ["mins", "minutes", "mintes"]|random }} late. {{ valediction }}

Screenshot of phone with multiple SMSScreenshot of phone with multiple SMS

El resto de nuestro código de gestión de plantillas tiene un aspecto muy similar al de los contactos, aunque disponemos de un sencillo sistema de desglose/expansión para poder ver rápidamente el contenido de la plantilla.

@template.command("list")
def template_list():
    """View all templates"""
    viewing_templates = True
    all_templates = templates_db.all()

    while viewing_templates:
        questions = [
            {
                "type": "list",
                "message": "View template",
                "name": "template",
                "choices": [
                    {"name": f"{template['name']}", "value": template}
                    for template in all_templates
                ],
            }
        ]

        answers = prompt(questions, style=questions_style)

        click.echo(answers["template"]["name"])
        click.echo("---")
        click.echo(answers["template"]["template"])

        if not click.confirm("View another template?"):
            viewing_templates = False

Envío de varios SMS con Python

Sending multiple/bulk SMS via the CLISending multiple/bulk SMS via the CLI

Con el Cliente Python Nexmo enviar un SMS es una llamada a una sola función; la mayor parte de nuestro código de envío de SMS es permitir al usuario seleccionar qué números van a recibir el mensaje, y qué plantilla debemos utilizar.

@click.command()
def send():
    """Send SMS"""

    questions = [
        {
            "type": "checkbox",
            "qmark": "?",
            "message": "Select contacts to message",
            "name": "contacts",
            "choices": [
                {
                    "name": f"{contact['name']} - {contact['phonenumber']}",
                    "value": contact,
                }
                for contact in contacts_db.all()
            ],
            "validate": lambda answer: "You must choose at least one contact."
            if len(answer) == 0
            else True,
        },
        {
            "type": "list",
            "message": "Select template",
            "name": "template",
            "choices": [
                {"name": f"{template['name']}", "value": template["template"]}
                for template in templates_db.all()
            ],
        },
    ]

    answers = prompt(questions, style=questions_style)
    template = Template(answers["template"])

    with click.progressbar(answers["contacts"], label="Sending messages") as contacts:
        for contact in contacts:
            message = template.render(
                name=contact["name"],
                phonenumber=contact["phonenumber"],
                diminutive=random.choice(contact["diminutive"]),
                greeting=random.choice(contact["greeting"]),
                valediction=random.choice(contact["valediction"]),
            )

            nexmo_client.send_message(
                {
                    "from": os.environ["MY_NUMBER"],
                    "to": contact["phonenumber"],
                    "text": message,
                }
            )

He modificado el código anterior para registrar los resultados de mi mensaje de Navidad, de modo que puedas ver el tipo de variación que produce en los distintos contactos.

Multiple messages output from bulk SMS CLI in PythonMultiple messages output from bulk SMS CLI in Python

Compartir:

https://a.storyblok.com/f/270183/150x150/a3d03a85fd/placeholder.svg
Aaron BassettAntiguos alumnos de Vonage

Aaron era un defensor de los desarrolladores en Nexmo. Ingeniero de software experimentado y aspirante a artista digital, Aaron suele crear cosas con código o electrónica; a veces ambas cosas. Cuando está trabajando en algo nuevo, suele percibir el olor a componentes quemados en el aire.