bot_framework 0.1.0beta

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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +49 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +60 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/bot_framework.gemspec +31 -0
  13. data/examples/echo/bot.rb +8 -0
  14. data/examples/echo/config.ru +5 -0
  15. data/examples/stock/bot.rb +38 -0
  16. data/examples/stock/config.ru +5 -0
  17. data/lib/bot_framework.rb +48 -0
  18. data/lib/bot_framework/api_base.rb +31 -0
  19. data/lib/bot_framework/bot.rb +48 -0
  20. data/lib/bot_framework/bot_state.rb +89 -0
  21. data/lib/bot_framework/connector.rb +34 -0
  22. data/lib/bot_framework/conversation.rb +33 -0
  23. data/lib/bot_framework/errors.rb +4 -0
  24. data/lib/bot_framework/models/activity.rb +106 -0
  25. data/lib/bot_framework/models/api_response.rb +23 -0
  26. data/lib/bot_framework/models/attachment.rb +52 -0
  27. data/lib/bot_framework/models/attachment_data.rb +26 -0
  28. data/lib/bot_framework/models/attachment_info.rb +41 -0
  29. data/lib/bot_framework/models/attachment_view.rb +31 -0
  30. data/lib/bot_framework/models/base.rb +187 -0
  31. data/lib/bot_framework/models/bot_data.rb +15 -0
  32. data/lib/bot_framework/models/card_action.rb +43 -0
  33. data/lib/bot_framework/models/card_image.rb +36 -0
  34. data/lib/bot_framework/models/channel_account.rb +18 -0
  35. data/lib/bot_framework/models/conversation_account.rb +22 -0
  36. data/lib/bot_framework/models/conversation_parameters.rb +46 -0
  37. data/lib/bot_framework/models/entity.rb +26 -0
  38. data/lib/bot_framework/models/fact.rb +29 -0
  39. data/lib/bot_framework/models/geo_coordinates.rb +49 -0
  40. data/lib/bot_framework/models/hero_card.rb +62 -0
  41. data/lib/bot_framework/models/object.rb +19 -0
  42. data/lib/bot_framework/models/place.rb +56 -0
  43. data/lib/bot_framework/models/receipt_card.rb +78 -0
  44. data/lib/bot_framework/models/receipt_item.rb +60 -0
  45. data/lib/bot_framework/models/resource_response.rb +13 -0
  46. data/lib/bot_framework/models/signin_card.rb +35 -0
  47. data/lib/bot_framework/models/thumbnail_card.rb +62 -0
  48. data/lib/bot_framework/server.rb +56 -0
  49. data/lib/bot_framework/token_validator.rb +87 -0
  50. data/lib/bot_framework/util.rb +19 -0
  51. data/lib/bot_framework/version.rb +3 -0
  52. metadata +206 -0
