stealth-messagebird 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a1228659f9b45371d5a9ccf1abaf8e6a6df5f3a4fff7a3a49ec7412e28459870
4
+ data.tar.gz: d10bcc20c96acf5aa4950ba62aaab8ebba6c0b39030d549144f6252f70416626
5
+ SHA512:
6
+ metadata.gz: 520c7678bc8d73f64c487e1badad84438e58dae307782f09305ca3bc74df92ad58bfe07c2fc8670b67d56b5975f5f7720251ec524200a46e07951057d6300bf1
7
+ data.tar.gz: c9eaeae10966f14f6eb943511fe5b865414a96e549c21fd5dbf84858d1eedd189d07e27ceb8235fdf4993dcee95c93eb797b67016d9189df04be9209e11777a3
@@ -0,0 +1,60 @@
1
+ # Ruby CircleCI 2.0 configuration file
2
+ #
3
+ # Check https://circleci.com/docs/2.0/language-ruby/ for more details
4
+ #
5
+ version: 2
6
+ jobs:
7
+ build:
8
+ docker:
9
+ # specify the version you desire here
10
+ - image: circleci/ruby:2.4.1-node-browsers
11
+ environment:
12
+ STEALTH_ENV: test
13
+
14
+ # Specify service dependencies here if necessary
15
+ # CircleCI maintains a library of pre-built images
16
+ # documented at https://circleci.com/docs/2.0/circleci-images/
17
+ # - image: circleci/postgres:9.4
18
+
19
+ working_directory: ~/repo
20
+
21
+ steps:
22
+ - checkout
23
+
24
+ # Download and cache dependencies
25
+ - restore_cache:
26
+ keys:
27
+ - v1-dependencies-{{ checksum "Gemfile.lock" }}
28
+ # fallback to using the latest cache if no exact match is found
29
+ - v1-dependencies-
30
+
31
+ - run:
32
+ name: install dependencies
33
+ command: |
34
+ bundle install --jobs=4 --retry=3 --path vendor/bundle
35
+
36
+ - save_cache:
37
+ paths:
38
+ - ./vendor/bundle
39
+ key: v1-dependencies-{{ checksum "Gemfile.lock" }}
40
+
41
+ # run tests!
42
+ - run:
43
+ name: run tests
44
+ command: |
45
+ mkdir /tmp/test-results
46
+ TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
47
+
48
+ bundle exec rspec --format progress \
49
+ --format RspecJunitFormatter \
50
+ --out /tmp/test-results/rspec.xml \
51
+ --format progress \
52
+ -- \
53
+ $TEST_FILES
54
+
55
+ # collect reports
56
+ - store_test_results:
57
+ path: /tmp/test-results
58
+ - store_artifacts:
59
+ path: /tmp/test-results
60
+ destination: test-results
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ # please add general patterns to your global ignore list
2
+ # see https://github.com/github/gitignore#readme
3
+ .DS_STORE
4
+ *.swp
5
+ *.rbc
6
+ *.sass-cache
7
+ /pkg
8
+ /doc/api
9
+ /coverage
10
+ .yardoc
11
+ doc/
12
+ *.gem
13
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'stealth', '>= 2.0.0.beta'
4
+
5
+ gemspec
6
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Emilie Morissette
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # Messagebird WhatsApp Driver for Stealth
2
+
3
+ The [Stealth](https://github.com/hellostealth/stealth) WhatsApp driver adds the ability to build your bot using [Messagebird](https://www.messagebird.com/en/solutions/whatsapp-api) service.
4
+
5
+ Messagebird allow you to use your [own mobile number](https://support.messagebird.com/hc/en-us/articles/360000244558-How-to-pick-a-number-for-WhatsApp-Business) for WhatsApp. So no geographic restrictions compare to virtual mobile numbers (VMNs).
6
+
7
+ First thing first, create a Messagebird account and install your [WhatsApp Business channel](https://support.messagebird.com/hc/en-us/articles/360000258437-WhatsApp-Business-step-by-step-onboarding). This gem is not supporting the Messagebird Sandbox, since their Sandbox has limitations.
8
+
9
+ ## WhatsApp Supported Reply Types
10
+
11
+ * Text
12
+ * Image
13
+ * Audio
14
+ * Video/GIF (receive only)
15
+ * File
16
+ * Delay
17
+
18
+ * Location
19
+
20
+ In your replies files
21
+ ```
22
+ - reply_type: location
23
+ latitude: 41.69352000000001
24
+ longitude: 44.801473999999985
25
+ ```
26
+
27
+ More info here: https://developers.messagebird.com/quickstarts/whatsapp-deepdive/
28
+
29
+ ## Configure Messagebird with your Stealth bot
30
+ Once your WhatsApp Business Channel is active, you will receive a `channel_id`.
31
+ To visualize your channel_id, go to `Channels` in the sidebar of your Messagebird Dashboard.
32
+ You will need your Live API Key as well, go to `Developers` in the sidebar of your Messagebird Dashboard.
33
+
34
+ In `services.yml`
35
+ ```
36
+ messagebird:
37
+ access_key: <%= ENV['MESSAGEBIRD_ACCESS_KEY'] %>
38
+ channel_id: <%= ENV['MESSAGEBIRD_CHANNEL_ID'] %>
39
+ ```
40
+
41
+ ## Setup your Webhook
42
+ Create your webhook so that Messagebird can communicate with your app when receiving a message from a user.
43
+ Subscribe to `message.created` event only.
44
+
45
+ ```
46
+ curl -X POST "https://conversations.messagebird.com/v1/webhooks/" \
47
+ -H "Authorization: AccessKey YOUR_ACCESS_KEY" \
48
+ -H "Content-Type: application/json" \
49
+ -d '{
50
+ "events": ["message.created"],
51
+ "channelId": "YOUR_CHANNEL_ID",
52
+ "url": "https://1cfd8wc098r0.ngrok.io/incoming/messagebird"
53
+ }'
54
+ ```
55
+ When you provide your local ngrok URL to a messaging service, you will have to add /incoming/messagebird. More infos on [Stealth Repo](https://github.com/hellostealth/stealth/wiki/Local-Development).
56
+
57
+ For more infos on webhooks, follow the [Messagebird instructions](https://developers.messagebird.com/api/conversations/#create-webhook).
58
+
59
+ ## current_message
60
+ When calling the `current_message` method in your Stealth bot, you can retrieve the following informations:
61
+ ```
62
+ #<Stealth::Services::Messagebird::MessagebirdServiceMessage:0x00009ceg7cef4cd3
63
+ @attachments=[],
64
+ @conversation_id="1cdeee16bd432a987c167f67b611056f",
65
+ @display_name="Emilie",
66
+ @first_name="",
67
+ @last_name="",
68
+ @location={},
69
+ @message="Yo",
70
+ @messagebird_id="9881a12f6hhe4g41b9935hh4d299c01f",
71
+ @platform="whatsapp",
72
+ @read={},
73
+ @sender_id="33320110909",
74
+ @service="messagebird",
75
+ @target_id="098882ge-b745-1f12-a02f-4c1239da7d01",
76
+ @timestamp="2021-04-13T12:23:16Z">
77
+ ```
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,4 @@
1
+ require 'pry'
2
+ require 'json'
3
+ require 'stealth/services/messagebird/version'
4
+ require 'stealth/services/messagebird/client'
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ require 'messagebird'
3
+ require 'stealth/services/messagebird/message_handler'
4
+ require 'stealth/services/messagebird/messagebird_service_message'
5
+ require 'stealth/services/messagebird/reply_handler'
6
+ require 'stealth/services/messagebird/setup'
7
+
8
+ module Stealth
9
+ module Services
10
+ module Messagebird
11
+ class Client < Stealth::Services::BaseClient
12
+ attr_reader :messagebird_client, :reply
13
+
14
+ def initialize(reply:)
15
+ @reply = reply
16
+ access_key = Stealth.config.messagebird.access_key
17
+ @messagebird_client = MessageBird::Client.new(access_key)
18
+ end
19
+
20
+ def transmit
21
+ # Don't transmit anything for delays
22
+ return true if reply.blank?
23
+
24
+ response = messagebird_client.send_conversation_message(reply[:from], reply[:to], reply)
25
+
26
+ if response.status == "failed" || response.status == "rejected"
27
+ case response.error.code
28
+ when 301 # Message failed to send
29
+ raise Stealth::Errors::UserOptOut
30
+ when 302 # Contact is not registered on WhatsApp
31
+ raise Stealth::Errors::UserOptOut
32
+ when 470 # Outside the support window for freeform messages
33
+ raise Stealth::Errors::UserOptOut
34
+ when 2 # Request not allowed
35
+ raise Stealth::Errors::UserOptOut
36
+ when 25 # Not enough balance
37
+ raise Stealth::Errors::UserOptOut
38
+ else
39
+ raise
40
+ end
41
+ end
42
+
43
+ message = "Transmitting. Response: #{response.status}: "
44
+ if response.status == "failed" || response.status == "rejected"
45
+ message += response.error.description
46
+ end
47
+ Stealth::Logger.l(topic: "messagebird", message: message)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+ module Stealth
3
+ module Services
4
+ module Messagebird
5
+ class MessageHandler < Stealth::Services::BaseMessageHandler
6
+ attr_reader :service_message, :params, :headers
7
+
8
+ def initialize(params:, headers:)
9
+ @params = params
10
+ @headers = headers
11
+ end
12
+
13
+ def coordinate
14
+ if params.dig('message', 'direction') == 'received'
15
+ Stealth::Services::HandleMessageJob.perform_async(
16
+ 'messagebird',
17
+ params,
18
+ headers
19
+ )
20
+ end
21
+ # Relay our acceptance
22
+ [200, 'OK']
23
+ end
24
+
25
+ def process
26
+ @service_message = MessagebirdServiceMessage.new(service: 'messagebird')
27
+ service_message.sender_id = params['contact']['msisdn'].to_s
28
+ service_message.target_id = params['message']['channelId']
29
+ service_message.timestamp = params['message']['createdDatetime']
30
+ service_message.message = params['message']['content']['text']
31
+ service_message.conversation_id = params['message']['conversationId']
32
+ service_message.messagebird_id = params['contact']['id']
33
+ service_message.display_name = params['contact']['displayName']
34
+ service_message.first_name = params['contact']['firstName']
35
+ service_message.last_name = params['contact']['lastName']
36
+ service_message.platform = params['message']['platform']
37
+
38
+ if params['message']['type'] == 'image'
39
+ service_message.attachments = [{
40
+ type: params['message']['type'],
41
+ url: params['message']['content']['image']['url']
42
+ }]
43
+ elsif params['message']['type'] == 'video'
44
+ service_message.attachments = [{
45
+ type: params['message']['type'],
46
+ url: params['message']['content']['video']['url']
47
+ }]
48
+ elsif params['message']['type'] == 'audio'
49
+ service_message.attachments = [{
50
+ type: params['message']['type'],
51
+ url: params['message']['content']['audio']['url']
52
+ }]
53
+ elsif params['message']['type'] == 'file'
54
+ service_message.attachments = [{
55
+ type: params['message']['type'],
56
+ location: params['message']['content']['file']['url']
57
+ }]
58
+ elsif params['message']['type'] == 'location'
59
+ service_message.location = [{
60
+ type: params['message']['type'],
61
+ location: params['message']['content']['location']
62
+ }]
63
+ end
64
+
65
+ service_message
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,15 @@
1
+ module Stealth
2
+ module Services
3
+ module Messagebird
4
+ class MessagebirdServiceMessage < Stealth::ServiceMessage
5
+ attr_accessor :conversation_id,
6
+ :messagebird_id,
7
+ :platform,
8
+ :display_name,
9
+ :first_name,
10
+ :last_name
11
+
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+ module Stealth
3
+ module Services
4
+ module Messagebird
5
+ class ReplyHandler < Stealth::Services::BaseReplyHandler
6
+
7
+ attr_reader :recipient_id, :reply
8
+
9
+ def initialize(recipient_id: nil, reply: nil)
10
+ @recipient_id = recipient_id
11
+ @reply = reply
12
+ end
13
+
14
+ def text
15
+ check_text_length
16
+
17
+ translated_reply = reply['text']
18
+
19
+ suggestions = generate_suggestions(suggestions: reply['suggestions'])
20
+ buttons = generate_buttons(buttons: reply['buttons'])
21
+
22
+ if suggestions.present?
23
+ translated_reply = [
24
+ translated_reply,
25
+ I18n.t(
26
+ "stealth.messagebird.respond_with_a_number",
27
+ default: "Respond with a number:"
28
+ )
29
+ ].join("\n\n")
30
+
31
+ suggestions.each_with_index do |suggestion, i|
32
+ translated_reply = [
33
+ translated_reply,
34
+ I18n.t(
35
+ "stealth.messagebird.number_option",
36
+ number: i + 1,
37
+ suggestion: suggestion,
38
+ default: "%{number} for %{suggestion}"
39
+ )
40
+ ].join("\n")
41
+ end
42
+ end
43
+
44
+ if buttons.present?
45
+ buttons.each do |button|
46
+ translated_reply = [
47
+ translated_reply,
48
+ button
49
+ ].join("\n\n")
50
+ end
51
+ end
52
+
53
+ # format_response({ body: translated_reply })
54
+ format_response(type: 'text', content: { text: translated_reply })
55
+ end
56
+
57
+ def image
58
+ check_text_length
59
+ format_response(type: 'image', content: { image: { caption: reply['text'], url: reply['image_url'] } })
60
+ end
61
+
62
+ def audio
63
+ check_text_length
64
+
65
+ format_response(type: 'audio', content: { audio: { caption: reply['text'], url: reply['audio_url'] } })
66
+ end
67
+
68
+ def video
69
+ check_text_length
70
+
71
+ format_response(type: 'video', content: { video: { caption: reply['text'], url: reply['video_url'] } })
72
+ end
73
+
74
+ def file
75
+ check_text_length
76
+
77
+ format_response(type: 'file', content: { file: { caption: reply['text'], url: reply['file_url'] } })
78
+ end
79
+
80
+ def location
81
+ check_text_length
82
+
83
+ format_response(type: 'location', content: { location: { latitude: reply['latitude'], longitude: reply['longitude'] } })
84
+ end
85
+
86
+ def delay
87
+
88
+ end
89
+
90
+ private
91
+
92
+ def check_text_length
93
+ if reply['text'].present? && reply['text'].size > 1600
94
+ raise(ArgumentError, "Text messages must be 1600 characters or less.")
95
+ end
96
+ end
97
+
98
+ def format_response(response)
99
+ sender_info = {
100
+ from: Stealth.config.messagebird.channel_id.to_s,
101
+ to: recipient_id.to_s
102
+ }
103
+ response.merge(sender_info)
104
+ end
105
+
106
+ def generate_suggestions(suggestions:)
107
+ return if suggestions.blank?
108
+
109
+ mf = suggestions.collect do |suggestion|
110
+ suggestion['text']
111
+ end.compact
112
+ end
113
+
114
+ def generate_buttons(buttons:)
115
+ return if buttons.blank?
116
+
117
+ sms_buttons = buttons.map do |button|
118
+ case button['type']
119
+ when 'url'
120
+ "#{button['text']}: #{button['url']}"
121
+ when 'payload'
122
+ I18n.t(
123
+ "stealth.messagebird.text_option",
124
+ text: button['text'],
125
+ option: button['payload'],
126
+ default: "For %{text}: Text %{option}"
127
+ )
128
+ when 'call'
129
+ "#{button['text']}: #{button['phone_number']}"
130
+ end
131
+ end.compact
132
+
133
+ sms_buttons
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ require 'stealth/services/messagebird/client'
3
+
4
+ module Stealth
5
+ module Services
6
+ module Messagebird
7
+
8
+ class Setup
9
+
10
+ class << self
11
+ def trigger
12
+ Stealth::Logger.l(topic: "messagebird", message: "There is no setup needed!")
13
+ end
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stealth
4
+ module Services
5
+ module Messagebird
6
+ module Version
7
+ def self.version
8
+ File.read(File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'VERSION')).strip
9
+ end
10
+ end
11
+
12
+ VERSION = Version.version
13
+ end
14
+ end
15
+ end
@@ -0,0 +1 @@
1
+ require 'stealth/messagebird'
@@ -0,0 +1,14 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+
5
+ require 'stealth'
6
+ require 'stealth-messagebird'
7
+
8
+ # Requires supporting files with custom matchers and macros, etc,
9
+ # in ./support/ and its subdirectories.
10
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
11
+
12
+ RSpec.configure do |config|
13
+
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
4
+
5
+ describe "Stealth::Services::Messagebird::Version" do
6
+
7
+ let(:version_in_file) { File.read(File.join(File.dirname(__FILE__), '..', 'VERSION')).strip }
8
+
9
+ it "should return the current gem version" do
10
+ expect(Stealth::Services::Messagebird::Version.version).to eq version_in_file
11
+ end
12
+
13
+ it "should return the current gem version via a constant" do
14
+ expect(Stealth::Services::Messagebird::VERSION).to eq version_in_file
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
2
+
3
+ version = File.read(File.join(File.dirname(__FILE__), 'VERSION')).strip
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'stealth-messagebird'
7
+ s.summary = 'Stealth MessageBird Whatsapp driver'
8
+ s.description = 'MessageBird Whatsapp driver for Stealth.'
9
+ s.homepage = 'https://github.com/emorissettegregoire/stealth-messagebird'
10
+ s.licenses = ['MIT']
11
+ s.version = version
12
+ s.author = 'Emilie Morissette'
13
+ s.email = 'emorissettegregoire@gmail.com'
14
+
15
+ s.add_dependency 'stealth', '>= 2.0.0.beta'
16
+ s.add_dependency 'messagebird-rest', '1.4.2'
17
+
18
+ s.add_development_dependency 'rspec', '~> 3.6'
19
+ s.add_development_dependency 'rspec_junit_formatter', '~> 0.3'
20
+ s.add_development_dependency 'rack-test', '~> 1.1'
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
25
+ s.require_paths = ['lib']
26
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stealth-messagebird
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Emilie Morissette
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-09-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: stealth
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.0.beta
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.0.beta
27
+ - !ruby/object:Gem::Dependency
28
+ name: messagebird-rest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.4.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.4.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.6'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec_junit_formatter
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rack-test
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.1'
83
+ description: MessageBird Whatsapp driver for Stealth.
84
+ email: emorissettegregoire@gmail.com
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - ".circleci/config.yml"
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - LICENSE
93
+ - README.md
94
+ - VERSION
95
+ - lib/stealth-messagebird.rb
96
+ - lib/stealth/messagebird.rb
97
+ - lib/stealth/services/messagebird/client.rb
98
+ - lib/stealth/services/messagebird/message_handler.rb
99
+ - lib/stealth/services/messagebird/messagebird_service_message.rb
100
+ - lib/stealth/services/messagebird/reply_handler.rb
101
+ - lib/stealth/services/messagebird/setup.rb
102
+ - lib/stealth/services/messagebird/version.rb
103
+ - spec/spec_helper.rb
104
+ - spec/version_spec.rb
105
+ - stealth-messagebird.gemspec
106
+ homepage: https://github.com/emorissettegregoire/stealth-messagebird
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubygems_version: 3.0.8
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Stealth MessageBird Whatsapp driver
129
+ test_files:
130
+ - spec/spec_helper.rb
131
+ - spec/version_spec.rb