mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-04 12:31:08 +08:00
Using `return` inside a block attempts to return from the enclosing method rather than from the block itself. In Sidekiq's `job_retry` context this raises `LocalJumpError: unexpected return`, which shows up in logs as: ``` Job exception: unexpected return ``` The correct Ruby idiom for exiting a block with a value is `next <value>`. Four jobs had the same pattern: - `app/jobs/regular/user_email.rb` - `app/jobs/regular/group_smtp_email.rb` - `app/jobs/regular/notify_mailing_list_subscribers.rb` - `plugins/automation/app/jobs/regular/discourse_automation/trigger.rb`
116 lines
3.8 KiB
Ruby
116 lines
3.8 KiB
Ruby
# frozen_string_literal: true
|
||
|
||
module Jobs
|
||
class GroupSmtpEmail < ::Jobs::Base
|
||
include Skippable
|
||
|
||
sidekiq_options queue: "critical"
|
||
|
||
sidekiq_retry_in do |count, exception|
|
||
# retry in an hour when SMTP server is busy
|
||
# or use default sidekiq retry formula. returning
|
||
# nil/0 will trigger the default sidekiq
|
||
# retry formula
|
||
#
|
||
# See https://github.com/mperham/sidekiq/blob/3330df0ee37cfd3e0cd3ef01e3e66b584b99d488/lib/sidekiq/job_retry.rb#L216-L234
|
||
case exception.wrapped
|
||
when Net::SMTPServerBusy
|
||
next 1.hour + (rand(30) * (count + 1))
|
||
end
|
||
end
|
||
|
||
def execute(args)
|
||
return if quit_email_early?
|
||
email = args[:email]
|
||
recipient_user = User.find_by_email(email, primary: true)
|
||
|
||
post = Post.find_by(id: args[:post_id])
|
||
return skip(email, nil, recipient_user, :group_smtp_post_deleted) if post.blank?
|
||
|
||
group = Group.find_by(id: args[:group_id])
|
||
return if group.blank?
|
||
|
||
if !group.smtp_enabled
|
||
return skip(email, post, recipient_user, :group_smtp_disabled_for_group)
|
||
end
|
||
|
||
if !Topic.exists?(id: post.topic_id)
|
||
return skip(email, post, recipient_user, :group_smtp_topic_deleted)
|
||
end
|
||
|
||
cc_addresses =
|
||
args[:cc_emails].filter { |address| EmailAddressValidator.valid_value?(address) }
|
||
|
||
# Mask the email addresses of non-staged users so
|
||
# they are not revealed unnecessarily when we are sending
|
||
# the email notification out.
|
||
bcc_addresses = User.not_staged.with_email(cc_addresses).pluck(:email)
|
||
cc_addresses = cc_addresses - bcc_addresses
|
||
|
||
# The EmailLog record created by the sender will have the raw email
|
||
# stored, the group smtp ID, and any cc addresses recorded for later
|
||
# cross referencing.
|
||
message =
|
||
GroupSmtpMailer.send_mail(
|
||
group,
|
||
email,
|
||
post,
|
||
cc_addresses: cc_addresses,
|
||
bcc_addresses: bcc_addresses,
|
||
)
|
||
|
||
# Idempotency check – if the EmailLog already exists, do not send again.
|
||
return if EmailLog.exists?(message_id: message.message_id)
|
||
|
||
begin
|
||
Email::Sender.new(message, :group_smtp, recipient_user).send
|
||
rescue Net::ReadTimeout => err
|
||
# We can't be sure if the send actually failed or if ENTER . ENTER (to end
|
||
# the SMTP data sequence) just timed out, as is the case with Gmail occasionally,
|
||
# where they can do this if they suspect you are sending spam.
|
||
Discourse.warn_exception(
|
||
err,
|
||
message: "Got SMTP read timeout when sending group SMTP email",
|
||
env: args,
|
||
)
|
||
raise err # Re-raise the error so Sidekiq's retry mechanism kicks in.
|
||
end
|
||
|
||
# Create an incoming email record for tracking purposes.
|
||
begin
|
||
IncomingEmail.create!(
|
||
user_id: post.user_id,
|
||
topic_id: post.topic_id,
|
||
post_id: post.id,
|
||
raw: message.to_s,
|
||
subject: message.subject,
|
||
message_id: message.message_id,
|
||
to_addresses: message.to,
|
||
cc_addresses: message.cc,
|
||
from_address: message.from,
|
||
created_via: IncomingEmail.created_via_types[:group_smtp],
|
||
)
|
||
rescue => err
|
||
Discourse.warn_exception(
|
||
err,
|
||
message: "Failed to create IncomingEmail record when sending group SMTP email",
|
||
env: args,
|
||
)
|
||
end
|
||
end
|
||
|
||
def quit_email_early?
|
||
SiteSetting.disable_emails == "yes" || !SiteSetting.enable_smtp
|
||
end
|
||
|
||
def skip(email, post, recipient_user, reason)
|
||
create_skipped_email_log(
|
||
email_type: :group_smtp,
|
||
to_address: email,
|
||
user_id: recipient_user&.id,
|
||
post_id: post&.id,
|
||
reason_type: SkippedEmailLog.reason_types[reason],
|
||
)
|
||
end
|
||
end
|
||
end
|