Creating "Higher Priority" smtp_settings in Discourse Software Mailers - A Future Plugin Idea

When we first migrated to Discourse months ago, the default digest settings, combined with a large number of legacy user accounts ported over from our original forums, caused a flood of email from the new Discourse setup. This flood of emails caused our email server to shut down, temporarily.

It then occurred to me that Discourse should permit different smtp_settings for "high priority" such as user password resets and email logins as well as admin setup emails; and a different set of smtp_settings for "low priority" emails like topic digests.

Today, I looked into this by reviewing the Discourse code and found that this is very easy to do. If we look at the ActionMailers in the Discourse application by class:

class AdminConfirmationMailer < ActionMailer::Base
class DownloadBackupMailer < ActionMailer::Base
class GroupSmtpMailer < ActionMailer::Base
class InviteMailer < ActionMailer::Base
class RejectionMailer < ActionMailer::Base
class SubscriptionMailer < ActionMailer::Base
class TestMailer < ActionMailer::Base
class UserNotifications < ActionMailer::Base
class VersionMailer < ActionMailer::Base

There are basically two ActionMailer::Base classes which should be "high priority" and sent in different SMTP channel. These are:

class AdminConfirmationMailer < ActionMailer::Base
class UserNotifications < ActionMailer::Base

To accomplish this should quite easy.

Write a plugin which extends the two classes above, something like this:

HIGH_PRIORITY_SMTP_SETTINGS = {
   :address=>"my.email.server.com", 
   :port=>587, 
   :user_name=>"my_email_address", 
   :password=>"my_email_password", 
   :authentication=>"plain", 
   :enable_starttls_auto=>true
}

after_initialize do

  AdminConfirmationMailer.class_eval do
    before_action :high_priority_smtp_settings

    def  high_priority_smtp_settings
      self.smtp_settings = HIGH_PRIORITY_SMTP_SETTINGS
    end
  end

UserNotifications.class_eval do
    before_action :high_priority_smtp_settings

    def  high_priority_smtp_settings
      self.smtp_settings = HIGH_PRIORITY_SMTP_SETTINGS
    end
  end
end

I think it is important to have two SMTP channels, one for HIGH_PRIORITY SMTP traffic and one for LOW PRIORITY SMTP traffic (the DEFAULT).

When I looked at the UserNotifications mailer, I noticed that the digest emails are in the same mailer as lost password reset requests, etc. This means the simple code above will not accomplish our goal for the UserNotifications mailer.

It might seem unfortunate that all these mailer actions are lumped into one mailer class; but we can get around this (I think, not confirmed) by changing the callback like this (for example):

 before_action :high_priority_smtp_settings, only: [:digest]

Hence, the prototype Discourse plugin code will be more like this:

HIGH_PRIORITY_SMTP_SETTINGS = {
   :address=>"my.email.server.com", 
   :port=>587, 
   :user_name=>"my_email_address", 
   :password=>"my_email_password", 
   :authentication=>"plain", 
   :enable_starttls_auto=>true
}

after_initialize do

  AdminConfirmationMailer.class_eval do
    before_action :high_priority_smtp_settings

    def  high_priority_smtp_settings
      self.smtp_settings = HIGH_PRIORITY_SMTP_SETTINGS
    end
  end

UserNotifications.class_eval do
     before_action :high_priority_smtp_settings, only: [:digest]

    def  high_priority_smtp_settings
      self.smtp_settings = HIGH_PRIORITY_SMTP_SETTINGS
    end
  end
end

Caveat: I have not confirmed the only property for the before_action callback works as expected in an ActionMailer.

On the other hand, I have enough in mind to start prototyping this later on!

See Also:

1 Like

Untested first draft of plugin:

1 Like

3 posts were split to a new topic: Dev Notes for Action Mailer Plugin:

Here is the current iteration of this plugin.

The plugin "works" however during testing, sometimes (before this version) a "non-priority" email was sent via the priority channel, unexpectedly. This might have been an artifact of how the Discourse Job scheduler works. Because these emails are initiated by the sidekiq (the Discourse Job controller); I this this version (0.0.27) has fixed this issue.

So far, so good with v0.0.27v and the problem with "unexpected channels mixups" has not resurfaced with the updated code below:

# name: discourse-priority-action-mailer
# about: plugin to add priority smtp_settings to action mailer
# version: 0.0.27
# date: 27 Nov 2020
# authors: Neo
# url: https://community.unix.com/t/creating-higher-priority-smtp-settings-in-discourse-software-mailers-a-future-plugin-idea/380865

