转发带附件的电子邮件

时间:2013-10-01 13:13:05

标签: ruby mail-gem

我想在不下载附件然后重新附加到新电子邮件的情况下执行此操作 这就是我的尝试:

$emailslist.each do |e|
  Mail.deliver do
    from   fromstr
    to "mailman@somedomain.com"
    subject "[Events] #{subjectstr}"

    if e.attachments.length>0
      e.attachments.each do |a| 
       add_file a
     end
   end
 end
end

#error in 'e.attachments.each'=>undefined method `attachments' for
#<TypeError: can't convert nil into String> 

修改 我已经使用这个代码好几个月了,它运行良好。

我现在介绍的新内容是上面的代码。

无论如何,我会根据要求粘贴整个代码。

require 'mail'

$subscribers=[]

File.new("C:/Users/j.de_miguel/Desktop/mailman.forma/subscribers2.txt",'r').each do |line| 
  line=line.sub("\n","")
  $subscribers.push(line) if line =~ /@/
end

puts $subscribers

$errorfile=File.new("C:/Users/j.de_miguel/Desktop/mailman.forma/error_log2.txt",'a+')
$errorfile.write("#{Time.now}\n")
$errorfile.flush

def deleteSubjectRecursion(subjstr)

  if subjstr =~ /(.\[FORMA 2013\])+/
    subjstr.gsub!(/.\[FORMA 2013\]/,"")
  end

  if subjstr =~ /((?i)Re: ){2,}/
    subjstr.gsub!(/((?i)Re: ){2,}/,"Re: ")
  end

  return subjstr
end

def UserIsRegistered(mailaddr)

  registered = false
  $subscribers.each{|s| registered = true if mailaddr==s}
  if registered == false
    $errorfile.write("#{Time.now} : user #{mailaddr} attempted to mailman\n")
    $errorfile.flush
  end

  return registered

end


Mail.defaults do
  retriever_method :imap, { :address    => "imap.1and1.es",
                            :port       => 143, 
                            :user_name  => "mailman@somedomain.com",
                            :password   => "xxxxxxxx",
                            :enable_ssl => false }

  delivery_method :smtp, { :address              => "smtp.1and1.es",
                           :port                 => 587,
                           :domain               => '1and1.es',
                           :user_name            => 'mailman@somaedomain.com',
                           :password             => 'xxxxxxxxxxxx',
                           :authentication       => 'plain',
                           :enable_starttls_auto => true  }
end

#$emailslist=Mail.find(keys: ['NOT','SEEN'])
$emailslist=[Mail.last]

$emailslist.each do |e|

  eplain_part = e.text_part ? e.text_part.body.decoded : nil
  ehtml_part = e.html_part ? e.html_part.body.decoded : nil

  type=e.charset
  type_plain=eplain_part ? e.text_part.charset.to_s : nil
  type_html=ehtml_part ? e.html_part.charset.to_s : nil

  bodystr= type ? e.body.decoded.to_s.force_encoding(type) : nil

  type=type ? type.to_s : type_plain
  puts type.inspect

  subjectstr=e.subject.to_s.encode(type)
  fromstr=e.from.first.to_s.encode(type)
  puts fromstr

  bodystr_plain=eplain_part ? eplain_part.force_encoding(type_plain) : nil
  bodystr_html=ehtml_part ? ehtml_part.force_encoding(type_html) : nil

  $subscribers.each do |tostr|

    puts tostr.inspect

    if (not subjectstr =~ /^\[FORMA 2013\]/  ) && (UserIsRegistered(fromstr) == true)
      subjectstr=deleteSubjectRecursion(subjectstr)

      begin
        Mail.deliver do

          from   fromstr
          to      "mailman@somedomain.com"
          bcc  tostr
          subject "[FORMA 2013] #{subjectstr}"

          if ehtml_part != nil
            html_part do
              content_type("text/html; charset=#  {type_html}")
              #content_transfer_encoding("7bit")
              body "# {bodystr_html}\nmailman@forma.culturadigital.cc para darte de baja escribe \"baja\" a info@culturadigital.cc"
            end
          end

          if eplain_part != nil
            text_part do
              content_type("text/plain; charset=# {type_plain}")
              #content_transfer_encoding("7bit")
              body "#{bodystr_plain}\nmailman@forma.culturadigital.cc para darte de baja escribe \"baja\" a info@culturadigital.cc"
            end
          end

          if eplain_part == nil && ehtml_part == nil
            body "#{bodystr}\nmailman@forma.culturadigital.cc para darte de baja escribe \"baja\" a info@culturadigital.cc" 
            charset=type
          end
          #puts e.attachments.inspect
          if e.attachments.length>0
            e.attachments.each do |a| 
              add_file a.encoded
            end
          end



        end
        puts "1 email sent"
      rescue => e
        puts "error: #{e}"
        $errorfile.write("#{Time.now}\nerror sending to #{tostr}: #{e},\nemail subject: #{subjectstr}\n\n")
        $errorfile.flush()
      end
    end
  end
