Fallstudien
Wie die Launch School Academy das Engagement der Studierenden verdreifachte
An der Tealeaf Academy steht die Schaffung einer „Gemeinsam lernen, gemeinsam Fortschritte machen“-Erfahrung für unsere Studierenden im Mittelpunkt unserer Lehrmethode. Eines unserer wichtigsten Tools ist das Diskussionsforum, in dem Studierende Fragen stellen, Ideen austauschen, bei Hausaufgaben zusammenarbeiten und Lehrer schnell einspringen, um den Studierenden bei Problemen zu helfen. Eine unserer jüngsten Prioritäten bestand darin, die Reibungsverluste bei der Nutzung des Diskussionsforums zu verringern und mehr Diskussionen mit einer ergänzenden E-Mail-Benachrichtigung und einem „Per E-Mail antworten, um im Diskussionsforum zu posten“-Workflow zu fördern.
Sobald wir den untenstehenden Code über die Mailgun Routes API implementiert hatten, verdreifachte sich die Aktivität in unserem Diskussionsforum. Fragen werden nun in der Regel innerhalb einer Stunde, manchmal sogar innerhalb von Minuten, beantwortet, und die Studierenden können viel schneller zur nächsten Reihe von Aufgaben übergehen. So haben wir das gemacht:
Unser Workflow sah folgendermaßen aus:
- Wenn ein Beitrag (höchstwahrscheinlich eine Frage) erstellt wird, werden alle Kursteilnehmer durch eine E-Mail-Benachrichtigung informiert.
- Kursteilnehmer können direkt aus ihrem E-Mail-Posteingang auf die E-Mail-Benachrichtigungen antworten, ohne sich für den Kurs anmelden zu müssen.
- Diese Antwort wird im Online-Diskussionsforum veröffentlicht und auch an andere Kursteilnehmer gesendet, um die Konversation am Laufen zu halten.
Warum wir uns für Mailgun entschieden haben
Der wichtigste Teil dieses Workflows ist das Empfangen und Parsen von eingehenden E-Mail-Nachrichten. Wir haben uns bei verschiedenen E-Mail-Service-Providern umgesehen und uns letztendlich für Mailgun entschieden, weil:
- Es ist sehr entwicklerfreundlich – wir sind Entwickler, und Mailgun spricht uns an. Die APIs bieten viele Low-Level-Optionen, die Anpassungen ermöglichen. Wir mögen die Routes im Besonderen – sie bieten eine schöne Abstraktionsschicht, die die Integration mit Apps sehr einfach macht. (siehe unten, wie wir sie verwenden)
- Es ist der Service mit dem umfassendsten Funktionsumfang, den wir gefunden haben – wir können Mailgun für Transaktions-E-Mails, Kampagnen sowie E-Mail-Listen nutzen – es ist schön, nur einen einzigen Anbieter zu haben, der alles abwickelt, was wir brauchen.
- Der Preis ist angemessen und die Möglichkeit, ein Upgrade auf eine dedizierte IP-Adresse und benutzerdefiniertes DKIM vorzunehmen, ist gut, auch wenn wir dies noch nicht benötigen.
- Der Support ist erstklassig. Es gibt einen Live-Chat, in dem ich bei Problemen direkt mit ihren Entwicklern sprechen kann, und er war für uns sehr nützlich, um Probleme zu lösen.
Einrichtung der E-Mail-Infrastruktur
- Der erste Schritt war die Erstellung einer neuen Domain bei Mailgun. In unserem Fall ist es messaging.gotealeaf.com, die wir für das Senden und Empfangen von E-Mails verwenden.
- Als Nächstes haben wir ein MailgunGateway als Wrapper für die API von Mailgun erstellt. Dieser Wrapper kümmert sich über unser Konto um alle unsere Interaktionen mit Mailgun. Hier verwenden wir die send_batch_messages API von Mailgun, um HTML-E-Mails zu senden. Sie ermöglicht es uns, mit einem einzigen API-Aufruf eine Nachricht an mehrere Empfänger zu senden. Für zusätzliche Sicherheit bewahren wir unseren API-Schlüssel als Umgebungsvariablen auf dem Server auf. Es gibt auch einen einfachen Zustellungsfilter, sodass in der Entwicklungsumgebung keine E-Mails gesendet werden; in der Staging-Umgebung werden alle E-Mails an Chris (meinen Mitgründer) und mich gesendet, sodass wir Dinge testen können, ohne befürchten zu müssen, unsere Nutzer mit Spam zu belästigen; nur in der Produktionsumgebung werden E-Mails an echte Nutzer gesendet.
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
E-Mail-Benachrichtigungen für neuen Beitrag oder Kommentar senden
Sobald ein Beitrag oder Kommentar erstellt wurde, senden wir eine E-Mail-Benachrichtigung an alle Kursteilnehmer. Vorerst senden wir E-Mails synchron, aber wenn wir mehr Nutzer haben, werden wir dies wahrscheinlich an einen Hintergrundjob auslagern wollen.
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
Der CourseNotifier ist die Klasse, in die wir unsere anwendungsspezifische Logik für Benachrichtigungen einfügen. Beachten Sie, dass das MailgunGateway als Standard-Gateway eingefügt wird – dies stammt aus der Zeit, als wir noch mehrere E-Mail-Service-Provider für Kampagnen, Listen und Transaktions-E-Mails hatten. Jetzt, da wir alle Anforderungen für die E-Mail-Zustellung bei Mailgun konsolidiert haben, ist dies weniger notwendig!
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
Die replytoaddress ist der Ort, an dem wir das Beitrags-Token in die „Reply-To“-Kopfzeile einfügen. Der Inhalt der E-Mails ist recht einfach, daher setzen wir ihn einfach hier in die Klasse. Hätten wir einen aufwändigeren E-Mail-Stil, hätten wir das Rendern von Vorlagen verwendet, um dies zu handhaben. Mit der sendbatchmessage API von Mailgun können wir die API nur einmal aufrufen, um an mehrere Empfänger zu senden, und die recipient_variables Methode ist der Ort, an dem wir E-Mail-Nachrichten für jeden Empfänger anpassen, um ihre Namen hinzuzufügen und eine persönliche Note zu verleihen.
Umgang mit eingehenden Nachrichten in der Anwendung
Wenn wir zu Mailgun wechseln, haben wir unter „Routes“ im Control Panel eine Route wie die folgende erstellt:
- Filter Expression: match_recipient(“reply+(.*)@messaging.gotealeaf.com”)
- Aktion: forward(“http://www.gotealeaf.com/api/incoming_messages/?post_token=1”)
Wenn ein Nutzer auf eine E-Mail antwortet, antwortet er an eine E-Mail-Adresse wie „reply+fj42gq4v@messaging.gotealeaf.com“, und diese Route leitet die E-Mail an einen Webhook weiter, den wir bereitstellen, um eingehende Nachrichten zu verarbeiten.
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
Hier verwenden wir die E-Mail des Absenders, um den Autor zu finden, und verwenden das Beitrags-Token, um den Beitrag zu finden, unter dem diese Antwort zusammengefasst werden soll. Mailgun gibt uns den sehr nützlichen bereinigten Text, der den ursprünglichen Nachrichtenteil entfernt, sodass nur die tatsächliche Antwort enthalten ist! Am Ende senden wir eine 200er-Kopfzeile zurück, um Mailgun mitzuteilen, dass diese Interaktion erfolgreich war. Andernfalls denkt Mailgun, dass unser Server ausgefallen ist, und wird weiterhin gewissenhaft versuchen, unseren Webhook aufzurufen.
Das Ergebnis der Implementierung dieses Workflows ist beeindruckend – die Aktivität in unserem Diskussionsforum hat sich verdreifacht, und Fragen werden nun in der Regel innerhalb einer Stunde, manchmal sogar in Minuten, beantwortet. Die Studierenden können viel schneller zur nächsten Reihe von Aufgaben übergehen, und wir sind sehr glücklich darüber, wie sich das entwickelt hat.
„Durch den Einsatz von Mailgun können wir einen besseren Service für unsere Marketer gewährleisten. Sie zählen auf uns, und wir zählen auf Mailgun: und diese Beziehung hilft uns, die Glaubwürdigkeit bei unseren Kunden zu erhalten.“