mirror of
https://github.com/mastodon/mastodon.git
synced 2024-08-20 21:08:15 -07:00
Add digest e-mails
This commit is contained in:
parent
f5457cc3d2
commit
6b81d10030
25 changed files with 117 additions and 32 deletions
|
@ -79,6 +79,7 @@ class ApiController < ApplicationController
|
||||||
|
|
||||||
def require_user!
|
def require_user!
|
||||||
current_resource_owner
|
current_resource_owner
|
||||||
|
set_user_activity
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
render json: { error: 'This method requires an authenticated user' }, status: 422
|
render json: { error: 'This method requires an authenticated user' }, status: 422
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,6 +14,7 @@ class Settings::PreferencesController < ApplicationController
|
||||||
reblog: user_params[:notification_emails][:reblog] == '1',
|
reblog: user_params[:notification_emails][:reblog] == '1',
|
||||||
favourite: user_params[:notification_emails][:favourite] == '1',
|
favourite: user_params[:notification_emails][:favourite] == '1',
|
||||||
mention: user_params[:notification_emails][:mention] == '1',
|
mention: user_params[:notification_emails][:mention] == '1',
|
||||||
|
digest: user_params[:notification_emails][:digest] == '1',
|
||||||
}
|
}
|
||||||
|
|
||||||
current_user.settings['interactions'] = {
|
current_user.settings['interactions'] = {
|
||||||
|
@ -33,6 +34,6 @@ class Settings::PreferencesController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def user_params
|
def user_params
|
||||||
params.require(:user).permit(:locale, :setting_default_privacy, notification_emails: [:follow, :follow_request, :reblog, :favourite, :mention], interactions: [:must_be_follower, :must_be_following])
|
params.require(:user).permit(:locale, :setting_default_privacy, notification_emails: [:follow, :follow_request, :reblog, :favourite, :mention, :digest], interactions: [:must_be_follower, :must_be_following])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,6 +29,11 @@ class Formatter
|
||||||
sanitize(html, tags: %w(a br p span), attributes: %w(href rel class))
|
sanitize(html, tags: %w(a br p span), attributes: %w(href rel class))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def plaintext(status)
|
||||||
|
return status.text if status.local?
|
||||||
|
strip_tags(status.text)
|
||||||
|
end
|
||||||
|
|
||||||
def simplified_format(account)
|
def simplified_format(account)
|
||||||
return reformat(account.note) unless account.local?
|
return reformat(account.note) unless account.local?
|
||||||
|
|
||||||
|
|
|
@ -49,4 +49,17 @@ class NotificationMailer < ApplicationMailer
|
||||||
mail to: @me.user.email, subject: I18n.t('notification_mailer.follow_request.subject', name: @account.acct)
|
mail to: @me.user.email, subject: I18n.t('notification_mailer.follow_request.subject', name: @account.acct)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def digest(recipient, opts = {})
|
||||||
|
@me = recipient
|
||||||
|
@since = opts[:since] || @me.user.last_emailed_at || @me.user.current_sign_in_at
|
||||||
|
@notifications = Notification.where(account: @me, activity_type: 'Mention').where('created_at > ?', @since)
|
||||||
|
@follows_since = Notification.where(account: @me, activity_type: 'Follow').where('created_at > ?', @since).count
|
||||||
|
|
||||||
|
return if @notifications.empty?
|
||||||
|
|
||||||
|
I18n.with_locale(@me.user.locale || I18n.default_locale) do
|
||||||
|
mail to: @me.user.email, subject: I18n.t('notification_mailer.digest.subject', count: @notifications.size)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
class Setting < RailsSettings::Base
|
class Setting < RailsSettings::Base
|
||||||
source Rails.root.join('config/settings.yml')
|
source Rails.root.join('config/settings.yml')
|
||||||
namespace Rails.env
|
|
||||||
|
|
||||||
def to_param
|
def to_param
|
||||||
var
|
var
|
||||||
|
|
|
@ -14,9 +14,10 @@ class User < ApplicationRecord
|
||||||
validates :locale, inclusion: I18n.available_locales.map(&:to_s), unless: 'locale.nil?'
|
validates :locale, inclusion: I18n.available_locales.map(&:to_s), unless: 'locale.nil?'
|
||||||
validates :email, email: true
|
validates :email, email: true
|
||||||
|
|
||||||
scope :prolific, -> { joins('inner join statuses on statuses.account_id = users.account_id').select('users.*, count(statuses.id) as statuses_count').group('users.id').order('statuses_count desc') }
|
scope :prolific, -> { joins('inner join statuses on statuses.account_id = users.account_id').select('users.*, count(statuses.id) as statuses_count').group('users.id').order('statuses_count desc') }
|
||||||
scope :recent, -> { order('id desc') }
|
scope :recent, -> { order('id desc') }
|
||||||
scope :admins, -> { where(admin: true) }
|
scope :admins, -> { where(admin: true) }
|
||||||
|
scope :confirmed, -> { where.not(confirmed_at: nil) }
|
||||||
|
|
||||||
def send_devise_notification(notification, *args)
|
def send_devise_notification(notification, *args)
|
||||||
devise_mailer.send(notification, self, *args).deliver_later
|
devise_mailer.send(notification, self, *args).deliver_later
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<%= yield %>
|
<%= yield %>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<%= t('application_mailer.signature', instance: Rails.configuration.x.local_domain) %>
|
<%= t('application_mailer.signature', instance: Rails.configuration.x.local_domain) %>
|
||||||
|
<%= t('application_mailer.settings', link: settings_preferences_url) %>
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
<%= strip_tags(@status.content) %>
|
<%= raw Formatter.instance.plaintext(status) %>
|
||||||
|
|
||||||
<%= web_url("statuses/#{@status.id}") %>
|
<%= raw t('application_mailer.view')%> <%= web_url("statuses/#{status.id}") %>
|
||||||
|
|
15
app/views/notification_mailer/digest.text.erb
Normal file
15
app/views/notification_mailer/digest.text.erb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<%= display_name(@me) %>,
|
||||||
|
|
||||||
|
<%= raw t('notification_mailer.digest.body', since: @since, instance: root_url) %>
|
||||||
|
<% @notifications.each do |notification| %>
|
||||||
|
|
||||||
|
* <%= raw t('notification_mailer.digest.mention', name: notification.from_account.acct) %>
|
||||||
|
|
||||||
|
<%= raw Formatter.instance.plaintext(notification.target_status) %>
|
||||||
|
|
||||||
|
<%= raw t('application_mailer.view')%> <%= web_url("statuses/#{notification.target_status.id}") %>
|
||||||
|
<% end %>
|
||||||
|
<% if @follows_since > 0 %>
|
||||||
|
|
||||||
|
<%= raw t('notification_mailer.digest.new_followers_summary', count: @follows_since) %>
|
||||||
|
<% end %>
|
|
@ -1,5 +1,5 @@
|
||||||
<%= display_name(@me) %>,
|
<%= display_name(@me) %>,
|
||||||
|
|
||||||
<%= t('notification_mailer.favourite.body', name: @account.acct) %>
|
<%= raw t('notification_mailer.favourite.body', name: @account.acct) %>
|
||||||
|
|
||||||
<%= render partial: 'status' %>
|
<%= render partial: 'status', locals: { status: @status } %>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<%= display_name(@me) %>,
|
<%= display_name(@me) %>,
|
||||||
|
|
||||||
<%= t('notification_mailer.follow.body', name: @account.acct) %>
|
<%= raw t('notification_mailer.follow.body', name: @account.acct) %>
|
||||||
|
|
||||||
<%= web_url("accounts/#{@account.id}") %>
|
<%= raw t('application_mailer.view')%> <%= web_url("accounts/#{@account.id}") %>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<%= display_name(@me) %>,
|
<%= display_name(@me) %>,
|
||||||
|
|
||||||
<%= t('notification_mailer.follow_request.body', name: @account.acct) %>
|
<%= raw t('notification_mailer.follow_request.body', name: @account.acct) %>
|
||||||
|
|
||||||
<%= web_url("follow_requests") %>
|
<%= raw t('application_mailer.view')%> <%= web_url("follow_requests") %>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<%= display_name(@me) %>,
|
<%= display_name(@me) %>,
|
||||||
|
|
||||||
<%= t('notification_mailer.mention.body', name: @status.account.acct) %>
|
<%= raw t('notification_mailer.mention.body', name: @status.account.acct) %>
|
||||||
|
|
||||||
<%= render partial: 'status' %>
|
<%= render partial: 'status', locals: { status: @status } %>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<%= display_name(@me) %>,
|
<%= display_name(@me) %>,
|
||||||
|
|
||||||
<%= t('notification_mailer.reblog.body', name: @account.acct) %>
|
<%= raw t('notification_mailer.reblog.body', name: @account.acct) %>
|
||||||
|
|
||||||
<%= render partial: 'status' %>
|
<%= render partial: 'status', locals: { status: @status } %>
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
= ff.input :reblog, as: :boolean, wrapper: :with_label
|
= ff.input :reblog, as: :boolean, wrapper: :with_label
|
||||||
= ff.input :favourite, as: :boolean, wrapper: :with_label
|
= ff.input :favourite, as: :boolean, wrapper: :with_label
|
||||||
= ff.input :mention, as: :boolean, wrapper: :with_label
|
= ff.input :mention, as: :boolean, wrapper: :with_label
|
||||||
|
= ff.input :digest, as: :boolean, wrapper: :with_label
|
||||||
|
|
||||||
= f.simple_fields_for :interactions, hash_to_object(current_user.settings.interactions) do |ff|
|
= f.simple_fields_for :interactions, hash_to_object(current_user.settings.interactions) do |ff|
|
||||||
= ff.input :must_be_follower, as: :boolean, wrapper: :with_label
|
= ff.input :must_be_follower, as: :boolean, wrapper: :with_label
|
||||||
|
|
14
app/workers/digest_mailer_worker.rb
Normal file
14
app/workers/digest_mailer_worker.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class DigestMailerWorker
|
||||||
|
include Sidekiq::Worker
|
||||||
|
|
||||||
|
sidekiq_options queue: 'mailers'
|
||||||
|
|
||||||
|
def perform(user_id)
|
||||||
|
user = User.find(user_id)
|
||||||
|
return unless user.settings.notification_emails['digest']
|
||||||
|
NotificationMailer.digest(user.account).deliver_now!
|
||||||
|
user.touch(:last_emailed_at)
|
||||||
|
end
|
||||||
|
end
|
|
@ -49,12 +49,5 @@ module Mastodon
|
||||||
Doorkeeper::AuthorizedApplicationsController.layout 'admin'
|
Doorkeeper::AuthorizedApplicationsController.layout 'admin'
|
||||||
Doorkeeper::Application.send :include, ApplicationExtension
|
Doorkeeper::Application.send :include, ApplicationExtension
|
||||||
end
|
end
|
||||||
|
|
||||||
config.action_dispatch.default_headers = {
|
|
||||||
'Server' => 'Mastodon',
|
|
||||||
'X-Frame-Options' => 'DENY',
|
|
||||||
'X-Content-Type-Options' => 'nosniff',
|
|
||||||
'X-XSS-Protection' => '1; mode=block',
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -109,4 +109,11 @@ Rails.application.configure do
|
||||||
config.to_prepare do
|
config.to_prepare do
|
||||||
StatsD.backend = StatsD::Instrument::Backends::NullBackend.new if ENV['STATSD_ADDR'].blank?
|
StatsD.backend = StatsD::Instrument::Backends::NullBackend.new if ENV['STATSD_ADDR'].blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
config.action_dispatch.default_headers = {
|
||||||
|
'Server' => 'Mastodon',
|
||||||
|
'X-Frame-Options' => 'DENY',
|
||||||
|
'X-Content-Type-Options' => 'nosniff',
|
||||||
|
'X-XSS-Protection' => '1; mode=block',
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,6 +29,8 @@ en:
|
||||||
unfollow: Unfollow
|
unfollow: Unfollow
|
||||||
application_mailer:
|
application_mailer:
|
||||||
signature: Mastodon notifications from %{instance}
|
signature: Mastodon notifications from %{instance}
|
||||||
|
settings: 'Change e-mail preferences: %{link}'
|
||||||
|
view: 'View:'
|
||||||
applications:
|
applications:
|
||||||
invalid_url: The provided URL is invalid
|
invalid_url: The provided URL is invalid
|
||||||
auth:
|
auth:
|
||||||
|
@ -83,6 +85,15 @@ en:
|
||||||
reblog:
|
reblog:
|
||||||
body: 'Your status was boosted by %{name}:'
|
body: 'Your status was boosted by %{name}:'
|
||||||
subject: "%{name} boosted your status"
|
subject: "%{name} boosted your status"
|
||||||
|
digest:
|
||||||
|
subject:
|
||||||
|
one: "1 new notification since your last visit 🐘"
|
||||||
|
other: "%{count} new notifications since your last visit 🐘"
|
||||||
|
body: 'Here is a brief summary of what you missed on %{instance} since your last visit on %{since}:'
|
||||||
|
mention: "%{name} mentioned you in:"
|
||||||
|
new_followers_summary:
|
||||||
|
one: You have acquired one new follower! Yay!
|
||||||
|
other: You have gotten %{count} new followers! Amazing!
|
||||||
pagination:
|
pagination:
|
||||||
next: Next
|
next: Next
|
||||||
prev: Prev
|
prev: Prev
|
||||||
|
|
|
@ -34,6 +34,7 @@ en:
|
||||||
follow_request: Send e-mail when someone requests to follow you
|
follow_request: Send e-mail when someone requests to follow you
|
||||||
mention: Send e-mail when someone mentions you
|
mention: Send e-mail when someone mentions you
|
||||||
reblog: Send e-mail when someone reblogs your status
|
reblog: Send e-mail when someone reblogs your status
|
||||||
|
digest: Send digest e-mails
|
||||||
'no': 'No'
|
'no': 'No'
|
||||||
required:
|
required:
|
||||||
mark: "*"
|
mark: "*"
|
||||||
|
|
|
@ -11,6 +11,7 @@ defaults: &defaults
|
||||||
favourite: false
|
favourite: false
|
||||||
mention: false
|
mention: false
|
||||||
follow_request: true
|
follow_request: true
|
||||||
|
digest: true
|
||||||
interactions:
|
interactions:
|
||||||
must_be_follower: false
|
must_be_follower: false
|
||||||
must_be_following: false
|
must_be_following: false
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddLastEmailedAtToUsers < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
add_column :users, :last_emailed_at, :datetime, null: true, default: nil
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20170301222600) do
|
ActiveRecord::Schema.define(version: 20170303212857) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -283,6 +283,7 @@ ActiveRecord::Schema.define(version: 20170301222600) do
|
||||||
t.string "encrypted_otp_secret_salt"
|
t.string "encrypted_otp_secret_salt"
|
||||||
t.integer "consumed_timestep"
|
t.integer "consumed_timestep"
|
||||||
t.boolean "otp_required_for_login"
|
t.boolean "otp_required_for_login"
|
||||||
|
t.datetime "last_emailed_at"
|
||||||
t.index ["account_id"], name: "index_users_on_account_id", using: :btree
|
t.index ["account_id"], name: "index_users_on_account_id", using: :btree
|
||||||
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
|
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
|
||||||
t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
|
t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
|
||||||
|
|
|
@ -43,7 +43,7 @@ namespace :mastodon do
|
||||||
namespace :feeds do
|
namespace :feeds do
|
||||||
desc 'Clear timelines of inactive users'
|
desc 'Clear timelines of inactive users'
|
||||||
task clear: :environment do
|
task clear: :environment do
|
||||||
User.where('current_sign_in_at < ?', 14.days.ago).find_each do |user|
|
User.confirmed.where('current_sign_in_at < ?', 14.days.ago).find_each do |user|
|
||||||
Redis.current.del(FeedManager.instance.key(:home, user.account_id))
|
Redis.current.del(FeedManager.instance.key(:home, user.account_id))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -53,4 +53,13 @@ namespace :mastodon do
|
||||||
Redis.current.keys('feed:*').each { |key| Redis.current.del(key) }
|
Redis.current.keys('feed:*').each { |key| Redis.current.del(key) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
namespace :emails do
|
||||||
|
desc 'Send out digest e-mails'
|
||||||
|
task digest: :environment do
|
||||||
|
User.confirmed.joins(:account).where(accounts: { silenced: false, suspended: false }).where('current_sign_in_at < ?', 20.days.ago).find_each do |user|
|
||||||
|
DigestMailerWorker.perform_async(user.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,24 +1,31 @@
|
||||||
# Preview all emails at http://localhost:3000/rails/mailers/notification_mailer
|
# Preview all emails at http://localhost:3000/rails/mailers/notification_mailer
|
||||||
class NotificationMailerPreview < ActionMailer::Preview
|
class NotificationMailerPreview < ActionMailer::Preview
|
||||||
|
|
||||||
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/mention
|
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/mention
|
||||||
def mention
|
def mention
|
||||||
# NotificationMailer.mention
|
m = Mention.last
|
||||||
|
NotificationMailer.mention(m.account, Notification.find_by(activity: m))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/follow
|
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/follow
|
||||||
def follow
|
def follow
|
||||||
# NotificationMailer.follow
|
f = Follow.last
|
||||||
|
NotificationMailer.follow(f.target_account, Notification.find_by(activity: f))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/favourite
|
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/favourite
|
||||||
def favourite
|
def favourite
|
||||||
# NotificationMailer.favourite
|
f = Favourite.last
|
||||||
|
NotificationMailer.favourite(f.status.account, Notification.find_by(activity: f))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/reblog
|
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/reblog
|
||||||
def reblog
|
def reblog
|
||||||
# NotificationMailer.reblog
|
r = Status.where.not(reblog_of_id: nil).first
|
||||||
|
NotificationMailer.reblog(r.reblog.account, Notification.find_by(activity: r))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/digest
|
||||||
|
def digest
|
||||||
|
NotificationMailer.digest(Account.first, since: 90.days.ago)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue