hutzbot 0.1.3

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2041220becd680d7be9abe4f656037f3c680e483fd6479dd687b460089219f6e
4
+ data.tar.gz: 7213aae4ff4b82d3c1a2b2188c272928e621f67addefa4ba9c48195c3a063e95
5
+ SHA512:
6
+ metadata.gz: 745b62c428bd5070d7d881304c56334b5d6dc5d8a3539573ed87b356cfdd4a0284dcd9fece28677d9dbef82ec1e0f49d39e9e7375d157dd20601658f094eca54
7
+ data.tar.gz: 15dc53c6db2b0a0c795dc58530de3bbfaa1ae5c3a3fee67f15dba2f3314227c004b0f20865ab073bf40d6c0bd1add5c2ba5b91123852a3cff8d23afda600556b
@@ -0,0 +1,206 @@
1
+ require 'hutzbot/version'
2
+ require 'httparty'
3
+
4
+ module Hutzbot
5
+ class APIError < StandardError; end
6
+
7
+ class Response
8
+ attr_accessor :id
9
+ attr_accessor :speaker
10
+ attr_accessor :speaker_name
11
+ attr_accessor :statement
12
+ attr_accessor :metadata
13
+
14
+ def initialize attrs
15
+ @id, @speaker, @speaker_name, @statement, @metadata = *attrs.values_at('id', 'speaker', 'speaker_name', 'statement', 'metadata')
16
+ end
17
+
18
+ def system?
19
+ speaker=='system'
20
+ end
21
+ end
22
+
23
+ class ValidResponse
24
+ attr_accessor :answer
25
+ attr_accessor :response
26
+
27
+ def initialize attrs
28
+ @answer, @response = *attrs.values_at('answer', 'response')
29
+ end
30
+ end
31
+
32
+ class Conversation
33
+ attr_accessor :id
34
+ attr_accessor :current_state
35
+ attr_accessor :taxonomy
36
+ attr_accessor :tagged_document
37
+ attr_accessor :user
38
+ attr_accessor :application
39
+ attr_accessor :responses
40
+ attr_accessor :valid_responses
41
+
42
+ def initialize attrs
43
+ @id, @current_state, @taxonomy, @tagged_document, @user, @application = *attrs.values_at('id', 'current_state', 'taxonomy', 'tagged_document', 'user', 'application')
44
+ @responses = attrs['responses'].map{ |r| Response.new(r) } if attrs['responses']
45
+ @valid_responses = attrs['valid_responses'].map{ |vr| ValidResponse.new(vr) } if attrs['valid_responses']
46
+ end
47
+
48
+ def closed?
49
+ current_state=='closed'
50
+ end
51
+ end
52
+
53
+ class Taxonomy
54
+ attr_accessor :id
55
+ attr_accessor :name
56
+
57
+ def initialize attrs
58
+ @id, @name = *attrs.values_at('id', 'name')
59
+ end
60
+ end
61
+
62
+ class TaggedDocument
63
+ attr_accessor :id
64
+ attr_accessor :title
65
+
66
+ def initialize attrs
67
+ @id, @title = *attrs.values_at('id', 'title')
68
+ end
69
+ end
70
+
71
+
72
+ class Hutzbot
73
+ def initialize logger, host, user, key
74
+ @logger = logger
75
+ @host = host
76
+ @user = user
77
+ @key = key
78
+ @options = { headers: { 'X-User-Email' => @user, 'X-User-Token' => @key, 'Accept' => 'application/json' } }
79
+ end
80
+
81
+ def logger
82
+ @logger
83
+ end
84
+
85
+ def start_conversation
86
+ taxonomy = get_default_taxonomy
87
+ tagged_document = get_default_tagged_document
88
+ conversation = create_conversation({ taxonomy_id: taxonomy.id, tagged_document_id: tagged_document.id })
89
+ end
90
+
91
+ def get_conversations metadata=nil
92
+ request = [@host, 'conversations'].join('/')
93
+ request += "?metadata=#{metadata}" unless metadata.nil?
94
+ response = HTTParty.get(request, @options)
95
+ check_errors response
96
+ JSON.parse(response.body)['conversations'].map{ |c| Conversation.new(c) }
97
+ end
98
+
99
+ def get_conversation id
100
+ response = HTTParty.get("#{@host}/conversations/#{id}", @options)
101
+ check_errors response
102
+ Conversation.new(JSON.parse(response.body))
103
+ end
104
+
105
+ def tag_responses conversation_id, metadata
106
+ response = HTTParty.patch("#{@host}/conversations/#{conversation_id}/tag_responses", @options.merge({ query: { metadata: metadata } }))
107
+ check_errors response
108
+ Conversation.new(JSON.parse(response.body))
109
+ end
110
+
111
+ def add_response conversation_id, answer
112
+ response = HTTParty.patch("#{@host}/conversations/#{conversation_id}/add_response", @options.merge({ query: { answer: answer } }))
113
+ check_errors response
114
+ Conversation.new(JSON.parse(response.body))
115
+ end
116
+
117
+ def get_default_taxonomy
118
+ response = HTTParty.get("#{@host}/taxonomies/default", @options)
119
+ check_errors response
120
+ Taxonomy.new(JSON.parse(response.body))
121
+ end
122
+
123
+ def get_default_tagged_document
124
+ response = HTTParty.get("#{@host}/tagged_documents/default", @options)
125
+ check_errors response
126
+ TaggedDocument.new(JSON.parse(response.body))
127
+ end
128
+
129
+ def create_conversation params
130
+ response = HTTParty.post("#{@host}/conversations", @options.merge({ query: { conversation: params.merge({ application: :email }) } }))
131
+ check_errors response
132
+ Conversation.new(JSON.parse(response.body))
133
+ end
134
+
135
+ def converse metadata, body
136
+ if metadata.nil? || metadata.empty?
137
+ # start a conversation via the API
138
+ logger.info 'Starting a new conversation'
139
+ conversation = start_conversation
140
+ logger.info "Conversation ID: #{conversation.id}"
141
+ else
142
+ logger.info "Finding a conversation by metadata (#{metadata})"
143
+
144
+ # find the conversation by metadata
145
+ conversations = get_conversations metadata
146
+ unless conversations.empty?
147
+ # found a matching conversation
148
+ conversation = conversations.first
149
+ logger.info "Found conversation (#{conversation.id})"
150
+
151
+ # now get the full conversation (more attributes than available in index call)
152
+ conversation = get_conversation conversation.id
153
+
154
+ # get the answer index
155
+ matches = /(-?\d+)/.match(body)
156
+ if matches.nil?
157
+ # invalid answer
158
+ logger.warn("No answer found in body (#{body})")
159
+ else
160
+ answer_index = matches[1].to_i
161
+ logger.info("Found an answer in the message with index #{answer_index} in (#{conversation.valid_responses})")
162
+
163
+ matching_answer = conversation.valid_responses[answer_index-1]
164
+ logger.info("That answer is #{matching_answer.answer} (#{matching_answer.response})")
165
+ conversation = add_response conversation.id, matching_answer.answer
166
+ end
167
+ end
168
+ end
169
+
170
+ if conversation
171
+ logger.info "Preparing response"
172
+ responses = conversation.responses.select{ |r| r.system? && (r.metadata.nil? || r.metadata.empty?) }
173
+ contents = responses.map do |r|
174
+ "<p>#{r.statement}</p>"
175
+ end.join("<br>")
176
+ logger.debug "Response is: #{contents}"
177
+
178
+ logger.info "Preparing prompt"
179
+ prompt = nil
180
+ unless conversation.closed?
181
+ prompt = conversation.valid_responses.map do |valid_response|
182
+ "<li>#{valid_response.response}</li>"
183
+ end.join
184
+ prompt = "<p>Please make your selection: </p><ol>#{prompt}</ol>"
185
+ end
186
+ logger.debug "Prompt is: #{prompt}"
187
+
188
+ return conversation, contents, prompt
189
+ else
190
+ return nil, ""
191
+ end
192
+ end
193
+
194
+ private
195
+ def check_errors response
196
+ case response.code
197
+ when 200..299
198
+ "Success"
199
+ when 400..499
200
+ raise APIError, "HutzbotAPI Error: #{response.code}"
201
+ else
202
+ raise APIError, "HutzbotAPI Error: #{response.code}"
203
+ end
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,3 @@
1
+ module Hutzbot
2
+ VERSION = "0.1.3"
3
+ end
data/lib/hutzbot.rb ADDED
@@ -0,0 +1,5 @@
1
+ Dir[File.join(File.dirname(__FILE__), "hutzbot", "*.rb")].each { |f| require_relative f }
2
+
3
+ module Hutzbot
4
+
5
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hutzbot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - Chris Wise
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-02-19 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.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
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: rspec-mocks
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.8.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.8.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 3.5.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.5.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: httparty
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.16.4
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.16.4
97
+ description:
98
+ email:
99
+ - cwise@murmurinformatics.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - lib/hutzbot.rb
105
+ - lib/hutzbot/hutzbot.rb
106
+ - lib/hutzbot/version.rb
107
+ homepage: https://github.com/cwise/hutzbot-ruby
108
+ licenses:
109
+ - MIT
110
+ metadata:
111
+ allowed_push_host: https://rubygems.org
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 2.7.3
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: A Ruby interface to the Hutzbot service
132
+ test_files: []