• Stars
    star
    732
  • Rank 61,915 (Top 2 %)
  • Language
    Ruby
  • License
    Other
  • Created over 5 years ago
  • Updated 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Heya πŸ‘‹ is a campaign mailer for Rails. Think of it like ActionMailer, but for timed email sequences. It can also perform other actions like sending a text message.

Heya πŸ‘‹

Test Gem Version Maintainability

Heya is a campaign mailer for Rails. Think of it like ActionMailer, but for timed email sequences. It can also perform other actions like sending a text message.

Getting started

Getting started with Heya is easy:

  1. Install the gem
  2. Create a campaign
  3. Run the scheduler

Prerequisites

Heya was built to work with PostgreSQL. Pull requests are welcome to support more databases.

Installing the Heya gem

  1. Add this line to your application's Gemfile:
gem "heya", github: "honeybadger-io/heya"
  1. Then execute:
bundle install
rails generate heya:install
rails db:migrate

This will:

  1. Copy Heya's migration files to db/migrate
  2. Copy Heya's default initializer to config/initializers/heya.rb
  3. Create the file app/campaigns/application_campaign.rb
  4. Run local migrations
Note: Heya doesn't store a copy of your user data; instead, it reads from your existing User model (it never writes). If you have a different user model, change the user_type configuration option in config/initializers/heya.rb.
# config/initializers/heya.rb
Heya.configure do |config|
  config.user_type = "MyUser"
end

Creating your first campaign

  1. Create a campaign:
rails generate heya:campaign Onboarding welcome:0
  1. Add a user to your campaign:
OnboardingCampaign.add(user)

Add the following to your User model to send them the campaign when they first signup:

after_create_commit do
  OnboardingCampaign.add(self)
end

Running the scheduler

To start queuing emails, run the scheduler task periodically:

rails heya:scheduler

Heya uses ActiveJob to send emails in the background. Make sure your ActiveJob backend is configured to process the heya queue. For example, here's how you might start Sidekiq:

bundle exec sidekiq -q default -q heya

You can change Heya's default queue using the queue option:

# app/campaigns/application_campaign.rb
class ApplicationCampaign < Heya::Campaigns::Base
  default queue: "custom"
end

Bonus: tips for working with email in Rails

Use MailCatcher to see emails sent from your dev environment
# config/environments/development.rb
Rails.application.configure do
  # ..

  # Use MailCatcher to inspect emails
  # http://mailcatcher.me
  # Usage:
  #   gem install mailcatcher
  #   mailcatcher
  #   # => Starting MailCatcher
  #   # => ==> smtp://127.0.0.1:1025
  #   # => ==> http://127.0.0.1:1080
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {host: "localhost", port: 1025}
end
Use Maildown to write your emails in Markdown
$ bundle add maildown
$ rails generate heya:campaign Onboarding welcome
      create  app/campaigns/application_campaign.rb
      create  app/campaigns/onboarding_campaign.rb
      create  app/views/heya/campaign_mailer/onboarding_campaign/welcome.md.erb

☝️ Notice how only one template was generated; Maildown automatically builds the HTML and text variants from the markdown file.

Use ActionMailer::Preview to preview emails as you write them

Heya's campaign generator generates previews for campaigns at (test|spec)/mailers/previews/*_campaign_preview.rb. To see them, open http://localhost:3000/rails/mailers/. If you didn't use the generator, you can still build your own preview:

# test/mailers/previews/onboarding_campaign_preview.rb
class OnboardingCampaignPreview < ActionMailer::Preview
  def welcome
    OnboardingCampaign.welcome(user)
  end

  private

  def user
    User.where(id: params[:user_id]).first || User.first
  end
end

Configuration

You can use the following options to configure Heya (find this file in config/initializers/heya.rb):

Heya.configure do |config|
  # The name of the model you want to use with Heya.
  config.user_type = "User"

  # The default options to use when processing campaign steps.
  config.campaigns.default_options = {from: "[email protected]"}

  # Campaign priority. When a user is added to multiple campaigns, they are
  # sent in this order. Campaigns are sent in the order that the users were
  # added if no priority is configured.
  config.campaigns.priority = [
    "FirstCampaign",
    "SecondCampaign",
    "ThirdCampaign"
  ]
