trikle-mail 0.0.5 → 0.0.6
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.
- checksums.yaml +5 -5
- data/bin/trikle-mail +18 -120
- data/lib/triklemailer.rb +98 -0
- metadata +45 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1bc1f6ac3d5593c2b07c79554fd73ac007913e2b8e257d044611328eca81d1c5
|
4
|
+
data.tar.gz: 779eb9f435e07e043f91d5eff45d2092a16076049157dbbbfaa1ee785b99cf5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b9635c1c57938ea53c143458d48587234855f6b3910b1877c3f99b9bbedef48403748289338c38c2344b27e1915eec6806a8a3dd269225be85661d62fcc8eaa
|
7
|
+
data.tar.gz: baee1a420a5bcc5cd1ca280b3caee0aae9a0af0f98ff7d8423f49a7f90ab3e0c32cb639773decd0c3021a28edbf66ebbb21ef5d2c196741608099be655c2568e
|
data/bin/trikle-mail
CHANGED
@@ -3,18 +3,19 @@ require 'commander'
|
|
3
3
|
require 'mandrill'
|
4
4
|
require 'csv'
|
5
5
|
require 'mail'
|
6
|
+
require 'triklemailer'
|
6
7
|
|
7
8
|
class TrikleMail
|
8
9
|
extend Commander::Methods
|
9
10
|
|
10
11
|
program :name, 'Trikle Mail'
|
11
|
-
program :version,
|
12
|
-
program :description, 'Send your bulk email in a trikle
|
12
|
+
program :version, Triklemailer::VERSION
|
13
|
+
program :description, 'Send your bulk email from the cli in a trikle'
|
13
14
|
|
14
|
-
default_command :
|
15
|
+
default_command :smtp
|
15
16
|
|
16
17
|
command :smtp do |c|
|
17
|
-
c.syntax = 'trikle-mail
|
18
|
+
c.syntax = 'trikle-mail csv_of_emails.csv [options]'
|
18
19
|
c.description = 'Send a message to each email in the csv using the template'
|
19
20
|
c.option '--host STRING', String, 'The host of the mail server e.g. <mail.gmail.com>'
|
20
21
|
c.option '--port INTEGER', Integer, 'Port number of the mail server e.g. 587'
|
@@ -28,122 +29,19 @@ class TrikleMail
|
|
28
29
|
c.option '--minutes INTEGER', Integer, 'The number of minutes over which to send the emails'
|
29
30
|
|
30
31
|
c.action do |args, options|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
data_hashes.each do |hash|
|
45
|
-
template = File.read(hash.fetch(:template, options.template)).
|
46
|
-
gsub(/%(?!{)/, '%%') % hash
|
47
|
-
mail = Mail.new do
|
48
|
-
from hash.fetch(:from, options.from)
|
49
|
-
to hash.fetch(:to, hash.fetch(:email) { raise 'no email address found in to column!' })
|
50
|
-
subject hash.fetch(:subject, options.subject)
|
51
|
-
|
52
|
-
if options.html
|
53
|
-
html_part do
|
54
|
-
content_type 'text/html; charset=UTF-8'
|
55
|
-
body template
|
56
|
-
end
|
57
|
-
else
|
58
|
-
text_part do
|
59
|
-
body template
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
mail.delivery_method :smtp, {
|
65
|
-
address: hash.fetch(:host, options.host),
|
66
|
-
port: hash.fetch(:port, options.port),
|
67
|
-
user_name: hash.fetch(:username, options.username),
|
68
|
-
password: hash.fetch(:password, options.password),
|
69
|
-
authentication: :login,
|
70
|
-
enable_starttls_auto: true
|
71
|
-
}
|
72
|
-
|
73
|
-
puts mail.deliver!
|
74
|
-
random_wait_time = rand(0..time_range)
|
75
|
-
puts "waiting #{random_wait_time} seconds"
|
76
|
-
sleep(random_wait_time)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
command :run do |c|
|
82
|
-
c.syntax = 'trikle-mail csv_of_emails.csv [options]'
|
83
|
-
c.description = 'Send a message to each email address in the csv'
|
84
|
-
c.option '--apikey STRING', String, 'The Mandrill API key'
|
85
|
-
c.option '--from_email STRING', String, 'The email address of the sender'
|
86
|
-
c.option '--from_name STRING', String, 'The name of the sender'
|
87
|
-
c.option '--subject STRING', String, 'The subject line of the message'
|
88
|
-
c.option '--template STRING', String, 'The template slug of the message template'
|
89
|
-
c.option '--subaccount STRING', String, 'The mandril subaccount for this batch of email'
|
90
|
-
c.option '--hours INTEGER', Integer, 'The number of hours over which to send the emails'
|
91
|
-
c.option '--minutes INTEGER', Integer, 'The number of minutes over which to send the emails'
|
92
|
-
|
93
|
-
c.action do |args, options|
|
94
|
-
api = Mandrill::API.new(options.apikey)
|
95
|
-
|
96
|
-
title_row, *data_rows = CSV.read(args[0])
|
97
|
-
|
98
|
-
data_hashes = data_rows.map do |row|
|
99
|
-
title_row.map.with_index do |title, index|
|
100
|
-
[title.downcase, row[index]]
|
101
|
-
end.to_h
|
102
|
-
end
|
103
|
-
|
104
|
-
options.default({hours: 0, minutes: 0})
|
105
|
-
time_range = ((options.hours * 60 * 60) + (options.minutes * 60)) /
|
106
|
-
data_hashes.length.to_f
|
107
|
-
|
108
|
-
data_hashes.each do |hash|
|
109
|
-
response = api.messages.send_template(
|
110
|
-
hash['template'] || options.template,
|
111
|
-
[],
|
112
|
-
{
|
113
|
-
subaccount: hash['subaccount'] || options.subaccount,
|
114
|
-
subject: hash['subject'] || options.subject,
|
115
|
-
from_email: hash['from_email'] || options.from_email,
|
116
|
-
from_name: hash['from_name'] || options.from_name,
|
117
|
-
to: [{
|
118
|
-
email: hash['email'],
|
119
|
-
name: hash['name'],
|
120
|
-
type: 'to'
|
121
|
-
}],
|
122
|
-
merge_vars: [{
|
123
|
-
rcpt: hash['email'],
|
124
|
-
vars: hash.map { |key, value| { name: key, content: value } }
|
125
|
-
}]
|
126
|
-
}
|
127
|
-
)
|
128
|
-
TrikleMail.log(response)
|
129
|
-
random_wait_time = rand(0..time_range)
|
130
|
-
puts "waiting #{random_wait_time} seconds"
|
131
|
-
sleep(random_wait_time)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def self.log(json)
|
137
|
-
Array(json).each do |entry|
|
138
|
-
# write to log file
|
139
|
-
File.open('./log.json', 'a') { |f| f.write(JSON.dump(entry) + "\n") }
|
140
|
-
# write nicely to cli
|
141
|
-
puts [
|
142
|
-
entry['status'],
|
143
|
-
'email to',
|
144
|
-
entry['email'],
|
145
|
-
entry['reject_reason'] ? "becuase #{entry['reject_reason']}" : ''
|
146
|
-
].join(' ')
|
32
|
+
recipients = CSV.new(args[0], headers: true).map(&:to_h)
|
33
|
+
sent_file_name = ['sent', options.template.split('.').first].join('_')
|
34
|
+
sent = CSV.new(sent_file_name, headers: true).map { |r| r[:email] }
|
35
|
+
|
36
|
+
mail_options = Triklemailer::Options.new(
|
37
|
+
template: File.read(options.template),
|
38
|
+
template_name: options.template,
|
39
|
+
is_html: options.template.end_with?('html'),
|
40
|
+
from: options.from,
|
41
|
+
subject: options.subject
|
42
|
+
)
|
43
|
+
|
44
|
+
Triklemailer.send_mail(mail_options, recipients, sent)
|
147
45
|
end
|
148
46
|
end
|
149
47
|
end
|
data/lib/triklemailer.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'mail'
|
2
|
+
require 'csv'
|
3
|
+
|
4
|
+
module Triklemailer
|
5
|
+
VERSION = '0.0.6'
|
6
|
+
Options = Struct.new(:template, :template_name, :is_html, :from, :subject, keyword_init: true)
|
7
|
+
|
8
|
+
# It should take:
|
9
|
+
# * A list of emails and data
|
10
|
+
# * SMTP account details
|
11
|
+
# * A template
|
12
|
+
# * SMTP Server details
|
13
|
+
#
|
14
|
+
# and it should send an email to each email address from the csv using
|
15
|
+
# the template provided, filled in with the meta data provided for the
|
16
|
+
# email address. When each email is sent it should record that in a
|
17
|
+
# separate csv called sent_<template-name>.csv. Failures to send a
|
18
|
+
# messages should not be recorded. When sending emails it should check
|
19
|
+
# the sent_<template-name>.csv and if it exists, only send emails to
|
20
|
+
# email address not included in the sent_<template-name>.csv file.
|
21
|
+
#
|
22
|
+
# Read provided csv
|
23
|
+
# Check for sent_ csv
|
24
|
+
# For each email, meta_data in provided csv
|
25
|
+
# If email in sent_ csv
|
26
|
+
# Next email
|
27
|
+
#
|
28
|
+
# Read template file
|
29
|
+
# Apply meta_data to template file
|
30
|
+
# Send email with data from cli or meta data with template
|
31
|
+
# If there was an error sending
|
32
|
+
# Next email
|
33
|
+
# Record email sent to template in sent_ csv
|
34
|
+
#
|
35
|
+
def self.send_mail(options, recipients, sent)
|
36
|
+
cleaned_template = options.template.gsub(/%(?!{)/, '%%')
|
37
|
+
|
38
|
+
recipients.
|
39
|
+
reject { |recipient| sent.include?(recipient[:email]) }.
|
40
|
+
each do |recipient|
|
41
|
+
mail = Mail.new do
|
42
|
+
from recipient.fetch(:from, options.from)
|
43
|
+
to recipient.fetch(:to, recipient.fetch(:email) {
|
44
|
+
raise "Some rows are missing either a 'to' or 'email' column."
|
45
|
+
})
|
46
|
+
subject recipient.fetch(:subject, options.subject)
|
47
|
+
|
48
|
+
if options.is_html
|
49
|
+
html_part do
|
50
|
+
content_type 'text/html; charset=UTF-8'
|
51
|
+
body cleaned_template % recipient
|
52
|
+
end
|
53
|
+
else
|
54
|
+
text_part do
|
55
|
+
body cleaned_template % recipient
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if block_given?
|
61
|
+
yield(mail, recipient)
|
62
|
+
else
|
63
|
+
mail.delivery_method :smtp, {
|
64
|
+
address: recipient.fetch(:host, options.host),
|
65
|
+
port: recipient.fetch(:port, options.port),
|
66
|
+
user_name: recipient.fetch(:username, options.username),
|
67
|
+
password: recipient.fetch(:password, options.password),
|
68
|
+
authentication: :login,
|
69
|
+
enable_starttls_auto: true
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
begin
|
74
|
+
mail.deliver!
|
75
|
+
# might fail in here though...
|
76
|
+
log_sent(
|
77
|
+
recipient.fetch(:to, recipient[:email]),
|
78
|
+
options.template_name)
|
79
|
+
rescue => e
|
80
|
+
puts "Failed to send email to #{recipient[:email]}."
|
81
|
+
next
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def log_sent(email, template)
|
87
|
+
log_file_name = ['sent', template.split('.').first].join('_')
|
88
|
+
|
89
|
+
CSV.open(
|
90
|
+
"./#{log_file_name}.csv",
|
91
|
+
'a',
|
92
|
+
write_headers: true,
|
93
|
+
headers: ['email', 'sent_at']
|
94
|
+
) do |csv|
|
95
|
+
csv << [email, Time.now]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trikle-mail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jasper Lyons
|
@@ -38,6 +38,48 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: mail
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mail
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2'
|
41
83
|
description: Send bulk email in a trickle over mandrill
|
42
84
|
email: jasper.lyons@gmail.com
|
43
85
|
executables:
|
@@ -46,6 +88,7 @@ extensions: []
|
|
46
88
|
extra_rdoc_files: []
|
47
89
|
files:
|
48
90
|
- bin/trikle-mail
|
91
|
+
- lib/triklemailer.rb
|
49
92
|
homepage: ''
|
50
93
|
licenses:
|
51
94
|
- MIT
|
@@ -65,10 +108,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
108
|
- !ruby/object:Gem::Version
|
66
109
|
version: '0'
|
67
110
|
requirements: []
|
68
|
-
|
69
|
-
rubygems_version: 2.5.1
|
111
|
+
rubygems_version: 3.0.3
|
70
112
|
signing_key:
|
71
113
|
specification_version: 4
|
72
114
|
summary: Send a trickle of mail
|
73
115
|
test_files: []
|
74
|
-
has_rdoc:
|