talktome 1.0.0 → 1.3.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d6ec4ba471da7a770b79a04a0f30cd58514f0d43b72d66b15074f2fa984887bd
4
- data.tar.gz: 8a58f1a1e7af3440a8066e11b2fa44e4aa606c595c5b9d9e6952dd0b22d6fb97
3
+ metadata.gz: 59ff5a2d705d895d7b3e03e7b6d6a9df13e6a818df6198abb238b6b2b13f34a1
4
+ data.tar.gz: 8390299781fe6800b7e0e7b68fb5d0d504b4d90ee84c8c6452a1703b24ce2ad6
5
5
  SHA512:
6
- metadata.gz: 7340d04e04977b3d1a4a5def53d02b141a108e67514886f57d0d22dc87dfe340fe3a52d8f4552e7bd074229ba841256bb8006bfc900485b48cd8f0aa86522b39
7
- data.tar.gz: 961f6e4931782c5b0f0e732c9ab3a5ea0578efe76e0346bbd8782f3b21a3a8ecd58731b51e3d3dd2e32ccb0c2d9ef48f3081f1e2f810722fbb6320348deef796
6
+ metadata.gz: 60ad688912efaefcdc60847da49b07aaadf07fdfaed1036b4905db407ff7d97c3d190ae41f8d7b70537ddfd2a7cd2dd711aa2cd1f346f48c92f6e5cac94a9686
7
+ data.tar.gz: a05b855ab7e15f29b39f8d42eee139d193788c876077e7ff0a3fd3819bb38314005a1eced8ba83c7c0d919319cb61a0adcf945e44e00847e142f1282031bebd3
data/README.md CHANGED
@@ -94,12 +94,19 @@ TALKTOME_EMAIL_DEFAULT_FROM default From: to use for email sending
94
94
  TALKTOME_EMAIL_DEFAULT_REPLYTO default Reply-To: to use for email sending
95
95
  TALKTOME_EMAIL_DEFAULT_TO default To: to use for email sending
96
96
 
97
+ TALKTOME_EMAIL_SUBJECT Set the subject of the default "contact us" email
98
+ TALKTOME_EMAIL_FOOTER Set the footer of the default "contact us" email
99
+
100
+ TALKTOME_LAYOUTS_FOLDER Set the folder to use for messaging layouts
101
+
97
102
  TALKTOME_SMTP_ADDRESS host address for smtp sending
98
103
  TALKTOME_SMTP_PORT port of smtp server to use
99
104
  TALKTOME_SMTP_DOMAIN sending domain
100
105
  TALKTOME_SMTP_USER user for smtp authentication
101
106
  TALKTOME_SMTP_PASSWORD password for smtp authentication
102
107
  TALKTOME_SMTP_STARTTLS_AUTO true or false (see ruby Mail library)
