lita-zendesk 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f63a7f1bbf0fd4a6a93e619e1f763fd63e995393
4
- data.tar.gz: 8d74f5d3f2c59e1b9a505b63eb3d510460ce51b0
3
+ metadata.gz: 38716ec59f092963728bc33ed8856579870eda1a
4
+ data.tar.gz: 967a1bba6739466e7522a8c7bb4c3327b2ef7612
5
5
  SHA512:
6
- metadata.gz: 21f85ea9ec84d9e7cc735408da833d378e7939d319957ed8fe20b17104bd058bb6231fc1ad748095fa505ec186c5f5830059c1018153f36b23827489e95d1c84
7
- data.tar.gz: 8500af1c1e8f18900892da420e741130335b3147a29fb197623af66669da48291faf197df75a24abf1ca977c045b47c5a5e1d92939524eba1238e43e4ac71858
6
+ metadata.gz: 9684aee6cc7da35e3bbef1ba930842d51b8464e6e6cc753e9f99f15c68cb02e8e6b2f90c8e81b760ff4ff40c9617f3443a9a4b990f391a632e6368ecf0235052
7
+ data.tar.gz: 3c906b02640c45574f335bee74a7a7032b8f44499c917a31358c65ff72a2021a6e3d4e4d013a92f86df91094cd17d383fc1b99d5432f738f207a0a0eb7524d76
@@ -1,5 +1,12 @@
1
1
  CHANGELOG
2
2
  ---------
3
+ - **0.1.0** - 2015-09-12
4
+ - Additions
5
+ - Add comments to ticket details
6
+ - Add ticket requester to ticket details
7
+ - Add use of official Zendesk SDK
8
+ - Breaking Changes
9
+ - Update config to match Zendesk SDK, `user` is now `username`
3
10
  - **0.0.6** - 2016-09-05
4
11
  - Make routes case insensitive
5
12
  - Add additional logging
data/README.md CHANGED
@@ -11,7 +11,7 @@ Lita Zendesk Handler
11
11
  [![Docs][docs-rubydoc-svg]][docs-rubydoc-link]
12
12
  [![License][license-svg]][license-link]
13
13
 
