followupboss_client 1.0.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.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.env.example +12 -0
  3. data/.gitignore +18 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.tool-versions +1 -0
  8. data/.travis.yml +9 -0
  9. data/CHANGELOG.md +149 -0
  10. data/CODE_OF_CONDUCT.md +49 -0
  11. data/Gemfile +4 -0
  12. data/LICENSE.txt +22 -0
  13. data/README.md +882 -0
  14. data/Rakefile +6 -0
  15. data/bin/console +14 -0
  16. data/bin/setup +8 -0
  17. data/followupboss_client.gemspec +43 -0
  18. data/lib/followupboss_client.rb +16 -0
  19. data/lib/fub_client/action_plan.rb +5 -0
  20. data/lib/fub_client/appointment.rb +42 -0
  21. data/lib/fub_client/appointment_outcome.rb +51 -0
  22. data/lib/fub_client/appointment_type.rb +51 -0
  23. data/lib/fub_client/call.rb +4 -0
  24. data/lib/fub_client/client.rb +200 -0
  25. data/lib/fub_client/compatibility.rb +18 -0
  26. data/lib/fub_client/configuration.rb +54 -0
  27. data/lib/fub_client/cookie_client.rb +190 -0
  28. data/lib/fub_client/custom_field.rb +5 -0
  29. data/lib/fub_client/deal.rb +41 -0
  30. data/lib/fub_client/deal_attachment.rb +61 -0
  31. data/lib/fub_client/deal_custom_field.rb +47 -0
  32. data/lib/fub_client/em_event.rb +5 -0
  33. data/lib/fub_client/email_template.rb +5 -0
  34. data/lib/fub_client/event.rb +8 -0
  35. data/lib/fub_client/group.rb +58 -0
  36. data/lib/fub_client/her_patch.rb +101 -0
  37. data/lib/fub_client/identity.rb +33 -0
  38. data/lib/fub_client/message.rb +41 -0
  39. data/lib/fub_client/middleware/authentication.rb +26 -0
  40. data/lib/fub_client/middleware/cookie_authentication.rb +61 -0
  41. data/lib/fub_client/middleware/parser.rb +59 -0
  42. data/lib/fub_client/middleware.rb +8 -0
  43. data/lib/fub_client/note.rb +4 -0
  44. data/lib/fub_client/people_relationship.rb +34 -0
  45. data/lib/fub_client/person.rb +5 -0
  46. data/lib/fub_client/person_attachment.rb +50 -0
  47. data/lib/fub_client/pipeline.rb +45 -0
  48. data/lib/fub_client/property.rb +26 -0
  49. data/lib/fub_client/rails8_patch.rb +39 -0
  50. data/lib/fub_client/resource.rb +33 -0
  51. data/lib/fub_client/shared_inbox.rb +389 -0
  52. data/lib/fub_client/smart_list.rb +5 -0
  53. data/lib/fub_client/stage.rb +39 -0
  54. data/lib/fub_client/task.rb +18 -0
  55. data/lib/fub_client/team.rb +65 -0
  56. data/lib/fub_client/team_inbox.rb +65 -0
  57. data/lib/fub_client/text_message.rb +46 -0
  58. data/lib/fub_client/text_message_template.rb +49 -0
  59. data/lib/fub_client/user.rb +4 -0
  60. data/lib/fub_client/version.rb +3 -0
  61. data/lib/fub_client/webhook.rb +47 -0
  62. data/lib/fub_client.rb +61 -0
  63. data/scripts/test_api.rb +110 -0
  64. data/scripts/test_shared_inbox.rb +90 -0
  65. metadata +335 -0
