<commit_before>module AhoyEmail
  class Processor
    attr_reader :message, :mailer, :ahoy_message

    UTM_PARAMETERS = %w(utm_source utm_medium utm_term utm_content utm_campaign)

    def initialize(message, mailer = nil)
      @message = message
      @mailer = mailer
    end

    def process
      action_name = mailer.action_name.to_sym
      if options[:message] && (!options[:only] || options[:only].include?(action_name)) && !options[:except].to_a.include?(action_name)
        @ahoy_message = AhoyEmail.message_model.new
        ahoy_message.token = generate_token
        ahoy_message.to = message.to.join(", ") if ahoy_message.respond_to?(:to=)
        ahoy_message.user = options[:user] if options[:user]

        track_open if options[:open]
        track_links if options[:utm_params] || options[:click]

        ahoy_message.mailer = options[:mailer] if ahoy_message.respond_to?(:mailer=)
        ahoy_message.subject = message.subject if ahoy_message.respond_to?(:subject=)
        ahoy_message.content = message.to_s if ahoy_message.respond_to?(:content=)
        ahoy_message.message_id = message.message_id if ahoy_message.respond_to?(:message_id=)

        UTM_PARAMETERS.each do |k|
          ahoy_message.send("#{k}=", options[k.to_sym]) if ahoy_message.respond_to?("#{k}=")
        end

        ahoy_message.assign_attributes(options[:extra] || {})

        ahoy_message.save
        message["Ahoy-Message-Id"] = ahoy_message.id.to_s
      end
    rescue => e
      report_error(e)
    end

    def track_send
      if (message_id = message["Ahoy-Message-Id"])
        ahoy_message = AhoyEmail.message_model.where(id: message_id.to_s).first
        if ahoy_message
          ahoy_message.sent_at = Time.now
          ahoy_message.save
        end
        message["Ahoy-Message-Id"] = nil
      end
    rescue => e
      report_error(e)
    end

    protected

    def options
      @options ||= begin
        options = AhoyEmail.options.merge(mailer.class.ahoy_options)
        if mailer.ahoy_options
          options = options.except(:only, :except).merge(mailer.ahoy_options)
        end
        options.each do |k, v|
          if v.respond_to?(:call)
            options[k] = v.call(message, mailer)
          end
        end
        options
      end
    end

    def generate_token
      SecureRandom.urlsafe_base64(32).gsub(/[\-_]/, "").first(32)
    end

    def track_open
      if html_part?
        raw_source = (message.html_part || message).body.raw_source
        regex = /<\/body>/i
        url =
          url_for(
            controller: "ahoy/messages",
            action: "open",
            id: ahoy_message.token,
            format: "gif"
          )
        pixel = ActionController::Base.helpers.image_tag(url, size: "1x1", alt: nil)

        # try to add before body tag
        if raw_source.match(regex)
          raw_source.gsub!(regex, "#{pixel}\\0")
        else
          raw_source << pixel
        end
      end
    end

    def track_links
      if html_part?
        body = (message.html_part || message).body

        doc = Nokogiri::HTML(body.raw_source)
        doc.css("a[href]").each do |link|
          # utm params first
          if options[:utm_params] && !skip_attribute?(link, "utm-params")
            uri = Addressable::URI.parse(link["href"])
            params = uri.query_values || {}
            UTM_PARAMETERS.each do |key|
              params[key] ||= options[key.to_sym] if options[key.to_sym]
            end
            uri.query_values = params
            link["href"] = uri.to_s
          end

          if options[:click] && !skip_attribute?(link, "click")
            signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha1"), AhoyEmail.secret_token, link["href"])
            link["href"] =
              url_for(
                controller: "ahoy/messages",
                action: "click",
                id: ahoy_message.token,
                url: link["href"],
                signature: signature
              )
          end
        end

        # hacky
        body.raw_source.sub!(body.raw_source, doc.to_s)
      end
    end

    def html_part?
      (message.html_part || message).content_type =~ /html/
    end

    def skip_attribute?(link, suffix)
      attribute = "data-skip-#{suffix}"
      if link[attribute]
        # remove it
        link.remove_attribute(attribute)
        true
      elsif link["href"].to_s =~ /unsubscribe/i
        # try to avoid unsubscribe links
        true
      elsif link["href"].to_s.start_with?("mailto:")
        # mailto's shouldn't go through a redirect
        true
      else
        false
      end
    end

    def url_for(opt)
      opt = (ActionMailer::Base.default_url_options || {})
            .merge(options[:url_options])
            .merge(opt)
      AhoyEmail::Engine.routes.url_helpers.url_for(opt)
    end

    # not a fan of quiet errors
    # but tracking should *not* break
    # email delivery in production
    def report_error(e)
      if Rails.env.production?
        $stderr.puts e
      else
        raise e
      end
    end
  end
