bulkmail 0.1 → 0.2
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/CHANGELOG +12 -0
- data/README +11 -12
- data/Rakefile +1 -2
- data/bin/bulkmail +42 -31
- data/doc/rdoc/classes/Array.html +155 -0
- data/doc/rdoc/classes/BulkMail.html +310 -0
- data/doc/rdoc/classes/String.html +178 -0
- data/doc/rdoc/classes/Symbol.html +155 -0
- data/doc/rdoc/created.rid +1 -0
- data/doc/rdoc/files/CHANGELOG.html +165 -0
- data/doc/rdoc/files/COPYING.html +129 -0
- data/doc/rdoc/files/README.html +160 -0
- data/doc/rdoc/files/lib/bulkmail_rb.html +108 -0
- data/doc/rdoc/fr_class_index.html +30 -0
- data/doc/rdoc/fr_file_index.html +30 -0
- data/doc/rdoc/fr_method_index.html +35 -0
- data/doc/rdoc/index.html +24 -0
- data/doc/rdoc/rdoc-style.css +208 -0
- data/lib/bulkmail.rb +97 -62
- metadata +22 -12
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
3
|
+
<!DOCTYPE html
|
4
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
5
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
6
|
+
|
7
|
+
<!--
|
8
|
+
|
9
|
+
Methods
|
10
|
+
|
11
|
+
-->
|
12
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
13
|
+
<head>
|
14
|
+
<title>Methods</title>
|
15
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
16
|
+
<link rel="stylesheet" href="rdoc-style.css" type="text/css" />
|
17
|
+
<base target="docwin" />
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
<div id="index">
|
21
|
+
<h1 class="section-bar">Methods</h1>
|
22
|
+
<div id="index-entries">
|
23
|
+
<a href="classes/String.html#M000009">email_addr (String)</a><br />
|
24
|
+
<a href="classes/BulkMail.html#M000002">format_headers (BulkMail)</a><br />
|
25
|
+
<a href="classes/BulkMail.html#M000005">send_bulk (BulkMail)</a><br />
|
26
|
+
<a href="classes/BulkMail.html#M000004">send_message (BulkMail)</a><br />
|
27
|
+
<a href="classes/BulkMail.html#M000001">server_settings (BulkMail)</a><br />
|
28
|
+
<a href="classes/BulkMail.html#M000003">smtp (BulkMail)</a><br />
|
29
|
+
<a href="classes/Array.html#M000007">split (Array)</a><br />
|
30
|
+
<a href="classes/String.html#M000008">to_header (String)</a><br />
|
31
|
+
<a href="classes/Symbol.html#M000006">to_header (Symbol)</a><br />
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
</body>
|
35
|
+
</html>
|
data/doc/rdoc/index.html
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
2
|
+
<!DOCTYPE html
|
3
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
|
4
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
|
5
|
+
|
6
|
+
<!--
|
7
|
+
|
8
|
+
Bulkmail Documentation
|
9
|
+
|
10
|
+
-->
|
11
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
12
|
+
<head>
|
13
|
+
<title>Bulkmail Documentation</title>
|
14
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
15
|
+
</head>
|
16
|
+
<frameset rows="20%, 80%">
|
17
|
+
<frameset cols="25%,35%,45%">
|
18
|
+
<frame src="fr_file_index.html" title="Files" name="Files" />
|
19
|
+
<frame src="fr_class_index.html" name="Classes" />
|
20
|
+
<frame src="fr_method_index.html" name="Methods" />
|
21
|
+
</frameset>
|
22
|
+
<frame src="files/README.html" name="docwin" />
|
23
|
+
</frameset>
|
24
|
+
</html>
|
@@ -0,0 +1,208 @@
|
|
1
|
+
|
2
|
+
body {
|
3
|
+
font-family: Verdana,Arial,Helvetica,sans-serif;
|
4
|
+
font-size: 90%;
|
5
|
+
margin: 0;
|
6
|
+
margin-left: 40px;
|
7
|
+
padding: 0;
|
8
|
+
background: white;
|
9
|
+
}
|
10
|
+
|
11
|
+
h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
|
12
|
+
h1 { font-size: 150%; }
|
13
|
+
h2,h3,h4 { margin-top: 1em; }
|
14
|
+
|
15
|
+
a { background: #eef; color: #039; text-decoration: none; }
|
16
|
+
a:hover { background: #039; color: #eef; }
|
17
|
+
|
18
|
+
/* Override the base stylesheet's Anchor inside a table cell */
|
19
|
+
td > a {
|
20
|
+
background: transparent;
|
21
|
+
color: #039;
|
22
|
+
text-decoration: none;
|
23
|
+
}
|
24
|
+
|
25
|
+
/* and inside a section title */
|
26
|
+
.section-title > a {
|
27
|
+
background: transparent;
|
28
|
+
color: #eee;
|
29
|
+
text-decoration: none;
|
30
|
+
}
|
31
|
+
|
32
|
+
/* === Structural elements =================================== */
|
33
|
+
|
34
|
+
div#index {
|
35
|
+
margin: 0;
|
36
|
+
margin-left: -40px;
|
37
|
+
padding: 0;
|
38
|
+
font-size: 90%;
|
39
|
+
}
|
40
|
+
|
41
|
+
|
42
|
+
div#index a {
|
43
|
+
margin-left: 0.7em;
|
44
|
+
}
|
45
|
+
|
46
|
+
div#index .section-bar {
|
47
|
+
margin-left: 0px;
|
48
|
+
padding-left: 0.7em;
|
49
|
+
background: #ccc;
|
50
|
+
font-size: small;
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
div#classHeader, div#fileHeader {
|
55
|
+
width: auto;
|
56
|
+
color: white;
|
57
|
+
padding: 0.5em 1.5em 0.5em 1.5em;
|
58
|
+
margin: 0;
|
59
|
+
margin-left: -40px;
|
60
|
+
border-bottom: 3px solid #006;
|
61
|
+
}
|
62
|
+
|
63
|
+
div#classHeader a, div#fileHeader a {
|
64
|
+
background: inherit;
|
65
|
+
color: white;
|
66
|
+
}
|
67
|
+
|
68
|
+
div#classHeader td, div#fileHeader td {
|
69
|
+
background: inherit;
|
70
|
+
color: white;
|
71
|
+
}
|
72
|
+
|
73
|
+
|
74
|
+
div#fileHeader {
|
75
|
+
background: #057;
|
76
|
+
}
|
77
|
+
|
78
|
+
div#classHeader {
|
79
|
+
background: #048;
|
80
|
+
}
|
81
|
+
|
82
|
+
|
83
|
+
.class-name-in-header {
|
84
|
+
font-size: 180%;
|
85
|
+
font-weight: bold;
|
86
|
+
}
|
87
|
+
|
88
|
+
|
89
|
+
div#bodyContent {
|
90
|
+
padding: 0 1.5em 0 1.5em;
|
91
|
+
}
|
92
|
+
|
93
|
+
div#description {
|
94
|
+
padding: 0.5em 1.5em;
|
95
|
+
background: #efefef;
|
96
|
+
border: 1px dotted #999;
|
97
|
+
}
|
98
|
+
|
99
|
+
div#description h1,h2,h3,h4,h5,h6 {
|
100
|
+
color: #125;;
|
101
|
+
background: transparent;
|
102
|
+
}
|
103
|
+
|
104
|
+
div#validator-badges {
|
105
|
+
text-align: center;
|
106
|
+
}
|
107
|
+
div#validator-badges img { border: 0; }
|
108
|
+
|
109
|
+
div#copyright {
|
110
|
+
color: #333;
|
111
|
+
background: #efefef;
|
112
|
+
font: 0.75em sans-serif;
|
113
|
+
margin-top: 5em;
|
114
|
+
margin-bottom: 0;
|
115
|
+
padding: 0.5em 2em;
|
116
|
+
}
|
117
|
+
|
118
|
+
|
119
|
+
/* === Classes =================================== */
|
120
|
+
|
121
|
+
table.header-table {
|
122
|
+
color: white;
|
123
|
+
font-size: small;
|
124
|
+
}
|
125
|
+
|
126
|
+
.type-note {
|
127
|
+
font-size: small;
|
128
|
+
color: #DEDEDE;
|
129
|
+
}
|
130
|
+
|
131
|
+
.xxsection-bar {
|
132
|
+
background: #eee;
|
133
|
+
color: #333;
|
134
|
+
padding: 3px;
|
135
|
+
}
|
136
|
+
|
137
|
+
.section-bar {
|
138
|
+
color: #333;
|
139
|
+
border-bottom: 1px solid #999;
|
140
|
+
margin-left: -20px;
|
141
|
+
}
|
142
|
+
|
143
|
+
|
144
|
+
.section-title {
|
145
|
+
background: #79a;
|
146
|
+
color: #eee;
|
147
|
+
padding: 3px;
|
148
|
+
margin-top: 2em;
|
149
|
+
margin-left: -30px;
|
150
|
+
border: 1px solid #999;
|
151
|
+
}
|
152
|
+
|
153
|
+
.top-aligned-row { vertical-align: top }
|
154
|
+
.bottom-aligned-row { vertical-align: bottom }
|
155
|
+
|
156
|
+
/* --- Context section classes ----------------------- */
|
157
|
+
|
158
|
+
.context-row { }
|
159
|
+
.context-item-name { font-family: monospace; font-weight: bold; color: black; }
|
160
|
+
.context-item-value { font-size: small; color: #448; }
|
161
|
+
.context-item-desc { color: #333; padding-left: 2em; }
|
162
|
+
|
163
|
+
/* --- Method classes -------------------------- */
|
164
|
+
.method-detail {
|
165
|
+
background: #efefef;
|
166
|
+
padding: 0;
|
167
|
+
margin-top: 0.5em;
|
168
|
+
margin-bottom: 1em;
|
169
|
+
border: 1px dotted #ccc;
|
170
|
+
}
|
171
|
+
.method-heading {
|
172
|
+
color: black;
|
173
|
+
background: #ccc;
|
174
|
+
border-bottom: 1px solid #666;
|
175
|
+
padding: 0.2em 0.5em 0 0.5em;
|
176
|
+
}
|
177
|
+
.method-signature { color: black; background: inherit; }
|
178
|
+
.method-name { font-weight: bold; }
|
179
|
+
.method-args { font-style: italic; }
|
180
|
+
.method-description { padding: 0 0.5em 0 0.5em; }
|
181
|
+
|
182
|
+
/* --- Source code sections -------------------- */
|
183
|
+
|
184
|
+
a.source-toggle { font-size: 90%; }
|
185
|
+
div.method-source-code {
|
186
|
+
background: #262626;
|
187
|
+
color: #ffdead;
|
188
|
+
margin: 1em;
|
189
|
+
padding: 0.5em;
|
190
|
+
border: 1px dashed #999;
|
191
|
+
overflow: hidden;
|
192
|
+
}
|
193
|
+
|
194
|
+
div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
195
|
+
|
196
|
+
/* --- Ruby keyword styles --------------------- */
|
197
|
+
|
198
|
+
.standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
|
199
|
+
|
200
|
+
.ruby-constant { color: #7fffd4; background: transparent; }
|
201
|
+
.ruby-keyword { color: #00ffff; background: transparent; }
|
202
|
+
.ruby-ivar { color: #eedd82; background: transparent; }
|
203
|
+
.ruby-operator { color: #00ffee; background: transparent; }
|
204
|
+
.ruby-identifier { color: #ffdead; background: transparent; }
|
205
|
+
.ruby-node { color: #ffa07a; background: transparent; }
|
206
|
+
.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
|
207
|
+
.ruby-regexp { color: #ffa07a; background: transparent; }
|
208
|
+
.ruby-value { color: #7fffd4; background: transparent; }
|
data/lib/bulkmail.rb
CHANGED
@@ -1,75 +1,110 @@
|
|
1
|
-
require '
|
1
|
+
require 'net/smtp'
|
2
2
|
|
3
|
-
#
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
3
|
+
# Symbol extensions
|
4
|
+
class Symbol
|
5
|
+
# Returns the symbol as a header string
|
6
|
+
def to_header
|
7
|
+
to_s.split('_').map{|w| w.capitalize}.join('-')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class String
|
12
|
+
# Returns self
|
13
|
+
def to_header
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the address part of a recipient
|
18
|
+
def email_addr
|
19
|
+
s = strip
|
20
|
+
return nil if s.empty?
|
21
|
+
s =~ /<(.*)>/ ? $1 : s
|
15
22
|
end
|
23
|
+
end
|
16
24
|
|
17
|
-
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@subject = opts[:subject]
|
26
|
-
@from = opts[:from]
|
27
|
-
@content_type = opts[:content_type]
|
28
|
-
@content = load_content(opts[:content_file])
|
29
|
-
@progress = 0
|
30
|
-
@delivered = 0
|
31
|
-
@total = @recipients.size
|
32
|
-
@errors = []
|
33
|
-
@thread = Thread.new {process}
|
25
|
+
class Array
|
26
|
+
# Splits the array into smaller arrays of fixed size
|
27
|
+
def split(batch)
|
28
|
+
result = []
|
29
|
+
idx = 0
|
30
|
+
while idx < size
|
31
|
+
result << slice(idx, batch)
|
32
|
+
idx += batch
|
34
33
|
end
|
34
|
+
result
|
35
|
+
end
|
36
|
+
end
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
38
|
+
class BulkMail
|
39
|
+
@@server_settings = {
|
40
|
+
:host => 'localhost',
|
41
|
+
:port => 25,
|
42
|
+
:helo_domain => nil,
|
43
|
+
:user => nil,
|
44
|
+
:password => nil,
|
45
|
+
:auth => :plain
|
46
|
+
}
|
47
|
+
|
48
|
+
# Returns the server_settings hash
|
49
|
+
def self.server_settings
|
50
|
+
@@server_settings
|
51
|
+
end
|
52
|
+
|
53
|
+
LINE_BREAK = "\r\n"
|
54
|
+
HEADER_FMT = "%s: %s\r\n".freeze
|
55
|
+
|
56
|
+
# Returns the message headers formatted
|
57
|
+
def self.format_headers(headers)
|
58
|
+
headers.inject('') do |m, kv|
|
59
|
+
m << (HEADER_FMT % [kv[0].to_header, kv[1]])
|
49
60
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
63
|
-
@progress = 1
|
61
|
+
end
|
62
|
+
|
63
|
+
# Starts an SMTP session and yields
|
64
|
+
def self.smtp
|
65
|
+
Net::SMTP.start(
|
66
|
+
@@server_settings[:host],
|
67
|
+
@@server_settings[:port],
|
68
|
+
@@server_settings[:helo_domain] || @@server_settings[:host],
|
69
|
+
@@server_settings[:user],
|
70
|
+
@@server_settings[:password],
|
71
|
+
@@server_settings[:auth]) do |smtp|
|
72
|
+
yield smtp
|
64
73
|
end
|
65
74
|
end
|
66
75
|
|
67
|
-
|
68
|
-
|
76
|
+
# Sends an email
|
77
|
+
def self.send_message(smtp, from, to, body, headers = nil)
|
78
|
+
if headers && !headers.empty?
|
79
|
+
msg = format_headers(headers)
|
80
|
+
else
|
81
|
+
msg = LINE_BREAK
|
82
|
+
end
|
83
|
+
msg << LINE_BREAK
|
84
|
+
msg << body
|
85
|
+
smtp.send_message msg, from, to
|
69
86
|
end
|
70
87
|
|
71
|
-
|
72
|
-
|
88
|
+
# Sends bulk mail to a large number of recipients.
|
89
|
+
def self.send_bulk(sender, recipients, body, headers)
|
90
|
+
failed = []
|
91
|
+
succeeded = []
|
92
|
+
headers[:from] = headers[:to] = sender
|
93
|
+
recipients = recipients.map {|r| r.email_addr}.compact
|
94
|
+
recipients.split(10).each do |list|
|
95
|
+
begin
|
96
|
+
smtp {|s| send_message(s, sender.email_addr, list, body, headers)}
|
97
|
+
succeeded.concat list
|
98
|
+
puts "Sent message to #{list.join(', ')}"
|
99
|
+
rescue => e
|
100
|
+
failed.concat list
|
101
|
+
puts "Failed to send to #{list.join(', ')}: #{e.message}\r\n#{e.backtrace.first}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
# log failed addresses
|
105
|
+
unless failed.empty?
|
106
|
+
File.open('failed.bulkmail', 'w') {|f| failed.each {|l|f.puts l}}
|
107
|
+
end
|
108
|
+
puts "Message to sent to #{succeeded.size} of #{recipients.size} recipients"
|
73
109
|
end
|
74
110
|
end
|
75
|
-
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: bulkmail
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: "0.
|
7
|
-
date: 2006-10-
|
6
|
+
version: "0.2"
|
7
|
+
date: 2006-10-27 00:00:00 +02:00
|
8
8
|
summary: Ruby bulk-mailer.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -33,6 +33,24 @@ files:
|
|
33
33
|
- README
|
34
34
|
- Rakefile
|
35
35
|
- bin/bulkmail
|
36
|
+
- doc/rdoc
|
37
|
+
- doc/rdoc/classes
|
38
|
+
- doc/rdoc/created.rid
|
39
|
+
- doc/rdoc/files
|
40
|
+
- doc/rdoc/fr_class_index.html
|
41
|
+
- doc/rdoc/fr_file_index.html
|
42
|
+
- doc/rdoc/fr_method_index.html
|
43
|
+
- doc/rdoc/index.html
|
44
|
+
- doc/rdoc/rdoc-style.css
|
45
|
+
- doc/rdoc/classes/Array.html
|
46
|
+
- doc/rdoc/classes/BulkMail.html
|
47
|
+
- doc/rdoc/classes/String.html
|
48
|
+
- doc/rdoc/classes/Symbol.html
|
49
|
+
- doc/rdoc/files/CHANGELOG.html
|
50
|
+
- doc/rdoc/files/COPYING.html
|
51
|
+
- doc/rdoc/files/lib
|
52
|
+
- doc/rdoc/files/README.html
|
53
|
+
- doc/rdoc/files/lib/bulkmail_rb.html
|
36
54
|
- lib/bulkmail.rb
|
37
55
|
- CHANGELOG
|
38
56
|
test_files: []
|
@@ -61,13 +79,5 @@ extensions: []
|
|
61
79
|
|
62
80
|
requirements: []
|
63
81
|
|
64
|
-
dependencies:
|
65
|
-
|
66
|
-
name: actionmailer
|
67
|
-
version_requirement:
|
68
|
-
version_requirements: !ruby/object:Gem::Version::Requirement
|
69
|
-
requirements:
|
70
|
-
- - ">"
|
71
|
-
- !ruby/object:Gem::Version
|
72
|
-
version: 0.0.0
|
73
|
-
version:
|
82
|
+
dependencies: []
|
83
|
+
|