followupboss_client 1.0.0 → 1.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 +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +9 -1
- data/followupboss_client.gemspec +1 -1
- data/lib/fub_client/client.rb +43 -13
- data/lib/fub_client/configuration.rb +13 -5
- data/lib/fub_client/cookie_client.rb +29 -8
- data/lib/fub_client/middleware/cookie_authentication.rb +16 -2
- data/lib/fub_client/shared_inbox.rb +179 -61
- data/lib/fub_client/user.rb +21 -0
- data/lib/fub_client/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1d1312cbac7957779291cc5318bd1935c88c3b01d3dfa05aab44ceee9fdd6d84
|
|
4
|
+
data.tar.gz: b7970a95962e3be54613ffd330f75201664a26c235815a0795223e004b8ff03d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b6dad911222d02c831b051d44f1e79c5de579c3bf2d4521b4fd605ff61304dece6fdcf2fbb7f1fabb20f60383917b32b829d4492eea41fba36b0c57c7ffc6714
|
|
7
|
+
data.tar.gz: 12048f77d3a9c3ca7c785b813032b14a14ccbd5ac65611fd0a2395f587947386408be399f81d75d9f18eea05ee8f0a5ff89ac2e22f1e872a0ec163f9bfa8dace
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.1.0] - 2025-07-13
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Multi-authentication system improvements**
|
|
12
|
+
- Enhanced client authentication handling
|
|
13
|
+
- Improved cookie-based authentication flow
|
|
14
|
+
- Better authentication middleware integration
|
|
15
|
+
- Fixed authentication configuration issues
|
|
16
|
+
|
|
17
|
+
### Enhanced
|
|
18
|
+
- **SharedInbox functionality** with improved authentication support
|
|
19
|
+
- **User authentication** with additional auth features
|
|
20
|
+
- **Cookie client** with better multi-auth support
|
|
21
|
+
- **Configuration system** for handling multiple authentication methods
|
|
22
|
+
|
|
23
|
+
### Updated
|
|
24
|
+
- Documentation with multi-auth usage examples
|
|
25
|
+
- README with enhanced authentication guidance
|
|
26
|
+
|
|
8
27
|
## [1.0.0] - 2025-01-07
|
|
9
28
|
|
|
10
29
|
### Major Release - Enhanced Fork
|
data/README.md
CHANGED
|
@@ -91,7 +91,15 @@ export FUB_API_KEY=your_api_key
|
|
|
91
91
|
**Configuration Block:**
|
|
92
92
|
```ruby
|
|
93
93
|
FubClient.configure do |config|
|
|
94
|
-
|
|
94
|
+
# For Her-based resources
|
|
95
|
+
config.api_key = 'your_api_key_here'
|
|
96
|
+
|
|
97
|
+
# Option 1: Direct cookie (use this OR the GIST option below, not both)
|
|
98
|
+
config.cookie = 'cookie string'
|
|
99
|
+
|
|
100
|
+
# Option 2: GIST + Encryption (cookie comes from encrypted GIST)
|
|
101
|
+
config.gist_url = "gist url"
|
|
102
|
+
config.encryption_key = "encryption key"
|
|
95
103
|
end
|
|
96
104
|
```
|
|
97
105
|
|
data/followupboss_client.gemspec
CHANGED
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
|
|
20
20
|
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
21
21
|
|
|
22
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
22
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) || f.match(%r{\.gem$}) }
|
|
23
23
|
spec.bindir = 'exe'
|
|
24
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
25
25
|
spec.require_paths = ['lib']
|
data/lib/fub_client/client.rb
CHANGED
|
@@ -9,10 +9,18 @@ module FubClient
|
|
|
9
9
|
# Allow explicitly setting the instance (for testing)
|
|
10
10
|
class << self
|
|
11
11
|
attr_writer :instance
|
|
12
|
+
|
|
13
|
+
# Convenience method to access configuration
|
|
14
|
+
def config
|
|
15
|
+
FubClient.configuration
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Alias for configuration
|
|
19
|
+
def configuration
|
|
20
|
+
FubClient.configuration
|
|
21
|
+
end
|
|
12
22
|
end
|
|
13
23
|
|
|
14
|
-
attr_writer :api_key
|
|
15
|
-
attr_accessor :cookies, :subdomain
|
|
16
24
|
attr_reader :her_api
|
|
17
25
|
|
|
18
26
|
def initialize
|
|
@@ -20,19 +28,40 @@ module FubClient
|
|
|
20
28
|
end
|
|
21
29
|
|
|
22
30
|
def api_key
|
|
23
|
-
@api_key
|
|
31
|
+
return @api_key if defined?(@api_key) && @api_key
|
|
32
|
+
FubClient.configuration.api_key || ENV['FUB_API_KEY']
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def api_key=(value)
|
|
36
|
+
@api_key = value
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def subdomain
|
|
40
|
+
return @subdomain if defined?(@subdomain) && @subdomain
|
|
41
|
+
FubClient.configuration.subdomain || ENV['FUB_SUBDOMAIN']
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def subdomain=(value)
|
|
45
|
+
@subdomain = value
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def cookies
|
|
49
|
+
return @cookies if defined?(@cookies) && @cookies
|
|
50
|
+
FubClient.configuration.cookie || ENV['FUB_COOKIE']
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def cookies=(value)
|
|
54
|
+
@cookies = value
|
|
24
55
|
end
|
|
25
56
|
|
|
26
57
|
def api_uri
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
URI::HTTPS.build(host: API_URL, path: "/#{API_VERSION}")
|
|
35
|
-
end
|
|
58
|
+
if use_cookies? && subdomain
|
|
59
|
+
# Use subdomain-specific URL for cookie-based auth
|
|
60
|
+
URI::HTTPS.build(host: "#{subdomain}.followupboss.com", path: "/api/#{API_VERSION}")
|
|
61
|
+
else
|
|
62
|
+
# Use default API URL for API key auth
|
|
63
|
+
URI::HTTPS.build(host: API_URL, path: "/#{API_VERSION}")
|
|
64
|
+
end
|
|
36
65
|
end
|
|
37
66
|
|
|
38
67
|
# Login to obtain cookies
|
|
@@ -164,7 +193,8 @@ module FubClient
|
|
|
164
193
|
|
|
165
194
|
# Use cookie authentication?
|
|
166
195
|
def use_cookies?
|
|
167
|
-
|
|
196
|
+
current_cookies = cookies
|
|
197
|
+
!current_cookies.nil? && !current_cookies.empty?
|
|
168
198
|
end
|
|
169
199
|
|
|
170
200
|
# Reset the HER API connection with current settings
|
|
@@ -3,11 +3,11 @@ module FubClient
|
|
|
3
3
|
attr_accessor :api_key, :subdomain, :gist_url, :encryption_key, :cookie
|
|
4
4
|
|
|
5
5
|
def initialize
|
|
6
|
-
@api_key = ENV['FUB_API_KEY']
|
|
7
|
-
@subdomain = ENV['FUB_SUBDOMAIN']
|
|
8
|
-
@gist_url = ENV['FUB_GIST_URL']
|
|
9
|
-
@encryption_key = ENV['FUB_ENCRYPTION_KEY']
|
|
10
|
-
@cookie = ENV['FUB_COOKIE']
|
|
6
|
+
@api_key = ENV['FUB_API_KEY'] if ENV['FUB_API_KEY']
|
|
7
|
+
@subdomain = ENV['FUB_SUBDOMAIN'] if ENV['FUB_SUBDOMAIN']
|
|
8
|
+
@gist_url = ENV['FUB_GIST_URL'] if ENV['FUB_GIST_URL']
|
|
9
|
+
@encryption_key = ENV['FUB_ENCRYPTION_KEY'] if ENV['FUB_ENCRYPTION_KEY']
|
|
10
|
+
@cookie = ENV['FUB_COOKIE'] if ENV['FUB_COOKIE']
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def valid?
|
|
@@ -46,9 +46,17 @@ module FubClient
|
|
|
46
46
|
|
|
47
47
|
def self.configure
|
|
48
48
|
yield(configuration) if block_given?
|
|
49
|
+
# Reset the client's Her API to pick up new configuration
|
|
50
|
+
if defined?(FubClient::Client) && FubClient::Client.instance
|
|
51
|
+
FubClient::Client.instance.reset_her_api
|
|
52
|
+
end
|
|
49
53
|
end
|
|
50
54
|
|
|
51
55
|
def self.reset_configuration!
|
|
52
56
|
@configuration = Configuration.new
|
|
57
|
+
# Also reset the client's Her API if it exists
|
|
58
|
+
if defined?(FubClient::Client) && FubClient::Client.instance
|
|
59
|
+
FubClient::Client.instance.reset_her_api
|
|
60
|
+
end
|
|
53
61
|
end
|
|
54
62
|
end
|
|
@@ -6,9 +6,10 @@ module FubClient
|
|
|
6
6
|
def initialize(subdomain: nil, gist_url: nil, encryption_key: nil, cookie: nil)
|
|
7
7
|
config = FubClient.configuration
|
|
8
8
|
|
|
9
|
-
@subdomain = subdomain || config.subdomain
|
|
10
|
-
@gist_url = gist_url || config.gist_url
|
|
11
|
-
@encryption_key = encryption_key || config.encryption_key
|
|
9
|
+
@subdomain = subdomain || config.subdomain || ENV['FUB_SUBDOMAIN']
|
|
10
|
+
@gist_url = gist_url || config.gist_url || ENV['FUB_GIST_URL']
|
|
11
|
+
@encryption_key = encryption_key || config.encryption_key || ENV['FUB_ENCRYPTION_KEY']
|
|
12
|
+
cookie ||= config.cookie || ENV['FUB_COOKIE']
|
|
12
13
|
|
|
13
14
|
raise ArgumentError, 'Subdomain is required for cookie authentication' unless @subdomain
|
|
14
15
|
|
|
@@ -26,7 +27,7 @@ module FubClient
|
|
|
26
27
|
|
|
27
28
|
def cookies=(value)
|
|
28
29
|
@cookies = value
|
|
29
|
-
|
|
30
|
+
# Don't modify the global client anymore
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
def client
|
|
@@ -34,13 +35,33 @@ module FubClient
|
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def configure_client
|
|
37
|
-
client
|
|
38
|
-
|
|
39
|
-
client.reset_her_api
|
|
38
|
+
# Don't modify the global client - create our own Her API instance
|
|
39
|
+
@her_api = create_cookie_her_api
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def reset_her_api
|
|
43
|
-
|
|
43
|
+
@her_api = create_cookie_her_api
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def her_api
|
|
47
|
+
@her_api ||= create_cookie_her_api
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def create_cookie_her_api
|
|
53
|
+
api_uri = URI::HTTPS.build(host: "#{@subdomain}.followupboss.com", path: "/api/#{FubClient::Client::API_VERSION}")
|
|
54
|
+
|
|
55
|
+
her_api = Her::API.new
|
|
56
|
+
her_api.setup url: api_uri.to_s do |c|
|
|
57
|
+
# Use cookie authentication middleware with our cookies
|
|
58
|
+
c.use FubClient::Middleware::CookieAuthentication, cookies: @cookies
|
|
59
|
+
c.request :url_encoded
|
|
60
|
+
c.use FubClient::Middleware::Parser
|
|
61
|
+
c.adapter :net_http
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
her_api
|
|
44
65
|
end
|
|
45
66
|
|
|
46
67
|
def fetch_cookie_from_gist
|
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
module FubClient
|
|
2
2
|
module Middleware
|
|
3
3
|
class CookieAuthentication < Faraday::Middleware
|
|
4
|
+
def initialize(app, cookies: nil)
|
|
5
|
+
super(app)
|
|
6
|
+
@cookies = cookies
|
|
7
|
+
end
|
|
8
|
+
|
|
4
9
|
def call(env)
|
|
5
|
-
# Get the cookies from the client
|
|
6
|
-
cookies = FubClient::Client.instance.cookies
|
|
10
|
+
# Get the cookies from the parameter, client, or Her API instance
|
|
11
|
+
cookies = @cookies || FubClient::Client.instance.cookies
|
|
12
|
+
|
|
13
|
+
# If still no cookies, try to get from the Her API instance
|
|
14
|
+
if (!cookies || cookies.empty?) && env[:request] && env[:request].respond_to?(:connection)
|
|
15
|
+
connection = env[:request].connection
|
|
16
|
+
if connection.respond_to?(:instance_variable_get)
|
|
17
|
+
api_cookies = connection.instance_variable_get(:@cookies)
|
|
18
|
+
cookies = api_cookies if api_cookies && !api_cookies.empty?
|
|
19
|
+
end
|
|
20
|
+
end
|
|
7
21
|
|
|
8
22
|
if cookies && !cookies.empty?
|
|
9
23
|
# CRITICAL: Must set a request header to enable cookie-based auth
|
|
@@ -103,45 +103,18 @@ module FubClient
|
|
|
103
103
|
root_element :shared_inbox
|
|
104
104
|
include_root_in_json true
|
|
105
105
|
|
|
106
|
-
def self.all_inboxes
|
|
107
|
-
puts 'Calling SharedInbox.all_inboxes using
|
|
106
|
+
def self.all_inboxes(cookie_client: nil)
|
|
107
|
+
puts 'Calling SharedInbox.all_inboxes using cookie authentication' if ENV['DEBUG']
|
|
108
108
|
|
|
109
|
-
client =
|
|
110
|
-
|
|
111
|
-
subdomain = client.subdomain
|
|
109
|
+
client = resolve_cookie_client(cookie_client)
|
|
110
|
+
return [] unless client
|
|
112
111
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return []
|
|
116
|
-
end
|
|
112
|
+
conn = create_faraday_connection_from_client(client)
|
|
113
|
+
return [] unless conn
|
|
117
114
|
|
|
118
|
-
|
|
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')
|
|
115
|
+
response = conn.get('/api/v1/sharedInboxes?showAllBypass=true&limit=200&offset=0')
|
|
135
116
|
|
|
136
117
|
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
118
|
puts "Response status: #{response.status}"
|
|
146
119
|
puts "Response body: #{response.body[0..100]}..." if response.body && response.body.length > 100
|
|
147
120
|
end
|
|
@@ -149,7 +122,7 @@ module FubClient
|
|
|
149
122
|
if response.status == 200
|
|
150
123
|
data = JSON.parse(response.body, symbolize_names: true)
|
|
151
124
|
inboxes = data[:sharedInboxes] || []
|
|
152
|
-
puts "Found #{inboxes.count} shared inboxes via
|
|
125
|
+
puts "Found #{inboxes.count} shared inboxes via cookie client" if ENV['DEBUG']
|
|
153
126
|
inboxes
|
|
154
127
|
else
|
|
155
128
|
puts "Error: HTTP #{response.status} - #{response.body}" if ENV['DEBUG']
|
|
@@ -161,33 +134,14 @@ module FubClient
|
|
|
161
134
|
[]
|
|
162
135
|
end
|
|
163
136
|
|
|
164
|
-
def self.get_inbox(id)
|
|
165
|
-
puts "Calling SharedInbox.get_inbox(#{id}) using
|
|
137
|
+
def self.get_inbox(id, cookie_client: nil)
|
|
138
|
+
puts "Calling SharedInbox.get_inbox(#{id}) using cookie authentication" if ENV['DEBUG']
|
|
166
139
|
|
|
167
|
-
client =
|
|
168
|
-
|
|
169
|
-
subdomain = client.subdomain
|
|
140
|
+
client = resolve_cookie_client(cookie_client)
|
|
141
|
+
return nil unless client
|
|
170
142
|
|
|
171
|
-
|
|
172
|
-
|
|
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
|
|
143
|
+
conn = create_faraday_connection_from_client(client)
|
|
144
|
+
return nil unless conn
|
|
191
145
|
|
|
192
146
|
response = conn.get("/api/v1/sharedInboxes/#{id}")
|
|
193
147
|
|
|
@@ -212,7 +166,7 @@ module FubClient
|
|
|
212
166
|
end
|
|
213
167
|
|
|
214
168
|
if data.is_a?(Hash) && !data.empty?
|
|
215
|
-
puts "Found inbox with ID #{id} via
|
|
169
|
+
puts "Found inbox with ID #{id} via cookie client" if ENV['DEBUG']
|
|
216
170
|
data.extend(SharedInboxMethods)
|
|
217
171
|
data
|
|
218
172
|
else
|
|
@@ -229,6 +183,170 @@ module FubClient
|
|
|
229
183
|
nil
|
|
230
184
|
end
|
|
231
185
|
|
|
186
|
+
def self.update_inbox(id, attributes, cookie_client: nil, merge_with_existing: true)
|
|
187
|
+
puts "Calling SharedInbox.update_inbox(#{id}) using cookie authentication" if ENV['DEBUG']
|
|
188
|
+
|
|
189
|
+
client = resolve_cookie_client(cookie_client)
|
|
190
|
+
return nil unless client
|
|
191
|
+
|
|
192
|
+
# If merge_with_existing is true, get current inbox data and merge
|
|
193
|
+
if merge_with_existing
|
|
194
|
+
current_inbox = get_inbox(id, cookie_client: client)
|
|
195
|
+
unless current_inbox
|
|
196
|
+
puts 'Error: Could not retrieve current inbox data for merging' if ENV['DEBUG']
|
|
197
|
+
return nil
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Create full payload by merging existing data with new attributes
|
|
201
|
+
full_payload = {
|
|
202
|
+
name: current_inbox[:name],
|
|
203
|
+
phones: current_inbox[:phones] || [],
|
|
204
|
+
users: current_inbox[:users] || [],
|
|
205
|
+
replyFrom: current_inbox[:replyFrom] || "",
|
|
206
|
+
replyFromPersonalized: current_inbox[:replyFromPersonalized] || false,
|
|
207
|
+
officeHoursBehaviorType: current_inbox[:officeHoursBehaviorType] || "Voicemail",
|
|
208
|
+
officeHoursForwardNumber: current_inbox[:officeHoursForwardNumber] || [],
|
|
209
|
+
incomingForwardTeam: current_inbox[:incomingForwardTeam] || [],
|
|
210
|
+
agentViewAll: current_inbox[:agentViewAll] || 0,
|
|
211
|
+
incomingForwardNumber: current_inbox[:incomingForwardNumber] || [],
|
|
212
|
+
incomingBehaviorType: current_inbox[:incomingBehaviorType] || "Voicemail",
|
|
213
|
+
unansweredForwardNumber: current_inbox[:unansweredForwardNumber] || []
|
|
214
|
+
}.merge(attributes)
|
|
215
|
+
|
|
216
|
+
puts "Merged payload with existing data" if ENV['DEBUG']
|
|
217
|
+
else
|
|
218
|
+
full_payload = attributes
|
|
219
|
+
puts "Using provided attributes without merging" if ENV['DEBUG']
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
conn = create_faraday_connection_from_client(client)
|
|
223
|
+
return nil unless conn
|
|
224
|
+
|
|
225
|
+
response = conn.put do |req|
|
|
226
|
+
req.url "/api/v1/sharedInboxes/#{id}"
|
|
227
|
+
req.headers['Content-Type'] = 'application/json; charset=UTF-8'
|
|
228
|
+
req.headers['X-FUB-JS-Version'] = '198593'
|
|
229
|
+
req.body = JSON.generate(full_payload)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
if ENV['DEBUG']
|
|
233
|
+
puts "Update response status: #{response.status}"
|
|
234
|
+
puts "Update response body: #{response.body[0..200]}..." if response.body && response.body.length > 200
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
if [200, 204].include?(response.status)
|
|
238
|
+
if response.status == 200 && !response.body.empty?
|
|
239
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
240
|
+
puts "Updated inbox with ID #{id} via cookie client" if ENV['DEBUG']
|
|
241
|
+
data.extend(SharedInboxMethods) if data.is_a?(Hash)
|
|
242
|
+
data
|
|
243
|
+
else
|
|
244
|
+
puts "Updated inbox with ID #{id} (no response body)" if ENV['DEBUG']
|
|
245
|
+
true
|
|
246
|
+
end
|
|
247
|
+
else
|
|
248
|
+
puts "Error updating inbox: HTTP #{response.status} - #{response.body}" if ENV['DEBUG']
|
|
249
|
+
nil
|
|
250
|
+
end
|
|
251
|
+
rescue StandardError => e
|
|
252
|
+
puts "Error in update_inbox: #{e.message}" if ENV['DEBUG']
|
|
253
|
+
puts e.backtrace.join("\n") if ENV['DEBUG']
|
|
254
|
+
nil
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def self.find_by_phone(phone_number, cookie_client: nil)
|
|
258
|
+
puts "Calling SharedInbox.find_by_phone(#{phone_number}) using cookie authentication" if ENV['DEBUG']
|
|
259
|
+
|
|
260
|
+
client = resolve_cookie_client(cookie_client)
|
|
261
|
+
return nil unless client
|
|
262
|
+
|
|
263
|
+
# Get all inboxes (now with limit=200, should cover all 142 inboxes)
|
|
264
|
+
inboxes = all_inboxes(cookie_client: client)
|
|
265
|
+
return nil if inboxes.empty?
|
|
266
|
+
|
|
267
|
+
# Normalize the phone number for comparison (remove formatting)
|
|
268
|
+
normalized_search = normalize_phone(phone_number)
|
|
269
|
+
|
|
270
|
+
puts "Searching #{inboxes.length} inboxes for phone: #{phone_number} (normalized: #{normalized_search})" if ENV['DEBUG']
|
|
271
|
+
|
|
272
|
+
# Search through all inboxes for matching phone number
|
|
273
|
+
matching_inbox = inboxes.find do |inbox|
|
|
274
|
+
phones = inbox[:phones] || []
|
|
275
|
+
phones.any? do |phone_obj|
|
|
276
|
+
phone = phone_obj[:phone] || phone_obj['phone']
|
|
277
|
+
next false unless phone
|
|
278
|
+
|
|
279
|
+
normalized_inbox_phone = normalize_phone(phone)
|
|
280
|
+
match = normalized_inbox_phone == normalized_search
|
|
281
|
+
|
|
282
|
+
if ENV['DEBUG']
|
|
283
|
+
puts " Checking inbox '#{inbox[:name]}' phone '#{phone}' (normalized: #{normalized_inbox_phone}) - Match: #{match}"
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
match
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
if matching_inbox
|
|
291
|
+
puts "Found matching inbox: '#{matching_inbox[:name]}' (ID: #{matching_inbox[:id]})" if ENV['DEBUG']
|
|
292
|
+
matching_inbox.extend(SharedInboxMethods)
|
|
293
|
+
matching_inbox
|
|
294
|
+
else
|
|
295
|
+
puts "No inbox found with phone number: #{phone_number}" if ENV['DEBUG']
|
|
296
|
+
nil
|
|
297
|
+
end
|
|
298
|
+
rescue StandardError => e
|
|
299
|
+
puts "Error in find_by_phone: #{e.message}" if ENV['DEBUG']
|
|
300
|
+
puts e.backtrace.join("\n") if ENV['DEBUG']
|
|
301
|
+
nil
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
private_class_method def self.resolve_cookie_client(cookie_client)
|
|
306
|
+
# Use provided cookie_client or try to create one from configuration
|
|
307
|
+
client = cookie_client || create_cookie_client
|
|
308
|
+
|
|
309
|
+
unless client
|
|
310
|
+
puts 'Error: No cookie client available for authentication' if ENV['DEBUG']
|
|
311
|
+
return nil
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
client
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
private_class_method def self.normalize_phone(phone_number)
|
|
318
|
+
# Remove all non-digit characters for comparison
|
|
319
|
+
return '' unless phone_number
|
|
320
|
+
phone_number.to_s.gsub(/\D/, '')
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def self.create_cookie_client
|
|
324
|
+
config = FubClient.configuration
|
|
325
|
+
return nil unless config.has_cookie_auth?
|
|
326
|
+
|
|
327
|
+
begin
|
|
328
|
+
FubClient::CookieClient.new
|
|
329
|
+
rescue => e
|
|
330
|
+
puts "Error creating cookie client: #{e.message}" if ENV['DEBUG']
|
|
331
|
+
nil
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def self.create_faraday_connection_from_client(cookie_client)
|
|
336
|
+
return nil unless cookie_client && cookie_client.cookies && cookie_client.subdomain
|
|
337
|
+
|
|
338
|
+
Faraday.new(url: "https://#{cookie_client.subdomain}.followupboss.com") do |f|
|
|
339
|
+
f.headers['Cookie'] = cookie_client.cookies
|
|
340
|
+
f.headers['Accept'] = 'application/json, text/javascript, */*; q=0.01'
|
|
341
|
+
f.headers['Accept-Language'] = 'en-US,en;q=0.9'
|
|
342
|
+
f.headers['X-Requested-With'] = 'XMLHttpRequest'
|
|
343
|
+
f.headers['X-System'] = 'fub-spa'
|
|
344
|
+
f.headers['User-Agent'] =
|
|
345
|
+
'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'
|
|
346
|
+
f.adapter :net_http
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
232
350
|
def self.create_faraday_connection
|
|
233
351
|
client = FubClient::Client.instance
|
|
234
352
|
cookies = client.cookies
|
data/lib/fub_client/user.rb
CHANGED
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
module FubClient
|
|
2
2
|
class User < Resource
|
|
3
|
+
# Add a method to get the relation if needed
|
|
4
|
+
def self.relation
|
|
5
|
+
Her::Model::Relation.new(self)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Add convenience methods
|
|
9
|
+
def self.active
|
|
10
|
+
where(status: 'Active').to_a
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.agents
|
|
14
|
+
where(role: 'Agent').to_a
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.by_email(email)
|
|
18
|
+
where(email: email).first
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.by_name(name)
|
|
22
|
+
where(name: name).first
|
|
23
|
+
end
|
|
3
24
|
end
|
|
4
25
|
end
|
data/lib/fub_client/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: followupboss_client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Connor Gallopo
|
|
8
8
|
- Kyoto Kopz
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-07-
|
|
11
|
+
date: 2025-07-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activemodel
|