108
+
109
+ TALKTOME_BEARER_SECRET secret for the webapi, to let send emails to anyone
103
110
  ```
104
111
 
105
112
  ## Hacking Talktome
data/lib/talktome.rb CHANGED
@@ -26,7 +26,7 @@ module Talktome
26
26
  def set_env(which, value, &bl)
27
27
  old, ENV[which] = ENV[which], value
28
28
  bl.call.tap{
29
- ENV[which] = old unless old.nil?
29
+ ENV[which] = old
30
30
  }
31
31
  end
32
32
  module_function :set_env
@@ -78,6 +78,10 @@ module Talktome
78
78
  }
79
79
  }
80
80
 
81
+ if layouts_folder = ENV['TALKTOME_LAYOUTS_FOLDER']
82
+ options[:layouts] = Path(layouts_folder)
83
+ end
84
+
81
85
  options
82
86
  end
83
87
  module_function :auto_options
data/lib/talktome/app.rb CHANGED
@@ -13,21 +13,26 @@ module Talktome
13
13
 
14
14
  set :raise_errors, true
15
15
  set :show_exceptions, false
16
+ set :talktome, Talktome::Client::Local.new(ROOT_FOLDER/'templates')
16
17
 
17
18
  VALIDATION_SCHEMA = ::Finitio.system(<<~FIO)
18
19
  @import finitio/data
19
20
  Email = String(s | s =~ /^[^@]+@[^@]+$/ )
20
21
  {
22
+ to :? Email
21
23
  reply_to :? Email
22
24
  ... : .Object
23
25
  }
24
26
  FIO
25
27
 
26
- TALKTOME = Talktome::Client::Local.new(ROOT_FOLDER/'templates')
27
-
28
- post %r{/([a-z-]+)/} do |action|
28
+ post %r{/([a-z-]+([\/][a-z-]+)*)/} do |action, _|
29
29
  begin
30
- TALKTOME.talktome(action, {}, info, [:email]){|email|
30
+ as_array = info.map{|k,v| {'key' => k.capitalize, 'value' => v}}
31
+ subject = Talktome.env('TALKTOME_EMAIL_SUBJECT', 'Someone wants to reach you!')
32
+ footer = Talktome.env('TALKTOME_EMAIL_FOOTER', "Truly yours,\n
33
+ Sent by [Enspirit.be](https://enspirit.be/), contact us if you need help with any IT task.")
34
+ user = load_user_from_info!
35
+ settings.talktome.talktome(action, user, info.merge(allvars: as_array, subject: subject, footer: footer), [:email]){|email|
31
36
  email.reply_to = info[:reply_to] if info.has_key?(:reply_to)
32
37
  }
33
38
  [ 200, { "Content-Type" => "text/plain"}, ["Ok"] ]
@@ -35,8 +40,10 @@ module Talktome
35
40
  fail!("Invalid data")
36
41
  rescue Finitio::Error => ex
37
42
  fail!(ex.message)
38
- rescue ::Talktome::InvalidEmailError => ex
43
+ rescue ::Talktome::InvalidEmailError
39
44
  fail!("Invalid email address")
45
+ rescue ::Talktome::TemplateNotFoundError
46
+ fail!("No such template", 404)
40
47
  end
41
48
  end
42
49
 
@@ -48,8 +55,19 @@ module Talktome
48
55
  }
49
56
  end
50
57
 
51
- def fail!(message)
52
- [ 400, { "Content-Type" => "text/plain"}, [message] ]
58
+ def load_user_from_info!
59
+ if to = info[:to]
60
+ secret = Talktome.env('TALKTOME_BEARER_SECRET')
61
+ fail!("Missing secret", 400) unless secret
62
+ fail!("Invalid secret", 401) unless "Bearer #{secret}" == env["HTTP_AUTHORIZATION"]
63
+ { email: info[:to] }
64
+ else
65
+ {}
66
+ end
67
+ end
68
+
69
+ def fail!(message, status = 400)
70
+ halt([ status, { "Content-Type" => "text/plain"}, [message] ])
53
71
  end
54
72
 
55
73
  def not_a_robot!(info)
@@ -17,7 +17,7 @@ module Talktome
17
17
 
18
18
  def load_message!(identifier, strategies)
19
19
  folder = self.folder/identifier
20
- raise InvalidMessageError, "No such message `#{identifier}`" unless folder.exists?
20
+ raise TemplateNotFoundError, "No such message `#{identifier}`" unless folder.exists?
21
21
  raise InvalidMessageError, "Message `#{identifier}` should be a folder" unless folder.directory?
22
22
  strategies.each do |s|
23
23
  if (file = folder.glob("#{s}.*").first) && file.file?
@@ -33,7 +33,7 @@ module Talktome
33
33
  end
34
34
 
35
35
  def templater(strategy)
