share facebook facebook2 twitter menu hatena pocket slack

2014.08.28 THU

ruby-gmailにて、ある1日のメールを絞り込んで取得する

廣山 豊

WRITTEN BY廣山 豊

cloudpack廣山豊です。

ruby-gmailにて、ある1日のメールを絞り込んで取得するには、
:ONに当該日付を設定する。

:BEFOREや:AFTERに設定してはいけない。

:BEFOREと:AFTERに同一の日付を設定すると、絞り込みが行われなくなる。
厄介な仕様だ(´・д・`)

使い勝手が悪いので、コードを変更。
以下のような処理を挿入。

lib/gmail/mailbox.rb

if opts[:after] === opts[:before]
  opts[:on]     = opts[:before]
  opts[:after]  = nil
  opts[:before] = nil
end

ダメモトで本家にも、つたない英語で’Pull Request’投げてみたら、通った!!
やってみるもんだ。

dcparker/ruby-gmailへのプルリクエストが受領されました

次回のバージョンからは反映させるだろう。

なお、コードの全貌は以下。

lib/gmail/mailbox.rb

require 'date'
require 'time'
class Object
  def to_imap_date
    Date.parse(to_s).strftime("%d-%B-%Y")
  end
end

class Gmail
  class Mailbox
    attr_reader :name

    def initialize(gmail, name)
      @gmail = gmail
      @name = name
    end

    def inspect
      "<#Mailbox name=#{@name}>"
    end

    def to_s
      name
    end

    # Method: emails
    # Args: [ :all | :unread | :read ]
    # Opts: {:since => Date.new}
    def emails(key_or_opts = :all, opts={})
      if key_or_opts.is_a?(Hash) && opts.empty?
        search = ['ALL']
        opts = key_or_opts
      elsif key_or_opts.is_a?(Symbol) && opts.is_a?(Hash)
        aliases = {
          :all => ['ALL'],
          :unread => ['UNSEEN'],
          :read => ['SEEN']
        }
        search = aliases[key_or_opts]
      elsif key_or_opts.is_a?(Array) && opts.empty?
        search = key_or_opts
      else
        raise ArgumentError, "Couldn't make sense of arguments to #emails - should be an optional hash of options preceded by an optional read-status bit; OR simply an array of parameters to pass directly to the IMAP uid_search call."
      end
      if !opts.empty?

        if opts[:after] === opts[:before]
          opts[:on]     = opts[:before]
          opts[:after]  = nil
          opts[:before] = nil
        end

        # Support for several search macros
        # :before => Date, :on => Date, :since => Date, :from => String, :to => String
        search.concat ['SINCE', opts[:after].to_imap_date] if opts[:after]
        search.concat ['BEFORE', opts[:before].to_imap_date] if opts[:before]
        search.concat ['ON', opts[:on].to_imap_date] if opts[:on]
        search.concat ['FROM', opts[:from]] if opts[:from]
        search.concat ['TO', opts[:to]] if opts[:to]
        search.concat ['SUBJECT', opts[:subject]] if opts[:subject]

        # Gmail offers us to search the same way we do on the web interface
        # https://developers.google.com/gmail/imap_extensions
        # example: gmail.emails(gm: 'has:attachment in:unread "who is john galt"')
        #
        search.concat ['X-GM-RAW', opts[:gm]] if opts[:gm]
      end

      # puts "Gathering #{(aliases[key] || key).inspect} messages for mailbox '#{name}'..."
      @gmail.in_mailbox(self) do
        @gmail.imap.uid_search(search).collect { |uid| messages[uid] ||= Message.new(@gmail, self, uid) }
      end
    end

    # This is a convenience method that really probably shouldn't need to exist, but it does make code more readable
    # if seriously all you want is the count of messages.
    def count(*args)
      emails(*args).length
    end

    def messages
      @messages ||= {}
    end
  end
end

元記事はこちらです。
ruby-gmailにて、ある1日のメールを絞り込んで取得する