FLYING

/* TODO: 気の利いた説明を書く */

メール一括送信スクリプトの覚え書き

さくらインターネットSMTPサーバーからメールを一括送信するためのスクリプト。「ruby 1.9.2p136 (2010-12-25) [i386-mingw32]」でのみ動作を確認済み。M17N絡みの不具合を避けるためにforce_encodingしていたりするので,あんまり参考にすべきコードではないかも……

#!/usr/local/bin/ruby
# -*- coding: utf-8 -*-
require 'kconv'
require 'net/smtp'
require 'time'
require 'cgi'
require 'csv'

##################################################
## 送信設定
##################################################

MAIL_FROM = "hoge@fuga.jp"
SMTP_HOST = "fuga.sakura.ne.jp"
SMTP_ACCOUNT = "hoge@fuga.sakura.ne.jp"
SMTP_PASSWORD = "your_password"
MAIL_FIELD = "mail"

SUBJECT = "subject"
BODY = Proc.new {|hash|
<<END_OF_MESSAGE
#{hash['name']}

ここに本文を書く
#{hash['field_name']}
こうやってパラメータを展開できる

--
なんちゃら
END_OF_MESSAGE
}

##################################################
## ここから先は変更不要
##################################################

class Mail
  attr_accessor :to, :from, :subject, :body
  
  def encoded
    # 参考: http://code.nanigac.com/source/view/108
    packed = [@subject.tojis].pack('m').gsub("\n", '').gsub("\r", '')
    encoded_subject = "=?ISO-2022-JP?B?" + packed + "?="
    
    header = <<END_OF_HEADER
To: #{@to}
From: #{@from}
Mime-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Type: Text/Plain; charset=iso-2022-jp
Subject: #{encoded_subject}
Date: #{Time.now.rfc2822}
END_OF_HEADER
    
    # 参考: http://d.hatena.ne.jp/takehikom/20091122/1258835349
    (header + "\n" + @body).tojis.force_encoding("US-ASCII")
  end
end

class SendMail
  attr_accessor :host, :account, :password
  
  def send(mail) 
    smtp = Net::SMTP.new(@host, 587)
    smtp.start('localhost.localdomain', @account, @password, :login) {|smtp|
      smtp.sendmail(mail.encoded, mail.from, mail.to)
    }
  end
end

class CSVMailer
  def initialize
    @sender          = SendMail.new
    @sender.host     = SMTP_HOST
    @sender.account  = SMTP_ACCOUNT
    @sender.password = SMTP_PASSWORD
    @mail_from       = MAIL_FROM
    @mail_field      = MAIL_FIELD
    @database        = []
  end
  
  def load(filename)
    count_row = 0
    headers = []
    CSV.parse(File.read(filename, :mode => 'r')) do |row|
      count_col = 0
      hash = Hash.new
      row.each {|elem|
        if count_row == 0 then
          headers << elem
        else
          hash[ headers[count_col] ] = elem
        end
        count_col += 1
      }
      if count_row != 0 then
        @database << hash
      end
      count_row += 1
    end
  end
  
  def sendmail(subject, &block)
    @database.each {|hash|
      puts("sending: #{hash[@mail_field]}")
      mail         = Mail.new
      mail.subject = subject
      mail.from    = @mail_from
      mail.to      = hash[@mail_field]
      mail.body    = block.call(hash)
      @sender.send(mail)
    }
  end
end

instance = CSVMailer.new
instance.load(ARGV.shift)
instance.sendmail(SUBJECT, &BODY)

読み込ませるCSVとしては次のようなものを用意する。

name,mail,field_name
おなまえ,foo@bar.jp,パラメータ
おなまえ,foo@baz.jp,パラメータ