end

$errorfile.close()

2 个答案:

答案 0 :(得分:2)

这是未经测试的,并不是真正尝试查找或修复错误。这是为了展示你的代码应该的样子,用更惯用的Ruby代码编写。因此,它可能会解决您所看到的问题。如果没有,至少你会更好地了解如何编写代码:

require 'mail'
  • 为可重用的文字字符串定义一些常量。在顶部执行此操作,这样您就不必搜索代码来更改多个位置的内容,从而可能会错过其中一个。

    PATH_TO_FILES = "C:/Users/j.de_miguel/Desktop/mailman.forma"
    BODY_BOILERPLATE_FORMAT = "%s\nmailman@forma.culturadigital.cc para darte de baja escribe \"baja\" a info@culturadigital.cc"
    
  • 在常量之后将方法分组到文件顶部。

  • 我们使用'a'而不是'a+'打开。我们不需要读/写,我们只需要写。
  • 根据需要打开和关闭文件。
  • 自动关闭文件会执行刷新。
  • 如果您经常调用日志方法,那么有更好的方法可以做到这一点,但这不是一个重量级的脚本。
  • 我正在使用File.join根据路径构建文件名。 File.join知道路径分隔符并自动执行正确的操作。
  • String.%可以轻松创建标准输出格式。

    def log(text)
    
      File.open(File.join(PATH_TO_FILES, "error_log2.txt"), 'a') do |log_file|
        log_file.puts "%s : %s" % [Time.now, text]
      end
    
    end
    
  • Ruby中的方法名称是snake_case,而不是CamelCase。

  • 没有理由拥有多个gsub!,也不需要进行条件测试。如果要清除的子字符串存在于字符串gsub中,则会执行此操作,否则它将继续运行。链接gsub方法会将代码减少到一行。
  • gsub可能/应该是sub,除非您知道可以在字符串中替换多个匹配。
  • return是多余的,所以除非我们明确地返回一个值以过早地保留一个块,否则我们不会使用它。

    def delete_subject_recursion(subjstr)
    
      subjstr.gsub(/.\[FORMA 2013\]/,"").gsub(/((?i)Re: ){2,}/, "Re: ")
    
    end
    
  • 由于registered应该是布尔值,因此请使用any?进行测试。如果找到任何匹配项any?,则退出并返回true

    def user_is_registered(mailaddr)
    
      registered = subscribers.any?{ |s| mailaddr == s }
      log("user #{ mailaddr } attempted to mailman") unless registered
    
      registered
    
    end
    
  • 使用foreach迭代文件行。

    subscribers = []
    File.foreach(File.join(PATH_TO_FILES, "subscribers2.txt")) do |line| 
      subscribers << line.chomp if line['@']
    end
    
    puts subscribers
    
    log('')
    
    Mail.defaults do
    
      retriever_method(
        :imap,
        {
          :address    => "imap.1and1.es",
          :port       => 143, 
          :user_name  => "mailman@somedomain.com",
          :password   => "xxxxxxxx",
          :enable_ssl => false 
        }
      )
    
      delivery_method(
        :smtp,
        {
          :address              => "smtp.1and1.es",
          :port                 => 587,
          :domain               => '1and1.es',
          :user_name            => 'mailman@somaedomain.com',
          :password             => 'xxxxxxxxxxxx',
          :authentication       => 'plain',
          :enable_starttls_auto => true  
        }
      )
    
    end
    
    #emailslist=Mail.find(keys: ['NOT','SEEN'])
    emailslist = [Mail.last]
    
    emailslist.each do |e|
    
  • 这里使用三元语句可能不太理想,但我离开了。

  • 格式化为列使其更易于阅读。
  • 整理您的作业和用途,以便它们不会散布在整个文件中。

      eplain_part = e.text_part ? e.text_part.body.decoded : nil
      type_plain  = eplain_part ? e.text_part.charset.to_s : nil
      ehtml_part  = e.html_part ? e.html_part.body.decoded : nil
      type_html   = ehtml_part  ? e.html_part.charset.to_s : nil
    
      e_charset = e.charset
      body_str  = e_charset ? e.body.decoded.to_s.force_encoding(e_charset) : nil
      e_charset = e_charset ? e_charset.to_s : type_plain
      puts e_charset.inspect
    
      subjectstr = e.subject.to_s.encode(e_charset)
      fromstr    = e.from.first.to_s.encode(e_charset)
      puts fromstr
    
      bodystr_plain = eplain_part ? eplain_part.force_encoding(type_plain) : nil
      bodystr_html  = ehtml_part  ? ehtml_part.force_encoding(type_html)   : nil
    
      subscribers.each do |subscriber|
    
        puts subscriber.inspect
    
        if !subjectstr[/^\[FORMA 2013\]/] && user_is_registered(fromstr)
    
          subjectstr = delete_subject_recursion(subjectstr)
    
          begin
    
            Mail.deliver do
    
              from   fromstr
              to      "mailman@somedomain.com"
              bcc  subscriber
              subject "[FORMA 2013] #{ subjectstr }"
    
              if ehtml_part
                html_part do
                  content_type("text/html; charset=#{ type_html }")
                  #content_transfer_encoding("7bit")
                  body BODY_BOILERPLATE_FORMAT % bodystr_html
                end
              end
    
              if eplain_part
                text_part do
                  content_type("text/plain; charset=#{ type_plain }")
                  #content_transfer_encoding("7bit")
                  body BODY_BOILERPLATE_FORMAT % bodystr_plain
                end
              end
    
              if !eplain_part && !ehtml_part
                body BODY_BOILERPLATE_FORMAT % body_str
                charset = e_charset
              end
    
              #puts e.attachments.inspect
              e.attachments.each { |a| add_file a.encoded } if e.attachments.length > 0
            end
    
            puts "1 email sent"
    
          rescue => e
    
            puts "error: #{ e }"
            log("error sending to #{ subscriber }: #{ e },\nemail subject: #{ subjectstr }")
    
          end
        end
      end
    end
    

