2024-04-29 00:17:15 +02:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2018-08-29 11:43:00 +02:00
|
|
|
class Helpscout::API
|
|
|
|
MAILBOXES = 'mailboxes'
|
|
|
|
CONVERSATIONS = 'conversations'
|
|
|
|
TAGS = 'tags'
|
|
|
|
FIELDS = 'fields'
|
2018-11-28 15:19:12 +01:00
|
|
|
CUSTOMERS = 'customers'
|
|
|
|
PHONES = 'phones'
|
2018-08-29 11:43:00 +02:00
|
|
|
OAUTH2_TOKEN = 'oauth2/token'
|
|
|
|
|
2024-06-11 19:09:34 +02:00
|
|
|
RATELIMIT_KEY = "helpscout-rate-limit-remaining"
|
|
|
|
|
2019-01-03 17:24:24 +01:00
|
|
|
def ready?
|
|
|
|
required_secrets = [
|
|
|
|
Rails.application.secrets.helpscout[:mailbox_id],
|
|
|
|
Rails.application.secrets.helpscout[:client_id],
|
|
|
|
Rails.application.secrets.helpscout[:client_secret]
|
|
|
|
]
|
|
|
|
required_secrets.all?(&:present?)
|
|
|
|
end
|
|
|
|
|
2018-08-29 11:43:00 +02:00
|
|
|
def add_tags(conversation_id, tags)
|
|
|
|
call_api(:put, "#{CONVERSATIONS}/#{conversation_id}/#{TAGS}", {
|
|
|
|
tags: tags
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
2024-06-13 13:27:32 +02:00
|
|
|
def create_conversation(email, subject, text, blob)
|
2018-08-29 11:43:00 +02:00
|
|
|
body = {
|
|
|
|
subject: subject,
|
|
|
|
customer: customer(email),
|
2019-09-11 17:16:48 +02:00
|
|
|
mailboxId: user_support_mailbox_id,
|
2018-08-29 11:43:00 +02:00
|
|
|
type: 'email',
|
|
|
|
status: 'active',
|
|
|
|
threads: [
|
|
|
|
{
|
|
|
|
type: 'customer',
|
|
|
|
customer: customer(email),
|
|
|
|
text: text,
|
2024-06-13 13:27:32 +02:00
|
|
|
attachments: attachments(blob)
|
2018-08-29 11:43:00 +02:00
|
|
|
}
|
|
|
|
]
|
|
|
|
}.compact
|
|
|
|
|
|
|
|
call_api(:post, CONVERSATIONS, body)
|
|
|
|
end
|
|
|
|
|
2024-06-11 19:09:34 +02:00
|
|
|
def list_old_conversations(status, before, page: 1)
|
|
|
|
body = {
|
|
|
|
page:,
|
|
|
|
status:, # active, open, closed, pending, spam. "all" does not work
|
|
|
|
query: "(
|
|
|
|
modifiedAt:[* TO #{before.iso8601}]
|
|
|
|
)",
|
|
|
|
sortField: "modifiedAt",
|
|
|
|
sortOrder: "desc"
|
|
|
|
}
|
|
|
|
|
|
|
|
response = call_api(:get, "#{CONVERSATIONS}?#{body.to_query}")
|
|
|
|
if !response.success?
|
|
|
|
raise StandardError, "Error while listing conversations: #{response.response_code} '#{response.body}'"
|
|
|
|
end
|
|
|
|
|
|
|
|
body = parse_response_body(response)
|
|
|
|
[body[:_embedded][:conversations], body[:page]]
|
|
|
|
end
|
|
|
|
|
|
|
|
def delete_conversation(conversation_id)
|
|
|
|
call_api(:delete, "#{CONVERSATIONS}/#{conversation_id}")
|
|
|
|
end
|
|
|
|
|
2018-11-28 15:19:12 +01:00
|
|
|
def add_phone_number(email, phone)
|
2021-12-07 12:07:48 +01:00
|
|
|
query = CGI.escape("(email:#{email})")
|
2019-09-11 17:16:48 +02:00
|
|
|
response = call_api(:get, "#{CUSTOMERS}?mailbox=#{user_support_mailbox_id}&query=#{query}")
|
2018-11-28 15:19:12 +01:00
|
|
|
if response.success?
|
|
|
|
body = parse_response_body(response)
|
|
|
|
if body[:page][:totalElements] > 0
|
|
|
|
customer_id = body[:_embedded][:customers].first[:id]
|
|
|
|
call_api(:post, "#{CUSTOMERS}/#{customer_id}/#{PHONES}", {
|
|
|
|
type: "work",
|
|
|
|
value: phone
|
|
|
|
})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-09-11 17:16:48 +02:00
|
|
|
def productivity_report(year, month)
|
|
|
|
Rails.logger.info("[HelpScout API] Retrieving productivity report for #{month}-#{year}…")
|
2018-12-18 10:35:23 +01:00
|
|
|
|
|
|
|
params = {
|
2019-09-11 17:16:48 +02:00
|
|
|
mailboxes: [user_support_mailbox_id].join(','),
|
2018-12-18 10:35:23 +01:00
|
|
|
start: Time.utc(year, month).iso8601,
|
|
|
|
end: Time.utc(year, month).next_month.iso8601
|
|
|
|
}
|
|
|
|
|
2019-09-11 17:16:48 +02:00
|
|
|
response = call_api(:get, 'reports/productivity?' + params.to_query)
|
2018-12-18 10:35:23 +01:00
|
|
|
if !response.success?
|
2019-09-11 17:16:48 +02:00
|
|
|
raise StandardError, "Error while fetching productivity report: #{response.response_code} '#{response.body}'"
|
2018-12-18 10:35:23 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
parse_response_body(response)
|
|
|
|
end
|
|
|
|
|
2018-08-29 11:43:00 +02:00
|
|
|
private
|
|
|
|
|
2024-06-13 13:27:32 +02:00
|
|
|
def attachments(blob)
|
|
|
|
if blob.present?
|
2018-08-29 11:43:00 +02:00
|
|
|
[
|
|
|
|
{
|
2024-06-13 13:27:32 +02:00
|
|
|
fileName: blob.filename,
|
|
|
|
mimeType: blob.content_type,
|
|
|
|
data: Base64.strict_encode64(blob.download)
|
2018-08-29 11:43:00 +02:00
|
|
|
}
|
|
|
|
]
|
|
|
|
else
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def customer(email)
|
|
|
|
{
|
|
|
|
email: email
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def custom_fields
|
|
|
|
@custom_fields ||= get_custom_fields.reduce({}) do |fields, field|
|
|
|
|
fields[field[:name].to_sym] = field[:id]
|
|
|
|
fields
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_custom_fields
|
|
|
|
parse_response_body(fetch_custom_fields)[:_embedded][:fields]
|
|
|
|
end
|
|
|
|
|
|
|
|
def fetch_custom_fields
|
2019-09-11 17:16:48 +02:00
|
|
|
call_api(:get, "#{MAILBOXES}/#{user_support_mailbox_id}/#{FIELDS}")
|
2018-08-29 11:43:00 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def call_api(method, path, body = nil)
|
|
|
|
url = "#{HELPSCOUT_API_URL}/#{path}"
|
|
|
|
|
|
|
|
case method
|
|
|
|
when :get
|
|
|
|
Typhoeus.get(url, {
|
|
|
|
headers: headers
|
|
|
|
})
|
|
|
|
when :post
|
|
|
|
Typhoeus.post(url, {
|
|
|
|
body: body.to_json,
|
|
|
|
headers: headers
|
|
|
|
})
|
|
|
|
when :put
|
|
|
|
Typhoeus.put(url, {
|
|
|
|
body: body.to_json,
|
|
|
|
headers: headers
|
|
|
|
})
|
2024-06-11 19:09:34 +02:00
|
|
|
when :delete
|
|
|
|
Typhoeus.delete(url, {
|
|
|
|
body: body.to_json,
|
|
|
|
headers: headers
|
|
|
|
})
|
|
|
|
end.tap do |response|
|
|
|
|
Rails.cache.write(RATELIMIT_KEY, response.headers["X-Ratelimit-Remaining-Minute"], expires_in: 1.minute)
|
2018-08-29 11:43:00 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def parse_response_body(response)
|
|
|
|
JSON.parse(response.body, symbolize_names: true)
|
|
|
|
end
|
|
|
|
|
2019-09-11 17:16:48 +02:00
|
|
|
def user_support_mailbox_id
|
2018-08-29 11:43:00 +02:00
|
|
|
Rails.application.secrets.helpscout[:mailbox_id]
|
|
|
|
end
|
|
|
|
|
|
|
|
def headers
|
|
|
|
{
|
|
|
|
'Authorization': "Bearer #{access_token}",
|
|
|
|
'Content-Type': 'application/json; charset=UTF-8'
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def access_token
|
|
|
|
@access_token ||= get_access_token
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_access_token
|
|
|
|
parse_response_body(fetch_access_token)[:access_token]
|
|
|
|
end
|
|
|
|
|
|
|
|
def fetch_access_token
|
|
|
|
Typhoeus.post("#{HELPSCOUT_API_URL}/#{OAUTH2_TOKEN}", body: {
|
|
|
|
grant_type: 'client_credentials',
|
|
|
|
client_id: Rails.application.secrets.helpscout[:client_id],
|
|
|
|
client_secret: Rails.application.secrets.helpscout[:client_secret]
|
|
|
|
})
|
|
|
|
end
|
|
|
|
end
|