36
- return nil unless tpl_folder = options[:templates]
36
+ return nil unless tpl_folder = options[:layouts] || options[:templates]
37
37
  ->(message, src, ctype) {
38
38
  if (file = tpl_folder/"#{strategy}.#{ctype}").file?
39
39
  data = { metadata: message.metadata, yield: src }
@@ -2,4 +2,5 @@ module Talktome
2
2
  class Error < StandardError; end
3
3
  class InvalidMessageError < Error; end
4
4
  class InvalidEmailError < Error; end
5
+ class TemplateNotFoundError < Error; end
5
6
  end
@@ -1,8 +1,8 @@
1
1
  module Talktome
2
2
  module Version
3
3
  MAJOR = 1
4
- MINOR = 0
5
- TINY = 0
4
+ MINOR = 3
5
+ TINY = 1
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
8
8
  end
data/spec/app/test_app.rb CHANGED
@@ -9,6 +9,7 @@ module Talktome
9
9
  }
10
10
 
11
11
  before(:each) do
12
+ Talktome::App.set :talktome, Talktome::Client::Local.new(Path.dir.parent/'fixtures')
12
13
  ENV['TALKTOME_EMAIL_DEFAULT_TO'] = "to@talktome.com"
13
14
  ENV['TALKTOME_EMAIL_DEFAULT_FROM'] = "from@talktome.com"
14
15
  Mail::TestMailer.deliveries.clear
@@ -19,13 +20,47 @@ module Talktome
19
20
  it 'works' do
20
21
  post "/contact-us/", {
21
22
  reply_to: 'hello@visitor.com',
22
- message: 'Hello from visitor'
23
+ message: 'Hello from visitor',
24
+ key: 'value',
23
25
  }.to_json, { "CONTENT_TYPE" => "application/json" }
24
26
  expect(last_response).to be_ok
25
27
  expect(Mail::TestMailer.deliveries.length).to eql(1)
26
28
  expect(Mail::TestMailer.deliveries.first.to).to eql(["to@talktome.com"])
27
29
  expect(Mail::TestMailer.deliveries.first.from).to eql(["from@talktome.com"])
28
30
  expect(Mail::TestMailer.deliveries.first.subject).to eql("Someone wants to reach you!")
