unfuddle_my_email 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +22 -0
- data/VERSION.yml +4 -0
- data/bin/unfuddle_my_email +12 -0
- data/configuration-sample.yml +13 -0
- data/lib/net/pop3_ssl.rb +1000 -0
- data/lib/unfuddle_my_email/configuration.rb +29 -0
- data/lib/unfuddle_my_email/email_ticket.rb +47 -0
- data/lib/unfuddle_my_email/fetcher.rb +42 -0
- data/lib/unfuddle_my_email/poster.rb +44 -0
- data/lib/unfuddle_my_email/runner.rb +26 -0
- data/lib/unfuddle_my_email.rb +9 -0
- data/test/configuration_test.rb +29 -0
- data/test/email_ticket_test.rb +27 -0
- data/test/fetcher_test.rb +41 -0
- data/test/poster_test.rb +38 -0
- data/test/test_configuration.yml +12 -0
- metadata +75 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module UnfuddleMyEmail
|
4
|
+
# Configuration loads the given +path+ as a YAML file and enables
|
5
|
+
# method-like access to configuration information.
|
6
|
+
#
|
7
|
+
# The expected format of the YAML file is:
|
8
|
+
#
|
9
|
+
# configuration_key: configuration_value
|
10
|
+
# configuration_second_key: configuration_value
|
11
|
+
#
|
12
|
+
# etc.
|
13
|
+
#
|
14
|
+
# Example usage:
|
15
|
+
#
|
16
|
+
# configuration = Configuration.new("path/to/yaml.yml")
|
17
|
+
# configuration.key # => value
|
18
|
+
class Configuration
|
19
|
+
attr_accessor :settings
|
20
|
+
|
21
|
+
def initialize(path)
|
22
|
+
@settings = YAML.load_file(path)
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing(method_symbol, *args)
|
26
|
+
@settings[method_symbol.to_s]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'builder'
|
2
|
+
|
3
|
+
module UnfuddleMyEmail
|
4
|
+
# Create an XML formatted ticket.
|
5
|
+
#
|
6
|
+
# Expects an object that responds to the following methods:
|
7
|
+
#
|
8
|
+
# * +subject+: The summary for the ticket
|
9
|
+
# * +from+: An array of authors
|
10
|
+
# * +body+: Will be added the the ticket description
|
11
|
+
class EmailTicket
|
12
|
+
attr_accessor :message
|
13
|
+
|
14
|
+
def initialize(message)
|
15
|
+
@message = message
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
# Returns an XML representation of an Unfuddle ticket.
|
20
|
+
def to_xml
|
21
|
+
builder = Builder::XmlMarkup.new
|
22
|
+
return xml = builder.ticket { |ticket|
|
23
|
+
ticket.priority "1"
|
24
|
+
ticket.summary subject
|
25
|
+
ticket.description description
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def subject
|
32
|
+
@message.subject
|
33
|
+
end
|
34
|
+
|
35
|
+
def description
|
36
|
+
"From: #{from}\n\n#{body}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def from
|
40
|
+
@message.from.join(',')
|
41
|
+
end
|
42
|
+
|
43
|
+
def body
|
44
|
+
@message.body
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'net/pop3_ssl'
|
2
|
+
require 'tmail'
|
3
|
+
|
4
|
+
module UnfuddleMyEmail
|
5
|
+
# Fetcher iterates through all emails in the given POP3 mailbox and
|
6
|
+
# yields each one, optionally deleting the message after yield. The
|
7
|
+
# message yielded is an instance of TMail::Mail.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# fetch = Fetcher.new('pop.example.com',995,true,'username','password',false)
|
12
|
+
# fetcher.each do |message|
|
13
|
+
# puts message.subject
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # Enumerable is included so you can use those features (except for sort,min,max) as well:
|
17
|
+
# fetcher.to_a # => [#<TMail::Mail instance>]
|
18
|
+
class Fetcher
|
19
|
+
def initialize(server, port, ssl, username, password, delete=false)
|
20
|
+
@pop3_server = server
|
21
|
+
@pop3_port = port
|
22
|
+
@pop3_ssl = ssl
|
23
|
+
@pop3_username = username
|
24
|
+
@pop3_password = password
|
25
|
+
@pop3_delete = delete
|
26
|
+
end
|
27
|
+
|
28
|
+
def each
|
29
|
+
if @pop3_ssl
|
30
|
+
Net::POP3.enable_ssl(OpenSSL::SSL::VERIFY_NONE)
|
31
|
+
end
|
32
|
+
Net::POP3.start(@pop3_server, @pop3_port, @pop3_username, @pop3_password) do |pop|
|
33
|
+
pop.each_mail { |message|
|
34
|
+
mail_item = TMail::Mail.parse(message.pop)
|
35
|
+
yield mail_item
|
36
|
+
message.delete if @pop3_delete
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
include Enumerable
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
|
3
|
+
module UnfuddleMyEmail
|
4
|
+
class Poster
|
5
|
+
# Return a new Net::HTTP session for the given +domain+. Optionally,
|
6
|
+
# enable +ssl+.
|
7
|
+
def self.http(domain, ssl = false)
|
8
|
+
http = Net::HTTP.new(domain, ssl ? 443 : 80)
|
9
|
+
if ssl
|
10
|
+
http.use_ssl = true
|
11
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
12
|
+
end
|
13
|
+
return http
|
14
|
+
end
|
15
|
+
|
16
|
+
# Post XML content with basic authentication.
|
17
|
+
#
|
18
|
+
# Arguments:
|
19
|
+
# * +domain+: The domain name to use
|
20
|
+
# * +uri+: The url minuse the domain
|
21
|
+
# * +ssl+: Set true to enable SSL.
|
22
|
+
# * +username+: The username to use for Basic Auth.
|
23
|
+
# * +password+: The password to use for Basic Auth.
|
24
|
+
# * +content+: The content to POST via HTTP.
|
25
|
+
#
|
26
|
+
# Example:
|
27
|
+
#
|
28
|
+
# Poster::post('example.com','/tickets',false,'john','doe','<data>value</data>')
|
29
|
+
#
|
30
|
+
# Raises error on anything but success or redirect.
|
31
|
+
def self.post(domain, uri, ssl, username, password, content)
|
32
|
+
request = Net::HTTP::Post.new(uri, {'Content-type' => 'application/xml'})
|
33
|
+
request.basic_auth username, password
|
34
|
+
request.body = content
|
35
|
+
response = http(domain, ssl).request(request)
|
36
|
+
case response
|
37
|
+
when Net::HTTPSuccess, Net::HTTPRedirection
|
38
|
+
return true
|
39
|
+
else
|
40
|
+
response.error!
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module UnfuddleMyEmail
|
2
|
+
class Runner
|
3
|
+
def initialize(configuration_path)
|
4
|
+
@options = Configuration.new(configuration_path)
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
fetcher = Fetcher.new(@options.pop3_server, @options.pop3_port, @options.pop3_ssl, @options.pop3_username, @options.pop3_password, @options.pop3_delete)
|
9
|
+
fetcher.each do |message|
|
10
|
+
ticket = EmailTicket.new(message)
|
11
|
+
p "Posting ticket: #{ticket.message.subject}"
|
12
|
+
Poster::post(domain, url, @options.unfuddle_ssl, @options.unfuddle_username, @options.unfuddle_password, ticket.to_xml)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def domain
|
19
|
+
"#{@options.unfuddle_subdomain}.unfuddle.com"
|
20
|
+
end
|
21
|
+
|
22
|
+
def url
|
23
|
+
"#{@options.unfuddle_api_url}/projects/#{@options.unfuddle_project_id}/tickets"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'shoulda'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'unfuddle_my_email'
|
4
|
+
|
5
|
+
class ConfigurationTest < Test::Unit::TestCase
|
6
|
+
include UnfuddleMyEmail
|
7
|
+
|
8
|
+
context "a Configuration instance" do
|
9
|
+
setup do
|
10
|
+
@configuration = Configuration.new("test/test_configuration.yml")
|
11
|
+
end
|
12
|
+
|
13
|
+
should "assign Hash from configuration to settings accessor" do
|
14
|
+
assert @configuration.settings.is_a?(Hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
context "for each key in configuration.yml file" do
|
18
|
+
setup do
|
19
|
+
@data = YAML.load_file("test/test_configuration.yml")
|
20
|
+
end
|
21
|
+
|
22
|
+
should "return value for key when called as a method" do
|
23
|
+
@data.each_pair do |key,value|
|
24
|
+
assert_equal value, @configuration.send(key)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'flexmock/test_unit'
|
2
|
+
require 'shoulda'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'unfuddle_my_email'
|
5
|
+
|
6
|
+
class EmailTicketTest < Test::Unit::TestCase
|
7
|
+
include UnfuddleMyEmail
|
8
|
+
|
9
|
+
context "a EmailTicket instance" do
|
10
|
+
setup do
|
11
|
+
message = flexmock('message',
|
12
|
+
:subject => 'Hello, world',
|
13
|
+
:from => ['Matt Haley <matt@example.com>'],
|
14
|
+
:body => 'Message body.')
|
15
|
+
@email_ticket = EmailTicket.new(message)
|
16
|
+
end
|
17
|
+
|
18
|
+
should "assign a message" do
|
19
|
+
assert @email_ticket.message = "a message"
|
20
|
+
end
|
21
|
+
|
22
|
+
should "return xml" do
|
23
|
+
assert_equal "<ticket><priority>1</priority><summary>Hello, world</summary><description>From: Matt Haley <matt@example.com>\n\nMessage body.</description></ticket>",
|
24
|
+
@email_ticket.to_xml
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'flexmock/test_unit'
|
2
|
+
require 'net/pop3_ssl'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'test/unit'
|
5
|
+
require 'tmail'
|
6
|
+
require 'unfuddle_my_email'
|
7
|
+
|
8
|
+
class FetcherTest < Test::Unit::TestCase
|
9
|
+
include UnfuddleMyEmail
|
10
|
+
|
11
|
+
context "a Fetcher instance" do
|
12
|
+
setup do
|
13
|
+
@fetcher = Fetcher.new('pop.example.com', 110, false, 'user', 'password')
|
14
|
+
|
15
|
+
@message = flexmock('message', :test => true)
|
16
|
+
@message.should_receive(:pop).and_return(
|
17
|
+
"From: john@example.com\n" +
|
18
|
+
"Subject: Test subject\n\n" +
|
19
|
+
"This is a test.\n.\n"
|
20
|
+
)
|
21
|
+
|
22
|
+
@pop3_int = flexmock('pop3_int')
|
23
|
+
@pop3_int.should_receive(:each_mail).and_yield(@message)
|
24
|
+
|
25
|
+
@pop3_lib = flexmock(Net::POP3)
|
26
|
+
@pop3_lib.should_receive(:start).and_yield(@pop3_int)
|
27
|
+
|
28
|
+
@tmail_lib = flexmock(TMail::Mail)
|
29
|
+
@tmail_lib.should_receive(:parse).and_return(@message)
|
30
|
+
end
|
31
|
+
|
32
|
+
should "yield each message" do
|
33
|
+
success = false
|
34
|
+
@fetcher.each do |message|
|
35
|
+
success = message.test
|
36
|
+
end
|
37
|
+
assert success
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
data/test/poster_test.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'unfuddle_my_email'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'flexmock/test_unit'
|
5
|
+
require 'shoulda'
|
6
|
+
|
7
|
+
class PosterTest < Test::Unit::TestCase
|
8
|
+
include UnfuddleMyEmail
|
9
|
+
|
10
|
+
context "a Poster class" do
|
11
|
+
should "return a Net::HTTP instance when http called" do
|
12
|
+
http = Poster.http('example.com')
|
13
|
+
assert http.is_a?(Net::HTTP)
|
14
|
+
end
|
15
|
+
|
16
|
+
should "set SSL mode" do
|
17
|
+
http = Poster.http('example.com', ssl = true)
|
18
|
+
assert http.use_ssl?
|
19
|
+
end
|
20
|
+
|
21
|
+
should "post content" do
|
22
|
+
post_i = flexmock('post_i')
|
23
|
+
post_i.should_receive(:basic_auth).with(String, String)
|
24
|
+
post_i.should_receive(:body=).with(String)
|
25
|
+
post = flexmock('post')
|
26
|
+
post.should_receive(:new).with(String).and_return(post_i)
|
27
|
+
|
28
|
+
http_request = flexmock('request')
|
29
|
+
http_request.should_receive(:request).and_return(Net::HTTPSuccess.new('','',''))
|
30
|
+
|
31
|
+
http_lib = flexmock(Net::HTTP)
|
32
|
+
http_lib.should_receive(:new).and_return(http_request)
|
33
|
+
http_lib.should_receive(:post).and_return(post)
|
34
|
+
|
35
|
+
assert Poster.post('example.com','/', false, 'user','password','test content')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
unfuddle_subdomain: 'mysubdomain'
|
2
|
+
unfuddle_username: 'username'
|
3
|
+
unfuddle_password: 'password'
|
4
|
+
unfuddle_ssl: true
|
5
|
+
unfuddle_project_id: 1234
|
6
|
+
unfuddle_api_url: '/api/v1/'
|
7
|
+
pop3_server: 'pop.example.com'
|
8
|
+
pop3_port: 110
|
9
|
+
pop3_username: 'username'
|
10
|
+
pop3_password: 'password'
|
11
|
+
pop3_delete: false
|
12
|
+
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: unfuddle_my_email
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matt Haley
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-25 00:00:00 -07:00
|
13
|
+
default_executable: unfuddle_my_email
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Post emails from POP server as Unfuddle Tickets.
|
17
|
+
email: matt@smajn.net
|
18
|
+
executables:
|
19
|
+
- unfuddle_my_email
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.rdoc
|
25
|
+
files:
|
26
|
+
- LICENSE
|
27
|
+
- VERSION.yml
|
28
|
+
- bin/unfuddle_my_email
|
29
|
+
- configuration-sample.yml
|
30
|
+
- lib/net/pop3_ssl.rb
|
31
|
+
- lib/unfuddle_my_email.rb
|
32
|
+
- lib/unfuddle_my_email/configuration.rb
|
33
|
+
- lib/unfuddle_my_email/email_ticket.rb
|
34
|
+
- lib/unfuddle_my_email/fetcher.rb
|
35
|
+
- lib/unfuddle_my_email/poster.rb
|
36
|
+
- lib/unfuddle_my_email/runner.rb
|
37
|
+
- test/configuration_test.rb
|
38
|
+
- test/email_ticket_test.rb
|
39
|
+
- test/fetcher_test.rb
|
40
|
+
- test/poster_test.rb
|
41
|
+
- test/test_configuration.yml
|
42
|
+
- README.rdoc
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://github.com/smajn/unfuddle_my_email
|
45
|
+
licenses: []
|
46
|
+
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options:
|
49
|
+
- --charset=UTF-8
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project: mhaley
|
67
|
+
rubygems_version: 1.3.5
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: Post emails from POP server as Unfuddle Tickets.
|
71
|
+
test_files:
|
72
|
+
- test/poster_test.rb
|
73
|
+
- test/configuration_test.rb
|
74
|
+
- test/fetcher_test.rb
|
75
|
+
- test/email_ticket_test.rb
|