Estudos de caso
Como a Launch School Academy aumentou o engajamento dos alunos em 3x
Na Tealeaf Academy, criar uma experiência de “Estudar Juntos, Progredir Juntos” para os nossos alunos está no centro da nossa forma de ensinar. Uma das nossas principais ferramentas é o fórum de discussão, onde os alunos fazem perguntas, compartilham ideias, colaboram nas tarefas de casa e os professores entram rapidamente para ajudar os alunos a resolverem seus problemas. Uma de nossas prioridades recentes era reduzir o atrito no uso do fórum de discussão e encorajar mais discussões com uma notificação por e-mail complementar e um fluxo de trabalho de “responder ao e-mail para postar no fórum de discussão”.
Assim que implementamos o código abaixo usando a API Routes do Mailgun, a atividade no nosso fórum de discussão aumentou três vezes, e as perguntas agora normalmente são respondidas em uma hora, às vezes até em minutos, e os alunos conseguem passar para o próximo conjunto de tarefas muito mais rapidamente. Veja como fizemos isso:
Nosso fluxo de trabalho funcionaria da seguinte maneira:
- Quando uma postagem (muito provavelmente, uma pergunta) é criada, todos os participantes do curso são avisados por uma notificação por e-mail
- Os participantes do curso podem responder às notificações por e-mail diretamente da sua caixa de entrada de e-mail, sem precisarem entrar no curso
- Essa resposta será publicada no fórum de discussão online e também é enviada para outros participantes do curso, para manter a conversa fluindo.
Por que escolhemos o Mailgun
A peça-chave deste fluxo de trabalho é receber e analisar as mensagens de e-mail de entrada. Nós pesquisamos por vários provedores de serviços de e-mail, e no final escolhemos o Mailgun porque:
- Ele é muito amigável para desenvolvedores – Nós somos desenvolvedores, e o Mailgun fala a nossa língua. As APIs expõem diversas opções de baixo nível que permitem ajustes. Nós gostamos das rotas em particular – É uma ótima camada de abstração que torna a integração com os aplicativos muito fácil. (veja como nós o usamos abaixo)
- É o serviço mais completo em recursos que encontramos – podemos usar o Mailgun para e-mails transacionais, campanhas, bem como listas de e-mails – é bom ter apenas um provedor de serviços para lidar com tudo o que precisamos.
- O preço é razoável e o caminho para fazer upgrade para IP dedicado e DKIM personalizado é ótimo, mesmo que ainda não precisemos disso.
- O suporte é de primeira linha. Há um chat ao vivo onde eu posso conversar com os desenvolvedores deles diretamente sobre os problemas, e tem sido muito útil para nós resolvermos os problemas.
Configurando a infraestrutura de e-mail
- O primeiro passo foi criar um novo domínio no Mailgun. No nosso caso, é messaging.gotealeaf.com, o qual usamos para enviar e receber e-mails.
- Em seguida, criamos um MailgunGateway como nosso wrapper para a API do Mailgun. Esse wrapper cuida de todas as nossas interações com o Mailgun através da nossa conta. Aqui usamos a API send_batch_messages do Mailgun para enviar e-mails em HTML. Ela nos permite enviar uma mensagem para vários destinatários com uma única chamada de API. Mantemos a nossa chave de API como variáveis de ambiente no servidor para maior segurança. Há também um filtro de entrega simples para que nenhum e-mail seja enviado no ambiente de desenvolvimento; No ambiente de homologação, todos os e-mails são enviados para Chris (meu cofundador) e para mim, para que possamos testar as coisas sem o receio de enviar spam aos nossos usuários; Somente no ambiente de produção os e-mails são enviados para usuários reais.
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
Enviar notificações por e-mail para nova postagem ou comentário
Assim que uma postagem ou comentário é criado, enviamos uma notificação por e-mail para todos os participantes do curso. Estamos enviando e-mails de forma síncrona por enquanto, mas, à medida que tivermos mais usuários, provavelmente vamos querer transferir isso para um trabalho em 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
O CourseNotifier é a classe onde colocamos a lógica específica do nosso aplicativo para notificações. Observe que o MailgunGateway é injetado como o gateway padrão – isso é da época em que costumávamos ter vários provedores de serviços de e-mail para campanhas, listas e e-mails transacionais. Isso é menos necessário agora que consolidamos todas as necessidades de entrega de e-mail no 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
O replytoaddress é onde inserimos o token da postagem no cabeçalho “Reply-To”. O conteúdo dos e-mails é bem simples, então nós apenas o colocamos aqui na classe. Se tivéssemos um estilo de e-mail mais elaborado, teríamos usado a renderização de modelo para lidar com isso. Com a API sendbatchmessage do Mailgun, podemos chamar a API apenas uma vez para enviar a vários destinatários, e o método recipient_variables é onde personalizamos as mensagens de e-mail para cada destinatário para incluir os seus nomes e adicionar um toque pessoal.
Lidando com mensagens de entrada no aplicativo
Indo para o Mailgun, em “Routes” no Painel de Controle, criamos uma rota da seguinte forma:
- Expressão de Filtro: match_recipient(“reply+(.*)@messaging.gotealeaf.com”)
- Ação: forward(“http://www.gotealeaf.com/api/incoming_messages/?post_token=1”)
Quando um usuário responde a um e-mail, ele o responde para um endereço de e-mail como “reply+fj42gq4v@messaging.gotealeaf.com”, e essa rota encaminhará o e-mail para um webhook que expomos para lidar com mensagens de entrada.
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
Aqui, usamos o e-mail do remetente para encontrar o autor, e usamos o token da postagem para encontrar a postagem sob a qual essa resposta deve ser agrupada. O Mailgun nos fornece o texto limpo, que é muito útil e remove a parte original da mensagem para conter apenas a resposta real! No final, retornamos um cabeçalho 200 para informar ao Mailgun que essa interação foi bem-sucedida, caso contrário, o Mailgun achará que nosso servidor está fora do ar e continuará fielmente tentando chamar o nosso webhook.
O resultado da implementação desse fluxo de trabalho é impressionante – a atividade no nosso fórum de discussão aumentou três vezes, e as perguntas agora normalmente são respondidas em uma hora, às vezes até em minutos, os alunos conseguem passar para o próximo conjunto de tarefas muito mais rapidamente e estamos muito felizes com o resultado.
“Conseguimos garantir melhor atendimento aos nossos profissionais de marketing usando o Mailgun. Eles contam conosco, e nós contamos com o Mailgun: e esse relacionamento nos ajuda a manter a credibilidade com nossos clientes.”