trikle-mail 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +5 -5
  2. data/bin/trikle-mail +18 -120
  3. data/lib/triklemailer.rb +98 -0
  4. metadata +45 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a2d157ac21d41c5b4950c97d6fbce4d15c951fc1
4
- data.tar.gz: dfe235800e9add4a2e42d87586c91f27af1b4343
2
+ SHA256:
3
+ metadata.gz: 1bc1f6ac3d5593c2b07c79554fd73ac007913e2b8e257d044611328eca81d1c5
4
+ data.tar.gz: 779eb9f435e07e043f91d5eff45d2092a16076049157dbbbfaa1ee785b99cf5b
5
5
  SHA512:
6
- metadata.gz: 01145d358fd1aca7133654e41773ac3be5547314fa46642ac725d6f059bdd30db4060cf324a16a3802700e40b0138032a2873c8c9b5cc2bcae8976bc00d6fa7b
7
- data.tar.gz: 7d2e1a1b879315855b9ae375e94ed980adbf65b4c686cb173a5d3787075d0ca787e676c2ecc071553ce7977240f4474a323f2082ed9c9043bc24802e4f0f625d
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, '0.0.0'
12
- program :description, 'Send your bulk email in a trikle over the mandril api'
12
+ program :version, Triklemailer::VERSION
13
+ program :description, 'Send your bulk email from the cli in a trikle'
13
14
 
14
- default_command :run
15
+ default_command :smtp
15
16
 
16
17
  command :smtp do |c|
17
- c.syntax = 'trikle-mail smtp csv_of_emails.csv [options]'
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
- title_row, *data_rows = CSV.read(args[0])
32
-
33
- data_hashes = data_rows.map do |row|
34
- title_row.map.with_index do |title, index|
35
- [title.downcase.to_sym, row[index]]
36
- end.to_h
37
- end
38
-
39
- options.default({hours: 0, minutes: 0, html: false})
40
-
41
- time_range = ((options.hours * 60 * 60) + (options.minutes * 60)) /
42
- data_hashes.length.to_f
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
@@ -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.5
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
- rubyforge_project:
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: