kns_email_endpoint 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +15 -0
  3. data/Gemfile.lock +71 -0
  4. data/Guardfile +10 -0
  5. data/LICENCE +21 -0
  6. data/README.md +1 -0
  7. data/Rakefile +2 -0
  8. data/kns_email_endpoint.gemspec +39 -0
  9. data/lib/kns_email_endpoint/configuration.rb +87 -0
  10. data/lib/kns_email_endpoint/connection.rb +97 -0
  11. data/lib/kns_email_endpoint/core_extensions/class.rb +44 -0
  12. data/lib/kns_email_endpoint/core_extensions/hash.rb +14 -0
  13. data/lib/kns_email_endpoint/core_extensions/imap.rb +36 -0
  14. data/lib/kns_email_endpoint/core_extensions/message.rb +26 -0
  15. data/lib/kns_email_endpoint/core_extensions/pop3.rb +0 -0
  16. data/lib/kns_email_endpoint/core_extensions/test_retriever.rb +42 -0
  17. data/lib/kns_email_endpoint/email_endpoint.rb +88 -0
  18. data/lib/kns_email_endpoint/message_state.rb +89 -0
  19. data/lib/kns_email_endpoint/process_email.rb +123 -0
  20. data/lib/kns_email_endpoint/storage/abstract_storage.rb +93 -0
  21. data/lib/kns_email_endpoint/storage/file_storage.rb +74 -0
  22. data/lib/kns_email_endpoint/storage/memcache_storage.rb +72 -0
  23. data/lib/kns_email_endpoint/storage/storage.rb +15 -0
  24. data/lib/kns_email_endpoint/version.rb +3 -0
  25. data/lib/kns_email_endpoint.rb +19 -0
  26. data/spec/a18x34/.app +4 -0
  27. data/spec/a18x34/a18x34.krl +91 -0
  28. data/spec/lib/kns_email_endpoint/configuration_spec.rb +44 -0
  29. data/spec/lib/kns_email_endpoint/connection_spec.rb +133 -0
  30. data/spec/lib/kns_email_endpoint/email_endpoint_spec.rb +97 -0
  31. data/spec/lib/kns_email_endpoint/message_state_spec.rb +69 -0
  32. data/spec/lib/kns_email_endpoint/process_email_spec.rb +67 -0
  33. data/spec/lib/kns_email_endpoint/storage/file_storage_spec.rb +16 -0
  34. data/spec/lib/kns_email_endpoint/storage/memcache_storage_spec.rb +17 -0
  35. data/spec/lib/kns_email_endpoint/storage/shared_storage.rb +65 -0
  36. data/spec/lib/kns_email_endpoint/storage/storage_spec.rb +14 -0
  37. data/spec/test_config_file.yml +65 -0
  38. data/spec/test_email.eml +43 -0
  39. metadata +181 -0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in kns_email_endpoint.gemspec
