mailinglist.rb/lib/distributor.rb

84 lines
2.8 KiB
Ruby

class Distributor
class Attributes < Hash
# Look at the subject if there are attributes parts.
# If not, look at the body too.
# TODO: this part can be improved with smarter attr detection
def self.parse(subject:, body:)
new = Attributes.new
subject_parts = subject.to_s.split(",")
if !subject_parts.empty?
subject_parts.each do
new.add_attribute!(_1)
end
elsif body.include?("=")
body.to_s.split("\r\n").each { new.add_attribute!(_1) }
end
new
end
def add_attribute!(key_eq_value)
k, v = key_eq_value.split("=", 2)
self[k.strip] = v.strip if k && v
end
end
def initialize
@smtp_client = Protocols::Smtp.new
@imap_client = Protocols::Imap.new
@imap_client.see_all_messages! if ENV["HARD_RESET_NOT_SEEN_MESSAGE"] == "true"
@handlers = {
:default => Actions::Distribute.new(distributor: self),
"subscribe" => Actions::Subscribe.new(distributor: self),
"unsubscribe" => Actions::Unsubscribe.new(distributor: self),
"help" => Actions::Help.new(distributor: self),
"set-permissions" => Actions::SetPermissions.new(distributor: self),
"list-users" => Actions::ListUsers.new(distributor: self),
"validate" => Actions::ValidateDistribute.new(distributor: self),
"refuse" => Actions::RefuseDistribute.new(distributor: self),
}
end
attr_reader :imap_client, :smtp_client, :handlers # if $debug # TODO: more alias for imap/smtp clients?
# Make sure any incoming email is properly directed to the right action.
def handle_one(mail)
$logger.info "incoming email from #{mail.from} | #{mail.subject}"
list = Mailinglist.search_mail(mail)
if list
subject, subject_attributes = mail.subject.split(",", 2)
attributes = Attributes.parse(subject: subject_attributes, body: mail.body)
handler = @handlers[subject] || @handlers[:default]
$logger.info "#{handler.class}#handle on <#{list.email}> for <#{mail.from}>"
handler.handle(list:, mail: mail, attributes:)
else
$logger.warn "list #{mail.to} do not exist (asked by #{mail.from})"
end
end
# alias for {Protocols::Smtp#distribute}
def distribute(*ary, **opt)
@smtp_client.distribute(*ary, **opt)
end
# Run the main loop that read all incoming emails.
def start(cpu_sleep: 1)
$logger.info "fetching new mail to distribute every #{cpu_sleep} second..."
loop do
begin
mail = @imap_client.fetch_next_unseen(inbox: Protocols::Imap::BASE_INBOX)
if mail
handle_one(mail)
mail.seen!(imap_client: @imap_client, inbox: Protocols::Imap::BASE_INBOX)
end
rescue StandardError => e
$logger.error(e)
end
sleep cpu_sleep
end
end
require_relative "distributor/action"
end