@@ -0,0 +1,60 @@
1
+ module BotFramework
2
+ class ReceiptItem < Base
3
+ # Title of the Card
4
+ attr_accessor :title
5
+
6
+ # Subtitle appears just below Title field, differs from Title in font styling only
7
+ attr_accessor :subtitle
8
+
9
+ # Text field appears just below subtitle, differs from Subtitle in font styling only
10
+ attr_accessor :text
11
+
12
+ # Image
13
+ attr_accessor :image
14
+
15
+ # Amount with currency
16
+ attr_accessor :price
17
+
18
+ # Number of items of given kind
19
+ attr_accessor :quantity
20
+
21
+ # This action will be activated when user taps on the Item bubble.
22
+ attr_accessor :tap
23
+
24
+ # Attribute type mapping.
25
+ def self.swagger_types
26
+ {
27
+ title: :String,
28
+ subtitle: :String,
29
+ text: :String,
30
+ image: :CardImage,
31
+ price: :String,
32
+ quantity: :String,
33
+ tap: :CardAction
34
+ }
35
+ end
36
+
37
+ # Initializes the object
38
+ # @param [Hash] attributes Model attributes in the form of hash
39
+ def initialize(attributes = {})
40
+ return unless attributes.is_a?(Hash)
41
+
42
+ # convert string to symbol for hash key
43
+ attributes = attributes.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
44
+
45
+ self.title = attributes[:title] if attributes.key?(:title)
46
+
47
+ self.subtitle = attributes[:subtitle] if attributes.key?(:subtitle)
48
+
49
+ self.text = attributes[:text] if attributes.key?(:text)
50
+
51
+ self.image = attributes[:image] if attributes.key?(:image)
52
+
53
+ self.price = attributes[:price] if attributes.key?(:price)
54
+
55
+ self.quantity = attributes[:quantity] if attributes.key?(:quantity)
56
+
57
+ self.tap = attributes[:tap] if attributes.key?(:tap)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,13 @@
1
+ module BotFramework
2
+ class ResourceResponse < Base
3
+ # Id of the resource
4
+ attr_accessor :id
5
+
6
+ # Attribute type mapping.
7
+ def self.swagger_types
8
+ {
9
+ id: :String
10
+ }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,35 @@
1
+ module BotFramework
2
+ # A card representing a request to signing
3
+ class SigninCard < Base
4
+ # Text for signin request
5
+ attr_accessor :text
6
+
7
+ # Action to use to perform signin
8
+ attr_accessor :buttons
9
+
10
+ # Attribute type mapping.
11
+ def self.swagger_types
12
+ {
13
+ text: :String,
14
+ buttons: :'Array<CardAction>'
15
+ }
16
+ end
17
+
18
+ # Initializes the object
19
+ # @param [Hash] attributes Model attributes in the form of hash
20
+ def initialize(attributes = {})
21
+ return unless attributes.is_a?(Hash)
22
+
23
+ # convert string to symbol for hash key
24
+ attributes = attributes.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
25
+
26
+ self.text = attributes[:text] if attributes.key?(:text)
27
+
28
+ if attributes.key?(:buttons)
29
+ if (value = attributes[:buttons]).is_a?(Array)
30
+ self.buttons = value
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,62 @@
1
+ module BotFramework
2
+ class ThumbnailCard < Base
3
+ # Title of the card
4
+ attr_accessor :title
5
+
6
+ # Subtitle of the card
7
+ attr_accessor :subtitle
8
+
9
+ # Text for the card
10
+ attr_accessor :text
11
+
12
+ # Array of i
13
+ attr_accessor :images
14
+
15
+ # Set of actions applicable to the current card
16
+ attr_accessor :buttons
17
+
18
+ # This action will be activated when user taps on the card itself
19
+ attr_accessor :tap
20
+
21
+ # Attribute type mapping.
22
+ def self.swagger_types
23
+ {
24
+ title: :String,
25
+ subtitle: :String,
26
+ text: :String,
27
+ images: :'Array<CardImage>',
28
+ buttons: :'Array<CardAction>',
29
+ tap: :CardAction
30
+ }
31
+ end
32
+
33
+ # Initializes the object
34
+ # @param [Hash] attributes Model attributes in the form of hash
35
+ def initialize(attributes = {})
36
+ return unless attributes.is_a?(Hash)
37
+
38
+ # convert string to symbol for hash key
39
+ attributes = attributes.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
40
+
41
+ self.title = attributes[:title] if attributes.key?(:title)
42
+
43
+ self.subtitle = attributes[:subtitle] if attributes.key?(:subtitle)
44
+
45
+ self.text = attributes[:text] if attributes.key?(:text)
46
+
47
+ if attributes.key?(:images)
48
+ if (value = attributes[:images]).is_a?(Array)
49
+ self.images = value
50
+ end
51
+ end
52
+
53
+ if attributes.key?(:buttons)
54
+ if (value = attributes[:buttons]).is_a?(Array)
55
+ self.buttons = value
56
+ end
57
+ end
58
+
59
+ self.tap = attributes[:tap] if attributes.key?(:tap)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,56 @@
1
+ module BotFramework
2
+ class Server
3
+ def self.call(env)
4
+ new.call(env)
5
+ end
6
+
7
+ def call(env)
8
+ @request = Rack::Request.new env
9
+ @response = Rack::Response.new
10
+ if @request.post?
11
+ if verify
12
+ receive
13
+ else
14
+ raise InvalidToken
15
+ end
16
+ end
17
+ @response.finish
18
+ end
19
+
20
+ # TODO: reply in separate thread t avoid timeout
21
+ def receive
22
+ # Thread.new {
23
+ activity = Activity.new.build_from_hash JSON.parse(@request.body.read)
24
+ Bot.receive(activity)
25
+ # }
26
+ end
27
+
28
+ def headers
29
+ env = @request.env
30
+ Hash[*env.select { |k, _v| k.start_with? 'HTTP_' }
31
+ .collect { |k, v| [k.sub(/^HTTP_/, ''), v] }
32
+ .collect { |k, v| [k.split('_').collect(&:capitalize).join('-'), v] }
33
+ .sort
34
+ .flatten]
35
+ end
36
+
37
+ # Use logger instead of puts
38
+ def verify
39
+ validator = TokenValidator.new(headers)
40
+ if validator.valid?
41
+ return true
42
+ else
43
+ p "Errors: #{validator.errors}"
44
+ return false
45
+ end
46
+ rescue JWT::DecodeError
47
+ [401, { 'Content-Type' => 'text/plain' }, ['A token must be passed.']]
48
+ rescue JWT::ExpiredSignature
49
+ [403, { 'Content-Type' => 'text/plain' }, ['The token has expired.']]
50
+ rescue JWT::InvalidIssuerError
51
+ [403, { 'Content-Type' => 'text/plain' }, ['The token does not have a valid issuer.']]
52
+ rescue JWT::InvalidIatError
53
+ [403, { 'Content-Type' => 'text/plain' }, ['The token does not have a valid "issued at" time.']]
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,87 @@
1
+ module BotFramework
2
+ class TokenValidator
3
+ include HTTParty
4
+ attr_accessor :headers, :errors
5
+
6
+ OPEN_ID_CONFIG_URI = 'https://api.aps.skype.com/v1/.well-known/openidconfiguration'.freeze
7
+
8
+ def initialize(headers)
9
+ @headers = headers
10
+ end
11
+
12
+ def valid?
13
+ valid_header? &&
14
+ valid_jwt? &&
15
+ valid_iss? &&
16
+ valid_audience? &&
17
+ valid_token? &&
18
+ valid_signature?
19
+ end
20
+
21
+ def errors
22
+ @errors ||= []
23
+ end
24
+
25
+ private
26
+
27
+ def open_id_config
28
+ JSON.parse(self.class.get(OPEN_ID_CONFIG_URI).body)
29
+ end
30
+
31
+ def jwks_uri
32
+ open_id_config['jwks_uri']
33
+ end
34
+
35
+ def valid_keys
36
+ JSON.parse(self.class.get(jwks_uri).body)['keys']
37
+ end
38
+
39
+ def auth_header
40
+ headers['Authorization']
41
+ end
42
+
43
+ def token
44
+ auth_header.gsub('Bearer ', '')
45
+ end
46
+
47
+ def valid_header?
48
+ # The token was sent in the HTTP Authorization header with "Bearer" scheme
49
+ condition = auth_header.start_with? 'Bearer'
50
+ errors << 'Invalid headers' unless condition
51
+ condition
52
+ end
53
+ # Validations
54
+
55
+ def valid_jwt?
56
+ # The token is valid JSON that conforms to the JWT standard (see references)
57
+ condition = JWT.decode token, nil, false
58
+ errors << 'Invalid jwt' unless condition
59
+ condition
60
+ end
61
+
62
+ def valid_iss?
63
+ # The token contains an issuer claim with value of https://api.botframework.com
64
+ condition = JWT.decode(token, nil, false).first['iss'] == 'https://api.botframework.com'
65
+ errors << 'Invalid iss' unless condition
66
+ condition
67
+ end
68
+
69
+ def valid_audience?
70
+ # The token contains an audience claim with a value equivalent to your bot’s Microsoft App ID.
71
+ condition = (JWT.decode(token, nil, false).first['aud'] == BotFramework.connector.app_id)
72
+ errors << 'Invalid audience' unless condition
73
+ condition
74
+ end
75
+
76
+ def valid_token?
77
+ # The token has not yet expired. Industry-standard clock-skew is 5 minutes.
78
+ # Should not raise JWT::ExpiredSignature
79
+ true
80
+ end
81
+
82
+ def valid_signature?
83
+ # The token has a valid cryptographic signature with a key listed in the OpenId keys document retrieved in step 1, above.
84
+ true
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,19 @@
1
+ module BotFramework
2
+ module Util
3
+ class << self
4
+ def to_underscore(string)
5
+ string = string.to_s unless string.is_a? String
6
+ string.gsub(/::/, '/')
7
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
8
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
9
+ .tr('-', '_')
10
+ .downcase
11
+ end
12
+
13
+ def camel_case_lower(string)
14
+ string = string.to_s unless string.is_a? String
15
+ string.split('_').inject([]) { |buffer, e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module BotFramework
2
+ VERSION = '0.1.0beta'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bot_framework
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0beta
5
+ platform: ruby
6
+ authors:
7
+ - Aboobacker MK
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-11-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
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.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: oauth2
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: jwt
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: httparty
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rack
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Unofficial ruby client for microsoft botframework
126
+ email:
127
+ - aboobackervyd@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".rspec"
134
+ - ".travis.yml"
135
+ - CODE_OF_CONDUCT.md
136
+ - Gemfile
137
+ - LICENSE.txt
138
+ - README.md
139
+ - Rakefile
140
+ - bin/console
141
+ - bin/setup
142
+ - bot_framework.gemspec
143
+ - examples/echo/bot.rb
144
+ - examples/echo/config.ru
145
+ - examples/stock/bot.rb
146
+ - examples/stock/config.ru
147
+ - lib/bot_framework.rb
148
+ - lib/bot_framework/api_base.rb
149
+ - lib/bot_framework/bot.rb
150
+ - lib/bot_framework/bot_state.rb
151
+ - lib/bot_framework/connector.rb
152
+ - lib/bot_framework/conversation.rb
153
+ - lib/bot_framework/errors.rb
154
+ - lib/bot_framework/models/activity.rb
155
+ - lib/bot_framework/models/api_response.rb
156
+ - lib/bot_framework/models/attachment.rb
157
+ - lib/bot_framework/models/attachment_data.rb
158
+ - lib/bot_framework/models/attachment_info.rb
159
+ - lib/bot_framework/models/attachment_view.rb
160
+ - lib/bot_framework/models/base.rb
161
+ - lib/bot_framework/models/bot_data.rb
162
+ - lib/bot_framework/models/card_action.rb
163
+ - lib/bot_framework/models/card_image.rb
164
+ - lib/bot_framework/models/channel_account.rb
165
+ - lib/bot_framework/models/conversation_account.rb
166
+ - lib/bot_framework/models/conversation_parameters.rb
167
+ - lib/bot_framework/models/entity.rb
168
+ - lib/bot_framework/models/fact.rb
169
+ - lib/bot_framework/models/geo_coordinates.rb
170
+ - lib/bot_framework/models/hero_card.rb
171
+ - lib/bot_framework/models/object.rb
172
+ - lib/bot_framework/models/place.rb
173
+ - lib/bot_framework/models/receipt_card.rb
174
+ - lib/bot_framework/models/receipt_item.rb
175
+ - lib/bot_framework/models/resource_response.rb
176
+ - lib/bot_framework/models/signin_card.rb
177
+ - lib/bot_framework/models/thumbnail_card.rb
178
+ - lib/bot_framework/server.rb
179
+ - lib/bot_framework/token_validator.rb
180
+ - lib/bot_framework/util.rb
181
+ - lib/bot_framework/version.rb
182
+ homepage: https://github.com/tachyons/bot-framework-ruby
183
+ licenses:
184
+ - MIT
185
+ metadata: {}
186
+ post_install_message:
187
+ rdoc_options: []
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ required_rubygems_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">"
198
+ - !ruby/object:Gem::Version
199
+ version: 1.3.1
200
+ requirements: []
201
+ rubyforge_project:
202
+ rubygems_version: 2.6.6
203
+ signing_key:
204
+ specification_version: 4
205
+ summary: Ruby client for microsoft botframework .
206
+ test_files: []