Estudios De Caso
Cómo triplicó Launch School Academy la interacción de los estudiantes
En Tealeaf Academy, crear una experiencia de “estudiar juntos, progresar juntos” para nuestros estudiantes es fundamental en nuestra forma de enseñar. Una de nuestras herramientas principales es el foro de debate, donde los estudiantes hacen preguntas, comparten ideas, colaboran en los deberes y los profesores intervienen rápidamente para ayudar a los estudiantes a resolver sus problemas. Una de nuestras prioridades recientes era reducir la fricción en el uso del foro de debate y fomentar más debates con una notificación por email complementaria y un flujo de trabajo de “responder al email para publicar en el foro de debate”.
Una vez que implementamos el siguiente código utilizando la API de rutas de Mailgun, la actividad en nuestro foro de debate se triplicó, y ahora las preguntas suelen responderse en una hora, a veces incluso en minutos, y los estudiantes pueden pasar al siguiente conjunto de tareas mucho más rápido. Así es como lo hicimos:
Nuestro flujo de trabajo sería el siguiente:
- Cuando se crea una publicación (muy probablemente, una pregunta), todos los participantes del curso reciben una notificación por email
- Los participantes del curso pueden responder a las notificaciones por email directamente desde su bandeja de entrada de email, sin tener que iniciar sesión en el curso
- Esa respuesta se publicará en el foro de debate online, y también se envía a otros participantes del curso para mantener la conversación en marcha.
Por qué elegimos Mailgun
La pieza clave de este flujo de trabajo es recibir y analizar mensajes de email entrantes. Buscamos entre varios proveedores de servicios de envío de emails, y al final elegimos Mailgun porque:
- Es muy fácil de usar para los desarrolladores: somos desarrolladores y Mailgun habla nuestro idioma. Las API exponen muchas opciones de bajo nivel que permiten hacer ajustes. Nos gustan las rutas en particular: es una buena capa de abstracción que hace que la integración con las aplicaciones sea muy fácil. (mira cómo lo usamos a continuación)
- Es el servicio más completo en características que hemos encontrado: podemos usar Mailgun para emails transaccionales, campañas y también para listas de contactos. Es estupendo tener un solo proveedor de servicios para encargarse de todo lo que necesitamos.
- El precio es razonable y el proceso para pasar a un plan superior con IP dedicada y DKIM personalizado está muy bien, aunque todavía no lo necesitemos.
- La asistencia es de primera categoría. Hay un chat en directo donde puedo hablar con sus desarrolladores directamente sobre los problemas y nos ha resultado muy útil para resolverlos.
Configuración de la infraestructura de email
- El primer paso fue crear un nuevo dominio en Mailgun. En nuestro caso, es messaging.gotealeaf.com, el cual utilizamos para enviar y recibir emails.
- A continuación, creamos un MailgunGateway como nuestro wrapper para la API de Mailgun. Este wrapper se encarga de todas nuestras interacciones con Mailgun a través de nuestra cuenta. Aquí utilizamos la API send_batch_messages de Mailgun para enviar emails en HTML. Nos permite enviar un mensaje a múltiples destinatarios con una sola llamada a la API. Mantenemos nuestra clave de API como variables de entorno en el servidor para mayor seguridad. También hay un simple filtro de entrega de modo que no se envían emails en el entorno de desarrollo; en staging, todos los emails se envían a Chris (mi cofundador) y a mí, para que podamos probar las cosas sin temor a enviar spam a nuestros usuarios; solo en el entorno de producción se envían emails a usuarios reales.
class MailgunGateway
def send_batch_message(options={})
RestClient.post(messaging_api_end_point,
from: default_sender,
to: delivery_filter(options[:to]),
subject: options[:subject],
html: options[:body],
:"h:Reply-To" => options[:reply_to],
:"recipient-variables" => options[:recipient_variables]
) if Rails.env.staging? || Rails.env.production?
end
end
private
def default_sender
"Tealeaf Academy "
end
def api_key
@api_key ||= ENV['mailgun_api_key']
end
def messaging_api_end_point
@messaging_api_end_piont ||=
"https://api:#{api_key}@api.mailgun.net/v2/messaging.gotealeaf.com/messages"
end
def delivery_filter(emails)
Rails.env.production? ? emails : "kevin@gotealeaf.com, chris@gotealeaf.com"
end
end
Envía notificaciones por email para nuevas publicaciones o comentarios
Una vez que se crea una publicación o comentario, enviamos una notificación por email a todos los participantes del curso. Por ahora estamos enviando emails de forma sincrónica, pero a medida que tengamos más usuarios, probablemente querremos delegar esto a un trabajo en segundo plano.
Launch School Code 1class Courses::PostsController < AuthenticatedController
expose(:course)
expose(:posts) { course.posts }
expose(:post)
def create
post.user = current_user
post.save
CourseNotifier.new(course).notify_course_participants_on_new_discussion(post)
redirect_to course_home_path(course)
end
...
end
class Courses::Posts::CommentsController < AuthenticatedController
expose(:course)
expose(:posts) { course.posts }
expose(:post)
expose(:comments) { post.comments }
expose(:comment)
def create
comment.user = current_user
comment.save
CourseNotifier.new(course).notify_course_participants_on_new_discussion(comment)
redirect_to course_home_path(course)
end
...
end
CourseNotifier es la clase donde ponemos la lógica específica de nuestra aplicación sobre las notificaciones. Ten en cuenta que MailgunGateway se inyecta como el gateway predeterminado: esto es de cuando solíamos tener varios proveedores de servicios de envío de emails para campañas, listas y emails transaccionales. ¡Ahora es menos necesario, ya que consolidamos todas las necesidades de entrega de emails en Mailgun!
class CourseNotifier
attr_reader :course, :gateway
def initialize(course, gateway=MailgunGateway.new)
@course = course
@gateway = gateway
end
def notify_course_participants_on_new_discussion(discussion)
gateway.send_batch_message(
to: notification_recipients(discussion).map(&:email).join(", "),
subject: notification_subject(discussion),
body: discussion_notification_text(discussion),
reply_to: reply_to_address(discussion),
recipient_variables: recipient_variables(
notification_recipients(discussion)
)
)
end
...
private
def notification_recipients(discussion)
course.participants.reject {|participant| participant.email == discussion.user.email }
end
def notification_subject(discussion)
discussion.is_a?(Post) ?
"[Tealeaf Academy] #{discussion.user.name} Posted a New Message on the Discussion Board" :
"[Tealeaf Academy] #{discussion.user.name} Replied to a Message on the Discussion Board"
end
def reply_to_address(discussion)
"reply+#{discussion.token}@messaging.gotealeaf.com"
end
def recipient_variables(recipients)
vars = recipients.map do |recipient|
""#{recipient.email}": {"name":"#{recipient.name}"}"
end
"{#{vars.join(', ')}}"
end
def discussion_notification_text(discussion)
<<-EMAIL
Hi %recipient.name%,
<p>#{discussion.user.name} says on the course dicussion board:</p>
"#{discussion.text}"
<br />
<p>Reply to this email directly or <a href="http://www.gotealeaf.com/courses/#{course.slug}/home">view it on the discussion board</a></p>
EMAIL
end
end
El replytoaddress es donde insertamos el token de la publicación en el encabezado «Reply-To». El contenido de los emails es bastante simple, así que simplemente lo ponemos aquí en la clase. Si tuviéramos un estilo de email más elaborado, habríamos utilizado la renderización de plantillas para manejarlo. Con la API sendbatchmessage de Mailgun, podemos llamar a la API solo una vez para enviar a múltiples destinatarios, y el método recipient_variables es donde personalizamos los mensajes de email para cada destinatario e incluimos sus nombres para darle un toque personal.
Gestión de mensajes entrantes en la aplicación
En Mailgun, bajo «Routes» en el Panel de Control, creamos una ruta como la siguiente:
- Expresión de filtro: match_recipient(«reply+(.*)@messaging.gotealeaf.com»)
- Acción: forward(«http://www.gotealeaf.com/api/incoming_messages/?post_token=1»)
Cuando un usuario responde a un email, lo responde a una dirección de email como «reply+fj42gq4v@messaging.gotealeaf.com», y esta ruta reenviará el email a un webhook que exponemos para manejar los mensajes entrantes.
class Api::IncomingMessagesController < ApplicationController
skip_before_filter :verify_authenticity_token
def create
user = User.where(email: params['sender']).first
post = Post.where(token: params['post_token']).first
text = params["stripped-text"]
if post && user && text.present?
comment = post.comments.create(user: user, text: text)
CourseNotifier.new(post.course).notify_course_participants_on_new_discussion(comment)
end
head(200)
end
end
Aquí, usamos el email del remitente para encontrar al autor, y usamos el token de la publicación para encontrar la publicación bajo la cual se debe cotejar esta respuesta. ¡Mailgun nos da el útil texto sin formato que elimina la parte del mensaje original para que solo contenga la respuesta en sí! Al final, devolvemos un encabezado 200 para decirle a Mailgun que esta interacción fue exitosa; de lo contrario, Mailgun pensará que nuestro servidor está caído y seguirá intentando llamar fielmente a nuestro webhook.
El resultado de implementar este flujo de trabajo es impresionante: la actividad en nuestro foro de discusión se triplicó, y las preguntas ahora suelen responderse en menos de una hora, a veces incluso en minutos, los estudiantes pueden pasar a la siguiente serie de tareas mucho más rápido y estamos muy contentos de cómo resultó.
«Podemos garantizar un mejor servicio para nuestros profesionales del marketing al usar Mailgun. Ellos cuentan con nosotros y nosotros contamos con Mailgun: y esta relación nos ayuda a mantener la credibilidad ante nuestros clientes».