@@ -0,0 +1,389 @@
1
+ module FubClient
2
+ module SharedInboxMethods
3
+ def messages(limit = 20, offset = 0)
4
+ inbox_id = is_a?(Hash) ? self[:id] : id
5
+ return [] unless inbox_id
6
+
7
+ begin
8
+ conn = FubClient::SharedInbox.create_faraday_connection
9
+ return [] unless conn
10
+
11
+ query = "?limit=#{limit}&offset=#{offset}"
12
+ response = conn.get("/api/v1/sharedInboxes/#{inbox_id}/messages#{query}")
13
+
14
+ if ENV['DEBUG']
15
+ puts "Fetching messages for inbox #{inbox_id}"
16
+ puts "Response status: #{response.status}"
17
+ end
18
+
19
+ if response.status == 200
20
+ data = JSON.parse(response.body, symbolize_names: true)
21
+ messages = data[:messages] || []
22
+ puts "Found #{messages.count} messages" if ENV['DEBUG']
23
+ messages
24
+ else
25
+ puts "Error: HTTP #{response.status} - #{response.body}" if ENV['DEBUG']
26
+ []
27
+ end
28
+ rescue StandardError => e
29
+ puts "Error fetching messages: #{e.message}" if ENV['DEBUG']
30
+ puts e.backtrace.join("\n") if ENV['DEBUG']
31
+ []
32
+ end
33
+ end
34
+
35
+ def settings
36
+ inbox_id = is_a?(Hash) ? self[:id] : id
37
+ return {} unless inbox_id
38
+
39
+ begin
40
+ conn = FubClient::SharedInbox.create_faraday_connection
41
+ return {} unless conn
42
+
43
+ response = conn.get("/api/v1/sharedInboxes/#{inbox_id}/settings")
44
+
45
+ if ENV['DEBUG']
46
+ puts "Fetching settings for inbox #{inbox_id}"
47
+ puts "Response status: #{response.status}"
48
+ end
49
+
50
+ if response.status == 200
51
+ data = JSON.parse(response.body, symbolize_names: true)
52
+ settings = data[:settings] || {}
53
+ puts 'Settings retrieved successfully' if ENV['DEBUG']
54
+ settings
55
+ else
56
+ puts "Error: HTTP #{response.status} - #{response.body}" if ENV['DEBUG']
57
+ {}
58
+ end
59
+ rescue StandardError => e
60
+ puts "Error fetching settings: #{e.message}" if ENV['DEBUG']
61
+ puts e.backtrace.join("\n") if ENV['DEBUG']
62
+ {}
63
+ end
64
+ end
65
+
66
+ def conversations(limit = 20, offset = 0, filter = nil)
67
+ inbox_id = is_a?(Hash) ? self[:id] : id
68
+ return [] unless inbox_id
69
+
70
+ begin
71
+ conn = FubClient::SharedInbox.create_faraday_connection
72
+ return [] unless conn
73
+
74
+ query = "?limit=#{limit}&offset=#{offset}"
75
+ query += "&filter=#{URI.encode_www_form_component(filter)}" if filter
76
+
77
+ response = conn.get("/api/v1/sharedInboxes/#{inbox_id}/conversations#{query}")
78
+
79
+ if ENV['DEBUG']
80
+ puts "Fetching conversations for inbox #{inbox_id}"
81
+ puts "Response status: #{response.status}"
82
+ end
83
+
84
+ if response.status == 200
85
+ data = JSON.parse(response.body, symbolize_names: true)
86
+ conversations = data[:conversations] || []
87
+ puts "Found #{conversations.count} conversations" if ENV['DEBUG']
88
+ conversations
89
+ else
90
+ puts "Error: HTTP #{response.status} - #{response.body}" if ENV['DEBUG']
91
+ []
92
+ end
93
+ rescue StandardError => e
94
+ puts "Error fetching conversations: #{e.message}" if ENV['DEBUG']
95
+ puts e.backtrace.join("\n") if ENV['DEBUG']
96
+ []
97
+ end
98
+ end
99
+ end
100
+
101
+ class SharedInbox < Resource
102
+ collection_path 'sharedInboxes'
103
+ root_element :shared_inbox
104
+ include_root_in_json true
105
+
106
+ def self.all_inboxes
107
+ puts 'Calling SharedInbox.all_inboxes using direct cookie authentication' if ENV['DEBUG']
108
+
109
+ client = FubClient::Client.instance
110
+ cookies = client.cookies
111
+ subdomain = client.subdomain
112
+
113
+ if !cookies || cookies.empty?
114
+ puts 'Error: No cookies available for authentication' if ENV['DEBUG']
115
+ return []
116
+ end
117
+
118
+ if !subdomain || subdomain.empty?
119
+ puts 'Error: No subdomain set for authentication' if ENV['DEBUG']
120
+ return []
121
+ end
122
+
123
+ conn = Faraday.new(url: "https://#{subdomain}.followupboss.com") do |f|
124
+ f.headers['Cookie'] = cookies
125
+ f.headers['Accept'] = 'application/json, text/javascript, */*; q=0.01'
126
+ f.headers['Accept-Language'] = 'en-US,en;q=0.9'
127
+ f.headers['X-Requested-With'] = 'XMLHttpRequest'
128
+ f.headers['X-System'] = 'fub-spa'
129
+ f.headers['User-Agent'] =
130
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36'
131
+ f.adapter :net_http
132
+ end
133
+
134
+ response = conn.get('/api/v1/sharedInboxes?showAllBypass=true&limit=20&offset=0')
135
+
136
+ if ENV['DEBUG']
137
+ puts 'Request headers:'
138
+ conn.headers.each do |k, v|
139
+ if k.downcase == 'cookie' && v.length > 50
140
+ puts " #{k}: #{v[0..50]}..."
141
+ else
142
+ puts " #{k}: #{v}"
143
+ end
144
+ end
145
+ puts "Response status: #{response.status}"
146
+ puts "Response body: #{response.body[0..100]}..." if response.body && response.body.length > 100
147
+ end
148
+
149
+ if response.status == 200
150
+ data = JSON.parse(response.body, symbolize_names: true)
151
+ inboxes = data[:sharedInboxes] || []
152
+ puts "Found #{inboxes.count} shared inboxes via direct request" if ENV['DEBUG']
153
+ inboxes
154
+ else
155
+ puts "Error: HTTP #{response.status} - #{response.body}" if ENV['DEBUG']
156
+ []
157
+ end
158
+ rescue StandardError => e
159
+ puts "Error in all_inboxes: #{e.message}" if ENV['DEBUG']
160
+ puts e.backtrace.join("\n") if ENV['DEBUG']
161
+ []
162
+ end
163
+
164
+ def self.get_inbox(id)
165
+ puts "Calling SharedInbox.get_inbox(#{id}) using direct cookie authentication" if ENV['DEBUG']
166
+
167
+ client = FubClient::Client.instance
168
+ cookies = client.cookies
169
+ subdomain = client.subdomain
170
+
171
+ if !cookies || cookies.empty?
172
+ puts 'Error: No cookies available for authentication' if ENV['DEBUG']
173
+ return nil
174
+ end
175
+
176
+ if !subdomain || subdomain.empty?
177
+ puts 'Error: No subdomain set for authentication' if ENV['DEBUG']
178
+ return nil
179
+ end
180
+
181
+ conn = Faraday.new(url: "https://#{subdomain}.followupboss.com") do |f|
182
+ f.headers['Cookie'] = cookies
183
+ f.headers['Accept'] = 'application/json, text/javascript, */*; q=0.01'
184
+ f.headers['Accept-Language'] = 'en-US,en;q=0.9'
185
+ f.headers['X-Requested-With'] = 'XMLHttpRequest'
186
+ f.headers['X-System'] = 'fub-spa'
187
+ f.headers['User-Agent'] =
188
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36'
189
+ f.adapter :net_http
190
+ end
191
+
192
+ response = conn.get("/api/v1/sharedInboxes/#{id}")
193
+
194
+ if ENV['DEBUG']
195
+ puts "Response status: #{response.status}"
196
+ puts "Response body: #{response.body[0..100]}..." if response.body && response.body.length > 100
197
+ end
198
+
199
+ if response.status == 200
200
+ if ENV['DEBUG']
201
+ puts "Raw response body: #{response.body[0..200]}..." if response.body.length > 200
202
+ puts "Raw response body: #{response.body}" if response.body.length <= 200
203
+ end
204
+
205
+ data = JSON.parse(response.body, symbolize_names: true)
206
+
207
+ if ENV['DEBUG']
208
+ puts "Parsed data keys: #{data.keys.inspect}"
209
+ puts "Parsed data type: #{data.class}"
210
+ puts "Has :id key? #{data.key?(:id)}"
211
+ puts "ID value: #{data[:id]}" if data.key?(:id)
212
+ end
213
+
214
+ if data.is_a?(Hash) && !data.empty?
215
+ puts "Found inbox with ID #{id} via direct request" if ENV['DEBUG']
216
+ data.extend(SharedInboxMethods)
217
+ data
218
+ else
219
+ puts "Unexpected response structure: #{data.inspect}" if ENV['DEBUG']
220
+ nil
221
+ end
222
+ else
223
+ puts "Error: HTTP #{response.status} - #{response.body}" if ENV['DEBUG']
224
+ nil
225
+ end
226
+ rescue StandardError => e
227
+ puts "Error in get_inbox: #{e.message}" if ENV['DEBUG']
228
+ puts e.backtrace.join("\n") if ENV['DEBUG']
229
+ nil
230
+ end
231
+
232
+ def self.create_faraday_connection
233
+ client = FubClient::Client.instance
234
+ cookies = client.cookies
235
+ subdomain = client.subdomain
236
+
237
+ if !cookies || cookies.empty?
238
+ puts 'Error: No cookies available for authentication' if ENV['DEBUG']
239
+ return nil
240
+ end
241
+
242
+ if !subdomain || subdomain.empty?
243
+ puts 'Error: No subdomain set for authentication' if ENV['DEBUG']
244
+ return nil
245
+ end
246
+
247
+ Faraday.new(url: "https://#{subdomain}.followupboss.com") do |f|
248
+ f.headers['Cookie'] = cookies
249
+ f.headers['Accept'] = 'application/json, text/javascript, */*; q=0.01'
250
+ f.headers['Accept-Language'] = 'en-US,en;q=0.9'
251
+ f.headers['X-Requested-With'] = 'XMLHttpRequest'
252
+ f.headers['X-System'] = 'fub-spa'
253
+ f.headers['User-Agent'] =
254
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36'
255
+ f.adapter :net_http
256
+ end
257
+ end
258
+
259
+ def messages(limit = 20, offset = 0)
260
+ inbox_id = is_a?(Hash) ? self[:id] : id
261
+ return [] unless inbox_id
262
+
263
+ begin
264
+ conn = self.class.create_faraday_connection
265
+ return [] unless conn
266
+
267
+ query = "?limit=#{limit}&offset=#{offset}"
268
+ response = conn.get("/api/v1/sharedInboxes/#{inbox_id}/messages#{query}")
269
+
270
+ if ENV['DEBUG']
271
+ puts "Fetching messages for inbox #{inbox_id}"
272
+ puts "Response status: #{response.status}"
273
+ end
274
+
275
+ if response.status == 200
276
+ data = JSON.parse(response.body, symbolize_names: true)
277
+ messages = data[:messages] || []
278
+ puts "Found #{messages.count} messages" if ENV['DEBUG']
279
+ messages
280
+ else
281
+ puts "Error: HTTP #{response.status} - #{response.body}" if ENV['DEBUG']
282
+ []
283
+ end
284
+ rescue StandardError => e
285
+ puts "Error fetching messages: #{e.message}" if ENV['DEBUG']
286
+ puts e.backtrace.join("\n") if ENV['DEBUG']
287
+ []
288
+ end
289
+ end
290
+
291
+ def settings
292
+ inbox_id = is_a?(Hash) ? self[:id] : id
293
+ return {} unless inbox_id
294
+
295
+ begin
296
+ conn = self.class.create_faraday_connection
297
+ return {} unless conn
298
+
299
+ response = conn.get("/api/v1/sharedInboxes/#{inbox_id}/settings")
300
+
301
+ if ENV['DEBUG']
302
+ puts "Fetching settings for inbox #{inbox_id}"
303
+ puts "Response status: #{response.status}"
304
+ end
305
+
306
+ if response.status == 200
307
+ data = JSON.parse(response.body, symbolize_names: true)
308
+ settings = data[:settings] || {}
309
+ puts 'Settings retrieved successfully' if ENV['DEBUG']
310
+ settings
311
+ else
312
+ puts "Error: HTTP #{response.status} - #{response.body}" if ENV['DEBUG']
313
+ {}
314
+ end
315
+ rescue StandardError => e
316
+ puts "Error fetching settings: #{e.message}" if ENV['DEBUG']
317
+ puts e.backtrace.join("\n") if ENV['DEBUG']
318
+ {}
319
+ end
320
+ end
321
+
322
+ def update_settings(settings_hash)
323
+ inbox_id = is_a?(Hash) ? self[:id] : id
324
+ return false unless inbox_id && settings_hash.is_a?(Hash)
325
+
326
+ begin
327
+ conn = self.class.create_faraday_connection
328
+ return false unless conn
329
+
330
+ response = conn.put do |req|
331
+ req.url "/api/v1/sharedInboxes/#{inbox_id}/settings"
332
+ req.headers['Content-Type'] = 'application/json'
333
+ req.body = JSON.generate({ settings: settings_hash })
334
+ end
335
+
336
+ if ENV['DEBUG']
337
+ puts "Updating settings for inbox #{inbox_id}"
338
+ puts "Response status: #{response.status}"
339
+ end
340
+
341
+ if [200, 204].include?(response.status)
342
+ puts 'Settings updated successfully' if ENV['DEBUG']
343
+ true
344
+ else
345
+ puts "Error: HTTP #{response.status} - #{response.body}" if ENV['DEBUG']
346
+ false
347
+ end
348
+ rescue StandardError => e
349
+ puts "Error updating settings: #{e.message}" if ENV['DEBUG']
350
+ puts e.backtrace.join("\n") if ENV['DEBUG']
351
+ false
352
+ end
353
+ end
354
+
355
+ def conversations(limit = 20, offset = 0, filter = nil)
356
+ inbox_id = is_a?(Hash) ? self[:id] : id
357
+ return [] unless inbox_id
358
+
359
+ begin
360
+ conn = self.class.create_faraday_connection
361
+ return [] unless conn
362
+
363
+ query = "?limit=#{limit}&offset=#{offset}"
364
+ query += "&filter=#{URI.encode_www_form_component(filter)}" if filter
365
+
366
+ response = conn.get("/api/v1/sharedInboxes/#{inbox_id}/conversations#{query}")
367
+
368
+ if ENV['DEBUG']
369
+ puts "Fetching conversations for inbox #{inbox_id}"
370
+ puts "Response status: #{response.status}"
371
+ end
372
+
373
+ if response.status == 200
374
+ data = JSON.parse(response.body, symbolize_names: true)
375
+ conversations = data[:conversations] || []
376
+ puts "Found #{conversations.count} conversations" if ENV['DEBUG']
377
+ conversations
378
+ else
379
+ puts "Error: HTTP #{response.status} - #{response.body}" if ENV['DEBUG']
380
+ []
381
+ end
382
+ rescue StandardError => e
383
+ puts "Error fetching conversations: #{e.message}" if ENV['DEBUG']
384
+ puts e.backtrace.join("\n") if ENV['DEBUG']
385
+ []
386
+ end
387
+ end
388
+ end
389
+ end
@@ -0,0 +1,5 @@
1
+ module FubClient
2
+ class SmartList < Resource
3
+ collection_path 'smartLists'
4
+ end
5
+ end
@@ -0,0 +1,39 @@
1
+ module FubClient
2
+ class Stage < Resource
3
+ collection_path 'stages'
4
+ root_element :stage
5
+ include_root_in_json true
6
+
7
+ scope :by_pipeline, ->(pipeline_id) { where(pipelineId: pipeline_id) }
8
+ scope :by_type, ->(type) { where(type: type) }
9
+
10
+ def self.active
11
+ where(active: true)
12
+ end
13
+
14
+ def self.inactive
15
+ where(active: false)
16
+ end
17
+
18
+ def deals
19
+ return [] unless id
20
+
21
+ FubClient::Deal.by_stage(id)
22
+ end
23
+
24
+ def deal_count
25
+ return 0 unless id
26
+
27
+ begin
28
+ response = self.class.get("#{id}/dealCount")
29
+ response[:count] || 0
30
+ rescue StandardError
31
+ 0
32
+ end
33
+ end
34
+
35
+ def move_to_position(position)
36
+ self.class.put("#{id}/move", { position: position })
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,18 @@
1
+ module FubClient
2
+ class Task < Resource
3
+ collection_path 'tasks'
4
+ root_element :task
5
+ include_root_in_json true
6
+
7
+ scope :by_person, ->(person_id) { where(personId: person_id) }
8
+ scope :by_type, ->(type) { where(type: type) }
9
+ scope :by_status, ->(status) { where(status: status) }
10
+ scope :due_before, ->(date) { where(dueBefore: date) }
11
+ scope :due_after, ->(date) { where(dueAfter: date) }
12
+ scope :assigned_to, ->(user_id) { where(assignedTo: user_id) }
13
+
14
+ def self.overdue
15
+ where(status: 'pending').where(dueBefore: Time.now.iso8601)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,65 @@
1
+ module FubClient
2
+ class Team < Resource
3
+ collection_path 'teams'
4
+ root_element :team
5
+ include_root_in_json true
6
+
7
+ # Convenience method to find teams by name (partial match)
8
+ scope :by_name, ->(name) { where(q: name) }
9
+
10
+ # Convenience method to find active teams
11
+ def self.active
12
+ where(active: true)
13
+ end
14
+
15
+ # Get users who are members of this team
16
+ def members
17
+ return [] unless id
18
+
19
+ begin
20
+ response = self.class.get("#{id}/members")
21
+ response[:members] || []
22
+ rescue StandardError
23
+ []
24
+ end
25
+ end
26
+
27
+ # Add a user to this team
28
+ def add_user(user_id, team_leader = false)
29
+ return false unless id && user_id
30
+
31
+ params = { userId: user_id }
32
+ params[:teamLeader] = team_leader if team_leader
33
+
34
+ begin
35
+ self.class.post("#{id}/members", params)
36
+ true
37
+ rescue StandardError
38
+ false
39
+ end
40
+ end
41
+
42
+ # Remove a user from this team
43
+ def remove_user(user_id)
44
+ return false unless id && user_id
45
+
46
+ begin
47
+ self.class.delete("#{id}/members/#{user_id}")
48
+ true
49
+ rescue StandardError
50
+ false
51
+ end
52
+ end
53
+
54
+ # Get team statistics
55
+ def stats
56
+ return {} unless id
57
+
58
+ begin
59
+ self.class.get("#{id}/stats")
60
+ rescue StandardError
61
+ {}
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,65 @@
1
+ module FubClient
2
+ class TeamInbox < Resource
3
+ collection_path 'teamInboxes'
4
+ root_element :team_inbox
5
+ include_root_in_json true
6
+
7
+ # Get all team inboxes
8
+ def self.all_inboxes
9
+ get('')
10
+ rescue StandardError
11
+ []
12
+ end
13
+
14
+ # Helper method to get the team associated with this inbox
15
+ def team
16
+ return nil unless respond_to?(:team_id) && team_id
17
+
18
+ FubClient::Team.find(team_id)
19
+ end
20
+
21
+ # Get messages in this team inbox
22
+ def messages(limit = 20, offset = 0)
23
+ return [] unless id
24
+
25
+ begin
26
+ params = {
27
+ limit: limit,
28
+ offset: offset
29
+ }
30
+
31
+ response = self.class.get("#{id}/messages", params)
32
+ response[:messages] || []
33
+ rescue StandardError
34
+ []
35
+ end
36
+ end
37
+
38
+ # Get participants in a conversation
39
+ def participants(conversation_id)
40
+ return [] unless id && conversation_id
41
+
42
+ begin
43
+ response = self.class.get("#{id}/conversations/#{conversation_id}/participants")
44
+ response[:participants] || []
45
+ rescue StandardError
46
+ []
47
+ end
48
+ end
49
+
50
+ # Add a message to a conversation
51
+ def add_message(conversation_id, content, user_id = nil)
52
+ return false unless id && conversation_id && content
53
+
54
+ params = { content: content }
55
+ params[:userId] = user_id if user_id
56
+
57
+ begin
58
+ self.class.post("#{id}/conversations/#{conversation_id}/messages", params)
59
+ true
60
+ rescue StandardError
61
+ false
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,46 @@
1
+ module FubClient
2
+ class TextMessage < Resource
3
+ collection_path 'textMessages'
4
+ root_element :text_message
5
+ include_root_in_json true
6
+
7
+ scope :for_person, ->(person_id) { where(personId: person_id) }
8
+ scope :sent_between, lambda { |start_date, end_date|
9
+ where(startDate: start_date, endDate: end_date)
10
+ }
11
+ scope :inbound, -> { where(direction: 'inbound') }
12
+ scope :outbound, -> { where(direction: 'outbound') }
13
+ scope :by_user, ->(user_id) { where(userId: user_id) }
14
+ scope :search, ->(query) { where(q: query) }
15
+
16
+ def self.unread
17
+ where(read: false)
18
+ end
19
+
20
+ def person
21
+ return nil unless respond_to?(:person_id) && person_id
22
+
23
+ FubClient::Person.find(person_id)
24
+ end
25
+
26
+ def user
27
+ return nil unless respond_to?(:user_id) && user_id
28
+
29
+ FubClient::User.find(user_id)
30
+ end
31
+
32
+ def self.send_message(person_id, message, user_id = nil)
33
+ params = {
34
+ personId: person_id,
35
+ message: message
36
+ }
37
+ params[:userId] = user_id if user_id
38
+
39
+ post('', params)
40
+ end
41
+
42
+ def mark_as_read
43
+ self.class.put("#{id}/read", {})
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,49 @@
1
+ module FubClient
2
+ class TextMessageTemplate < Resource
3
+ collection_path 'textMessageTemplates'
4
+ root_element :text_message_template
5
+ include_root_in_json true
6
+
7
+ # Convenience method to find templates by category
8
+ scope :by_category, ->(category) { where(category: category) }
9
+
10
+ # Convenience method to find active templates
11
+ def self.active
12
+ where(active: true)
13
+ end
14
+
15
+ # Convenience method to find inactive templates
16
+ def self.inactive
17
+ where(active: false)
18
+ end
19
+
20
+ # Merge template with person data
21
+ def merge(person_id)
22
+ return nil unless id && person_id
23
+
24
+ begin
25
+ self.class.post('merge', { id: id, personId: person_id })
26
+ rescue StandardError
27
+ nil
28
+ end
29
+ end
30
+
31
+ # Send template as a text message to a person
32
+ def send_to(person_id, user_id = nil)
33
+ return false unless id && person_id
34
+
35
+ params = {
36
+ templateId: id,
37
+ personId: person_id
38
+ }
39
+ params[:userId] = user_id if user_id
40
+
41
+ begin
42
+ TextMessage.post('', params)
43
+ true
44
+ rescue StandardError
45
+ false
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ module FubClient
2
+ class User < Resource
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module FubClient
2
+ VERSION = '1.0.0'
3
+ end