31
+ expect(Mail::TestMailer.deliveries.first.html_part.body).to include("<li>Key: value</li>")
32
+ expect(Mail::TestMailer.deliveries.first.html_part.body).to include("Truly yours")
33
+ end
34
+
35
+ it 'allows to use environment variable to tune the subject and the footer' do
36
+ Talktome.set_env('TALKTOME_EMAIL_SUBJECT', "Subject from environment") do
37
+ Talktome.set_env('TALKTOME_EMAIL_FOOTER', "Footer from environment") do
38
+ post "/contact-us/", {
39
+ reply_to: 'info@domain.com',
40
+ message: 'This is the message.'
41
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
42
+ expect(last_response).to be_ok
43
+ expect(Mail::TestMailer.deliveries.length).to eql(1)
44
+ expect(Mail::TestMailer.deliveries.first.subject).to eql("Subject from environment")
45
+ expect(Mail::TestMailer.deliveries.first.html_part.body).to include("Footer from environment")
46
+ end
47
+ end
48
+ end
49
+
50
+ it 'allows to use a token authentification to bypass default security measures, for e.g. passing the :to' do
51
+ Talktome.set_env('TALKTOME_BEARER_SECRET', "Some secret") do
52
+ header 'Authorization', 'Bearer Some secret'
53
+ post "/contact-us/", {
54
+ to: 'hello@visitor.com',
55
+ reply_to: 'hello@visitor.com',
56
+ message: 'Hello from visitor',
57
+ key: 'value',
58
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
59
+ expect(last_response).to be_ok
60
+ expect(Mail::TestMailer.deliveries.length).to eql(1)
61
+ expect(Mail::TestMailer.deliveries.first.to).to eql(["hello@visitor.com"])
62
+ expect(Mail::TestMailer.deliveries.first.from).to eql(["from@talktome.com"])
63
+ end
29
64
  end
30
65
 
31
66
  it 'detects invalid emails' do
@@ -53,6 +88,48 @@ module Talktome
53
88
  expect(Mail::TestMailer.deliveries.length).to eql(0)
54
89
  end
55
90
 
91
+ it 'forbids usage of :to unless a secret is provided' do
92
+ post "/contact-us/", {
93
+ to: 'hello@visitor.com',
94
+ reply_to: 'hello@visitor.com',
95
+ message: 'Hello from visitor',
96
+ key: 'value',
97
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
98
+ expect(last_response.status).to eql(400)
99
+ expect(Mail::TestMailer.deliveries.length).to eql(0)
100
+ end
101
+
102
+ it 'does not allow setting the :to without a valid AUTH token' do
103
+ Talktome.set_env('TALKTOME_BEARER_SECRET', "Invalid secret") do
104
+ post "/contact-us/", {
105
+ to: 'hello@visitor.com',
106
+ reply_to: 'hello@visitor.com',
107
+ message: 'Hello from visitor',
108
+ key: 'value',
109
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
110
+ expect(last_response.status).to eql(401)
111
+ expect(Mail::TestMailer.deliveries.length).to eql(0)
112
+ end
113
+ end
114
+
115
+ it 'requires a valid Email for :to' do
116
+ post "/contact-us/", {
117
+ to: nil,
118
+ reply_to: 'hello@visitor.com',
119
+ message: 'Hello from visitor',
120
+ key: 'value',
121
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
122
+ expect(last_response.status).to eql(400)
123
+
124
+ post "/contact-us/", {
125
+ to: "notavalidemail",
126
+ reply_to: 'hello@visitor.com',
127
+ message: 'Hello from visitor',
128
+ key: 'value',
129
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
130
+ expect(last_response.status).to eql(400)
131
+ end
132
+
56
133
  end
57
134
 
58
135
  context 'POST /contact-us/, regarding the Reply-To' do
@@ -86,5 +163,38 @@ module Talktome
86
163
  end
87
164
  end
88
165
 
166
+ context 'POST /multi-lingual/en/' do
167
+
168
+ it 'works' do
169
+ post "/multi-lingual/en/", {
170
+ reply_to: 'hello@visitor.com',
171
+ message: 'Hello from visitor',
172
+ key: 'value',
173
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
174
+ expect(last_response.status).to eql(200)
175
+ expect(last_response).to be_ok
176
+ expect(Mail::TestMailer.deliveries.length).to eql(1)
177
+ expect(Mail::TestMailer.deliveries.first.to).to eql(["to@talktome.com"])
178
+ expect(Mail::TestMailer.deliveries.first.from).to eql(["from@talktome.com"])
179
+ expect(Mail::TestMailer.deliveries.first.subject).to eql("Someone wants to reach you!")
180
+ expect(Mail::TestMailer.deliveries.first.html_part.body).to include("<li>Key: value</li>")
181
+ expect(Mail::TestMailer.deliveries.first.html_part.body).to include("Truly yours")
182
+ end
183
+ end
184
+
185
+ context 'POST /xxx when the template does not exist' do
186
+
187
+ it 'return a 404 error when the template doesn\'t exist' do
188
+ post "/multi-lingual/fr/", {
189
+ reply_to: 'hello@visitor.com',
190
+ message: 'Hello from visitor',
191
+ key: 'value',
192
+ }.to_json, { "CONTENT_TYPE" => "application/json" }
193
+
194
+ expect(last_response.status).to eql(404)
195
+ expect(last_response.body).to match(/No such template/)
196
+ end
197
+ end
198
+
89
199
  end
90
200
  end
@@ -29,7 +29,7 @@ module Talktome
29
29
  strategy.clear!
30
30
  }
31
31
 
32
- context "without templates" do
32
+ context "without layouts" do
33
33
  let(:options) {
34
34
  {}
35
35
  }
@@ -41,10 +41,10 @@ module Talktome
41
41
  end
42
42
  end
43
43
 
44
- context "with templates" do
44
+ context "with layouts under the :layouts option key" do
45
45
  let(:options) {
46
46
  {
47
- templates: Path.dir/"../fixtures/templates"
47
+ layouts: Path.dir/"../fixtures/layouts"
48
48
  }
49
49
  }
50
50
 
@@ -63,6 +63,20 @@ module Talktome
63
63
  end
64
64
  end
65
65
 
66
+ context "with layouts under the :templates option key (backward compatibility)" do
67
+ let(:options) {
68
+ {
69
+ templates: Path.dir/"../fixtures/layouts"
70
+ }
71
+ }
72
+
73
+ it 'sends email when requested' do
74
+ client.talktome("welcome", user, tpldata, [:email])
75
+ expect(strategy.last.message.to_html).to eql("<html><title>Hello Test user</title><body><h1>Hello Test user</h1>\n\n<p>Welcome to this email example!</p>\n\n<h3>Test user</h3>\n</body></html>\n")
76
+ end
77
+
78
+ end
79
+
66
80
  end
67
81
  end
68
82
  end
@@ -0,0 +1,13 @@
1
+ ---
2
+ subject: |-
3
+ {{subject}}
4
+ ---
5
+ Hello,
6
+
7
+ This person is trying to reach you:
8
+
9
+ {{#allvars}}
10
+ * {{key}}: {{value}}
11
+ {{/allvars}}
12
+
13
+ {{footer}}
File without changes
@@ -0,0 +1,13 @@
1
+ ---
2
+ subject: |-
3
+ {{subject}}
4
+ ---
5
+ Hello,
6
+
7
+ This person is trying to reach you:
8
+
9
+ {{#allvars}}
10
+ * {{key}}: {{value}}
11
+ {{/allvars}}
12
+
13
+ {{footer}}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: talktome
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernard Lambeau
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-30 00:00:00.000000000 Z
11
+ date: 2021-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -138,16 +138,22 @@ dependencies:
138
138
  name: finitio
139
139
  requirement: !ruby/object:Gem::Requirement
140
140
  requirements:
141
- - - "~>"
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: 0.10.0
144
+ - - "<"
142
145
  - !ruby/object:Gem::Version
143
- version: 0.8.0
146
+ version: 0.11.0
144
147
  type: :runtime
145
148
  prerelease: false
146
149
  version_requirements: !ruby/object:Gem::Requirement
147
150
  requirements:
148
- - - "~>"
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: 0.10.0
154
+ - - "<"
149
155
  - !ruby/object:Gem::Version
150
- version: 0.8.0
156
+ version: 0.11.0
151
157
  - !ruby/object:Gem::Dependency
152
158
  name: rack-robustness
153
159
  requirement: !ruby/object:Gem::Requirement
@@ -185,7 +191,9 @@ files:
185
191
  - lib/talktome/version.rb
186
192
  - spec/app/test_app.rb
187
193
  - spec/client/test_local.rb
188
- - spec/fixtures/templates/email.html
194
+ - spec/fixtures/contact-us/email.md
195
+ - spec/fixtures/layouts/email.html
196
+ - spec/fixtures/multi-lingual/en/email.md
189
197
  - spec/fixtures/welcome/email.md
190
198
  - spec/fixtures/welcome/footer.mustache
191
199
  - spec/message/test_initialize.rb
@@ -214,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
214
222
  - !ruby/object:Gem::Version
215
223
  version: '0'
216
224
  requirements: []
217
- rubygems_version: 3.0.8
225
+ rubygems_version: 3.2.15
218
226
  signing_key:
219
227
  specification_version: 4
220
228
  summary: Talktome helps you talk to users by email, messaging, sms, etc.