4
+ gemspec
5
+
6
+
7
+ group :development do
8
+
9
+ gem "rspec", ">= 2.0.0"
10
+ gem "guard-rspec"
11
+ gem "growl"
12
+ gem "rb-fsevent"
13
+ gem "awesome_print"
14
+
15
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,71 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ kns_email_endpoint (0.1.0)
5
+ dalli (~> 1.0.0)
6
+ kns_endpoint (>= 0.1.9)
7
+ mail (>= 2.2.6.1)
8
+ work_queue (~> 1.0.0)
9
+
10
+ GEM
11
+ remote: http://rubygems.org/
12
+ specs:
13
+ activesupport (3.0.3)
14
+ awesome_print (0.3.1)
15
+ configuration (1.2.0)
16
+ dalli (1.0.1)
17
+ diff-lcs (1.1.2)
18
+ growl (1.0.3)
19
+ guard (0.2.2)
20
+ open_gem (~> 1.4.2)
21
+ thor (~> 0.14.3)
22
+ guard-rspec (0.1.9)
23
+ guard (>= 0.2.2)
24
+ i18n (0.5.0)
25
+ json (1.4.6)
26
+ kns_endpoint (0.1.9)
27
+ json (~> 1.4.0)
28
+ rest-client (>= 1.6.1)
29
+ launchy (0.3.7)
30
+ configuration (>= 0.0.5)
31
+ rake (>= 0.8.1)
32
+ mail (2.2.14)
33
+ activesupport (>= 2.3.6)
34
+ i18n (>= 0.4.0)
35
+ mime-types (~> 1.16)
36
+ treetop (~> 1.4.8)
37
+ mime-types (1.16)
38
+ open_gem (1.4.2)
39
+ launchy (~> 0.3.5)
40
+ polyglot (0.3.1)
41
+ rake (0.8.7)
42
+ rb-fsevent (0.3.9)
43
+ rest-client (1.6.1)
44
+ mime-types (>= 1.16)
45
+ rspec (2.4.0)
46
+ rspec-core (~> 2.4.0)
47
+ rspec-expectations (~> 2.4.0)
48
+ rspec-mocks (~> 2.4.0)
49
+ rspec-core (2.4.0)
50
+ rspec-expectations (2.4.0)
51
+ diff-lcs (~> 1.1.2)
52
+ rspec-mocks (2.4.0)
53
+ thor (0.14.6)
54
+ treetop (1.4.9)
55
+ polyglot (>= 0.3.1)
56
+ work_queue (1.0.0)
57
+
58
+ PLATFORMS
59
+ ruby
60
+
61
+ DEPENDENCIES
62
+ awesome_print
63
+ dalli (~> 1.0.0)
64
+ growl
65
+ guard-rspec
66
+ kns_email_endpoint!
67
+ kns_endpoint (>= 0.1.9)
68
+ mail (>= 2.2.6.1)
69
+ rb-fsevent
70
+ rspec (>= 2.0.0)
71
+ work_queue (~> 1.0.0)
data/Guardfile ADDED
@@ -0,0 +1,10 @@
1
+ # A sample Guardfile
2
+ # More info at http://github.com/guard/guard#readme
3
+ #
4
+
5
+ guard 'rspec' do
6
+ watch('^spec/(.*)_spec.rb')
7
+ watch('^lib/(.*)\.rb') { |m| "spec/lib/#{m[1]}_spec.rb" }
8
+ watch('^lib/kns_email_endpoint/(.*)\.rb') { |m| "spec/kns_email_endpoint/lib/#{m[1]}_spec.rb" }
9
+ end
10
+
data/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2010 Kynetx Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ Coming Soon!
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,39 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "kns_email_endpoint/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "kns_email_endpoint"
7
+ s.version = KnsEmailEndpoint::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Michael Farmer"]
10
+ s.email = ["mjf@kynetx.com"]
11
+ s.homepage = "http://code.kynetx.com"
12
+ s.summary = %q{The Kynetx Email Endpoint is a ruby gem that makes setting up an email endpoint very simple.}
13
+ s.description = %q{The Kynetx Email Endpoint is a ruby gem that makes setting up an email endpoint very simple.}
14
+
15
+ s.rubyforge_project = "kns_email_endpoint"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency "kns_endpoint", ">= 0.1.9"
23
+ s.add_dependency "mail", ">= 2.2.6.1"
24
+ s.add_dependency "dalli", "~> 1.0.0"
25
+ s.add_dependency "work_queue", "~> 1.0.0"
26
+
27
+ #activesupport (3.0.0)
28
+ #daemons (1.1.0)
29
+ #fastthread (1.0.7)
30
+ #i18n (0.4.1)
31
+ #json (1.4.6)
32
+ #mail (2.2.6.1)
33
+ #mime-types (1.16)
34
+ #polyglot (0.3.1)
35
+ #sqlite3-ruby (1.3.1)
36
+ #tlsmail (0.0.1)
37
+ #treetop (1.4.8)
38
+ #work_queue (1.0.0)
39
+ end
@@ -0,0 +1,87 @@
1
+ require 'yaml'
2
+ require 'logger'
3
+ require 'fileutils'
4
+
5
+ module KNSEmailEndpoint
6
+
7
+
8
+ class Configuration
9
+ cattr_writer :logdir, :work_threads, :poll_delay, :connections
10
+ cattr_reader :storage_engine
11
+
12
+ class << self
13
+
14
+ def load_from_file(yaml_file)
15
+ conf = YAML.load_file(yaml_file)
16
+ @@logdir = conf["logdir"] if conf["logdir"]
17
+ @@work_threads = conf["workthreads"] if conf["workthreads"]
18
+ @@poll_delay = conf["polldelayinseconds"] if conf["polldelayinseconds"]
19
+ @@log_level = conf["logginglevel"] if conf["logginglevel"]
20
+ @@connections = conf["connections"] if conf["connections"]
21
+ self.storage = conf["storage"] || {}
22
+ end
23
+
24
+ def to_h
25
+ {
26
+ :logdir => logdir,
27
+ :work_threads => work_threads,
28
+ :poll_delay => poll_delay,
29
+ :storage => storage,
30
+ :connections => connections
31
+ }
32
+ end
33
+
34
+ def log
35
+ return @@logger if defined?(@@logger) && ! @logger.nil?
36
+ if @@logdir && ! @@logdir.empty?
37
+ FileUtils.mkdir_p @@logdir unless @@logdir == "" || @@logdir.nil?
38
+ log_dest = File.join(@@logdir, 'email_endpoint.log')
39
+ else
40
+ log_dest = STDOUT
41
+ end
42
+ @@logger = Logger.new(log_dest, "daily")
43
+ @@logger.level = eval("Logger::#{@@log_level.upcase}") rescue Logger::DEBUG
44
+ return @@logger
45
+ end
46
+
47
+ def log_level=(l)
48
+ @@log_level = l
49
+ @@log = nil
50
+ return @@log_level
51
+ end
52
+
53
+ def storage=(opts)
54
+ @@storage = opts
55
+ engine = opts.delete("engine")
56
+ @@storage_engine = MessageState.set_storage(engine, opts)
57
+ return @@storage
58
+ end
59
+
60
+ # defaults
61
+ def work_threads; @@work_threads ||= 10 end
62
+ def poll_delay; @@poll_delay ||= 30 end
63
+ def logdir; @@logdir ||= "" end
64
+ def connections; @@connections ||= [] end
65
+ def storage; @@storage ||= {} end
66
+
67
+ # Connection Handling
68
+
69
+ def [](name)
70
+ @@connections.each do |conn|
71
+ return conn if conn["name"] == name
72
+ end
73
+ raise "Invalid connection (#{name})"
74
+ end
75
+
76
+ def each_connection
77
+ @@connections.each do |conn|
78
+ yield Connection.new conn["name"]
79
+ end
80
+ end
81
+
82
+
83
+ end
84
+ end
85
+
86
+ end
87
+
@@ -0,0 +1,97 @@
1
+ require 'net/smtp'
2
+ require 'mail'
3
+
4
+ module KNSEmailEndpoint
5
+ class ConnectionException < StandardError
6
+ end
7
+
8
+
9
+ module ConnectionFactory
10
+ attr_reader :conn_log, :retriever, :sender, :name, :process_mode, :event_args, :appid, :environment, :max_retry_count
11
+
12
+ def setup_mail_connections(name)
13
+ @config = Configuration
14
+ @conn_config = @config[name]
15
+
16
+ if @config.logdir.empty?
17
+ log_dest = STDOUT
18
+ else
19
+ log_dest = "#{@config.logdir}/#{@conn_config['logfile']}"
20
+ end
21
+ @config.log.info "Initializing connection log to: #{log_dest}"
22
+ @conn_log = Logger.new(log_dest, "daily")
23
+ @conn_log.level = @config.log.level
24
+ @name = @conn_config["name"]
25
+ @process_mode = @conn_config["processmode"].to_sym
26
+ @max_retry_count = @conn_config["max_retry_count"] || 10
27
+ @event_args = @conn_config["args"] || {}
28
+ @appid = @conn_config["appid"]
29
+ if @conn_config["appversion"] &&
30
+ (@conn_config["appversion"] == "dev" || @conn_config["appversion"] == "development")
31
+ @environment = :development
32
+ else
33
+ @environment = :production
34
+ end
35
+
36
+
37
+ @in_method = @conn_config["incoming"]["method"]
38
+ @out_method = @conn_config["smtp"]["method"]
39
+
40
+ @retriever_settings = {
41
+ :address => @conn_config["incoming"]["host"],
42
+ :user_name => @conn_config["incoming"]["username"],
43
+ :password => @conn_config["incoming"]["password"],
44
+ :enable_ssl => @conn_config["incoming"]["ssl"],
45
+ :port => @conn_config["incoming"]["port"],
46
+ :authentication => @conn_config["incoming"]["authentication"] || nil
47
+ }
48
+
49
+ @mailbox = @conn_config["incoming"]["mailbox"]
50
+ @retriever = lookup_retriever
51
+
52
+ @sender_settings = {
53
+ :address => @conn_config["smtp"]["host"],
54
+ :user_name => @conn_config["smtp"]["username"],
55
+ :password => @conn_config["smtp"]["password"],
56
+ :port => @conn_config["smtp"]["port"],
57
+ :domain => @conn_config["smtp"]["helo_domain"],
58
+ :enable_starttls_auto => @conn_config["smtp"]["tls"]
59
+ }
60
+ if @conn_config["smtp"]["authentication"]
61
+ @sender_settings[:authentication] = @conn_config["smtp"]["authentication"].downcase
62
+ end
63
+
64
+ @sender = lookup_sender
65
+ end
66
+
67
+
68
+ private
69
+
70
+ def lookup_retriever
71
+ case @in_method
72
+ when "imap" then return Mail::IMAP.new(@retriever_settings)
73
+ when "pop3" then return Mail::POP3.new(@retriever_settings)
74
+ when "test" then return Mail::TestRetriever.new(@retriever_settings)
75
+ else return Mail::IMAP.new(@retriever_settings)
76
+ end
77
+ end
78
+
79
+ def lookup_sender
80
+ case @out_method
81
+ when "smtp" then return Mail::SMTP.new(@sender_settings)
82
+ when "test" then return Mail::TestMailer.new(@sender_settings)
83
+ else return Mail::SMTP.new(@sender_settings)
84
+ end
85
+ end
86
+
87
+ end
88
+
89
+ class Connection
90
+ include ConnectionFactory
91
+ def initialize(name)
92
+ setup_mail_connections name
93
+ conn_log.info "Setup Connection for '#{name}'"
94
+ end
95
+ end
96
+ end
97
+
@@ -0,0 +1,44 @@
1
+ # Mail::TestRetriever uses cattr_accessor from rails. Since I don't want to include rails as a dependency
2
+ # I'm just adding the Rails code here.
3
+ # (Note, this is older Rails code slightly modified, but it should still work for what I need it for)
4
+ class Class
5
+ def cattr_accessor(*syms, &blk)
6
+ cattr_reader(*syms)
7
+ cattr_writer(*syms, &blk)
8
+ end
9
+
10
+ def cattr_reader(*syms)
11
+ syms.flatten.each do |sym|
12
+ next if sym.is_a?(Hash)
13
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
14
+ unless defined? @@#{sym}
15
+ @@#{sym} = nil
16
+ end
17
+
18
+ def self.#{sym}
19
+ @@#{sym}
20
+ end
21
+ EOS
22
+
23
+
24
+ end
25
+ end
26
+
27
+ def cattr_writer(*syms)
28
+ syms.flatten.each do |sym|
29
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
30
+ unless defined? @@#{sym}
31
+ @@#{sym} = nil
32
+ end
33
+
34
+ def self.#{sym}=(obj)
35
+ @@#{sym} = obj
36
+ end
37
+ EOS
38
+
39
+ self.send("#{sym}=", yield) if block_given?
40
+
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,14 @@
1
+ class Hash
2
+
3
+ def symbolize_keys
4
+ inject({}) do |options, (key, value)|
5
+ options[(key.to_sym rescue key) || key] = value
6
+ options
7
+ end
8
+ end
9
+
10
+ def symbolize_keys!
11
+ self.replace(self.symbolize_keys)
12
+ end
13
+
14
+ end
@@ -0,0 +1,36 @@
1
+ # Adding methods to imap for easier access to things we do commonly
2
+ require 'mail'
3
+
4
+ module Mail
5
+
6
+ class IMAP
7
+
8
+ # add a delete message
9
+ # so that we can delete without calling find again
10
+
11
+ def delete_messages(mailbox, messages)
12
+ mailbox = Net::IMAP.encode_utf7(mailbox)
13
+ messages.each do |message_hash|
14
+ imap = message_hash[:connection]
15
+ message_id = message_hash[:message_id]
16
+ message = message_hash[:message]
17
+ imap.uid_store(message_id, "+FLAGS", [Net::IMAP::DELETED]) if message.is_marked_for_delete?
18
+ end
19
+ start do |imap|
20
+ begin
21
+ imap.select(mailbox)
22
+ imap.expunge
23
+ rescue => e
24
+ puts e.message
25
+ puts e.backtrace.join("\n")
26
+ raise e
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,26 @@
1
+ module Mail
2
+ class Message
3
+ def to_h
4
+ {
5
+ :to => to,
6
+ :from => from,
7
+ :subject => subject,
8
+ :body => body.raw_source
9
+ }
10
+
11
+ end
12
+
13
+ def delivered_to
14
+ # Messages can be sent to multiple people AND if it was forwarded will have multiple
15
+ # Delivered-To headers. What this bit of code does is take the
16
+ # intersection of the "to" recipients and the "Delivered-To" recipients to find
17
+ # out who the message was actually delivered to. There will almost never
18
+ # be more than one recipient in that intersection, but if there is, we'll just
19
+ # return the first.
20
+ delivered_to_headers = []
21
+ self.header_fields.each { |f| delivered_to_headers << f.value if f.name == "Delivered-To"}
22
+ actual_to = self.to.to_set.intersection delivered_to_headers
23
+ return actual_to.first
24
+ end
25
+ end
26
+ end
File without changes
@@ -0,0 +1,42 @@
1
+ # Mail::TestRetriever additional methods
2
+ require 'mail'
3
+ module Mail
4
+ class TestRetriever
5
+ def delete_all
6
+ @@emails = []
7
+ end
8
+
9
+ remove_method :find
10
+
11
+ # Fix a bug in the find method when only returning a single email
12
+ def find(options = {}, &block)
13
+ options[:count] ||= :all
14
+ options[:order] ||= :asc
15
+ options[:what] ||= :first
16
+ emails = @@emails.dup
17
+ emails.reverse! if options[:what] == :last
18
+ emails = case count = options[:count]
19
+ when :all then emails
20
+ # when 1 then emails.first
21
+ when Fixnum then emails[0, count]
22
+ else
23
+ raise 'Invalid count option value: ' + count.inspect
24
+ end
25
+ if options[:what] == :last && options[:order] == :asc || options[:what] == :first && options[:order] == :desc
26
+ emails.reverse!
27
+ end
28
+ emails.each { |email| email.mark_for_delete = true } if options[:delete_after_find]
29
+
30
+ if block_given?
31
+ emails.each { |email| yield email }
32
+ else
33
+ emails
34
+ end.tap do |results|
35
+ emails.each { |email| @@emails.delete(email) if email.is_marked_for_delete? } if options[:delete_after_find]
36
+ end
37
+ return emails.size == 1 && options[:count] == 1 ? emails.first : emails
38
+ end
39
+
40
+ end
41
+ end
42
+
@@ -0,0 +1,88 @@
1
+ require 'mail'
2
+
3
+ module KNSEmailEndpoint
4
+
5
+ class EmailEndpoint < Kynetx::Endpoint
6
+ attr_reader :conn, :message_state
7
+
8
+ domain :mail
9
+ event :received do |p|
10
+ raise "Missing 'msg' parameter'." unless p[:msg]
11
+ message = p[:msg]
12
+ p[:to] = message.to.join(",")
13
+ p[:from] = message.from.join(",")
14
+ p[:subject] = message.subject
15
+
16
+ p[:unique_id] = message.message_id unless p[:unique_id]
17
+
18
+ if p[:extract_label]
19
+ label = message.delivered_to.match(/\+(.*)@/)[1]
20
+ p[:label] = label.to_s
21
+ end
22
+
23
+ end
24
+
25
+ def before_received
26
+ @message_state = MessageState.new(@conn, params[:msg])
27
+ @message_state.state = :processing
28
+ end
29
+
30
+ directive :delete
31
+ def after_delete(d)
32
+ @message_state.delete
33
+ end
34
+
35
+ directive :reply
36
+ def after_reply(d)
37
+ msg = params[:msg]
38
+ r_msg = Mail.new(msg)
39
+ r_msg.to = msg.from
40
+ r_msg.from = msg.reply_to.nil? ? msg.to : msg.reply_to
41
+ r_msg.subject = d["subject"] ? d["subject"] : "Re: " + msg.subject.to_s
42
+ r_msg.body d["body"] ? d["body"] + msg[:body].to_s : msg.body.to_s
43
+ @sender.deliver!(r_msg) if @sender
44
+ @message_state.state = :replied
45
+
46
+ if d["delete_message"]
47
+ @message_state.delete
48
+ end
49
+ end
50
+
51
+ directive :forward
52
+ def after_forward(d)
53
+ if d["to"] && @sender
54
+ msg = params[:msg]
55
+ f_msg = Mail.new do
56
+ to d["to"]
57
+ from msg.to
58
+ subject d["subject"] ? d["subject"] : "Fwd: " + msg.subject.to_s
59
+ body d["body"] ? d["body"] + msg[:body].to_s : msg.body.to_s
60
+ end
61
+ @sender.deliver!(f_msg)
62
+ @message_state.state = :forwarded
63
+ else
64
+ @message_state.state = :error
65
+ end
66
+
67
+ if d["delete_message"] && status == :forwarded
68
+ @message_state.delete
69
+ end
70
+ end
71
+
72
+ directive :processed
73
+ def after_processed(d)
74
+ @message_state.state = :processed
75
+ end
76
+
77
+ def initialize(conn, opts={}, sender=nil)
78
+ @conn = conn
79
+ @sender = sender
80
+ super(opts)
81
+ end
82
+
83
+ def status
84
+ @message_state.state
85
+ end
86
+
87
+ end
88
+ end