helpscout 0.0.3.alpha
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +12 -0
- data/README.markdown +40 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/helpscout.gemspec +64 -0
- data/lib/helpscout.rb +6 -0
- data/lib/helpscout/base.rb +270 -0
- data/lib/helpscout/models.rb +397 -0
- metadata +166 -0
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Helpscout Developers API gem
|
2
|
+
|
3
|
+
This gem is in alpha.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
1. Follow the instructions at [Help Scout's Developers site](http://developer.helpscout.net/) to generate an API key.
|
8
|
+
|
9
|
+
2. Initialize your HelpScout client
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
HelpScout::Base.load!(HELPSCOUT_API_KEY)
|
13
|
+
```
|
14
|
+
|
15
|
+
#### Fetching Users
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
users = HelpScout::Base.users
|
19
|
+
```
|
20
|
+
|
21
|
+
#### Fetching Mailboxes
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
mailboxes = HelpScout::Base.mailboxes
|
25
|
+
```
|
26
|
+
|
27
|
+
#### Fetching Customers
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
customers = HelpScout::Base.customers
|
31
|
+
```
|
32
|
+
|
33
|
+
#### Fetching Conversations
|
34
|
+
|
35
|
+
To fetch active conversations:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
conversations = HelpScout::Base.conversations(mailboxId, "active", nil)
|
39
|
+
```
|
40
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
require 'base64'
|
6
|
+
require 'simplecov'
|
7
|
+
begin
|
8
|
+
Bundler.setup(:default, :development)
|
9
|
+
rescue Bundler::BundlerError => e
|
10
|
+
$stderr.puts e.message
|
11
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
12
|
+
exit e.status_code
|
13
|
+
end
|
14
|
+
require 'rake'
|
15
|
+
|
16
|
+
require 'jeweler'
|
17
|
+
Jeweler::Tasks.new do |gem|
|
18
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
19
|
+
gem.name = "helpscout"
|
20
|
+
gem.homepage = "http://github.com/hramos/helpscout"
|
21
|
+
gem.license = "MIT"
|
22
|
+
gem.summary = %Q{HelpScout API Wrapper}
|
23
|
+
gem.description = %Q{}
|
24
|
+
gem.email = Base64.decode64("aGVjdG9yQGhlY3RvcnJhbW9zLmNvbQ==\n")
|
25
|
+
gem.authors = ["Héctor Ramos"]
|
26
|
+
# dependencies defined in Gemfile
|
27
|
+
end
|
28
|
+
Jeweler::RubygemsDotOrgTasks.new
|
29
|
+
|
30
|
+
require 'rake/testtask'
|
31
|
+
Rake::TestTask.new(:test) do |test|
|
32
|
+
test.libs << 'lib' << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
|
37
|
+
SimpleCov.start
|
38
|
+
|
39
|
+
require 'reek/rake/task'
|
40
|
+
Reek::Rake::Task.new do |t|
|
41
|
+
t.fail_on_error = true
|
42
|
+
t.verbose = false
|
43
|
+
t.source_files = 'lib/**/*.rb'
|
44
|
+
end
|
45
|
+
|
46
|
+
task :default => :test
|
47
|
+
|
48
|
+
require 'rdoc/task'
|
49
|
+
RDoc::Task.new do |rdoc|
|
50
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
51
|
+
|
52
|
+
rdoc.rdoc_dir = 'rdoc'
|
53
|
+
rdoc.title = "helpscout #{version}"
|
54
|
+
rdoc.rdoc_files.include('README*')
|
55
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
56
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.3.alpha
|
data/helpscout.gemspec
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "helpscout"
|
8
|
+
s.version = "0.0.3.alpha"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["H\u{e9}ctor Ramos"]
|
12
|
+
s.date = "2012-08-14"
|
13
|
+
s.description = ""
|
14
|
+
s.email = "hector@hectorramos.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.markdown"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
"Gemfile",
|
20
|
+
"README.markdown",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION",
|
23
|
+
"helpscout.gemspec",
|
24
|
+
"lib/helpscout.rb",
|
25
|
+
"lib/helpscout/base.rb",
|
26
|
+
"lib/helpscout/models.rb"
|
27
|
+
]
|
28
|
+
s.homepage = "http://github.com/hramos/helpscout"
|
29
|
+
s.licenses = ["MIT"]
|
30
|
+
s.require_paths = ["lib"]
|
31
|
+
s.rubygems_version = "1.8.24"
|
32
|
+
s.summary = "HelpScout API Wrapper"
|
33
|
+
|
34
|
+
if s.respond_to? :specification_version then
|
35
|
+
s.specification_version = 3
|
36
|
+
|
37
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
38
|
+
s.add_runtime_dependency(%q<httparty>, [">= 0"])
|
39
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.1.3"])
|
40
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
41
|
+
s.add_development_dependency(%q<httparty>, [">= 0"])
|
42
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
43
|
+
s.add_development_dependency(%q<reek>, ["~> 1.2.8"])
|
44
|
+
s.add_development_dependency(%q<rdoc>, [">= 0"])
|
45
|
+
else
|
46
|
+
s.add_dependency(%q<httparty>, [">= 0"])
|
47
|
+
s.add_dependency(%q<bundler>, ["~> 1.1.3"])
|
48
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
49
|
+
s.add_dependency(%q<httparty>, [">= 0"])
|
50
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
51
|
+
s.add_dependency(%q<reek>, ["~> 1.2.8"])
|
52
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
53
|
+
end
|
54
|
+
else
|
55
|
+
s.add_dependency(%q<httparty>, [">= 0"])
|
56
|
+
s.add_dependency(%q<bundler>, ["~> 1.1.3"])
|
57
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
58
|
+
s.add_dependency(%q<httparty>, [">= 0"])
|
59
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
60
|
+
s.add_dependency(%q<reek>, ["~> 1.2.8"])
|
61
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
data/lib/helpscout.rb
ADDED
@@ -0,0 +1,270 @@
|
|
1
|
+
require "date"
|
2
|
+
require "httparty"
|
3
|
+
require "helpscout/models"
|
4
|
+
require "erb"
|
5
|
+
|
6
|
+
module HelpScout
|
7
|
+
class Base
|
8
|
+
include HTTParty
|
9
|
+
base_uri 'https://api.helpscout.net/v1'
|
10
|
+
|
11
|
+
def initialize(key=nil)
|
12
|
+
Base.settings
|
13
|
+
|
14
|
+
if key.nil?
|
15
|
+
key = @@settings["api_key"]
|
16
|
+
end
|
17
|
+
|
18
|
+
@auth = { :username => key, :password => "X" }
|
19
|
+
@users = []
|
20
|
+
@mailboxes = []
|
21
|
+
end
|
22
|
+
|
23
|
+
@@settings ||= nil
|
24
|
+
|
25
|
+
def self.load!(api_key)
|
26
|
+
@auth = { :username => api_key, :password => "X" }
|
27
|
+
@users = []
|
28
|
+
@mailboxes = []
|
29
|
+
@@settings = {"api_key" => api_key}
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.settings
|
33
|
+
if @@settings.nil?
|
34
|
+
path = "config/helpscout.yml"
|
35
|
+
@@settings = YAML.load(ERB.new(File.new(path).read).result)
|
36
|
+
@auth = { :username => @@settings["api_key"], :password => "X" }
|
37
|
+
end
|
38
|
+
@@settings
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.parametrizedUrl(url, params={})
|
42
|
+
return url unless params
|
43
|
+
|
44
|
+
complete_url = url
|
45
|
+
|
46
|
+
parameterString = ""
|
47
|
+
|
48
|
+
params.each do |k,v|
|
49
|
+
if parameterString.length > 0
|
50
|
+
parameterString << "&"
|
51
|
+
else
|
52
|
+
parameterString << "?"
|
53
|
+
end
|
54
|
+
parameterString << "#{k}=#{v}"
|
55
|
+
end
|
56
|
+
|
57
|
+
if parameterString.length > 0
|
58
|
+
complete_url << parameterString
|
59
|
+
end
|
60
|
+
complete_url
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.requestItem(url, params={})
|
64
|
+
item = nil
|
65
|
+
complete_url = Base.parametrizedUrl(url, params)
|
66
|
+
|
67
|
+
response = Base.get(complete_url, {:basic_auth => @auth})
|
68
|
+
|
69
|
+
if 200 <= response.code && response.code < 300
|
70
|
+
envelope = SingleItemEnvelope.new(response)
|
71
|
+
if envelope.item
|
72
|
+
item = envelope.item
|
73
|
+
end
|
74
|
+
elsif 400 <= response.code && response.code < 500
|
75
|
+
if response["message"]
|
76
|
+
envelope = ErrorEnvelope.new(response)
|
77
|
+
raise StandardError, envelope.message
|
78
|
+
else
|
79
|
+
raise StandardError, response["error"]
|
80
|
+
end
|
81
|
+
else
|
82
|
+
raise StandardError, "Server Response: #{response.code}"
|
83
|
+
end
|
84
|
+
|
85
|
+
item
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.requestItems(url, params={})
|
89
|
+
items = []
|
90
|
+
complete_url = Base.parametrizedUrl(url, params)
|
91
|
+
|
92
|
+
response = Base.get(complete_url, {:basic_auth => @auth})
|
93
|
+
|
94
|
+
if 200 <= response.code && response.code < 300
|
95
|
+
envelope = CollectionsEnvelope.new(response)
|
96
|
+
if envelope.items
|
97
|
+
envelope.items.each do |item|
|
98
|
+
items << item
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
if envelope.page < envelope.pages
|
103
|
+
params[:page] = envelope.page + 1
|
104
|
+
items << Base.requestItems(url, params)
|
105
|
+
end
|
106
|
+
elsif 400 <= response.code && response.code < 500
|
107
|
+
if response["message"]
|
108
|
+
envelope = ErrorEnvelope.new(response)
|
109
|
+
raise StandardError, envelope.message
|
110
|
+
else
|
111
|
+
raise StandardError, response["error"]
|
112
|
+
end
|
113
|
+
else
|
114
|
+
raise StandardError, "Server Response: #{response.code}"
|
115
|
+
end
|
116
|
+
|
117
|
+
items
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.user(userId)
|
121
|
+
url = "/users/#{userId}.json"
|
122
|
+
item = Base.requestItem(url, nil)
|
123
|
+
user = nil
|
124
|
+
if item
|
125
|
+
user = User.new(item)
|
126
|
+
end
|
127
|
+
user
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.users
|
131
|
+
url = "/users.json"
|
132
|
+
items = Base.requestItems(url, :page => 1)
|
133
|
+
@users = []
|
134
|
+
items.each do |item|
|
135
|
+
@users << User.new(item)
|
136
|
+
end
|
137
|
+
@users
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.usersInMailbox(mailboxId)
|
141
|
+
url ="/mailboxes/#{mailboxId}/users.json"
|
142
|
+
items = Base.requestItems(url, :page => 1)
|
143
|
+
users = []
|
144
|
+
items.each do |item|
|
145
|
+
users << User.new(item)
|
146
|
+
end
|
147
|
+
users
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.mailboxes
|
151
|
+
url = "/mailboxes.json"
|
152
|
+
@mailboxes = []
|
153
|
+
begin
|
154
|
+
items = Base.requestItems(url, {})
|
155
|
+
items.each do |item|
|
156
|
+
@mailboxes << Mailbox.new(item)
|
157
|
+
end
|
158
|
+
rescue StandardError => e
|
159
|
+
print "Request failed: " + e.message
|
160
|
+
end
|
161
|
+
@mailboxes
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.foldersInMailbox(mailboxId)
|
165
|
+
url ="/mailboxes/#{mailboxId}/folders.json"
|
166
|
+
items = Base.requestItems(url, :page => 1)
|
167
|
+
folders = []
|
168
|
+
items.each do |item|
|
169
|
+
folders << Mailbox::Folder.new(item)
|
170
|
+
end
|
171
|
+
folders
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.conversation(conversationId)
|
175
|
+
url = "/conversations/#{conversationId}.json"
|
176
|
+
item = Base.requestItem(url, nil)
|
177
|
+
conversation = nil
|
178
|
+
if item
|
179
|
+
conversation = Conversation.new(item)
|
180
|
+
end
|
181
|
+
conversation
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
CONVERSATION_STATUS_ACTIVE = "active"
|
186
|
+
CONVERSATION_STATUS_ALL = "all"
|
187
|
+
CONVERSATION_STATUS_PENDING = "pending"
|
188
|
+
|
189
|
+
def self.conversations(mailboxId, status, modifiedSince)
|
190
|
+
url = "/mailboxes/#{mailboxId}/conversations.json"
|
191
|
+
|
192
|
+
options = {}
|
193
|
+
if status
|
194
|
+
options["status"] = status
|
195
|
+
end
|
196
|
+
if modifiedSince
|
197
|
+
# TODO: Check modifiedSince format. Needs to be Datetime in UTC
|
198
|
+
options["modifiedSince"] = modifiedSince
|
199
|
+
end
|
200
|
+
|
201
|
+
conversations = []
|
202
|
+
begin
|
203
|
+
items = Base.requestItems(url, options)
|
204
|
+
items.each do |item|
|
205
|
+
conversations << Conversation.new(item)
|
206
|
+
end
|
207
|
+
rescue StandardError => e
|
208
|
+
print "Request failed: " + e.message
|
209
|
+
end
|
210
|
+
conversations
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.customer(customerId)
|
214
|
+
url = "/customers/#{customerId}.json"
|
215
|
+
item = Base.requestItem(url, nil)
|
216
|
+
customer = nil
|
217
|
+
if item
|
218
|
+
customer = Customer.new(item)
|
219
|
+
end
|
220
|
+
customer
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.customers
|
224
|
+
url = "/customers.json"
|
225
|
+
items = Base.requestItems(url, :page => 1)
|
226
|
+
customers = []
|
227
|
+
items.each do |item|
|
228
|
+
customers << Customer.new(item)
|
229
|
+
end
|
230
|
+
customers
|
231
|
+
end
|
232
|
+
|
233
|
+
def self.attachmentData(attachmentId)
|
234
|
+
url = "/attachments/#{attachmentId}/data.json"
|
235
|
+
item = Base.requestItem(url, nil)
|
236
|
+
attachmentData = nil
|
237
|
+
if item
|
238
|
+
attachmentData = Conversation::AttachmentData.new(item)
|
239
|
+
end
|
240
|
+
attachmentData
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# TODO: check response, if 200, it's one of the first two.
|
245
|
+
# If 403, error
|
246
|
+
class SingleItemEnvelope
|
247
|
+
attr_accessor :item
|
248
|
+
def initialize(object)
|
249
|
+
@item = object["item"]
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
class CollectionsEnvelope
|
254
|
+
attr_accessor :page, :pages, :count, :items
|
255
|
+
def initialize(object)
|
256
|
+
@page = object["page"]
|
257
|
+
@pages = object["pages"]
|
258
|
+
@count = object["count"]
|
259
|
+
@items = object["items"]
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
class ErrorEnvelope
|
264
|
+
attr_accessor :status, :message
|
265
|
+
def initialize(object)
|
266
|
+
@status = object["status"]
|
267
|
+
@message = object["message"]
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
@@ -0,0 +1,397 @@
|
|
1
|
+
require "date"
|
2
|
+
require "httparty"
|
3
|
+
|
4
|
+
module HelpScout
|
5
|
+
class Mailbox
|
6
|
+
attr_accessor :id, :name, :slug, :email, :createdAt, :modifiedAt
|
7
|
+
def initialize(object)
|
8
|
+
@id = object["id"]
|
9
|
+
@name = object["name"]
|
10
|
+
@slug = object["slug"]
|
11
|
+
@email = object["email"]
|
12
|
+
@createdAt = DateTime.iso8601(object["createdAt"]) if object["createdAt"]
|
13
|
+
@modifiedAt = DateTime.iso8601(object["modifiedAt"]) if object["modifiedAt"]
|
14
|
+
end
|
15
|
+
|
16
|
+
class Folder
|
17
|
+
attr_accessor :id, :name, :type, :userId, :totalCount, :activeCount, :modifiedAt
|
18
|
+
|
19
|
+
FOLDER_TYPE_UNASSIGNED = "unassigned"
|
20
|
+
FOLDER_TYPE_MY_TICKETS = "mytickets"
|
21
|
+
FOLDER_TYPE_DRAFTS = "drafts"
|
22
|
+
FOLDER_TYPE_ASSIGNED = "assigned"
|
23
|
+
FOLDER_TYPE_CLOSED = "closed"
|
24
|
+
FOLDER_TYPE_SPAM = "spam"
|
25
|
+
|
26
|
+
def initialize(object)
|
27
|
+
@id = object["id"]
|
28
|
+
@name = object["name"]
|
29
|
+
@type = object["type"]
|
30
|
+
@userId = object["userId"] # 0, unless type == FOLDER_TYPE_MY_TICKETS
|
31
|
+
@totalCount = object["totalCount"]
|
32
|
+
@activeCount = object["activeCount"]
|
33
|
+
@modifiedAt = DateTime.iso8601(object["modifiedAt"]) if object["modifiedAt"]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Conversation
|
39
|
+
attr_accessor :id, :folderId, :isDraft, :number, :owner, :mailbox, :customer, :threadCount, :status, :subject, :preview, :createdBy, :createdAt, :modifiedAt, :closedAt, :closedBy, :source, :cc, :bcc, :tags, :threads
|
40
|
+
|
41
|
+
CONVERSATION_STATUS_ACTIVE = "active"
|
42
|
+
CONVERSATION_STATUS_PENDING = "pending"
|
43
|
+
CONVERSATION_STATUS_CLOSED = "closed"
|
44
|
+
CONVERSATION_STATUS_SPAM = "spam"
|
45
|
+
|
46
|
+
def initialize(object)
|
47
|
+
@id = object["id"]
|
48
|
+
@folderId = object["folderId"]
|
49
|
+
@isDraft = object["isDraft"]
|
50
|
+
@number = object["number"]
|
51
|
+
@owner = User.new(object["owner"]) if object["owner"]
|
52
|
+
@mailbox = Mailbox.new(object["mailbox"]) if object["mailbox"]
|
53
|
+
@customer = Customer.new(object["customer"]) if object["customer"]
|
54
|
+
@threadCount = object["threadCount"]
|
55
|
+
@status = object["status"]
|
56
|
+
@subject = object["subject"]
|
57
|
+
@preview = object["preview"]
|
58
|
+
@createdAt = DateTime.iso8601(object["createdAt"]) if object["createdAt"]
|
59
|
+
@modifiedAt = DateTime.iso8601(object["modifiedAt"]) if object["modifiedAt"]
|
60
|
+
@closedAt = DateTime.iso8601(object["closedAt"]) if object["closedAt"]
|
61
|
+
@closedBy = User.new(object["closedBy"]) if object["closedBy"]
|
62
|
+
|
63
|
+
@source = nil
|
64
|
+
if object["source"]
|
65
|
+
@source = Source.new(object["source"])
|
66
|
+
|
67
|
+
if object["createdBy"]
|
68
|
+
if @source.type == Source::SOURCE_VIA_CUSTOMER
|
69
|
+
@createdBy = Customer.new(object["createdBy"])
|
70
|
+
elsif @source.type == Source::SOURCE_VIA_USER
|
71
|
+
@createdBy = User.new(object["createdBy"])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
@cc = object["cc"]
|
77
|
+
@bcc = object["bcc"]
|
78
|
+
@tags = object["tags"]
|
79
|
+
|
80
|
+
@threads = []
|
81
|
+
if object["threads"]
|
82
|
+
object["threads"].each do |thread|
|
83
|
+
@threads << Thread.new(thread)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_s
|
89
|
+
"Last Modified: #{@modifiedAt}\nAssigned to: #{@owner}\nSubject: #{@subject}\n#{@preview}"
|
90
|
+
end
|
91
|
+
|
92
|
+
class Attachment
|
93
|
+
attr_accessor :id, :mimeType, :filename, :size, :width, :height, :url
|
94
|
+
|
95
|
+
def initialize(object)
|
96
|
+
@id = object["id"]
|
97
|
+
@mimeType = object["mimeType"]
|
98
|
+
@filename = object["filename"]
|
99
|
+
@size = object["size"]
|
100
|
+
@width = object["width"]
|
101
|
+
@height = object["height"]
|
102
|
+
@url = object["url"]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class AttachmentData
|
107
|
+
attr_accessor :id, :data
|
108
|
+
|
109
|
+
def initialize(object)
|
110
|
+
@id = object["id"]
|
111
|
+
@data = object["data"]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class Source
|
116
|
+
attr_accessor :type, :via
|
117
|
+
|
118
|
+
SOURCE_TYPE_EMAIL = "email"
|
119
|
+
SOURCE_TYPE_WEB = "web"
|
120
|
+
SOURCE_TYPE_NOTIFICATION = "notification"
|
121
|
+
SOURCE_TYPE_EMAIL_FWD = "emailfwd"
|
122
|
+
|
123
|
+
SOURCE_VIA_USER = "user"
|
124
|
+
SOURCE_VIA_CUSTOMER = "customer"
|
125
|
+
|
126
|
+
def initialize(object)
|
127
|
+
@type = object["type"]
|
128
|
+
@via = object["via"]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class Thread
|
133
|
+
attr_accessor :id, :assignedTo, :status, :createdAt, :createdBy, :source, :fromMailbox, :type, :state, :customer, :body, :to, :cc, :bcc, :attachments
|
134
|
+
|
135
|
+
THREAD_STATE_PUBLISHED = "published"
|
136
|
+
THREAD_STATE_DRAFT = "draft"
|
137
|
+
THREAD_STATE_UNDER_REVIEW = "underreview"
|
138
|
+
|
139
|
+
THREAD_STATUS_ACTIVE = "active"
|
140
|
+
THREAD_STATUS_NO_CHANGE = "nochange"
|
141
|
+
THREAD_STATUS_PENDING = "pending"
|
142
|
+
THREAD_STATUS_CLOSED = "closed"
|
143
|
+
THREAD_STATUS_SPAM = "spam"
|
144
|
+
|
145
|
+
THREAD_TYPE_NOTE = "note"
|
146
|
+
THREAD_TYPE_MESSAGE = "message"
|
147
|
+
THREAD_TYPE_CUSTOMER = "customer"
|
148
|
+
|
149
|
+
# A lineitem represents a change of state on the conversation. This could include, but not limited to, the conversation was assigned, the status changed, the conversation was moved from one mailbox to another, etc. A line item won't have a body, to/cc/bcc lists, or attachments.
|
150
|
+
THREAD_TYPE_LINEITEM = "lineitem"
|
151
|
+
|
152
|
+
# When a conversation is forwarded, a new conversation is created to represent the forwarded conversation.
|
153
|
+
THREAD_TYPE_FWD_PARENT = "forwardparent" # forwardparent is the type set on the thread of the original conversation that initiated the forward event.
|
154
|
+
THREAD_TYPE_FWD_CHILD = "forwardchild" # forwardchild is the type set on the first thread of the new forwarded conversation.
|
155
|
+
|
156
|
+
|
157
|
+
def initialize(object)
|
158
|
+
@id = object["id"]
|
159
|
+
@assignedTo = User.new(object["assignedTo"]) if object["assignedTo"]
|
160
|
+
@status = object["status"]
|
161
|
+
@createdAt = DateTime.iso8601(object["createdAt"]) if object["createdAt"]
|
162
|
+
|
163
|
+
@source = nil
|
164
|
+
if object["source"]
|
165
|
+
@source = Source.new(object["source"])
|
166
|
+
|
167
|
+
if object["createdBy"]
|
168
|
+
if @source.type == Source::SOURCE_VIA_CUSTOMER
|
169
|
+
@createdBy = Customer.new(object["createdBy"])
|
170
|
+
elsif @source.type == Source::SOURCE_VIA_USER
|
171
|
+
@createdBy = User.new(object["createdBy"])
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
@fromMailbox = Mailbox.new(object["fromMailbox"]) if object["fromMailbox"]
|
177
|
+
@type = object["type"]
|
178
|
+
@state = object["state"]
|
179
|
+
@customer = Customer.new(object["customer"]) if object["customer"]
|
180
|
+
@body = object["body"]
|
181
|
+
@to = object["to"]
|
182
|
+
@cc = object["cc"]
|
183
|
+
@bcc = object["bcc"]
|
184
|
+
|
185
|
+
@attachments = []
|
186
|
+
if object["attachments"]
|
187
|
+
object["attachments"].each do |item|
|
188
|
+
@attachments << Attachment.new(item)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def to_s
|
194
|
+
"#{@customer}: #{@body}"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
class User
|
201
|
+
attr_accessor :id, :firstName, :lastName, :email, :role, :timezone, :photoUrl, :createdAt, :createdBy
|
202
|
+
|
203
|
+
USER_ROLE_OWNER = "owner"
|
204
|
+
USER_ROLE_ADMIN = "admin"
|
205
|
+
USER_ROLE_USER = "user"
|
206
|
+
|
207
|
+
def initialize(object)
|
208
|
+
@id = object["id"]
|
209
|
+
@firstName = object["firstName"]
|
210
|
+
@lastName = object["lastName"]
|
211
|
+
@email = object["email"]
|
212
|
+
@role = object["role"]
|
213
|
+
@timezone = object["timezone"]
|
214
|
+
@photoUrl = object["photoUrl"]
|
215
|
+
@createdAt = DateTime.iso8601(object["createdAt"]) if object["createdAt"]
|
216
|
+
@createdBy = object["createdBy"]
|
217
|
+
end
|
218
|
+
|
219
|
+
def to_s
|
220
|
+
"#{@firstName} #{@lastName}"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
class Customer
|
225
|
+
attr_accessor :id, :firstName, :lastName, :photoUrl, :photoType, :gender, :age, :organization, :jobTitle, :location, :createdAt, :modifiedAt, :background, :address, :socialProfiles, :emails, :phones, :chats, :websites
|
226
|
+
|
227
|
+
CUSTOMER_PHOTO_TYPE_UNKNOWN = "unknown"
|
228
|
+
CUSTOMER_PHOTO_TYPE_GRAVATAR = "gravatar"
|
229
|
+
CUSTOMER_PHOTO_TYPE_TWITTER = "twitter"
|
230
|
+
CUSTOMER_PHOTO_TYPE_FACEBOOK = "facebook"
|
231
|
+
CUSTOMER_PHOTO_TYPE_GOOGLE_PROFILE = "googleprofile"
|
232
|
+
CUSTOMER_PHOTO_TYPE_GOOGLE_PLUS = "googleplus"
|
233
|
+
CUSTOMER_PHOTO_TYPE_LINKEDIN = "linkedin"
|
234
|
+
|
235
|
+
CUSTOMER_GENDER_MALE = "male"
|
236
|
+
CUSTOMER_GENDER_FEMALE = "female"
|
237
|
+
CUSTOMER_GENDER_UNKNOWN = "unknown"
|
238
|
+
|
239
|
+
def initialize(object)
|
240
|
+
@id = object["id"]
|
241
|
+
@firstName = object["firstName"]
|
242
|
+
@lastName = object["lastName"]
|
243
|
+
@photoUrl = object["photoUrl"]
|
244
|
+
@photoType = object["photoType"]
|
245
|
+
@gender = object["gender"]
|
246
|
+
@age = object["age"]
|
247
|
+
@organization = object["organization"]
|
248
|
+
@jobTitle = object["jobTitle"]
|
249
|
+
@location = object["location"]
|
250
|
+
@background = object["background"]
|
251
|
+
|
252
|
+
@address = nil
|
253
|
+
if object["address"]
|
254
|
+
@address = Address.new(object["address"])
|
255
|
+
end
|
256
|
+
|
257
|
+
@socialProfiles = []
|
258
|
+
if object["socialProfiles"]
|
259
|
+
object["socialProfiles"].each do |item|
|
260
|
+
@socialProfiles << SocialProfile.new(item)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
@emails = []
|
265
|
+
if object["emails"]
|
266
|
+
object["emails"].each do |item|
|
267
|
+
@emails << Email.new(item)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
@phones = []
|
272
|
+
if object["phones"]
|
273
|
+
object["phones"].each do |item|
|
274
|
+
@phones << Phone.new(item)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
@chats = []
|
279
|
+
if object["chats"]
|
280
|
+
object["chats"].each do |item|
|
281
|
+
@chats << Chat.new(item)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
@websites = []
|
286
|
+
if object["websites"]
|
287
|
+
object["websites"].each do |item|
|
288
|
+
@websites << Website.new(item)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
@createdAt = DateTime.iso8601(object["createdAt"]) if object["createdAt"]
|
293
|
+
@modifiedAt = DateTime.iso8601(object["modifiedAt"]) if object["modifiedAt"]
|
294
|
+
end
|
295
|
+
|
296
|
+
def to_s
|
297
|
+
"#{@firstName} #{@lastName}"
|
298
|
+
end
|
299
|
+
|
300
|
+
class Address
|
301
|
+
attr_accessor :id, :lines, :city, :state, :postalCode, :country, :createdAt, :modifiedAt
|
302
|
+
def initialize(object)
|
303
|
+
@id = object["id"]
|
304
|
+
@lines = object["lines"]
|
305
|
+
@city = object["city"]
|
306
|
+
@state = object["state"]
|
307
|
+
@postalCode = object["postalCode"]
|
308
|
+
@country = object["country"]
|
309
|
+
@createdAt = DateTime.iso8601(object["createdAt"]) if object["createdAt"]
|
310
|
+
@modifiedAt = DateTime.iso8601(object["modifiedAt"]) if object["modifiedAt"]
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
class Chat
|
315
|
+
attr_accessor :id, :value, :type
|
316
|
+
|
317
|
+
CHAT_TYPE_AIM = "aim"
|
318
|
+
CHAT_TYPE_GTALK = "gtalk"
|
319
|
+
CHAT_TYPE_ICQ = "icq"
|
320
|
+
CHAT_TYPE_XMPP = "xmpp"
|
321
|
+
CHAT_TYPE_MSN = "msn"
|
322
|
+
CHAT_TYPE_SKYPE = "skype"
|
323
|
+
CHAT_TYPE_YAHOO = "yahoo"
|
324
|
+
CHAT_TYPE_QQ = "qq"
|
325
|
+
CHAT_TYPE_OTHER = "other"
|
326
|
+
|
327
|
+
def initialize(object)
|
328
|
+
@id = object["id"]
|
329
|
+
@value = object["value"]
|
330
|
+
@type = object["type"]
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
class Email
|
335
|
+
attr_accessor :id, :value, :location
|
336
|
+
|
337
|
+
EMAIL_LOCATION_WORK = "work"
|
338
|
+
EMAIL_LOCATION_HOME = "home"
|
339
|
+
EMAIL_LOCATION_OTHER = "other"
|
340
|
+
|
341
|
+
def initialize(object)
|
342
|
+
@id = object["id"]
|
343
|
+
@value = object["value"]
|
344
|
+
@location = object["location"]
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
class Phone
|
349
|
+
attr_accessor :id, :value, :location
|
350
|
+
|
351
|
+
PHONE_LOCATION_HOME = "home"
|
352
|
+
PHONE_LOCATION_WORK = "work"
|
353
|
+
PHONE_LOCATION_MOBILE = "mobile"
|
354
|
+
PHONE_LOCATION_FAX = "fax"
|
355
|
+
PHONE_LOCATION_PAGER = "pager"
|
356
|
+
PHONE_LOCATION_OTHER = "other"
|
357
|
+
|
358
|
+
def initialize(object)
|
359
|
+
@id = object["id"]
|
360
|
+
@value = object["value"]
|
361
|
+
@location = object["location"]
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
class SocialProfile
|
366
|
+
attr_accessor :id, :value, :type
|
367
|
+
|
368
|
+
SOCIAL_PROFILE_TYPE_TWITTER = "twitter"
|
369
|
+
SOCIAL_PROFILE_TYPE_FACEBOOK = "facebook"
|
370
|
+
SOCIAL_PROFILE_TYPE_LINKEDIN = "linkedin"
|
371
|
+
SOCIAL_PROFILE_TYPE_ABOUTME = "aboutme"
|
372
|
+
SOCIAL_PROFILE_TYPE_GOOGLE = "google"
|
373
|
+
SOCIAL_PROFILE_TYPE_GOOGLE_PLUS = "googleplus"
|
374
|
+
SOCIAL_PROFILE_TYPE_TUNGLEME = "tungleme"
|
375
|
+
SOCIAL_PROFILE_TYPE_QUORA = "quora"
|
376
|
+
SOCIAL_PROFILE_TYPE_FOURSQUARE = "foursquare"
|
377
|
+
SOCIAL_PROFILE_TYPE_YOUTUBE = "youtube"
|
378
|
+
SOCIAL_PROFILE_TYPE_FLICKR = "flickr"
|
379
|
+
SOCIAL_PROFILE_TYPE_OTHER = "other"
|
380
|
+
|
381
|
+
def initialize(object)
|
382
|
+
@id = object["id"]
|
383
|
+
@value = object["value"]
|
384
|
+
@type = object["type"] # twitter, facebook, linkedin, aboutme, google, googleplus, tungleme, quora, foursquare, youtube, flickr, other
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
class Website
|
389
|
+
attr_accessor :id, :value
|
390
|
+
def initialize(object)
|
391
|
+
@id = object["id"]
|
392
|
+
@value = object["value"]
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
metadata
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: helpscout
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3.alpha
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Héctor Ramos
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: httparty
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: bundler
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.1.3
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.1.3
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: jeweler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.8.4
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.8.4
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: httparty
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: simplecov
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: reek
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 1.2.8
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 1.2.8
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rdoc
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: ''
|
127
|
+
email: hector@hectorramos.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files:
|
131
|
+
- README.markdown
|
132
|
+
files:
|
133
|
+
- Gemfile
|
134
|
+
- README.markdown
|
135
|
+
- Rakefile
|
136
|
+
- VERSION
|
137
|
+
- helpscout.gemspec
|
138
|
+
- lib/helpscout.rb
|
139
|
+
- lib/helpscout/base.rb
|
140
|
+
- lib/helpscout/models.rb
|
141
|
+
homepage: http://github.com/hramos/helpscout
|
142
|
+
licenses:
|
143
|
+
- MIT
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
require_paths:
|
147
|
+
- lib
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
149
|
+
none: false
|
150
|
+
requirements:
|
151
|
+
- - ! '>='
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
155
|
+
none: false
|
156
|
+
requirements:
|
157
|
+
- - ! '>'
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 1.3.1
|
160
|
+
requirements: []
|
161
|
+
rubyforge_project:
|
162
|
+
rubygems_version: 1.8.24
|
163
|
+
signing_key:
|
164
|
+
specification_version: 3
|
165
|
+
summary: HelpScout API Wrapper
|
166
|
+
test_files: []
|