smtp_address_priority = defined?(GlobalSetting.smtp_address_priority) ? GlobalSetting.smtp_address_priority : GlobalSetting.smtp_address
smtp_port_priority = defined?(GlobalSetting.smtp_port_priority) ? GlobalSetting.smtp_port_priority : GlobalSetting.smtp_port
smtp_user_name_priority = defined?(GlobalSetting.smtp_user_name_priority) ? GlobalSetting.smtp_user_name_priority : GlobalSetting.smtp_user_name
smtp_password_priority = defined?(GlobalSetting.smtp_password_priority) ? GlobalSetting.smtp_password_priority : GlobalSetting.smtp_password
smtp_authentication_priority = defined?(GlobalSetting.smtp_authentication_priority) ? GlobalSetting.smtp_authentication_priority : GlobalSetting.smtp_authentication
smtp_enable_start_tls_priority = defined?(GlobalSetting.smtp_enable_start_tls_priority) ? GlobalSetting.smtp_enable_start_tls_priority : GlobalSetting.smtp_enable_start_tls

Rails.application.config.priority_smtp_settings = {
  address: smtp_address_priority,
  port: smtp_port_priority,
  user_name: smtp_user_name_priority,
  password: smtp_password_priority,
  authentication: smtp_authentication_priority,
  enable_starttls_auto: smtp_enable_start_tls_priority,
}

after_initialize do
  AdminConfirmationMailer.class_eval do
    before_action :update_smtp_settings

    def update_smtp_settings
      if GlobalSetting.smtp_password_priority.present?
        AdminConfirmationMailer.smtp_settings = Rails.application.config.priority_smtp_settings
      end
    end
  end

  UserNotifications.class_eval do
    before_action :update_smtp_settings, only: [:email_login, :signup, :forgot_password, :admin_login]
    before_action :default_smtp_settings, except: [:email_login, :signup, :forgot_password, :admin_login]

    def update_smtp_settings
      if GlobalSetting.smtp_password_priority.present?
        UserNotifications.smtp_settings = Rails.application.config.priority_smtp_settings
      end
    end

    def default_smtp_settings
      if GlobalSetting.smtp_password_priority.present?
        UserNotifications.smtp_settings = Rails.application.config.action_mailer.smtp_settings
      end
    end
  end
end

Update: The live testing of the plugin is going well. The email channels are behaving as expected with the additional callback:

before_action :default_smtp_settings, except: [:email_login, :signup, :forgot_password, :admin_login]

Will live test for a few more days and if all continues to go well; will bump the version to 0.1.

1 Like

Update:

In this version, I have added another SMTP channel just for digest email:

# name: discourse-priority-action-mailer
# about: plugin to add priority smtp_settings to action mailer
# version: 0.0.37
# date: 28 Nov 2020
# authors: Neo
# url: https://community.unix.com/t/creating-higher-priority-smtp-settings-in-discourse-software-mailers-a-future-plugin-idea/380865

plugin_root = "#{Rails.root}/plugins/discourse-priority-action-mailer".freeze
configure_smtp_settings = "#{plugin_root}/lib/configure_smtp_settings.rb".freeze
load File.open(configure_smtp_settings)

after_initialize do
  AdminConfirmationMailer.class_eval do
    before_action :set_priority_smtp_settings

    def set_priority_smtp_settings
      if GlobalSetting.smtp_password_priority.present?
        AdminConfirmationMailer.smtp_settings = Rails.application.config.priority_smtp_settings
      end
    end
  end

  UserNotifications.class_eval do
    before_action :set_default_smtp_settings, except: [:email_login, :signup, :forgot_password, :admin_login, :digest]
    before_action :set_priority_smtp_settings, only: [:email_login, :signup, :forgot_password, :admin_login]
    before_action :set_digest_smtp_settings, only: [:digest]

    def set_priority_smtp_settings
      if GlobalSetting.smtp_password_priority.present?
        UserNotifications.smtp_settings = Rails.application.config.priority_smtp_settings
      end
    end

    def set_digest_smtp_settings
      if GlobalSetting.smtp_password_digest.present?
        UserNotifications.smtp_settings = Rails.application.config.digest_smtp_settings
      end
    end

    def set_default_smtp_settings
      if GlobalSetting.smtp_password_priority.present?
        UserNotifications.smtp_settings = Rails.application.config.action_mailer.smtp_settings
      end
    end
  end
end

See also:

Update: This plugin is working flawlessly :slight_smile: , working with two sendgrid API keys and one gsuite account.

Some sendgrid data after setting up this new plugin with a sendgrid account:

1 Like

This plugin has been "released", so I will close this "concept" / "idea" thread:

Continue Here: