lita-zendesk 0.0.6 → 0.1.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 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