読者です 読者をやめる 読者になる 読者になる

FLYING

〈全日本・紀文豆乳飲料シリーズ「麦芽コーヒー」の500ミリリットルパックを扱う小売店が少ないことに遺憾の意を表明する会〉活動記録

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

さくらインターネット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,パラメータ