Skip to content

Commit

Permalink
allow reply by email
Browse files Browse the repository at this point in the history
  • Loading branch information
siuying committed Dec 3, 2011
1 parent 5c849bc commit 2cadc1b
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 54 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -12,6 +12,7 @@ config/database.yml
config/mongoid.yml
config/config.yml
config/redis.yml
config/mailman.yml
config/mailer_daemon.yml
public/uploads/**/*
public/topics
Expand Down
9 changes: 8 additions & 1 deletion Gemfile
Expand Up @@ -54,7 +54,6 @@ gem 'mail_view', :git => 'git://github.com/37signals/mail_view.git'
gem "daemon-spawn", "~> 0.4.2"
gem "unicorn"
gem "rb-inotify"
gem "mailman"

# 用于组合小图片
gem "sprite-factory", "1.4.1"
Expand All @@ -68,6 +67,14 @@ group :assets do
gem 'uglifier'
end

group :mailman do
gem "rb-inotify"
gem "mailman"
gem "nokogiri", "1.5.0"
gem "daemon-spawn", "~> 0.4.2"
gem "resque", "~> 1.19.0", :require => "resque/server"
end

group :development do
gem 'capistrano', '2.9.0'
gem 'chunky_png', "1.2.5"
Expand Down
8 changes: 8 additions & 0 deletions README.markdown
Expand Up @@ -9,11 +9,13 @@ This is source code of [Ruby China Group](http://ruby-china.org)
cp config/config.yml.default config/config.yml
cp config/mongoid.yml.default config/mongoid.yml
cp config/redis.yml.default config/redis.yml
cp config/mailman.yml.default config/mailman.yml
bundle install
bundle update rails
rake assets:precompile
thin start -O -C config/thin.yml
./script/resque start
./script/mailman start
easy_install pygments # 或者 pip install pygments
```

Expand Down Expand Up @@ -53,6 +55,12 @@ This is source code of [Ruby China Group](http://ruby-china.org)

Dalli requires memcached 1.4.x +

## Mailman

如要啟動電郵回覆功能,請啟動 ./script/mailman

要設定 ./config/mailman.yml 到適當的 pop3 電郵,如果使用 gmail ,請確認已啟動 pop3接收郵件功能。

## Helpers

render_topic_title(topic)
Expand Down
35 changes: 35 additions & 0 deletions app/helpers/mail_helper.rb
@@ -0,0 +1,35 @@
require 'nokogiri'

module MailHelper

# find the email body in a message
def plaintext_body(message)
if message.multipart?
message.parts.each do |p|
if p.content_type =~ /text\/plain/
encoding = p.content_type.to_s.split("=").last.to_s
return extract_content(p.body,encoding)
end
end
raise "mail body multipart, but not text/plain part"
elsif message.content_type == 'text/plain'
return extract_content(message.body, message.encoding)
else
raise "mail body is not multipart nor text/plain"
end
end

# extract email content from a body
# use the sender email line as separation
def extract_reply(body, sender_email)
body.strip
.gsub(/\n^[^\r\n]*#{sender_email}.*:.*\z/m, '')
.strip
end

private
def extract_content(html, coding)
doc = Nokogiri::HTML(html.to_s, nil, coding)
return doc.css("body").text
end
end
14 changes: 9 additions & 5 deletions app/models/reply_listener.rb
@@ -1,18 +1,22 @@
# reply email and create topic as needed

class ReplyListener
def self.perform(reply_id, key, from, text, message_id)
@queue = :mailer

def self.perform(reply_id, key, message_id, from_email, reply_text)
previous_reply = Reply.find(reply_id)
recipient = User.find(from)
reply_user = User.find_by_email(from_email)

valid_reply_key = TopicMailer.reply_key(previous_reply.email_key, recipient.email)
valid_reply_key = TopicMailer.reply_key(previous_reply.email_key, reply_user.email)
if valid_reply_key != key
raise "Invalid reply: #{reply_id}, key: #{key}, from: #{from}"
end

reply = previous_reply.topic.replies.build
reply.body = text
reply = Reply.new
reply.topic_id = previous_reply.topic_id
reply.user_id = reply_user.id
reply.body = reply_text
reply.source = 'mail'
reply.message_id = message_id
reply.save!
end
Expand Down
4 changes: 2 additions & 2 deletions config/initializers/redis.rb
Expand Up @@ -19,5 +19,5 @@
require "topic"

Resque::Mailer.default_queue_name = "mailer"
Resque.redis = Redis.new(:host => redis_config['host'],:port => redis_config['port'])
Resque.redis.namespace = "resque:ruby-taiwan"
Resque.redis = Redis.new(:host => redis_config['host'], :port => redis_config['port'])
Resque.redis.namespace = redis_config['redis_namespace']
20 changes: 20 additions & 0 deletions config/mailman.yml.default
@@ -0,0 +1,20 @@
defaults: &defaults
server: 'pop.gmail.com'
port: 995
ssl: true

development:
<<: *defaults
username: ''
password: ''

test:
<<: *defaults
username: ''
password: ''

production:
<<: *defaults
username: ''
password: ''

69 changes: 69 additions & 0 deletions script/mailman
@@ -0,0 +1,69 @@
#!/usr/bin/env ruby
require 'rubygems'
require 'bundler'
Bundler.require :mailman

require File.expand_path('../../app/helpers/mail_helper', __FILE__)
require File.expand_path('../../app/models/reply_listener', __FILE__)
include MailHelper

require 'daemon_spawn'

env = ENV['RAILS_ENV'] || 'development'
config = YAML::load(open(File.expand_path('../../config/mailman.yml', __FILE__)))[env]
Mailman.config.pop3 = {
:username => config["username"],
:password => config["password"],
:server => config["server"],
:port => config["port"],
:ssl => config["ssl"]
}

redis_config = YAML.load_file(File.expand_path('../../config/redis.yml', __FILE__))[env]
Resque.redis = Redis.new(:host => redis_config['host'], :port => redis_config['port'])
Resque.redis.namespace = redis_config['redis_namespace']

working_dir = File.expand_path('../../', __FILE__)
logs = "#{working_dir}/log/mailman.log"
pids = "#{working_dir}/tmp/pids/mailman.pid"

class MailmanDaemon < DaemonSpawn::Base
def start(args)
Mailman::Application.run do
to('notification+%reply_id%-%key%@ruby-hk.org') do |reply_id, key|
begin
from_email = message.from.first
message_id = message.message_id
reply_text = extract_reply(plaintext_body(message), Setting.email_sender)
puts "receive email from #{from_email}, reply_id: #{reply_id}, key: #{key}"

Resque.enqueue(ReplyListener, reply_id, key, message_id, from_email, reply_text)
rescue StandardError => e
puts "ERROR: #{e.inspect}"

# write mail to a file for debug
emailfile = "#{working_dir}/log/mailman-#{rand(100000)}.eml"
puts "Write file to debug: #{emailfile}"
File.open(emailfile, 'w') {|f| f.write(message) }
end
end
end
end

def stop
puts "Try to shutdown mailman"
end
end


MailmanDaemon.spawn!({
:processes => 1,
:log_file => logs,
:pid_file => pids,
:sync_log => true,
:working_dir => working_dir,
:singleton => true
})



46 changes: 0 additions & 46 deletions script/mailman.rb

This file was deleted.

0 comments on commit 2cadc1b

Please sign in to comment.