if e.attachments.length>0
  e.attachments.each do |a| 
    add_file a
  end
end

可以使用尾随条件if测试将其重构为简单的单行:

e.attachments.each { |a| add_file a.encoded } if e.attachments.length > 0

当你做一些简单的事情时,使用这样的单行是可以的。不要将它们用于更复杂的代码,因为你会产生视觉噪音,这使得你很难理解和阅读你的代码。

但是让我们来看看上面代码实际上在做什么。此上下文中的e.attachments似乎返回一个数组或某种可枚举的集合,否则each将无效。 length将告诉我们attachments返回的“数组”(或其中的任何内容)中存在多少元素。

如果length为零,那么我们不想做任何事情,所以我们可以说:

e.attachments.each { |a| add_file a.encoded } unless e.attachments.empty?

(假设attachments实现了empty?方法。)

虽然这也是多余的。如果e.attachments已经为空,那么each会做什么?它会检查attachments是否返回一个包含任何元素的数组,如果它是空的,它将完全跳过它的块,实际上就像触发了尾随的if条件一样。 SOOOooo,我们可以改用它:

e.attachments.each { |a| add_file a.encoded }

Ruby Style指南:

第二个基于第一个。

答案 1 :(得分:0)

Tin Mans的回答大多有效。我更改了附件的添加方式,因为他的版本不适合我。

e.attachments.each { |a| attachments[a.filename] = a.decoded } if e.attachments.length > 0