14
- `lita-zendesk` is an handler for [Lita](https://www.lita.io/) that allows you to use the robot with [Zendesk](https://zendesk.com/) ticket queries.
14
+ `lita-zendesk` is a handler for [Lita](https://www.lita.io/) that allows you to use the robot with [Zendesk](https://zendesk.com/) ticket queries.
15
15
 
16
16
  ## Installation
17
17
 
@@ -23,27 +23,23 @@ gem "lita-zendesk"
23
23
 
24
24
  ## Configuration
25
25
 
26
- Both Token and Password authentication are supported using the `config.handlers.zendesk.auth_type` property which can be set to `token` or `password`.
26
+ Both Token and Password authentication are supported.
27
27
 
28
28
  ``` ruby
29
29
  Lita.configure do |config|
30
30
 
31
31
  # Zendesk user info
32
32
  config.handlers.zendesk.subdomain = 'my_zendesk_subdomain'
33
- config.handlers.zendesk.auth_type = 'password' # set to 'password' or 'token'
34
- config.handlers.zendesk.user = 'my_zendesk_user' # required for both 'password' and 'token'
35
- config.handlers.zendesk.password = 'my_zendesk_password'
36
- config.handlers.zendesk.token = 'my_zendesk_token'
37
-
38
- # Optional config
39
- config.handlers.zendesk.use_command = false # defaults to true
33
+ config.handlers.zendesk.username = 'my_zendesk_username'
34
+ config.handlers.zendesk.token = 'my_zendesk_token' # Use token or password
35
+ config.handlers.zendesk.password = 'my_zendesk_password' # Use token or password
40
36
 
41
37
  end
42
38
  ```
43
39
 
44
40
  ## Usage
45
41
 
46
- `zd` or `zendesk` both work for signaling the handler.
42
+ `zd` or `zendesk` both work for triggering the handler.
47
43
 
48
44
  ```
49
45
  Lita > @lita help
@@ -62,7 +58,7 @@ Lita: zd list pending tickets - returns a list of pending tickets
62
58
  Lita: zd list new tickets - returns a list of new tickets
63
59
  Lita: zd list esclated tickets - returns a list of escalated tickets
64
60
  Lita: zd list open tickets - returns a list of open tickets
65
- Lita: zd list onhold tickets - returns a list of on hold tickets
61
+ Lita: zd list on hold tickets - returns a list of on hold tickets
66
62
  Lita: zd ticket <ID> - returns information about the specified ticket
67
63
  ```
68
64
 
@@ -1,70 +1,48 @@
1
- require 'base64'
2
- require 'faraday'
3
- require 'faraday_middleware'
4
-
5
- require 'pp'
1
+ require 'zendesk_api'
6
2
 
7
3
  module Lita
8
4
  module Handlers
9
5
  class Zendesk < Handler
10
6
 
11
- VERSION_URL = 'api/v2'
12
- QUERY_SEARCH_PREFIX = 'search.json?query='
13
- QUERY_TICKETS_SEARCH = 'search.json?query=type:ticket '
14
- QUERY_TICKETS_ALL = 'tickets'
15
- QUERY_TICKETS_ESCALATED = 'search.json?query=tags:escalated+status:open+status:pending+type:ticket'
16
- QUERY_TICKETS_HOLD = 'search.json?query=status:hold+type:ticket'
17
- QUERY_TICKETS_OPEN = 'search.json?query=status:open+type:ticket'
18
- QUERY_TICKETS_NEW = 'search.json?query=status:new+type:ticket'
19
- QUERY_TICKETS_PENDING = 'search.json?query=status:pending+type:ticket'
20
- QUERY_TICKETS_UNSOLVED = 'search.json?query=status<solved+type:ticket'
7
+ API_VERSION_URL_PATH = 'api/v2'
8
+ WEB_TICKETS_URL_PATH = 'tickets'
9
+ QUERY_TICKETS_ALL = 'type:ticket'
10
+ QUERY_TICKETS_ESCALATED = 'type:ticket tags:escalated status:open status:pending'
11
+ QUERY_TICKETS_HOLD = 'type:ticket status:hold'
12
+ QUERY_TICKETS_OPEN = 'type:ticket status:open'
13
+ QUERY_TICKETS_NEW = 'type:ticket status:new'
14
+ QUERY_TICKETS_PENDING = 'type:ticket status:pending'
15
+ QUERY_TICKETS_UNSOLVED = 'type:ticket status<solved'
21
16
  QUERY_USERS = 'users'
22
17
 
23
- config :use_command, types: [TrueClass, FalseClass], default: true
24
-
25
18
  config :subdomain, type: String, required: true
26
- config :auth_type, type: String, default: 'password' # or token
27
- config :user, type: String, required: true
19
+ config :username, type: String, required: true
28
20
  config :token, type: String, default: ''
29
21
  config :password, type: String, default: ''
30
22
 
31
- def check_client(reload = false)
32
- return if @conn && !reload
33
- @base_url = base_url
34
- @version_url = "#{@base_url}/#{VERSION_URL}"
35
- @tickets_url = "#{@base_url}/tickets"
36
-
37
- if config.auth_type == 'password'
38
- @conn = Faraday.new(url: @version_url) do |faraday|
39
- faraday.headers['Authorization'] = "Basic #{basic_credentials}"
40
- faraday.response :json # JSON response
41
- faraday.response :logger # log requests to STDOUT
42
- faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
43
- end
44
- else
45
- @conn = Faraday.new(url: @version_url) do |faraday|
46
- faraday.response :json # JSON response
47
- faraday.response :logger # log requests to STDOUT
48
- faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
49
- end
50
- @conn.basic_auth("#{config.user}/token", config.token)
23
+ config :max_results, type: Integer, default: 10
24
+
25
+ def client
26
+ return @_client if @_client
27
+ Lita.logger.info "#{logger_prefix}Connecting Zendesk Client to #{api_version_url}"
28
+ @_client = ZendeskAPI::Client.new do |client|
29
+ client.url = api_version_url.to_s
30
+ client.username = config.username || config.user
31
+ client.token = config.token
32
+ client.password = config.password
51
33
  end
52
34
  end
53
35
 
54
36
  def base_url
55
- "https://#{config.subdomain.to_s}.zendesk.com"
37
+ "https://#{config.subdomain}.zendesk.com"
56
38
  end
57
39
 
58
- def basic_credentials
59
- Base64.encode64("#{config.user}:#{config.password}").gsub(/\s/,'')
40
+ def api_version_url
41
+ uri_join base_url, API_VERSION_URL_PATH
60
42
  end
61
43
 
62
- def zendesk_request(url)
63
- check_client
64
- if url.index('http') != 0
65
- url = "#{@version_url}/#{url}"
66
- end
67
- @conn.get url
44
+ def web_tickets_url
45
+ uri_join base_url, WEB_TICKETS_URL_PATH
68
46
  end
69
47
 
70
48
  # General
@@ -76,7 +54,7 @@ module Lita
76
54
 
77
55
  route(/^(?:zd|zendesk)\s+search\s+tickets?\s+(\S.*?)\s*$/i, :search_tickets, command: true, help: { 'zd search tickets <QUERY>' => 'returns search results' })
78
56
  def search_tickets(response)
79
- ticket_search response, QUERY_TICKETS_SEARCH, response.matches[0][0]
57
+ ticket_search response, response.matches[0][0]
80
58
  end
81
59
 
82
60
  # Ticket Counts
@@ -111,7 +89,7 @@ module Lita
111
89
  ticket_count response, QUERY_TICKETS_OPEN, 'open'
112
90
  end
113
91
 
114
- route(/^(?:zd|zendesk)\s+on\s+hold\s+tickets?\s*$/i, :onhold_tickets, command: true, help: { 'zd on hold tickets' => 'returns the count of all on hold tickets' })
92
+ route(/^(?:zd|zendesk)\s+on\s*hold\s+tickets?\s*$/i, :onhold_tickets, command: true, help: { 'zd on hold tickets' => 'returns the count of all on hold tickets' })
115
93
  def onhold_tickets(response)
116
94
  ticket_count response, QUERY_TICKETS_HOLD, 'on hold'
117
95
  end
@@ -148,73 +126,134 @@ module Lita
148
126
  ticket_list response, QUERY_TICKETS_OPEN, 'open'
149
127
  end
150
128
 
151
- route(/^(?:zd|zendesk)\s+list\s+on\s+hold\s+tickets?\s*$/i, :onhold_tickets_list, command: true, help: { 'zd list onhold tickets' => 'returns a list of on hold tickets' })
129
+ route(/^(?:zd|zendesk)\s+list\s+on\s*hold\s+tickets?\s*$/i, :onhold_tickets_list, command: true, help: { 'zd list on hold tickets' => 'returns a list of on hold tickets' })
152
130
  def onhold_tickets_list(response)
153
131
  ticket_list response, QUERY_TICKETS_HOLD, 'on hold'
154
132
  end
155
133
 
156
134
  # Ticket Details
157
135
 
158
- route(/^(?:zd|zendesk)\s+ticket\s+(\d+)\s*$/i, :ticket_details, command: true, help: { 'zd ticket <ID>' => 'returns information about the specified ticket' })
159
- def ticket_details(response)
136
+ route(/^(?:zd|zendesk)\s+ticket\s+(\d+)\s*$/i, :ticket_details_with_comments, command: true, help: { 'zd ticket <ID>' => 'returns information about the specified ticket' })
137
+ def ticket_details_with_comments(response)
160
138
  Lita.logger.info "#{logger_prefix}Processing Zendesk Ticket Details"
161
- ticket_id = response.matches[0][0]
162
- url = "#{QUERY_TICKETS_ALL}/#{ticket_id}.json"
163
- res = zendesk_request url
164
- data = res.body
165
-
166
- message = "Ticket #{data['ticket']['id']}: #{@tickets_url}/#{data['ticket']['id']}"
167
- message += "\nStatus: #{data['ticket']['status'].upcase}"
168
- message += "\nUpdated: " + data['ticket']['updated_at']
169
- message += "\nAdded: #{data['ticket']['created_at']}"
170
- message += "\nSubject: #{data['ticket']['subject']}"
171
- message += "\nDescription:\n-----\n#{data['ticket']['description']}\n-----\n"
172
- response.reply message
139
+ ticket_id = response.matches[0][0].to_i
140
+ begin
141
+ ticket = client.ticket.find!(id: ticket_id)
142
+ response.reply get_text_for_ticket_with_comments(ticket)
143
+ rescue => e
144
+ Lita.logger.warn "#{logger_prefix}#{e}"
145
+ response.reply "Error processing ticket #{ticket_id}"
146
+ end
173
147
  end
174
148
 
175
149
  private
176
150
 
177
- def ticket_count(response, url, ticket_type = '')
178
- Lita.logger.info "#{logger_prefix}Processing Zendesk Ticket Count"
179
- res = zendesk_request url
180
- ticket_count = res.body['count']
181
- ticket_word = ticket_count == 1 ? 'ticket' : 'tickets'
182
- ticket_desc = ticket_type == '' ? '' : "#{ticket_type} "
183
- response.reply "#{ticket_count} #{ticket_desc}#{ticket_word}."
151
+ def get_text_for_ticket(ticket, include_description = true)
152
+ Lita.logger.info "#{logger_prefix}Processing Zendesk ticket details for [#{ticket.id}]"
153
+ unless ticket.is_a? ZendeskAPI::Ticket
154
+ raise 'ticket is not a ZendeskAPI::Ticket'
155
+ end
156
+ message = "# Ticket #{ticket.id}: #{ticket_url_web(ticket.id)}"
157
+ message += "\n- Subject: #{ticket.subject}"
158
+ message += "\n- Status: #{ticket.status}"
159
+ message += "\n- Updated: #{ticket.updated_at}"
160
+ message += "\n- Created: #{ticket.created_at}"
161
+ message += "\n- Requester: #{user_display(ticket.requester)}"
162
+ message += "\n- Description: #{ticket.description}" if include_description
163
+ return message
184
164
  end
185
165
 
186
- def ticket_search(response, url, query)
187
- Lita.logger.info "#{logger_prefix}Processing Zendesk Ticket Search"
188
- url += query
189
- res = zendesk_request url
190
- tickets = res.body['results']
191
- tickets.each do |ticket|
192
- response.reply "Ticket #{ticket['id']} is #{ticket['status']}: #{@tickets_url}/#{ticket['id']} - #{ticket['subject']}"
166
+ def get_text_for_ticket_comments(ticket)
167
+ Lita.logger.info "#{logger_prefix}Processing Zendesk ticket comments for [#{ticket.id}]"
168
+ unless ticket.is_a? ZendeskAPI::Ticket
169
+ raise 'ticket is not a ZendeskAPI::Ticket'
170
+ end
171
+ comments_text = []
172
+ ticket.audits.each_with_index do |audit,i|
173
+ if (comment = audit.events.detect {|e| e.type.downcase == 'comment'})
174
+ author_text = user_display comment.author
175
+ comment_text = "## Comment: #{author_text}"
176
+ comment_text += "\n- Created: #{audit.created_at}"
177
+ comment_text += "\n- Comment: #{comment.body}"
178
+ comments_text.push comment_text
179
+ end
180
+ end
181
+ return comments_text.reverse.join("\n")
182
+ end
183
+
184
+ def get_text_for_ticket_with_comments(ticket)
185
+ Lita.logger.info "#{logger_prefix}Processing Zendesk ticket details for [#{ticket.id}] with comments"
186
+ message = get_text_for_ticket ticket, false
187
+ comments = get_text_for_ticket_comments ticket
188
+ if !comments.nil? && comments.length>0
189
+ message += "\n#{comments}"
190
+ end
191
+ return message
192
+ end
193
+
194
+ def ticket_count(response, query, ticket_type = '')
195
+ Lita.logger.info "#{logger_prefix}Processing Zendesk Ticket Count Query [#{query}]"
196
+ begin
197
+ ticket_count = client.search!(query: query).count
198
+ Lita.logger.info "#{logger_prefix}Ticket count #{ticket_count}"
199
+ ticket_desc = ticket_type == '' ? '' : "#{ticket_type} "
200
+ ticket_word = ticket_count == 1 ? 'ticket' : 'tickets'
201
+ response.reply "#{ticket_count} #{ticket_desc}#{ticket_word}."
202
+ rescue
203
+ response.reply "A Zendesk error has been encountered."
193
204
  end
194
- ticket_length = tickets.length
195
- ticket_count = res.body['count']
196
- ticket_word = ticket_count == 1 ? 'result' : 'results'
197
- response.reply "Listing #{ticket_length} of #{ticket_count} matching #{ticket_word}."
198
- end
199
-
200
- def ticket_list(response, url, ticket_type = '')
201
- Lita.logger.info "#{logger_prefix}Processing Zendesk Ticket List"
202
- res = zendesk_request url
203
- tickets = res.body['results']
204
- tickets.each do |ticket|
205
- response.reply "Ticket #{ticket['id']} is #{ticket['status']}: #{@tickets_url}/#{ticket['id']} - #{ticket['subject']}"
205
+ end
206
+
207
+ def ticket_search(response, raw_query)
208
+ Lita.logger.info "#{logger_prefix}Processing Zendesk Ticket Search"
209
+ tickets = client.search!(query: "#{QUERY_TICKETS_ALL} #{raw_query}")
210
+ serp_count = reply_tickets response, tickets
211
+ response.reply "Listing #{serp_count} of #{tickets.count} #{ticket_word(tickets.count)} matching #{raw_query}."
212
+ end
213
+
214
+ def ticket_list(response, query, ticket_status = '')
215
+ Lita.logger.info "#{logger_prefix}Processing Zendesk ticket list query"
216
+ tickets = client.search!(query: query)
217
+ serp_count = reply_tickets response, tickets
218
+ ticket_desc = ticket_status == '' ? '' : "#{ticket_status} "
219
+ response.reply "Listing #{serp_count} of #{tickets.count} #{ticket_desc}#{ticket_word(tickets.count)}."
220
+ end
221
+
222
+ def reply_tickets(response, tickets)
223
+ serp_count = 0
224
+ tickets.each_with_index do |ticket, i|
225
+ break if i >= config.max_results
226
+ serp_count += 1
227
+ response.reply "Ticket #{ticket.id} is #{ticket.status}: #{ticket_url_web(ticket.id)} - #{ticket.subject}"
206
228
  end
207
- ticket_length = tickets.length
208
- ticket_count = res.body['count']
209
- ticket_word = ticket_count == 1 ? 'ticket' : 'tickets'
210
- ticket_desc = ticket_type == '' ? '' : "#{ticket_type} "
211
- response.reply "Listing #{ticket_length} of #{ticket_count} #{ticket_desc}#{ticket_word}."
229
+ return serp_count
212
230
  end
213
231
 
214
- def tickets(count)
232
+ def ticket_url_web(ticket_id)
233
+ uri_join web_tickets_url.to_s, ticket_id.to_s
234
+ end
235
+
236
+ def ticket_word(count)
215
237
  count == 1 ? 'ticket' : 'tickets'
216
238
  end
217
239
 
240
+ def user_display(user)
241
+ parts = []
242
+ if user.name.to_s.length > 0
243
+ parts.push user.name
244
+ end
245
+ if user.email.to_s.length > 0
246
+ if parts.length < 1 || user.email != user.name
247
+ parts.push "(#{user.email})"
248
+ end
249
+ end
250
+ user_text = parts.join(' ')
251
+ end
252
+
253
+ def uri_join(*args)
254
+ args.join('/').gsub(/\/\s*\//, '/').gsub(/\/+/, '/').gsub(/^(https?:\/)/i, '\1/')
255
+ end
256
+
218
257
  def logger_prefix
219
258
  " -- #{self.class.name}: "
220
259
  end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = 'lita-zendesk'
3
- spec.date = '2016-09-05'
4
- spec.version = '0.0.6'
3
+ spec.date = '2016-09-13'
4
+ spec.version = '0.1.0'
5
5
  spec.authors = ['John Wang']
6
6
  spec.email = ['johncwang@gmail.com']
7
7
  spec.description = %q{A Zendesk handler for Lita.}
@@ -15,9 +15,8 @@ Gem::Specification.new do |spec|
15
15
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
16
  spec.require_paths = ['lib']
17
17
 
18
- spec.add_runtime_dependency 'faraday', '~> 0.9', '>= 0.9'
19
- spec.add_runtime_dependency 'faraday_middleware', '~> 0', '>= 0'
20
18
  spec.add_runtime_dependency 'lita', '>= 4.4.3'
19
+ spec.add_runtime_dependency 'zendesk_api', '>= 1.14.0', '~> 1.0'
21
20
 
22
21
  spec.add_development_dependency 'bundler', '~> 1.3'
23
22
  spec.add_development_dependency 'pry-byebug'
metadata CHANGED
@@ -1,69 +1,49 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lita-zendesk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Wang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-05 00:00:00.000000000 Z
11
+ date: 2016-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: faraday
14
+ name: lita
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '0.9'
20
17
  - - ">="
21
18
  - !ruby/object:Gem::Version
22
- version: '0.9'
19
+ version: 4.4.3
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '0.9'
30
24
  - - ">="
31
25
  - !ruby/object:Gem::Version
32
- version: '0.9'
26
+ version: 4.4.3
33
27
  - !ruby/object:Gem::Dependency
34
- name: faraday_middleware
28
+ name: zendesk_api
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '0'
40
31
  - - ">="
41
32
  - !ruby/object:Gem::Version
42
- version: '0'
43
- type: :runtime
44
- prerelease: false
45
- version_requirements: !ruby/object:Gem::Requirement
46
- requirements:
33
+ version: 1.14.0
47
34
  - - "~>"
48
35
  - !ruby/object:Gem::Version
49
- version: '0'
50
- - - ">="
51
- - !ruby/object:Gem::Version
52
- version: '0'
53
- - !ruby/object:Gem::Dependency
54
- name: lita
55
- requirement: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - ">="
58
- - !ruby/object:Gem::Version
59
- version: 4.4.3
36
+ version: '1.0'
60
37
  type: :runtime
61
38
  prerelease: false
62
39
  version_requirements: !ruby/object:Gem::Requirement
63
40
  requirements:
64
41
  - - ">="
65
42
  - !ruby/object:Gem::Version
66
- version: 4.4.3
43
+ version: 1.14.0
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.0'
67
47
  - !ruby/object:Gem::Dependency
68
48
  name: bundler
69
49
  requirement: !ruby/object:Gem::Requirement