twi 0.0.1 → 0.3.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 +5 -5
- data/CHANGELOG.md +14 -0
- data/LICENSE.txt +17 -18
- data/README.md +84 -17
- data/lib/twi/config.rb +37 -0
- data/lib/twi/conversation.rb +82 -0
- data/lib/twi/conversation_event.rb +10 -0
- data/lib/twi/delivery.rb +20 -0
- data/lib/twi/delivery_event.rb +13 -0
- data/lib/twi/error.rb +11 -0
- data/lib/twi/event.rb +56 -0
- data/lib/twi/lio.rb +63 -0
- data/lib/twi/medium.rb +33 -0
- data/lib/twi/message.rb +77 -0
- data/lib/twi/message_event.rb +21 -0
- data/lib/twi/mock/conversation.rb +29 -0
- data/lib/twi/mock/event.rb +6 -0
- data/lib/twi/mock/medium.rb +8 -0
- data/lib/twi/mock/message.rb +12 -0
- data/lib/twi/mock/phone.rb +12 -0
- data/lib/twi/mock.rb +5 -0
- data/lib/twi/mocking.rb +33 -0
- data/lib/twi/participant.rb +14 -0
- data/lib/twi/participant_event.rb +19 -0
- data/lib/twi/phone.rb +21 -0
- data/lib/twi/resource.rb +22 -0
- data/lib/twi/version.rb +3 -1
- data/lib/twi.rb +32 -4
- metadata +45 -28
- data/.gitignore +0 -22
- data/Gemfile +0 -4
- data/Rakefile +0 -2
- data/twi.gemspec +0 -23
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a3da0ed4a9e8746dd8609affeb9cf316a156dafa9a20b0da5e5307c37ea95ad0
|
|
4
|
+
data.tar.gz: 647d377b97c2fc7951ecd61f3caeb100b5d4ceeb8e1a8d788a5ba0b623a60ef9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 375a364348e16d823426c5670e22a900f52d50d5ee9ac2405f80cd6cd9e3cd7052e20d2d8511f50d44036f579ce273d8a2929908f470fd01aeeaf576e0ec6993
|
|
7
|
+
data.tar.gz: 97361223e531df80107ed916fa9f81774a9a8694a2e9df8b7acc5dd58468f02c10616df901e798d88f3be3e93b9f1cd4b4c8a3952525ab618378ea1d5a520063
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## [0.3.0] - 2026-05-27
|
|
2
|
+
|
|
3
|
+
- [New] Add Twi::Message#create
|
|
4
|
+
- [New] Add Twi::Conversation#upload
|
|
5
|
+
- [New] Add Twi.event, Twi.mock.medium
|
|
6
|
+
- [New] Add Twi.conversation, Twi.mock.conversation, Twi.mock.conversation_error
|
|
7
|
+
- [New] Add Twi::Event.params_for
|
|
8
|
+
- [New] Add Twi.create_phone, Twi.mock.phone, Twi.mock.phone_error
|
|
9
|
+
- [New] Add Twi::Phone
|
|
10
|
+
|
|
11
|
+
## [0.2.0] - 2026-05-25
|
|
12
|
+
|
|
13
|
+
- [New] Twi::Delivery as a wrapper for direct message deliveries
|
|
14
|
+
- [New] Twi::Message as a wrapper for incoming direct messages
|
data/LICENSE.txt
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Copyright (c) 2026 HouseAccount
|
|
4
4
|
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
the following conditions:
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
12
11
|
|
|
13
|
-
The above copyright notice and this permission notice shall be
|
|
14
|
-
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
15
14
|
|
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
OF
|
|
22
|
-
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
CHANGED
|
@@ -1,29 +1,96 @@
|
|
|
1
|
-
#
|
|
1
|
+
# The enhanced Twilio API Ruby client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Available methods
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
### Twi::Message
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
When receiving an incoming direct message via webhook:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
```ruby
|
|
10
|
+
message = Twi::Message.new params
|
|
11
|
+
message.id # => 'SM083e290bef7794c407f14e22a891aa6d'
|
|
12
|
+
message.content # => 'Hello world'
|
|
13
|
+
message.sender # => '8009007000'
|
|
14
|
+
message.recipient # => '8008008000'
|
|
15
|
+
message.wallflower # nil
|
|
16
|
+
message.opt_in? # false
|
|
17
|
+
message.opt_out? # false
|
|
18
|
+
message.image_urls # => ['https://example.com/image.png']
|
|
19
|
+
```
|
|
10
20
|
|
|
11
|
-
|
|
21
|
+
When building a Twilio-like webhook payload:
|
|
12
22
|
|
|
13
|
-
|
|
23
|
+
```ruby
|
|
24
|
+
Twi::Message.params_for id: 'SM12', content: 'Hello', sender: '8009007000', recipient: '8008008000'
|
|
25
|
+
# => { MessageSid: 'SM12', Body: 'Hello',From: '+18009007000', To: '+18008008000' }
|
|
26
|
+
```
|
|
14
27
|
|
|
15
|
-
|
|
28
|
+
### Twi::Delivery
|
|
16
29
|
|
|
17
|
-
|
|
30
|
+
When receiving a delivery notification via webhook:
|
|
18
31
|
|
|
19
|
-
|
|
32
|
+
```ruby
|
|
33
|
+
delivery = Twi::Delivery.new params
|
|
34
|
+
delivery.id # => 'SM083e290bef7794c407f14e22a891aa6d'
|
|
35
|
+
delivery.status # => 'draft'
|
|
36
|
+
delivery.code # => '30006'
|
|
37
|
+
```
|
|
20
38
|
|
|
21
|
-
|
|
39
|
+
When building a Twilio-like webhook payload:
|
|
22
40
|
|
|
23
|
-
|
|
41
|
+
```ruby
|
|
42
|
+
Twi::Delivery.params_for id: 'SM12', status: 'sent'
|
|
43
|
+
# => { SmsSid: 'SM12', MessgeStatus: 'sent', ErrorCode: nil }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Twi::Event
|
|
47
|
+
|
|
48
|
+
When receiving events about a conversation:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
event = Twi::Event.new params
|
|
52
|
+
event.id # => 'SM083e290bef7794c407f14e22a891aa6d'
|
|
53
|
+
event.conversation_id # => 'CH123'
|
|
54
|
+
event.target # => :participant
|
|
55
|
+
event.participant # => #<Participant id: 'SH12', phone: '9008009000', identity: nil>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Twi::Phone
|
|
59
|
+
|
|
60
|
+
To create an incoming phone number:
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
phone = Twi.create_phone area_code:, friendly_name:
|
|
64
|
+
phone.id # => 'SM083e290bef7794c407f14e65a891aa6d'
|
|
65
|
+
phone.number # => '5556667777'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Available mocks
|
|
69
|
+
|
|
70
|
+
Use these methods to mock request to Twilio when testing an app:
|
|
71
|
+
|
|
72
|
+
### Credentials
|
|
73
|
+
|
|
74
|
+
Mock an error when creating an incoming phone number:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
Jbr.mock.phone_error = { code: '21452' }
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Mock successfully creating an incoming phone number:
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
Jbr.mock.phone = { id: 'SM083e290bef7794c407f14e65a891aa6d', number: '8009005000' }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
## To Do
|
|
88
|
+
|
|
89
|
+
4. have an interface to send and receive SMS with photos
|
|
90
|
+
8. have an error code URL for each error code and a sid_url
|
|
91
|
+
9. Declare some phones like Twilio.homeowner_phone or Twilio.numbers[:ddd] and a default Twilio.number and similar Twilio.messaging_service
|
|
92
|
+
10. a way to reopen closed conversations
|
|
93
|
+
11. a way to create conversations
|
|
94
|
+
12. and upload pictures in a conversation
|
|
95
|
+
13. x_twilio_webhook_enabled: true
|
|
24
96
|
|
|
25
|
-
1. Fork it ( https://github.com/[my-github-username]/twi/fork )
|
|
26
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
27
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
28
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
|
29
|
-
5. Create a new Pull Request
|
data/lib/twi/config.rb
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# Provides methods to read and write global configuration settings.
|
|
4
|
+
#
|
|
5
|
+
# A typical usage is to set the +sid+ and +secret+ to interact with Twilio API.
|
|
6
|
+
#
|
|
7
|
+
# @example Set the +sid+ and +secret+ to interact with Twilio API:
|
|
8
|
+
# Twi.configure do |twilio|
|
|
9
|
+
# twilio.sid = 'AC8776d'
|
|
10
|
+
# twilio.secret = '588213'
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# Note that Twi.configure has precedence over values through with
|
|
14
|
+
# environment variables (see {Twi::Lio}).
|
|
15
|
+
#
|
|
16
|
+
module Config
|
|
17
|
+
# Yields the global configuration to the given block.
|
|
18
|
+
#
|
|
19
|
+
# @example
|
|
20
|
+
# Twi.configure do |twilio|
|
|
21
|
+
# twilio.sid = 'AC8776d'
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# @yield [Twi::Lio] The global configuration.
|
|
25
|
+
def configure
|
|
26
|
+
yield lio if block_given?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @return [Twi::Lio] The global configuration.
|
|
30
|
+
def lio
|
|
31
|
+
@lio ||= Twi::Lio.new
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @note Config is tauto-loaded in the Twi module to be able to invoke +Twi.configure+.
|
|
36
|
+
extend Config
|
|
37
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# The representation of a (classic) group conversation.
|
|
4
|
+
class Conversation < Resource
|
|
5
|
+
attr_reader :id, :status
|
|
6
|
+
|
|
7
|
+
def create_with(participants:)
|
|
8
|
+
params = create_params_for participants
|
|
9
|
+
conversation = conversation_service.conversation_with_participants.create **params
|
|
10
|
+
|
|
11
|
+
@id = conversation.sid
|
|
12
|
+
@status = conversation.state
|
|
13
|
+
rescue Twilio::REST::RestError => error
|
|
14
|
+
case error.code
|
|
15
|
+
when 50438 then raise ExistingConversationError.new(error.error_message)
|
|
16
|
+
when 50214 then raise TooManyConversationsError.new(error.error_message)
|
|
17
|
+
else raise
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def rename(friendly_name)
|
|
22
|
+
conversation.update friendly_name: friendly_name
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def close
|
|
26
|
+
conversation.update state: :closed
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def delete
|
|
30
|
+
conversation.delete
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def create_message(content:, image_ids: [])
|
|
34
|
+
conversation.messages.create **message_params_for(content, image_ids)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# TODO: Move into Medium -- this method doesn't use anything from this class
|
|
38
|
+
def upload(file)
|
|
39
|
+
uri = URI "https://mcs.us1.twilio.com/v1/Services/#{Twi.lio.conversation_sid}/Media"
|
|
40
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
41
|
+
http.use_ssl = true
|
|
42
|
+
|
|
43
|
+
headers = { 'Content-Type' => file.content_type, 'Content-Size' => file.byte_size.to_s }
|
|
44
|
+
request = Net::HTTP::Post.new uri.request_uri, headers
|
|
45
|
+
request.basic_auth Twi.lio.api_key, Twi.lio.secret
|
|
46
|
+
request.body = file.download
|
|
47
|
+
response = http.request request
|
|
48
|
+
JSON(response.body)['sid']
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def conversation
|
|
54
|
+
conversation_service.conversations @params[:id]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def create_params_for(participants)
|
|
58
|
+
{
|
|
59
|
+
messaging_service_sid: Twi.lio.messaging_sid, x_twilio_webhook_enabled: 'true',
|
|
60
|
+
friendly_name: @params[:friendly_name], participant: participant_params_for(participants),
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def participant_params_for(participants)
|
|
65
|
+
participants.map do |participant|
|
|
66
|
+
phone = "+1#{participant[:phone]}"
|
|
67
|
+
if participant[:identity]
|
|
68
|
+
{ messaging_binding: { projected_address: phone }, identity: participant[:identity] }
|
|
69
|
+
else
|
|
70
|
+
{ messaging_binding: { address: phone } }
|
|
71
|
+
end
|
|
72
|
+
end.map(&:to_json)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def message_params_for(content, image_ids)
|
|
76
|
+
{
|
|
77
|
+
author: @params[:author], body: content,
|
|
78
|
+
media_sid: image_ids, x_twilio_webhook_enabled: 'true',
|
|
79
|
+
}.compact_blank
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# An event signaling a status change for a conversation.
|
|
4
|
+
class ConversationEvent < Event
|
|
5
|
+
# @return [Hash] the shape of the payload send by Twilio to the callback URL.
|
|
6
|
+
def self.params_for(id:, status:)
|
|
7
|
+
{ ConversationSid: id, EventType: 'onConversationChanged', State: status.to_s }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
data/lib/twi/delivery.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# The representation of a direct message delivery notification.
|
|
4
|
+
class Delivery < Resource
|
|
5
|
+
# @return [String] unique identifier
|
|
6
|
+
def id = @params['SmsSid']
|
|
7
|
+
|
|
8
|
+
# @return [String] delivery status, one of accepted, scheduled, queued, sending, sent,
|
|
9
|
+
# delivery_unknown, delivered, undelivered, failed # TODO: make an enum
|
|
10
|
+
def status = @params['MessageStatus']
|
|
11
|
+
|
|
12
|
+
# @return [String, nil] error code
|
|
13
|
+
def code = @params['ErrorCode']
|
|
14
|
+
|
|
15
|
+
# @return [Hash] the shape of the payload send by Twilio to the callback URL.
|
|
16
|
+
def self.params_for(id:, status:, code: nil)
|
|
17
|
+
{ SmsSid: id, MessageStatus: status, ErrorCode: code }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# An event signaling the delivery of a message in a conversation.
|
|
4
|
+
class DeliveryEvent < Event
|
|
5
|
+
# @return [Hash] the shape of the payload send by Twilio to the callback URL.
|
|
6
|
+
def self.params_for(id:, participant_id:, message_id:, status:, code: nil)
|
|
7
|
+
{
|
|
8
|
+
ConversationSid: id, EventType: 'onDeliveryChanged', ParticipantSid: participant_id.to_s,
|
|
9
|
+
MessageSid: message_id, Status: status.to_s, ErrorCode: code
|
|
10
|
+
}.compact
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/twi/error.rb
ADDED
data/lib/twi/event.rb
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# An event tied to a (classic) conversation.
|
|
4
|
+
class Event < Resource
|
|
5
|
+
# @return [Symbol] event target type, can be :conversation, :participant, :message, :delivery.
|
|
6
|
+
def target = @params['EventType'].underscore.split('_').second.to_sym
|
|
7
|
+
|
|
8
|
+
# @return [String] conversation state, one of active, inactive, closed, initializing.
|
|
9
|
+
def status = @params['Status'] || @params['StateTo'] || @params['State']
|
|
10
|
+
|
|
11
|
+
# @return [Participant] the participant the event is about (e.g.: joined the conversation).
|
|
12
|
+
def participant = Participant.new @params
|
|
13
|
+
|
|
14
|
+
# @return [String] unique message identifier
|
|
15
|
+
def id = @params['MessageSid']
|
|
16
|
+
|
|
17
|
+
# @return [String, nil] content
|
|
18
|
+
def content = @params['Body']&.squish
|
|
19
|
+
|
|
20
|
+
# @return [Array<String>] URLs of image attachments
|
|
21
|
+
def image_urls
|
|
22
|
+
media = JSON(@params.fetch('Media', '[]')).map { |params| medium_for params }
|
|
23
|
+
media.filter(&:image?).map { |image| image.url }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [String] unique conversation identifier
|
|
27
|
+
def conversation_id = @params['ConversationSid']
|
|
28
|
+
|
|
29
|
+
# @return [String, nil] error code
|
|
30
|
+
def code = @params['ErrorCode']
|
|
31
|
+
|
|
32
|
+
# @return [Hash] the shape of the payload send by Twilio to the callback URL.
|
|
33
|
+
def self.params_for(id:, type:, status: nil, participant: nil)
|
|
34
|
+
{
|
|
35
|
+
ConversationSid: id, EventType: "on_#{type}_changed".camelize(:lower),
|
|
36
|
+
State: status&.to_s,
|
|
37
|
+
}.merge(participant_params_for participant).compact
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.participant_params_for(participant = nil)
|
|
41
|
+
if participant
|
|
42
|
+
if participant[:identity]
|
|
43
|
+
{ ParticipantSid: participant[:id], Identity: participant[:identity] }
|
|
44
|
+
else
|
|
45
|
+
{ ParticipantSid: participant[:id], 'MessagingBinding.Address' => "+1#{participant[:phone]}" }
|
|
46
|
+
end
|
|
47
|
+
else
|
|
48
|
+
{}
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def medium_for(params) = Medium.new params
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/twi/lio.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# Provides an object to store global configuration settings.
|
|
4
|
+
#
|
|
5
|
+
# This class is not used directly, but by calling {Twi::Config#configure Twi.configure},
|
|
6
|
+
# which creates and updates a single instance of {Twi::Lio}.
|
|
7
|
+
#
|
|
8
|
+
# @example Set the +sid+ and +secret+ to interact with Twilio API:
|
|
9
|
+
# Twi.configure do |config|
|
|
10
|
+
# twilio.sid = 'AC8776d'
|
|
11
|
+
# twilio.secret = '588213'
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# @see Twi::Lio for more examples.
|
|
15
|
+
#
|
|
16
|
+
# An alternative way to set global configuration settings is by storing
|
|
17
|
+
# them in the following environment variables:
|
|
18
|
+
#
|
|
19
|
+
# * +TWILIO_SID+ to store the Twilio SID
|
|
20
|
+
# * +TWILIO_SECRET+ to store the Twilio secret
|
|
21
|
+
# * +TWILIO_CONVERSATION_SERVICE_SID+ to store the Twilio Conversation service ID
|
|
22
|
+
#
|
|
23
|
+
# In case both methods are used together,
|
|
24
|
+
# {Twi::Config#configure Twi.configure} takes precedence.
|
|
25
|
+
#
|
|
26
|
+
# @example Set the +sid+ and +secret+ to interact with Twilio API:
|
|
27
|
+
# ENV['TWILIO_SID'] = 'AC8776d'
|
|
28
|
+
# ENV['TWILIO_SECRET'] = '588213'
|
|
29
|
+
#
|
|
30
|
+
class Lio
|
|
31
|
+
# Initialize the global configuration settings defaulting to matching environment variables.
|
|
32
|
+
def initialize
|
|
33
|
+
@api_key = ENV['TWILIO_SID']
|
|
34
|
+
@secret = ENV['TWILIO_SECRET']
|
|
35
|
+
@account_sid = ENV['TWILIO_ACCOUNT_SID']
|
|
36
|
+
@auth_token = ENV['TWILIO_AUTH_TOKEN']
|
|
37
|
+
@conversation_sid = ENV['TWILIO_CONVERSATION_SERVICE_SID']
|
|
38
|
+
@messaging_sid = ENV['TWILIO_MESSAGING_SID']
|
|
39
|
+
@emergency_address_sid = ENV['TWILIO_EMERGENCY_SID']
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [String] API key - used for basic operations like sending messages.
|
|
43
|
+
attr_reader :api_key
|
|
44
|
+
|
|
45
|
+
# @return [String] secret - to authenticate API requests made with +api_key+.
|
|
46
|
+
attr_reader :secret
|
|
47
|
+
|
|
48
|
+
# @return [String] Account SID - used for advanced operations like creating numbers.
|
|
49
|
+
attr_reader :account_sid
|
|
50
|
+
|
|
51
|
+
# @return [String] OAuth token - to authenticate API requests made with +account_sid+.
|
|
52
|
+
attr_reader :auth_token
|
|
53
|
+
|
|
54
|
+
# @return [String] the SID of the default Conversation service.
|
|
55
|
+
attr_reader :conversation_sid
|
|
56
|
+
|
|
57
|
+
# @return [String] the SID of the default Messaging service.
|
|
58
|
+
attr_reader :messaging_sid
|
|
59
|
+
|
|
60
|
+
# @return [String] the SID of the emergency address.
|
|
61
|
+
attr_reader :emergency_address_sid
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/twi/medium.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# The representation of a medium (image) attached to a message.
|
|
4
|
+
class Medium < Resource
|
|
5
|
+
# @return [String] unique identifier
|
|
6
|
+
def id = @params['Sid']
|
|
7
|
+
|
|
8
|
+
# @return [Boolean] whether the medium has the content type of an image.
|
|
9
|
+
def image? = content_type.match? %r{^image/}
|
|
10
|
+
|
|
11
|
+
# @return [String] the content type
|
|
12
|
+
def content_type = @params['ContentType']
|
|
13
|
+
|
|
14
|
+
# @return [String] a URL where the image can be accessed at least for a few minutes.
|
|
15
|
+
def url
|
|
16
|
+
uri = URI service_url
|
|
17
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
18
|
+
http.use_ssl = true
|
|
19
|
+
|
|
20
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
|
21
|
+
request.basic_auth Twi.lio.api_key, Twi.lio.secret
|
|
22
|
+
response = http.request(request)
|
|
23
|
+
|
|
24
|
+
JSON(response.body).dig 'links', 'content_direct_temporary'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def service_url
|
|
30
|
+
"https://mcs.us1.twilio.com/v1/Services/#{Twi.lio.conversation_sid}/Media/#{id}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/twi/message.rb
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# The representation of a direct message.
|
|
4
|
+
class Message < Resource
|
|
5
|
+
# @return [String] unique identifier
|
|
6
|
+
def id = @params['MessageSid']
|
|
7
|
+
|
|
8
|
+
# @return [String, nil] content
|
|
9
|
+
def content = @params['Body']&.squish
|
|
10
|
+
|
|
11
|
+
# @return [String] 10-digit phone number sending the SMS.
|
|
12
|
+
def sender = remove_prefix_from @params['From']
|
|
13
|
+
|
|
14
|
+
# @return [String] 10-digit phone number receiving the SMS.
|
|
15
|
+
def recipient = remove_prefix_from @params['To']
|
|
16
|
+
|
|
17
|
+
# @return [String] 10-digit phone number of any CC'd recipient.
|
|
18
|
+
def wallflower = remove_prefix_from @params['OtherRecipients0']
|
|
19
|
+
|
|
20
|
+
# @return [Boolean] whether the sender replied STOP to unsubscribe.
|
|
21
|
+
def opt_out? = @params['OptOutType'] == 'STOP'
|
|
22
|
+
|
|
23
|
+
# @return [Boolean] whether the sender replied START to resubscribe.
|
|
24
|
+
def opt_in? = @params['OptOutType'] == 'START'
|
|
25
|
+
|
|
26
|
+
# @return [Array<String>] URLs of image attachments
|
|
27
|
+
def image_urls
|
|
28
|
+
(0...@params['NumMedia'].to_i).filter_map do |index|
|
|
29
|
+
@params["MediaUrl#{index}"] if @params["MediaContentType#{index}"].match? %r{^image/}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
attr_reader :status
|
|
34
|
+
|
|
35
|
+
# Sends a message.
|
|
36
|
+
def create
|
|
37
|
+
message = api_client.messages.create messaging_service_sid: Twi.lio.messaging_sid.to_s,
|
|
38
|
+
from: "+1#{@params[:sender]}", to: "+1#{@params[:recipient]}", body: @params[:content]
|
|
39
|
+
|
|
40
|
+
@id = message.sid
|
|
41
|
+
@status = message.status
|
|
42
|
+
# todo rescue and then set @code
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @return [Hash] the shape of the payload send by Twilio to the callback URL.
|
|
46
|
+
def self.params_for(id:, sender:, recipient:, wallflower: nil, content: nil, opt: nil, media: [])
|
|
47
|
+
{
|
|
48
|
+
MessageSid: id,
|
|
49
|
+
From: "+1#{sender}",
|
|
50
|
+
To: "+1#{recipient}",
|
|
51
|
+
Body: content,
|
|
52
|
+
}.merge media_params_for(media).merge opt_params_for(opt).merge wallflower_params_for(wallflower)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def self.media_params_for(media = [])
|
|
58
|
+
media.each_with_index.inject({ NumMedia: media.size.to_s }) do |hash, (item, index)|
|
|
59
|
+
url_key = "MediaUrl#{index}".to_sym
|
|
60
|
+
content_type_key = "MediaContentType#{index}".to_sym
|
|
61
|
+
hash.merge url_key => item[:url], content_type_key => item[:content_type]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.opt_params_for(opt = nil)
|
|
66
|
+
case opt
|
|
67
|
+
when :out then { OptOutType: 'STOP' }
|
|
68
|
+
when :in then { OptOutType: 'START' }
|
|
69
|
+
else {}
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.wallflower_params_for(wallflower = nil)
|
|
74
|
+
wallflower ? { OtherRecipients0: "+1#{wallflower}" } : {}
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# An event signaling a new message in a conversation.
|
|
4
|
+
class MessageEvent < Event
|
|
5
|
+
# @return [Hash] the shape of the payload send by Twilio to the callback URL.
|
|
6
|
+
def self.params_for(id:, participant_id:, content: nil, media: [])
|
|
7
|
+
{
|
|
8
|
+
ConversationSid: id, EventType: 'onMessageChanged', ParticipantSid: participant_id.to_s,
|
|
9
|
+
MessageSid: "SM#{rand}", Body: content, Media: media_params_for(media),
|
|
10
|
+
}.compact_blank
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def self.media_params_for(media)
|
|
16
|
+
media.map do |medium|
|
|
17
|
+
{ Sid: medium[:id], ContentType: medium[:content_type] }
|
|
18
|
+
end.to_json if media.present?
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Twi
|
|
2
|
+
class Mock::Conversation < Conversation
|
|
3
|
+
def create_with(participants:)
|
|
4
|
+
if Twi.mock.conversation_error
|
|
5
|
+
if Twi.mock.conversation_error[:code] == 50438
|
|
6
|
+
raise ExistingConversationError.new(Twi.mock.conversation_error[:message])
|
|
7
|
+
elsif Twi.mock.conversation_error[:code] == 50214
|
|
8
|
+
Twi.mock.conversation_error = nil
|
|
9
|
+
raise TooManyConversationsError
|
|
10
|
+
end
|
|
11
|
+
elsif Twi.mock.conversation
|
|
12
|
+
@id = Twi.mock.conversation[:id]
|
|
13
|
+
@status = Twi.mock.conversation[:status]
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def upload(file) = 'fake-sid'
|
|
18
|
+
|
|
19
|
+
def rename(friendly_name); end
|
|
20
|
+
|
|
21
|
+
def close; end
|
|
22
|
+
|
|
23
|
+
def delete; end
|
|
24
|
+
|
|
25
|
+
def create_message(content:, image_ids: [])
|
|
26
|
+
Mock::Message.new Twi.mock.message
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/twi/mock.rb
ADDED
data/lib/twi/mocking.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Twi
|
|
2
|
+
module Mocking
|
|
3
|
+
def mock
|
|
4
|
+
@mock ||= Twi::Mock.new
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def create_phone(...)
|
|
8
|
+
phone(...).tap &:create
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def create_message(...)
|
|
12
|
+
message(...).tap &:create
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def conversation(...)
|
|
16
|
+
(@mock ? Mock::Conversation : Conversation).new(...)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def event(...)
|
|
20
|
+
(@mock ? Mock::Event : Event).new(...)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def phone(...)
|
|
24
|
+
(@mock ? Mock::Phone : Phone).new(...)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def message(...)
|
|
28
|
+
(@mock ? Mock::Message : Message).new(...)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
extend Mocking
|
|
33
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# The representation of a participant in a (clasic) conversation.
|
|
4
|
+
class Participant < Resource
|
|
5
|
+
# @return [String] unique identifier.
|
|
6
|
+
def id = @params['ParticipantSid']
|
|
7
|
+
|
|
8
|
+
# @return [String] 10-digit phone number.
|
|
9
|
+
def phone = remove_prefix_from @params['MessagingBinding.Address']
|
|
10
|
+
|
|
11
|
+
# @return [String, nil] optional identifier.
|
|
12
|
+
def identity = @params['Identity']
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# An event signaling a participant added to a conversation.
|
|
4
|
+
class ParticipantEvent < Event
|
|
5
|
+
# @return [Hash] the shape of the payload send by Twilio to the callback URL.
|
|
6
|
+
def self.params_for(id:, participant_id:, identity: nil, phone: nil)
|
|
7
|
+
{
|
|
8
|
+
ConversationSid: id, EventType: 'onParticipantChanged',
|
|
9
|
+
ParticipantSid: participant_id.to_s
|
|
10
|
+
}.merge participant_params_for(identity, phone)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def self.participant_params_for(identity, phone)
|
|
16
|
+
identity ? { Identity: identity } : { 'MessagingBinding.Address' => "+1#{phone}" }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/twi/phone.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# The representation of a phone number associated to the default messaging service.
|
|
4
|
+
class Phone < Resource
|
|
5
|
+
# Create an incoming phone number within the area code and friendly name.
|
|
6
|
+
def create
|
|
7
|
+
phone = client.incoming_phone_numbers.create area_code: @params[:area_code],
|
|
8
|
+
emergency_address_sid: Twi.lio.emergency_address_sid, friendly_name: @params[:friendly_name]
|
|
9
|
+
messaging_service.phone_numbers.create phone_number_sid: phone.sid
|
|
10
|
+
|
|
11
|
+
@id = phone.sid
|
|
12
|
+
@number = remove_prefix_from phone.phone_number
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @return [String] unique identifier of the phone.
|
|
16
|
+
attr_reader :id
|
|
17
|
+
|
|
18
|
+
# @return [String] 10-digit number.
|
|
19
|
+
attr_reader :number
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/twi/resource.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Enhances the Twilio Ruby gem with an object-oriented approach.
|
|
2
|
+
module Twi
|
|
3
|
+
# An abtract class for all Twilio resources.
|
|
4
|
+
class Resource
|
|
5
|
+
# @param params [ActionController::Parameters] the payload of Twilio hitting a callback URL.
|
|
6
|
+
def initialize(params = {})
|
|
7
|
+
@params = params
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def remove_prefix_from(number) = number&.strip&.delete_prefix '+1'
|
|
13
|
+
|
|
14
|
+
def client = Twilio::REST::Client.new Twi.lio.account_sid, Twi.lio.auth_token
|
|
15
|
+
|
|
16
|
+
def api_client = Twilio::REST::Client.new Twi.lio.api_key, Twi.lio.secret
|
|
17
|
+
|
|
18
|
+
def messaging_service = client.messaging.v1.services Twi.lio.messaging_sid.to_s
|
|
19
|
+
|
|
20
|
+
def conversation_service = client.conversations.v1.services Twi.lio.conversation_sid.to_s
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/twi/version.rb
CHANGED
data/lib/twi.rb
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
require 'action_controller'
|
|
4
|
+
require 'action_controller/metal/strong_parameters'
|
|
5
|
+
require 'active_support/core_ext/enumerable'
|
|
6
|
+
require 'active_support/core_ext/object/blank'
|
|
7
|
+
require 'json'
|
|
8
|
+
require 'net/http'
|
|
9
|
+
require 'twilio-ruby'
|
|
10
|
+
|
|
11
|
+
require 'twi/lio'
|
|
12
|
+
require 'twi/error'
|
|
13
|
+
require 'twi/config'
|
|
14
|
+
require 'twi/resource'
|
|
15
|
+
require 'twi/message'
|
|
16
|
+
require 'twi/delivery'
|
|
17
|
+
require 'twi/participant'
|
|
18
|
+
require 'twi/medium'
|
|
19
|
+
require 'twi/event'
|
|
20
|
+
require 'twi/conversation_event'
|
|
21
|
+
require 'twi/delivery_event'
|
|
22
|
+
require 'twi/message_event'
|
|
23
|
+
require 'twi/participant_event'
|
|
24
|
+
require 'twi/phone'
|
|
25
|
+
require 'twi/conversation'
|
|
26
|
+
|
|
27
|
+
require 'twi/mock'
|
|
28
|
+
require 'twi/mock/phone'
|
|
29
|
+
require 'twi/mock/conversation'
|
|
30
|
+
require 'twi/mock/event'
|
|
31
|
+
require 'twi/mock/medium'
|
|
32
|
+
require 'twi/mock/message'
|
|
33
|
+
require 'twi/mocking'
|
metadata
CHANGED
|
@@ -1,63 +1,83 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: twi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
bindir: bin
|
|
7
|
+
- Claudio Baccigalupo
|
|
8
|
+
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
13
|
+
name: actionpack
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
|
-
- - "
|
|
16
|
+
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
20
|
-
type: :
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
|
-
- - "
|
|
23
|
+
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
25
|
+
version: '0'
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
27
|
+
name: twilio-ruby
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
30
29
|
requirements:
|
|
31
30
|
- - ">="
|
|
32
31
|
- !ruby/object:Gem::Version
|
|
33
32
|
version: '0'
|
|
34
|
-
type: :
|
|
33
|
+
type: :runtime
|
|
35
34
|
prerelease: false
|
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
36
|
requirements:
|
|
38
37
|
- - ">="
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
39
|
version: '0'
|
|
41
|
-
description:
|
|
40
|
+
description: Enhances OpenSSL::HMAC with timestamp.
|
|
42
41
|
email:
|
|
43
|
-
- claudiob@
|
|
42
|
+
- claudiob@users.noreply.github.com
|
|
44
43
|
executables: []
|
|
45
44
|
extensions: []
|
|
46
45
|
extra_rdoc_files: []
|
|
47
46
|
files:
|
|
48
|
-
-
|
|
49
|
-
- Gemfile
|
|
47
|
+
- CHANGELOG.md
|
|
50
48
|
- LICENSE.txt
|
|
51
49
|
- README.md
|
|
52
|
-
- Rakefile
|
|
53
50
|
- lib/twi.rb
|
|
51
|
+
- lib/twi/config.rb
|
|
52
|
+
- lib/twi/conversation.rb
|
|
53
|
+
- lib/twi/conversation_event.rb
|
|
54
|
+
- lib/twi/delivery.rb
|
|
55
|
+
- lib/twi/delivery_event.rb
|
|
56
|
+
- lib/twi/error.rb
|
|
57
|
+
- lib/twi/event.rb
|
|
58
|
+
- lib/twi/lio.rb
|
|
59
|
+
- lib/twi/medium.rb
|
|
60
|
+
- lib/twi/message.rb
|
|
61
|
+
- lib/twi/message_event.rb
|
|
62
|
+
- lib/twi/mock.rb
|
|
63
|
+
- lib/twi/mock/conversation.rb
|
|
64
|
+
- lib/twi/mock/event.rb
|
|
65
|
+
- lib/twi/mock/medium.rb
|
|
66
|
+
- lib/twi/mock/message.rb
|
|
67
|
+
- lib/twi/mock/phone.rb
|
|
68
|
+
- lib/twi/mocking.rb
|
|
69
|
+
- lib/twi/participant.rb
|
|
70
|
+
- lib/twi/participant_event.rb
|
|
71
|
+
- lib/twi/phone.rb
|
|
72
|
+
- lib/twi/resource.rb
|
|
54
73
|
- lib/twi/version.rb
|
|
55
|
-
|
|
56
|
-
homepage: ''
|
|
74
|
+
homepage: https://github.com/claudiob/twi
|
|
57
75
|
licenses:
|
|
58
76
|
- MIT
|
|
59
|
-
metadata:
|
|
60
|
-
|
|
77
|
+
metadata:
|
|
78
|
+
homepage_uri: https://github.com/claudiob/twi
|
|
79
|
+
source_code_uri: https://github.com/claudiob/twi
|
|
80
|
+
changelog_uri: https://github.com/claudiob/twi
|
|
61
81
|
rdoc_options: []
|
|
62
82
|
require_paths:
|
|
63
83
|
- lib
|
|
@@ -65,17 +85,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
65
85
|
requirements:
|
|
66
86
|
- - ">="
|
|
67
87
|
- !ruby/object:Gem::Version
|
|
68
|
-
version:
|
|
88
|
+
version: 3.2.0
|
|
69
89
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
90
|
requirements:
|
|
71
91
|
- - ">="
|
|
72
92
|
- !ruby/object:Gem::Version
|
|
73
93
|
version: '0'
|
|
74
94
|
requirements: []
|
|
75
|
-
|
|
76
|
-
rubygems_version: 2.2.2
|
|
77
|
-
signing_key:
|
|
95
|
+
rubygems_version: 4.0.3
|
|
78
96
|
specification_version: 4
|
|
79
|
-
summary:
|
|
97
|
+
summary: API Signature with timestamped MAC (Message Authentication Code).
|
|
80
98
|
test_files: []
|
|
81
|
-
has_rdoc:
|
data/.gitignore
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
*.gem
|
|
2
|
-
*.rbc
|
|
3
|
-
.bundle
|
|
4
|
-
.config
|
|
5
|
-
.yardoc
|
|
6
|
-
Gemfile.lock
|
|
7
|
-
InstalledFiles
|
|
8
|
-
_yardoc
|
|
9
|
-
coverage
|
|
10
|
-
doc/
|
|
11
|
-
lib/bundler/man
|
|
12
|
-
pkg
|
|
13
|
-
rdoc
|
|
14
|
-
spec/reports
|
|
15
|
-
test/tmp
|
|
16
|
-
test/version_tmp
|
|
17
|
-
tmp
|
|
18
|
-
*.bundle
|
|
19
|
-
*.so
|
|
20
|
-
*.o
|
|
21
|
-
*.a
|
|
22
|
-
mkmf.log
|
data/Gemfile
DELETED
data/Rakefile
DELETED
data/twi.gemspec
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
-
require 'twi/version'
|
|
5
|
-
|
|
6
|
-
Gem::Specification.new do |spec|
|
|
7
|
-
spec.name = "twi"
|
|
8
|
-
spec.version = Twi::VERSION
|
|
9
|
-
spec.authors = ["claudiob"]
|
|
10
|
-
spec.email = ["claudiob@gmail.com"]
|
|
11
|
-
spec.summary = %q{Write a short summary. Required.}
|
|
12
|
-
spec.description = %q{Write a longer description. Optional.}
|
|
13
|
-
spec.homepage = ""
|
|
14
|
-
spec.license = "MIT"
|
|
15
|
-
|
|
16
|
-
spec.files = `git ls-files -z`.split("\x0")
|
|
17
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
-
spec.require_paths = ["lib"]
|
|
20
|
-
|
|
21
|
-
spec.add_development_dependency "bundler", "~> 1.6"
|
|
22
|
-
spec.add_development_dependency "rake"
|
|
23
|
-
end
|