journalduhacker/script/mail_new_activity

244 lines
6.6 KiB
Ruby
Executable file

#!/usr/bin/env ruby
ENV["RAILS_ENV"] ||= "production"
APP_PATH = File.expand_path('../../config/application', __FILE__)
require File.expand_path('../../config/boot', __FILE__)
require APP_PATH
Rails.application.require_environment!
class String
def quoted_printable(encoded_word = false)
s = [ self ].pack("M")
if encoded_word
s.split(/\r?\n/).map{|l| "=?UTF-8?Q?" <<
l.gsub(/=*$/, "").gsub(" ", "_") << "?=" }.join("\n\t")
else
s
end
end
# like ActionView::Helpers::TextHelper but preserve > and indentation when
# wrapping lines
def word_wrap(len)
split("\n").collect do |line|
if line.length <= len
line
elsif m = line.match(/^(> ?| +)(.*)/)
ind = m[1]
if len - ind.length < 0
ind = " "
end
m[2].gsub(/(.{1,#{len - ind.length}})(\s+|$)/, "#{ind}\\1\n").strip
else
line.gsub(/(.{1,#{len}})(\s+|$)/, "\\1\n").strip
end
end * "\n"
end
end
def story_subject(story, prefix = "")
ss = "#{prefix}#{story.title}"
story.tags.sort_by{|t| t.tag }.each do |t|
ss << " [#{t.tag}]"
end
ss.quoted_printable(true)
end
EMAIL_WIDTH = 72
LAST_STORY_KEY = "mailing:last_story_id"
LAST_COMMENT_KEY = "mailing:last_comment_id"
mailing_list_users = User.where("mailing_list_mode > 0").select{|u|
u.is_active? }
last_story_id = (Keystore.value_for(LAST_STORY_KEY) || Story.last.id).to_i
Story.where("id > ? AND is_expired = ?", last_story_id, false).order(:id).each do |s|
s.fetch_story_cache!
if s.story_cache.blank?
s.fetch_story_cache!
end
s.save
mailing_list_users.each do |u|
if (s.tags.map{|t| t.id } & u.tag_filters.map{|t| t.tag_id }).any?
# story has tags this user has filtered out
next
end
if s.is_hidden_by_user?(u)
# user has hidden this story
next
end
domain = Rails.application.domain
list = "#{Rails.application.shortname}-#{u.mailing_list_token}@" <<
Rails.application.domain
IO.popen([ {}, "/usr/sbin/sendmail", "-i", "-f",
"nobody@#{Rails.application.domain}", u.email ], "w") do |mail|
mail.puts "From: #{s.user.username} <#{s.user.username}@" <<
"#{Rails.application.domain}>"
mail.puts "Reply-To: #{list}"
mail.puts "To: #{list}"
mail.puts "X-BeenThere: #{list}"
mail.puts "List-Id: #{Rails.application.name} <#{list}>"
mail.puts "List-Unsubscribe: <" <<
"#{Rails.application.root_url}settings>"
mail.puts "Precedence: list"
mail.puts "MIME-Version: 1.0"
mail.puts "Content-Type: text/plain; charset=\"utf-8\""
mail.puts "Content-Transfer-Encoding: quoted-printable"
mail.puts "Message-ID: <#{s.mailing_list_message_id}>"
mail.puts "Date: " << s.created_at.strftime("%a, %d %b %Y %H:%M:%S %z")
mail.puts "Subject: " << story_subject(s)
mail.puts ""
body = []
if s.description.present?
body.push s.description.to_s.word_wrap(EMAIL_WIDTH)
end
if s.url.present?
if s.description.present?
body.push ""
end
body.push "Via: #{s.url}"
if s.story_cache.present?
body.push ""
body.push s.story_cache.to_s.word_wrap(EMAIL_WIDTH)
end
end
body.push ""
body.push "-- "
body.push "Vote: #{s.short_id_url}"
mail.puts body.join("\n").quoted_printable
end
end
last_story_id = s.id
end
Keystore.put(LAST_STORY_KEY, last_story_id)
# repeat for comments
last_comment_id = (Keystore.value_for(LAST_COMMENT_KEY) ||
Comment.last.id).to_i
Comment.where("id > ? AND (is_deleted = ? AND is_moderated = ?)",
last_comment_id, false, false).order(:id).each do |c|
# allow some time for newer comments to be edited before sending them out
if (Time.now - (c.updated_at || c.created_at)) < 2.minutes
break
end
mailing_list_users.each do |u|
if u.mailing_list_mode == 2
# stories only
next
end
if (c.story.tags.map{|t| t.id } & u.tag_filters.map{|t| t.tag_id }).any?
# story has tags this user has filtered out
next
end
if c.story.is_hidden_by_user?(u)
# user has hidden this story
next
end
domain = Rails.application.domain
list = "#{Rails.application.shortname}-#{u.mailing_list_token}@" <<
Rails.application.domain
IO.popen([ {}, "/usr/sbin/sendmail", "-i", "-f",
"nobody@#{domain}", u.email ], "w") do |mail|
from = "From: \"#{c.user.username}"
if c.hat
from << " (#{c.hat.hat})"
end
from << "\" <#{c.user.username}@#{domain}>"
mail.puts from
mail.puts "Reply-To: #{list}"
mail.puts "To: #{list}"
mail.puts "List-Id: #{Rails.application.name} <#{list}>"
mail.puts "List-Unsubscribe: <" <<
"#{Rails.application.root_url}settings>"
mail.puts "Precedence: list"
mail.puts "MIME-Version: 1.0"
mail.puts "Content-Type: text/plain; charset=\"utf-8\""
mail.puts "Content-Transfer-Encoding: quoted-printable"
mail.puts "Message-ID: <#{c.mailing_list_message_id}>"
refs = [ "<#{c.story.mailing_list_message_id}>" ]
if c.parent_comment_id
mail.puts "In-Reply-To: <#{c.parent_comment.mailing_list_message_id}>"
thread = []
indent_level = 0
Comment.where(:thread_id => c.thread_id).arrange_for_user(
nil).reverse.each do |cc|
if indent_level > 0 && cc.indent_level < indent_level
thread.unshift cc
indent_level = cc.indent_level
elsif cc.id == c.id
indent_level = cc.indent_level
end
end
thread.each do |cc|
refs.push "<#{cc.mailing_list_message_id}>"
end
else
mail.puts "In-Reply-To: <#{c.story.mailing_list_message_id}>"
end
mail.print "References:"
refs.each do |ref|
mail.puts " #{ref}"
end
mail.puts "Date: " << c.created_at.strftime("%a, %d %b %Y %H:%M:%S %z")
mail.puts "Subject: " << story_subject(c.story, "Re: ")
mail.puts ""
body = []
# if the comment has hard line breaks at <80, it likely came from an
# email, so don't re-wrap it at something shorter
com = c.comment.to_s
com_lines = com.split("\n")
if com_lines.length > 1 && com_lines.first.length < 80
body.push com.word_wrap(80)
else
body.push com.word_wrap(EMAIL_WIDTH)
end
body.push ""
body.push "-- "
body.push "Vote: #{c.short_id_url}"
mail.puts body.join("\n").quoted_printable
end
end
last_comment_id = c.id
end
Keystore.put(LAST_COMMENT_KEY, last_comment_id)