sms_broker 1.0.0
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 +7 -0
- data/.gitignore +21 -0
- data/.rspec +3 -0
- data/Gemfile +9 -0
- data/README.md +56 -0
- data/Rakefile +5 -0
- data/lib/sms_broker/client/base.rb +24 -0
- data/lib/sms_broker/client/nexmo.rb +44 -0
- data/lib/sms_broker/client/response/error.rb +32 -0
- data/lib/sms_broker/client/response/nexmo_error.rb +35 -0
- data/lib/sms_broker/client/response/nexmo_success.rb +37 -0
- data/lib/sms_broker/client/response/success.rb +40 -0
- data/lib/sms_broker/client/response/twilio_error.rb +51 -0
- data/lib/sms_broker/client/response/twilio_success.rb +41 -0
- data/lib/sms_broker/client/twilio.rb +49 -0
- data/lib/sms_broker/configuration.rb +56 -0
- data/lib/sms_broker/exceptions/invalid_service.rb +9 -0
- data/lib/sms_broker/exceptions/invalid_setup.rb +11 -0
- data/lib/sms_broker/message_sender.rb +81 -0
- data/lib/sms_broker/service.rb +50 -0
- data/lib/sms_broker/setup.rb +82 -0
- data/lib/sms_broker/version.rb +5 -0
- data/lib/sms_broker.rb +25 -0
- data/sms_broker.gemspec +29 -0
- data/spec/sms_broker/nexmo_spec.rb +92 -0
- data/spec/sms_broker/setup_spec.rb +161 -0
- data/spec/sms_broker/sms_broker_spec.rb +145 -0
- data/spec/sms_broker/twilio_spec.rb +122 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/nexmo_helpers.rb +150 -0
- metadata +176 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0b28741c2cf164576894b6b876444ae070f7303f
|
4
|
+
data.tar.gz: f0549e93ac7bbe3b87fff76d101a6f06152c192a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fcd96dffdafb77800af5a3798be97beff7ec3a921fcdbc061c0ad1f069f5ea127a976e0dace58624742c5ad510b400fadd866a3d6eba884d22a179d7577ae3c2
|
7
|
+
data.tar.gz: c6b9c850c13c0c43e2cc324b12fb6bf884bda46a3f0ab709a6ca13a06df0412a82341fd7e4e87d07aa0d47dfd100352906f9a685f6bf9a464c342a1c0cdccf10
|
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
.rvmrc
|
7
|
+
.ruby-version
|
8
|
+
.ruby-gemset
|
9
|
+
Gemfile.lock
|
10
|
+
InstalledFiles
|
11
|
+
_yardoc
|
12
|
+
coverage
|
13
|
+
doc/
|
14
|
+
lib/bundler/man
|
15
|
+
pkg
|
16
|
+
rdoc
|
17
|
+
spec/reports
|
18
|
+
spec/support/services_keys.yml
|
19
|
+
test/tmp
|
20
|
+
test/version_tmp
|
21
|
+
tmp
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
Sms broker
|
2
|
+
==========================
|
3
|
+
|
4
|
+
### Usage
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
SmsBroker.setup do |config|
|
8
|
+
config.services ['nexmo', 'twilio']
|
9
|
+
|
10
|
+
config.default_service 'nexmo'
|
11
|
+
|
12
|
+
config.nexmo_setup \
|
13
|
+
key: 'NEXMO_API_KEY',
|
14
|
+
secret: 'NEXMO_API_SECRET',
|
15
|
+
sender_id: 'NEXMO_SENDER_ID',
|
16
|
+
phone_number: 'NEXMO_PHONE_NUMBER'
|
17
|
+
|
18
|
+
config.twilio_setup \
|
19
|
+
auth_token: 'TWILIO_AUTH_TOKEN',
|
20
|
+
account_sid: 'TWILIO_ACCOUNT_SID',
|
21
|
+
sender_id: 'TWILIO_SENDER_ID',
|
22
|
+
phone_number: 'TWILIO_PHONE_NUMBER'
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
#### Basic usage
|
27
|
+
```ruby
|
28
|
+
message = SmsBroker.message('Get paid doing small tasks!').to('441234567890')
|
29
|
+
|
30
|
+
if message.valid?
|
31
|
+
response = message.deliver
|
32
|
+
|
33
|
+
# response.success?
|
34
|
+
# response.serialized
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
#### Specifying the provider
|
39
|
+
```ruby
|
40
|
+
SmsBroker.service(:twilio).message('Get paid doing small tasks!').to('441234567890')
|
41
|
+
```
|
42
|
+
|
43
|
+
###Installation
|
44
|
+
|
45
|
+
Add this line to your application's Gemfile:
|
46
|
+
|
47
|
+
gem 'sms_broker'
|
48
|
+
|
49
|
+
And then execute:
|
50
|
+
|
51
|
+
$ bundle
|
52
|
+
|
53
|
+
### Get in touch
|
54
|
+
|
55
|
+
If you have any questions, write an issue or get in touch dev@streetbees.com
|
56
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module SmsBroker
|
2
|
+
module Client
|
3
|
+
|
4
|
+
class Base
|
5
|
+
|
6
|
+
attr_reader :name,
|
7
|
+
:client,
|
8
|
+
:options,
|
9
|
+
:sender_id,
|
10
|
+
:phone_number
|
11
|
+
|
12
|
+
def initialize(name, client)
|
13
|
+
@name = name
|
14
|
+
@client = client
|
15
|
+
end
|
16
|
+
|
17
|
+
def serialize_number(number)
|
18
|
+
"#{number}".delete("+")
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module SmsBroker
|
2
|
+
module Client
|
3
|
+
|
4
|
+
class Nexmo < Base
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
nexmo_options = options.dup
|
8
|
+
|
9
|
+
auth_options = {
|
10
|
+
key: nexmo_options.delete(:key),
|
11
|
+
secret: nexmo_options.delete(:secret)
|
12
|
+
}
|
13
|
+
|
14
|
+
@sender_id = nexmo_options.delete(:sender_id)
|
15
|
+
@phone_number = nexmo_options.delete(:phone_number)
|
16
|
+
|
17
|
+
super(:nexmo, ::Nexmo::Client.new(auth_options))
|
18
|
+
end
|
19
|
+
|
20
|
+
def send_message(message)
|
21
|
+
response = client.send_message \
|
22
|
+
text: message[:text],
|
23
|
+
from: serialize_number(message[:from]),
|
24
|
+
to: serialize_number(message[:to])
|
25
|
+
|
26
|
+
if success_response?(response)
|
27
|
+
Response::NexmoSuccess.new(response)
|
28
|
+
else
|
29
|
+
Response::NexmoError.new(response)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def success_response?(response)
|
36
|
+
# just looking for the first message,
|
37
|
+
# right now only one message per call
|
38
|
+
response['messages'].length > 0 && response['messages'][0]['status'] == '0'
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SmsBroker
|
2
|
+
module Client
|
3
|
+
module Response
|
4
|
+
|
5
|
+
class Error
|
6
|
+
|
7
|
+
attr_reader :service,
|
8
|
+
:response,
|
9
|
+
:serialized
|
10
|
+
|
11
|
+
def initialize(service, response, serialized = {})
|
12
|
+
@service = service
|
13
|
+
@response = response
|
14
|
+
@serialized = { errors: serialized }
|
15
|
+
end
|
16
|
+
|
17
|
+
def success?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def invalid_sender_id?
|
22
|
+
(@serialized[:errors]['sender_id'] || {}).include?('is invalid')
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'sms_broker/client/response/nexmo_error'
|
32
|
+
require 'sms_broker/client/response/twilio_error'
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module SmsBroker
|
2
|
+
module Client
|
3
|
+
module Response
|
4
|
+
|
5
|
+
class NexmoError < Error
|
6
|
+
|
7
|
+
SENDER_ID_NOT_SUPPORTED = '15'
|
8
|
+
|
9
|
+
def initialize(nexmo_response)
|
10
|
+
super :nexmo, nexmo_response, serialize_error_response(nexmo_response)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def serialize_error_response(nexmo_response)
|
16
|
+
errors = {}.tap do |hash|
|
17
|
+
nexmo_response['messages'].each do |message|
|
18
|
+
hash[message['status']] = [message['error-text']]
|
19
|
+
end
|
20
|
+
|
21
|
+
hash
|
22
|
+
end
|
23
|
+
|
24
|
+
if errors.keys.include?(SENDER_ID_NOT_SUPPORTED)
|
25
|
+
errors['sender_id'] = ['is invalid']
|
26
|
+
end
|
27
|
+
|
28
|
+
errors
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module SmsBroker
|
2
|
+
module Client
|
3
|
+
module Response
|
4
|
+
|
5
|
+
class NexmoSuccess < Success
|
6
|
+
|
7
|
+
def initialize(nexmo_response)
|
8
|
+
super :nexmo, nexmo_response, serialize(nexmo_response)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def serialize(response)
|
14
|
+
single_response = response['messages'][0]
|
15
|
+
|
16
|
+
{
|
17
|
+
to: single_response['to'],
|
18
|
+
from: single_response['from'],
|
19
|
+
message_id: single_response['message-id'],
|
20
|
+
raw: {
|
21
|
+
to: single_response['to'],
|
22
|
+
from: single_response['from'],
|
23
|
+
status: single_response['status'],
|
24
|
+
network: single_response['network'],
|
25
|
+
message_id: single_response['message-id'],
|
26
|
+
client_ref: single_response['client-ref'],
|
27
|
+
remaining_balance: single_response['remaining-balance'],
|
28
|
+
message_price: single_response['message-price']
|
29
|
+
}
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module SmsBroker
|
2
|
+
module Client
|
3
|
+
module Response
|
4
|
+
|
5
|
+
class Success
|
6
|
+
|
7
|
+
attr_reader :raw,
|
8
|
+
:service,
|
9
|
+
:serialized
|
10
|
+
|
11
|
+
def initialize(service, response, serialized)
|
12
|
+
@raw = response
|
13
|
+
@service = service
|
14
|
+
@serialized = serialized
|
15
|
+
end
|
16
|
+
|
17
|
+
def success?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def to
|
22
|
+
serialized[:to]
|
23
|
+
end
|
24
|
+
|
25
|
+
def from
|
26
|
+
serialized[:from]
|
27
|
+
end
|
28
|
+
|
29
|
+
def message_id
|
30
|
+
serialized[:message_id]
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'sms_broker/client/response/nexmo_success'
|
40
|
+
require 'sms_broker/client/response/twilio_success'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module SmsBroker
|
2
|
+
module Client
|
3
|
+
module Response
|
4
|
+
|
5
|
+
class TwilioError < Error
|
6
|
+
|
7
|
+
SENDER_ID_NOT_SUPPORTED = '21212'
|
8
|
+
|
9
|
+
def initialize(twilio_response)
|
10
|
+
super :twilio, twilio_response, serialize(twilio_response)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def serialize(twilio_response)
|
16
|
+
if twilio_response.is_a?(::Twilio::REST::RequestError)
|
17
|
+
serialize_exeception_errors(twilio_response)
|
18
|
+
else
|
19
|
+
serialize_response_error(twilio_response)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def serialize_response_error(response)
|
24
|
+
errors = {
|
25
|
+
"#{response.error_code}" => [response.error_message]
|
26
|
+
}
|
27
|
+
|
28
|
+
if "#{response.error_code}" == SENDER_ID_NOT_SUPPORTED
|
29
|
+
errors['sender_id'] = ['is invalid']
|
30
|
+
end
|
31
|
+
|
32
|
+
errors
|
33
|
+
end
|
34
|
+
|
35
|
+
def serialize_exeception_errors(exception)
|
36
|
+
errors = {
|
37
|
+
"#{exception.code}" => [exception.message]
|
38
|
+
}
|
39
|
+
|
40
|
+
if "#{exception.code}" == SENDER_ID_NOT_SUPPORTED
|
41
|
+
errors['sender_id'] = ['is invalid']
|
42
|
+
end
|
43
|
+
|
44
|
+
errors
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module SmsBroker
|
2
|
+
module Client
|
3
|
+
module Response
|
4
|
+
|
5
|
+
class TwilioSuccess < Success
|
6
|
+
|
7
|
+
def initialize(twilio_response)
|
8
|
+
super :twilio, twilio_response, serialize(twilio_response)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def serialize(response)
|
14
|
+
{
|
15
|
+
to: response.to,
|
16
|
+
from: response.from,
|
17
|
+
message_id: response.sid,
|
18
|
+
raw: {
|
19
|
+
to: response.to,
|
20
|
+
sid: response.sid,
|
21
|
+
uri: response.uri,
|
22
|
+
from: response.from,
|
23
|
+
body: response.body,
|
24
|
+
price: response.price,
|
25
|
+
status: response.status,
|
26
|
+
price_unit: response.price_unit,
|
27
|
+
error_code: response.error_code,
|
28
|
+
account_sid: response.account_sid,
|
29
|
+
api_version: response.api_version,
|
30
|
+
date_created: response.date_created,
|
31
|
+
error_message: response.error_message,
|
32
|
+
messaging_service_sid: response.messaging_service_sid
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module SmsBroker
|
2
|
+
module Client
|
3
|
+
|
4
|
+
class Twilio < Base
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
twilio_options = options.dup
|
8
|
+
|
9
|
+
auth_options = {
|
10
|
+
account_sid: twilio_options.delete(:account_sid),
|
11
|
+
auth_token: twilio_options.delete(:auth_token)
|
12
|
+
}
|
13
|
+
|
14
|
+
@sender_id = twilio_options.delete(:sender_id)
|
15
|
+
@phone_number = twilio_options.delete(:phone_number)
|
16
|
+
|
17
|
+
super \
|
18
|
+
:twilio,
|
19
|
+
::Twilio::REST::Client.new(auth_options[:account_sid], auth_options[:auth_token])
|
20
|
+
end
|
21
|
+
|
22
|
+
def send_message(message)
|
23
|
+
begin
|
24
|
+
response = client.messages.create \
|
25
|
+
body: message[:text],
|
26
|
+
from: serialize_number(message[:from]),
|
27
|
+
to: serialize_number(message[:to])
|
28
|
+
|
29
|
+
if failed_response?(response)
|
30
|
+
Response::TwilioError.new(response)
|
31
|
+
else
|
32
|
+
Response::TwilioSuccess.new(response)
|
33
|
+
end
|
34
|
+
|
35
|
+
rescue ::Twilio::REST::RequestError => exception
|
36
|
+
Response::TwilioError.new(exception)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def failed_response?(response)
|
43
|
+
['undelivered', 'failed'].include?(response.status)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'sms_broker/setup'
|
2
|
+
require 'sms_broker/exceptions/invalid_setup'
|
3
|
+
|
4
|
+
module SmsBroker
|
5
|
+
|
6
|
+
module Configuration
|
7
|
+
|
8
|
+
@@configuration = nil
|
9
|
+
|
10
|
+
def default_service
|
11
|
+
configuration[:default_service]
|
12
|
+
end
|
13
|
+
|
14
|
+
def clear_setup
|
15
|
+
@@configuration = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def configuration
|
19
|
+
exception = \
|
20
|
+
Exceptions::InvalidSetup.new('setup does not exists')
|
21
|
+
|
22
|
+
@@configuration || (raise exception)
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup(&block)
|
26
|
+
setup = Setup.new
|
27
|
+
yield setup if block_given?
|
28
|
+
|
29
|
+
@@configuration = setup.options
|
30
|
+
|
31
|
+
setup
|
32
|
+
end
|
33
|
+
|
34
|
+
def setup!(&block)
|
35
|
+
setup = Setup.new
|
36
|
+
yield setup if block_given?
|
37
|
+
|
38
|
+
unless setup.valid?
|
39
|
+
exception = \
|
40
|
+
Exceptions::InvalidSetup.new('setup is invalid, check exception.errors')
|
41
|
+
|
42
|
+
exception.errors = setup.errors
|
43
|
+
|
44
|
+
raise exception
|
45
|
+
end
|
46
|
+
|
47
|
+
@@configuration = setup.options
|
48
|
+
|
49
|
+
setup
|
50
|
+
end
|
51
|
+
|
52
|
+
extend self
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module SmsBroker
|
2
|
+
|
3
|
+
class MessageSender
|
4
|
+
|
5
|
+
attr_reader :client,
|
6
|
+
:errors
|
7
|
+
|
8
|
+
def initialize(client)
|
9
|
+
@client = client
|
10
|
+
end
|
11
|
+
|
12
|
+
def to(number)
|
13
|
+
@message_to = number
|
14
|
+
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def message(text)
|
19
|
+
@message_text = text
|
20
|
+
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def deliver
|
25
|
+
unless valid?
|
26
|
+
return Client::Response::Error.new(client.name, errors, errors)
|
27
|
+
end
|
28
|
+
|
29
|
+
response = client.send_message(build_message)
|
30
|
+
|
31
|
+
if should_try_again_with_phone_number?(response)
|
32
|
+
return client.send_message(build_message(:phone_number))
|
33
|
+
end
|
34
|
+
|
35
|
+
response
|
36
|
+
end
|
37
|
+
|
38
|
+
def valid?
|
39
|
+
schema = {
|
40
|
+
message: Compel.string.required.max_length(140),
|
41
|
+
to: Compel.string.required
|
42
|
+
}
|
43
|
+
|
44
|
+
object = {
|
45
|
+
message: @message_text,
|
46
|
+
to: @message_to
|
47
|
+
}
|
48
|
+
|
49
|
+
result = Compel.hash.keys(schema).validate(object)
|
50
|
+
|
51
|
+
@errors = result.errors
|
52
|
+
|
53
|
+
result.valid?
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def build_message(from = :sender_id)
|
59
|
+
sender = \
|
60
|
+
if client.sender_id && from == :sender_id
|
61
|
+
client.sender_id
|
62
|
+
else
|
63
|
+
client.phone_number
|
64
|
+
end
|
65
|
+
|
66
|
+
{
|
67
|
+
text: @message_text,
|
68
|
+
from: sender,
|
69
|
+
to: @message_to
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def should_try_again_with_phone_number?(response)
|
74
|
+
response.is_a?(Client::Response::Error) &&
|
75
|
+
response.invalid_sender_id? &&
|
76
|
+
!!client.sender_id
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'sms_broker/message_sender'
|
2
|
+
|
3
|
+
require 'sms_broker/client/base'
|
4
|
+
require 'sms_broker/client/nexmo'
|
5
|
+
require 'sms_broker/client/twilio'
|
6
|
+
|
7
|
+
require 'sms_broker/exceptions/invalid_service'
|
8
|
+
|
9
|
+
module SmsBroker
|
10
|
+
|
11
|
+
CLIENTS = {
|
12
|
+
nexmo: Client::Nexmo,
|
13
|
+
twilio: Client::Twilio
|
14
|
+
}
|
15
|
+
|
16
|
+
class Service
|
17
|
+
|
18
|
+
def self.get(name)
|
19
|
+
options = service_configuration(name)
|
20
|
+
|
21
|
+
result = Service.validate(name, options)
|
22
|
+
|
23
|
+
unless result.valid?
|
24
|
+
raise Exceptions::InvalidService, { name.to_sym => result.errors }
|
25
|
+
end
|
26
|
+
|
27
|
+
new CLIENTS[name.to_sym].new(options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.service_configuration(name)
|
31
|
+
SmsBroker.configuration[:services_setups][name.to_sym]
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.validate(name, options)
|
35
|
+
Setup.service_validation_schemas[name.to_sym].validate(options)
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :client
|
39
|
+
|
40
|
+
def initialize(client)
|
41
|
+
@client = client
|
42
|
+
end
|
43
|
+
|
44
|
+
def message(message)
|
45
|
+
MessageSender.new(client).message(message)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|