0

添付ファイルをダウンロードしてから、新しいメールに再添付することなく、これを実行したいと思います。
これは私が試したことです:

$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()
4

2 に答える 2

2

これはテストされておらず、実際にバグを見つけたり修正したりする試みではありません。より慣用的な Ruby コードで書かれたコードがどのように見えるべきかを示すためのものです。そして、その結果、あなたが見ている問題を解決するかもしれません. そうでない場合は、少なくとも、コードをどのように記述すべきかをよりよく理解できます。

require 'mail'
  • 再利用されるリテラル文字列の定数をいくつか定義します。これを一番上で行うと、コードを検索して複数の場所を変更する必要がなくなり、そのうちの 1 つを見逃す可能性が高くなります。

    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+'ます。読み取り/書き込みは必要ありません。書き込みのみが必要です。
  • これにより、必要に応じてファイルが開いたり閉じたりします。
  • ファイルを閉じると、自動的にフラッシュされます。
  • log メソッドを頻繁に呼び出す場合は、これを行うためのより良い方法がありますが、これは重量級のスクリプトではありません。
  • 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 のメソッド名は CamelCase ではなく、snake_case です。

  • 複数持つ理由はgsub!なく、条件付きテストも必要ありません。パージしたい部分文字列が文字列に存在するgsub場合はそれを行い、そうでない場合は先に進みます。メソッドをチェーンgsubすると、コードが 1 行に減ります。
  • gsubsub文字列に複数のヒットが置換される可能性があることがわかっていない限り、可能性があります/そうあるべきです。
  • 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|
    
  • ここでの 3 項ステートメントの使用はおそらく望ましくありませんが、私はそのままにしました。

  • 列にフォーマットすると、読みやすくなります。
  • 割り当てと使用を整理して、ファイル全体に散らばらないようにします。

      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

簡単なことをするときは、このように 1 行で問題ありません。より複雑なコードには使用しないでください。視覚的なノイズが発生し、コードの理解と読み取りが難しくなります。

しかし、上のコードが実際に何をしているのか見てみましょう。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条件がトリガーされたように効果的に動作します。すっごく、代わりにこれを使うことができます:

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

Ruby スタイルガイド:

2 番目は 1 番目に基づいています。

于 2013-10-01T16:23:51.683 に答える
0

ティンマンの答えはほとんどうまくいきます。彼のバージョンがうまくいかなかったので、添付ファイルの追加方法を変更しました。

e.attachments.each { |a| attachments[a.filename] = a.decoded } if e.attachments.length > 0
于 2014-10-13T19:52:31.453 に答える