end

Campaigns

Creating campaigns

Heya stores campaigns in app/campaigns/, similar to how Rails stores mailers in app/mailers/. To create a campaign, run the following command inside your Rails project:

rails generate heya:campaign Onboarding first second third

This will:

  1. Create the file app/campaigns/onboarding_campaign.rb
  2. Create the directory app/views/heya/campaign_mailer/onboarding_campaign/
  3. Create email templates inside of app/views/heya/campaign_mailer/onboarding_campaign/
  4. Create an ActionMailer preview at (test|spec)/mailers/previews/onboarding_campaign_preview.rb

Here's the campaign that the above command generates:

# app/campaigns/application_campaign.rb
class ApplicationCampaign < Heya::Campaigns::Base
  default from: "[email protected]"
end

# app/campaigns/onboarding_campaign.rb
class OnboardingCampaign < ApplicationCampaign
  step :first,
    subject: "First subject"

  step :second,
    subject: "Second subject"

  step :third,
    subject: "Third subject"
end

Steps

The step method defines a new step in the sequence. When you add a user to the campaign, Heya completes each step in the order that it appears.

The default time to wait between steps is two days, calculated from the time the user completed the previous step (or the time the user entered the campaign, in the case of the first step).

Each step has several options available (see the section Creating messages).

Creating messages

Messages are defined inside Heya campaigns using the step method. When you add a user to a campaign, Heya completes each step in the order that it appears.

The most important part of each step is its name, which must be unique to the campaign. The step's name is how Heya tracks which user has received which message, so it's essential that you don't change it after the campaign is active (if you do, Heya will assume it's a new message).

Here's an example of defining a message inside a campaign:

class OnboardingCampaign < ApplicationCampaign
  step :welcome, wait: 1.day,
    subject: "Welcome to my app!"
end

In the above example, Heya will send a message named :welcome one day after a user enters the campaign, with the subject "Welcome to my app!"

The wait option tells Heya how long to wait before sending each message (the default is two days). There are a few scheduling options that you can customize for each step:

Option Name Default Description
wait 2.days The duration of time to wait before sending each message
segment nil The segment who should receive the message
action Heya::Campaigns::Actions::Email The action to perform (usually sending an email)
queue "heya" The ActiveJob queue

Heya uses the following additional options to build the message itself:

Option Name Default Description
subject required The email's subject
from Heya default The sender's email address
layout Heya default The email's layout file
to See below The recipient's name & email address
bcc nil BCC when sending emails
headers {} Headers to include when sending emails

You can change the default options using the default method at the top of the campaign. Heya applies default options to each step which doesn't supply its own:

class OnboardingCampaign < ApplicationCampaign
  default wait: 1.day,
    queue: "onboarding",
    from: "[email protected]",
    layout: "onboarding"

  # Will still be sent after one day from the
  # email address [email protected]
  step :welcome,
    subject: "Welcome to my app!"
end

Customizing the to field

You can customize the to field by passing a callable object, which Heya will invoke with the user. For instance:

class OnboardingCampaign < ApplicationCampaign
  step :welcome,
    subject: "Welcome to my app!",
    to: -> (user) { ActionMailer::Base.email_address_with_name(user.email, user.nickname) }
end

It is recommended to rely on ActionMailer::Base.email_address_with_name so that sanitization is correctly applied.

If the to param is not provided, Heya will default to:

  1. user#first_name
  2. user#name

If the user object doesn't respond to these methods, it will fallback to a simple user.email in the to field.

Quality control option

You may wish to apply quality control to individual steps of a campaign. For example, when adding a new step to an existing campaign it is a good idea to inspect real-time results in production. You can do this by using the bcc: step option, which would look like this:

class OnboardingCampaign < ApplicationCampaign
  default wait: 1.day,
    queue: "onboarding",
    from: "[email protected]"

  # Will still be sent after one day from the
  # email address [email protected]
  step :welcome,
    subject: "Welcome to my app!"

  step :added_two_months_later,
    subject: "We now have something new to say!",
    bcc: '[email protected]'
