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
         
     |