Tym razem chciałbym podzielić się moimi ostatnimi przygodami z budową listy dyskusyjnej. Jest z tym sporo zabawy, użerania się z kodowaniem maili i zabezpieczeniami ale efekt końcowy jest bardzo przyjemny :).
Do rzeczy: aby postawić własną listę dyskusyjną należy najpierw stworzyć aplikację RoR 3, a następnie w Gemfile dodać gemy: mailman oraz daemons (pierwszy odpowiada za pobieranie maili a drugi za odpalenie listy jako proces w tle. Dokumentacja jest w linkach).
Kolejnym krokiem jest stworzenie skryptu listy dyskusyjnej, polecam np.: lib/mailman_app.rb :
require 'mailman' # Warto dołączyć środowisko aplikacji aby później pozbyć się konieczności uruchamiania jako rails runner require File.expand_path(File.join(File.dirname(__FILE__), '../') + 'config/environment.rb') Mailman.config.logger = Logger.new("#{Rails.root}/log/mailman.log") # Teraz ważne: Mailman::Application zadziała tylko raz jeśli ustawimy poll_interval na 0. # W innym przypadku raz odpalony nie będzie mógł być modyfikowany. # Chodzi o to żeby za każdym razem startował od nowa i pobierał na nowo skład uczestników listy. Mailman.config.poll_interval = 0 # Przykładowa konfiguracja Mailman.config.pop3 = { :username => 'catch-all@example.pl', :password => '***********', :server => 'pop.example.pl', :port => 995, :ssl => true } end
Teraz część zasadnicza:
# Uruchamiamy w pętli nieskończonej while true # Startuje mailman Mailman::Application.run do # Za każdym wejściem w pętlę ładujemy na nowo członków grup (zakładam że grupy zmieniają co jakiś czas swój skład). Group.find(:all, :include => :members).each do |group| # Właściwa część skryptu, zakładam że pole email w modelu Group jest nazwą maila grupy to("#{group.email}@listy.example.pl") do # Tutaj obrabiamy każdego maila, sprawdzamy czy nadawca może pisać na listę itp. # W tym bloku następuje wysłanego listu do wszystkich członków grupy. end # Kasujemy te które nie pasują do grup które mamy. default do ; end end end # Możemy dać jakiegoś sleep'a sleep(10) if Rails.env == 'production' end
Warto pamiętać o tym że:
- nadawca może siedzieć w message.from lub w message.sender i czasem może być to tablica ;)
- message.multipart? powie nam jakiego rodzaju jest to wiadomość
- message.attachments.empty? dowiemy się czy są jakieś załączniki
- message.subject to oczywiście temat
- message.parts[0].body to treść wiadomości jeśli jest ona multiart
- message.parts[0].content_type_parameters może przechować hasha z kodowaniem (klucz to :charset)
Jeśli chcemy obsłużyć załączniki to warto je przechwycić w zasadniczej części skryptu. Można to zrobić np. tak:
unless message.attachments.empty? attachments = [] message.attachments.each do |attachment| attachments && [attachment.filename, attachment.decoded] end group.emails.each { |email| List.message("#{group.email}@listy.example.pl", email, message.subject, message.parts[0].body, sender, group.name, (message.parts[0].content_type_parameters.nil? ? nil : message.parts[0].content_type_parameters), attachments).deliver } end
W mailerze List należy ustawić tablicę attachments. Załóżmy że ostatni parametr w metodzie message nazywa się mail_attachments
unless mail_attachments.nil? mail_attachments.each { |m_a| attachments["#{m_a[0]}"] = m_a[1]} unless mail_attachments.empty? end mail(:from => from, :to => to, :subject => "[Nasza lista | #{group_name}] #{subject}".gsub(/\\[.*\\]/, "[Nasza lista | #{group_name}]"))
Jeśli są problemy z kodowaniem to treść maila warto przekonwetować. Poniższe to wielkie uproszczenie. Należy sprawdzić czy message.content_type_parameters nie jest nil’em. Zmienną body można użyć do zarchiwizowania treści maila w bazie lub do przesłania jej dalej.
body = Iconv.conv('UTF-8', message.content_type_parameters[:charset], message.parts[0].body.decoded)
Ostatnim krokiem jest demonizacja. Proponuję założyć katalog lib/pids i plik lib/mailman_control.rb a w nim: (a w tasku capistrano dodać linkowanie do katalogu tmp/pids)
# coding: UTF-8 #!/usr/bin/env ruby require 'rubygems' require 'daemons' daemon_options = { :app_name => 'mailman_example', :multiple => false, :dir_mode => :script, :dir => "pids", :backtrace => false, :monitor => true, :log_output => false } Daemons.run('mailman_app.rb', daemon_options)
Odpalenie listy jest już banalne, wystarczy wykonać komendę:
ruby mailman_control.rb start
I już wszystko śmiga :)
Dodaj komentarz!