unfuddle_my_email 0.1.3
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.
- 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
|