end
<commit_msg>empty string for tracking image alt<commit_after>module AhoyEmail
  class Processor
    attr_reader :message, :mailer, :ahoy_message

    UTM_PARAMETERS = %w(utm_source utm_medium utm_term utm_content utm_campaign)

    def initialize(message, mailer = nil)
      @message = message
      @mailer = mailer
    end

    def process
      action_name = mailer.action_name.to_sym
      if options[:message] && (!options[:only] || options[:only].include?(action_name)) && !options[:except].to_a.include?(action_name)
        @ahoy_message = AhoyEmail.message_model.new
        ahoy_message.token = generate_token
        ahoy_message.to = message.to.join(", ") if ahoy_message.respond_to?(:to=)
        ahoy_message.user = options[:user] if options[:user]

        track_open if options[:open]
        track_links if options[:utm_params] || options[:click]

        ahoy_message.mailer = options[:mailer] if ahoy_message.respond_to?(:mailer=)
        ahoy_message.subject = message.subject if ahoy_message.respond_to?(:subject=)
        ahoy_message.content = message.to_s if ahoy_message.respond_to?(:content=)
        ahoy_message.message_id = message.message_id if ahoy_message.respond_to?(:message_id=)

        UTM_PARAMETERS.each do |k|
          ahoy_message.send("#{k}=", options[k.to_sym]) if ahoy_message.respond_to?("#{k}=")
        end

        ahoy_message.assign_attributes(options[:extra] || {})

        ahoy_message.save
        message["Ahoy-Message-Id"] = ahoy_message.id.to_s
      end
    rescue => e
      report_error(e)
    end

    def track_send
      if (message_id = message["Ahoy-Message-Id"])
        ahoy_message = AhoyEmail.message_model.where(id: message_id.to_s).first
        if ahoy_message
          ahoy_message.sent_at = Time.now
          ahoy_message.save
        end
        message["Ahoy-Message-Id"] = nil
      end
    rescue => e
      report_error(e)
    end

    protected

    def options
      @options ||= begin
        options = AhoyEmail.options.merge(mailer.class.ahoy_options)
        if mailer.ahoy_options
          options = options.except(:only, :except).merge(mailer.ahoy_options)
        end
        options.each do |k, v|
          if v.respond_to?(:call)
            options[k] = v.call(message, mailer)
          end
        end
        options
      end
    end

    def generate_token
      SecureRandom.urlsafe_base64(32).gsub(/[\-_]/, "").first(32)
    end

    def track_open
      if html_part?
        raw_source = (message.html_part || message).body.raw_source
        regex = /<\/body>/i
        url =
          url_for(
            controller: "ahoy/messages",
            action: "open",
            id: ahoy_message.token,
            format: "gif"
          )
        pixel = ActionController::Base.helpers.image_tag(url, size: "1x1", alt: "")

        # try to add before body tag
        if raw_source.match(regex)
          raw_source.gsub!(regex, "#{pixel}\\0")
        else
          raw_source << pixel
        end
      end
    end

    def track_links
      if html_part?
        body = (message.html_part || message).body

        doc = Nokogiri::HTML(body.raw_source)
        doc.css("a[href]").each do |link|
          # utm params first
          if options[:utm_params] && !skip_attribute?(link, "utm-params")
            uri = Addressable::URI.parse(link["href"])
            params = uri.query_values || {}
            UTM_PARAMETERS.each do |key|
              params[key] ||= options[key.to_sym] if options[key.to_sym]
            end
            uri.query_values = params
            link["href"] = uri.to_s
          end

          if options[:click] && !skip_attribute?(link, "click")
            signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha1"), AhoyEmail.secret_token, link["href"])
            link["href"] =
              url_for(
                controller: "ahoy/messages",
                action: "click",
                id: ahoy_message.token,
                url: link["href"],
                signature: signature
              )
          end
        end

        # hacky
        body.raw_source.sub!(body.raw_source, doc.to_s)
      end
    end

    def html_part?
      (message.html_part || message).content_type =~ /html/
    end

    def skip_attribute?(link, suffix)
      attribute = "data-skip-#{suffix}"
      if link[attribute]
        # remove it
        link.remove_attribute(attribute)
        true
      elsif link["href"].to_s =~ /unsubscribe/i
        # try to avoid unsubscribe links
        true
      elsif link["href"].to_s.start_with?("mailto:")
        # mailto's shouldn't go through a redirect
        true
      else
        false
      end
    end

    def url_for(opt)
      opt = (ActionMailer::Base.default_url_options || {})
            .merge(options[:url_options])
            .merge(opt)
      AhoyEmail::Engine.routes.url_helpers.url_for(opt)
    end

    # not a fan of quiet errors
    # but tracking should *not* break
    # email delivery in production
    def report_error(e)
      if Rails.env.production?
        $stderr.puts e
      else
        raise e
      end
    end
  end
end

{'fraction_non_alphanumeric': 0.11181455147560145, 'fraction_numerical': 0.0019479887016655303, 'mean_word_length': 2.0797840431913617, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3682989', 'n_tokens_mistral': 3225, 'n_tokens_neox': 3188, 'n_words': 636}