end

Customizing email subjects for each user

The subject can be customized for each user by using a lambda instead of a String:

# app/campaigns/onboarding_campaign.rb
class OnboardingCampaign < ApplicationCampaign
  step :welcome,
    subject: ->(user) { "Heya #{user.first_name}!" }
end

Translations for email subjects (I18n)

If you don't pass a subject to the step method, Heya will try to find it in your translations. The performed lookup will use the pattern <campaign_scope>.<step_name>.subject to construct the key.

# app/campaigns/onboarding_campaign.rb
class OnboardingCampaign < ApplicationCampaign
  step :welcome
end
# config/locales/en.yml
en:
  onboarding_campaign:
    welcome:
      subject: "Heya!"

To define parameters for interpolation, define a #heya_attributes method on your user model:

# app/models/user.rb
class User < ApplicationRecord
  def heya_attributes
    {
      first_name: name.split(" ").first
    }
  end
end
# config/locales/en.yml
en:
  onboarding_campaign:
    welcome:
      subject: "Heya %{first_name}!"

Custom Actions

You can override the default step behavior to perform custom actions by passing a block to the step method:

class OnboardingCampaign < ApplicationCampaign
  step :first_email,
    subject: "You're about to receive a txt"

  step :sms do |user|
    SMS.new(to: user.cell, body: "Hi, #{user.first_name}!").deliver
  end

  step :second_email,
    subject: "Did you get it?"
end

Step blocks receive two optional arguments: user and step, and are processed in a background job alongside other actions.

Adding users to campaigns

Heya leaves when to add users to campaigns completely up to you; here's how to add a user to a campaign from anywhere in your app:

OnboardingCampaign.add(user)

To remove a user from a campaign:

OnboardingCampaign.remove(user)

Adding users to campaigns from Rails opens up some interesting automation possibilities--for instance, you can start or stop campaigns from ActiveRecord callbacks, or in response to other events that you're already tracking in your application. See here for a list of ideas.

Because Heya stacks campaigns by default (meaning it will never send more than one at a time), you can also queue up several campaigns for a user, and they'll receive them in order:

WelcomeCampaign.add(user)
OnboardingCampaign.add(user)
EvergreenCampaign.add(user)

Note: you can customize the priority of campaigns via Heya's configuration.

If you want to send a user two campaigns simultaneously, you can do so with the concurrent option:

FlashSaleCampaign.add(user, concurrent: true)

When you remove a user from a campaign and add them back later, they'll continue where they left off. If you want them to start over from the beginning, use the restart option:

TrialConversionCampaign.add(user, restart: true)

Automation ideas

Using ActiveSupport::Notifications to respond to lifecycle events (which could be sent from your Stripe controller, for instance):

ActiveSupport::Notifications.subscribe("user.trial_will_end") do |*args|
    event = ActiveSupport::Notifications::Event.new(*args)
    if event.payload[:user_id]
      user = User.find(event.payload[:user_id])
      TrialConversionCampaign.add(user, restart: true)
    end
end

Scheduling campaigns in ActiveRecord callbacks:

class User < ApplicationRecord
  after_create_commit do
    WelcomeCampaign.add(self)
    OnboardingCampaign.add(self)
    EvergreenCampaign.add(user)
  end
end

Customizing who gets what

Heya can send individual messages to certain users using the segment option. The following campaign will send the message to inactive users--active users will be skipped:

class ActivationCampaign < ApplicationCampaign
  step :activate, segment: ->(user) { user.inactive? }
end

When you're checking the value of a single method on the user, the segment can be simplified to the symbol version:

class ActivationCampaign < ApplicationCampaign
  step :activate, segment: :inactive?
end

Segmenting specific campaigns

You can also narrow entire campaigns to certain users using the segment method. For instance, if you have a campaign with a specific goal such as performing an action in your app, then you can send the campaign only to the users who haven't performed the action:

class UpgradeCampaign < ApplicationCampaign
  segment { |u| !u.upgraded? }

  step :one
  step :two
  step :three
end

If they upgrade half way through the campaign, Heya will stop sending messages and remove them from the campaign.

Likewise, you can require that users meet conditions to continue receiving a campaign. Here's a campaign which sends messages only to trial users--non-trial users will be removed from the campaign:

class TrialCampaign < ApplicationCampaign
  segment :trial?

  step :one
  step :two
  step :three
end

Segmenting all campaigns

Heya campaigns inherit options from parent campaigns. For example, to make sure unsubscribed users never receive an email from Heya, create a segment in the ApplicationCampaign, and then have all other campaigns inherit from it:

class ApplicationCampaign < Heya::Campaigns::Base
  segment :subscribed?
end

Handling exceptions

Heya campaigns are rescuable. Use the rescue_from method to handle exceptions in campaigns:

class OnboardingCampaign < ApplicationCampaign
  rescue_from Postmark::InactiveRecipientError, with: :log_error

  private

  def log_error(error)
    Rails.logger.error("Got Heya error: #{error}")
  end
end

See the Rails documentation for additional details.

Campaigns FAQ

What happens when:

I reorder messages in an active campaign?

Heya sends the next unsent message after the last message the user received. When you move a message, the users who last received it will be moved with it, and continue from that point in the campaign. Heya skips messages which the user has already seen.

I add a message to an active campaign?

Users who have already received a message after the new message will not receive the message.

I remove a message from an active campaign?

Users who last received the message will be moved up to the previously received message, and continue from that point in the campaign. Heya skips messages which the user has already seen.

I rename a message in an active campaign?

Renaming a message is equivalent to removing the message and adding a new one. Users who are waiting to receive an earlier message in the campaign will receive the new message. Users who last received the old message will also receive the new one since it has replaced its position in the campaign.

A user skips a message based on its conditions?

Heya waits the defined wait time for every message in the campaign. If a user doesn't match the conditions, Heya skips it. If the next message's wait time is less than or equal to the skipped message's, it sends it immediately. If the next wait period is longer, it sends it after the new wait time has elapsed.

I delete an active campaign?

Heya will immediately stop sending the campaign; the campaign's data will remain until you manually delete it. If you restore the file before deleting the campaign's data, Heya will resume sending the campaign.

I add a user to multiple campaigns?

By default, Heya sends each user one campaign at a time. It determines the order of campaigns using the campaign priority. When you add a user to a higher priority campaign, the new campaign will begin immediately. Once completed, the next highest priority campaign will resume sending.

To send a campaign concurrent to other active campaigns, use the concurrent option.

I add a user to a campaign they already completed?

When you add a user to a campaign that they previously completed, Heya sends new messages which were added to the end of the campaign. Skipped messages will not be sent. To resend all messages, use the restart option.

Less frequently asked questions:

Can the same message be delivered twice?

Nope, not without restarting the campaign using the restart option (which will resend all the messages).

Can the same campaign be sent twice?

Yep. When you add a user to a campaign that they previously completed, Heya sends new messages which were added to the end of the campaign. Skipped messages will not be sent. To resend all messages, use the restart option.

Can I resend a campaign to a user?

Yep. Use the restart option to resend a campaign to a user (if they are already in the campaign, the campaign will start over from the beginning).

Can I send a user two campaigns at the same time?

Yep. By default, Heya sends campaigns ain order of priority. Use the concurrent option to send campaigns concurrently.

Upgrading Heya

Heya adheres to Semantic Versioning, and should be considered unstable until version 1.0.0. Always check CHANGELOG.md prior to upgrading (breaking changes will always be called out there). Upgrade instructions for breaking changes are in UPGRADING.md.

Roadmap

See here for things we're considering adding to Heya.

Contributing

  1. Fork it.
  2. Create a topic branch git checkout -b my_branch
  3. Make your changes and add an entry to CHANGELOG.md.
  4. Commit your changes git commit -am "Boom"
  5. Push to your branch git push origin my_branch
  6. Send a pull request

Releasing

  1. gem install gem-release
  2. gem bump -v [version] -t -r
  3. Update unreleased heading in CHANGELOG.md (TODO: automate this in gem-release command)
  4. git push origin main --tags

License

Heya is licensed under the LGPL.

More Repositories

1

incoming

Incoming! helps you receive email in your Rack apps.
Ruby
309
star
2

honeybadger-ruby

Ruby gem for reporting errors to honeybadger.io
Ruby
244
star
3

honeybadger-elixir

Elixir client for Honeybadger.
Elixir
181
star
4

honeybadger-js

Universal JavaScript library for reporting errors to Honeybadger.io ⚑
TypeScript
109
star
5

pg_partition_manager

Manage PostgreSQL table partitions
Ruby
67
star
6

honeybadger-laravel

PHP/Laravel library for reporting errors to Honeybadger.io 🐘 ⚑
PHP
39
star
7

honeybadger-php

PHP library for reporting errors to Honeybadger.io 🐘 ⚑
PHP
37
star
8

honeybadger-go

Send Go (golang) panics and errors to Honeybadger.
Go
35
star
9

serverless-quickstart

A boilerplate project for getting started with Serverless/Node ⚑
JavaScript
33
star
10

honeybadger-webpack

A webpack plugin to send sourcemaps to Honeybadger
JavaScript
29
star
11

honeybadger-node

A node.js notifier for honeybadger.io
JavaScript
21
star
12

honeybadger-crystal

Crystal library for reporting errors to Honeybadger.io πŸ’Ž ⚑
Crystal
16
star
13

honeybadger-python

Send Python and Django errors to Honeybadger.
Python
15
star
14

nextjs-with-honeybadger

This is an example of how to use Honeybadger to catch & report errors on both client + server side in Next.js.
JavaScript
15
star
15

honeybadger-react

Official React integration for Honeybadger.io ⚑
TypeScript
14
star
16

honeybadger-vue

Official Vue.js integration for Honeybadger.io ⚑
JavaScript
11
star
17

github-notify-deploy-action

Send deployment notifications to Honeybadger via GitHub Actions
Shell
10
star
18

honeybadger-lambda-node

A Node template for AWS Lamda which reports errors to Honeybadger.io
JavaScript
10
star
19

honeybadger-java

Java Client to report exceptions to Honeybadger.io
Java
10
star
20

reck

🐍 Reck: the exception-powered Ruby web framework! 🐍
Ruby
9
star
21

honeybadger-rails-webpacker-example

Example Rails 5.1/Webpacker app with support for Source Maps in Honeybadger
Ruby
6
star
22

yabeda-honeybadger_insights

Yabeda integration with Honeybadger Insights
Ruby
6
star
23

github-upload-sourcemap-action

Upload source maps to Honeybadger via GitHub Actions
Shell
5
star
24

nova-honeybadger

Official Laravel Nova package for Honeybadger.io 🐘 ⚑
PHP
5
star
25

heya-app

This is my testbed/example Rails app for the heya gem.
Ruby
4
star
26

delayed_job_honeybadger

Notifies Honeybadger of errors in Delayed Job workers.
Ruby
4
star
27

honeybadger-dotnet

C#
3
star
28

crywolf-node

Honeybadger example for Node.JS
JavaScript
3
star
29

crywolf-elixir

Honeybadger example for Elixir
Elixir
3
star
30

honeybadger-react-native

Official React Native integration for Honeybadger.io ⚑
JavaScript
2
star
31

honeybadger-cocoa

Official Objective-C & Swift library for reporting errors and crashes to Honeybadger.io 🍎 ⚑
Objective-C
2
star
32

unicorn

Ruby
1
star
33

rpush-plugin

Rpush plugin for Honeybadger
Ruby
1
star
34

circleci-orb

CircleCI Orb that interacts with the Honeybadger.io API.
1
star
35

honeybadger-sinatra-example

Example sinatra app reporting errors w/ Honeybadger
Ruby
1
star
36

examples-react-on-rails

A simple example of how to integrate Honeybadger with React on Rails to unlock Universal (Isomorphic) error reporting in your React-based Rails apps.
Ruby
1
star
37

examples-rails

A sample Rails app for trying Honeybadger.io
Ruby
1
star