Class: Mass::Profile

Inherits:
BlackStack::Base
  • Object
show all
Defined in:
lib/base-line/profile.rb

Direct Known Subclasses

ProfileAPI, ProfileMTA, ProfileRPA

Constant Summary collapse

LOCKFILE_PATH =

DROPBOX LOCKFILE PARAMETERS

'/tmp/dropbox_upload.lock'
LOCK_TIMEOUT =

Path to your lockfile

60

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(h) ⇒ Profile

Returns a new instance of Profile.



168
169
170
171
# File 'lib/base-line/profile.rb', line 168

def initialize(h)
    super(h)
    self.type = Mass::ProfileType.new(h['profile_type_desc']).child_class_instance
end

Instance Attribute Details

#typeObject

Returns the value of attribute type.



3
4
5
# File 'lib/base-line/profile.rb', line 3

def type
  @type
end

Class Method Details

.object_nameObject



164
165
166
# File 'lib/base-line/profile.rb', line 164

def self.object_name
    'profile'
end

Instance Method Details

#acquire_lockObject

DROPBOX LOCKFILE FUNCTIONS



10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/base-line/profile.rb', line 10

def acquire_lock
    begin
        Timeout.timeout(LOCK_TIMEOUT) do
        # Wait until the lockfile can be created (i.e., it's not already taken)
        while File.exist?(LOCKFILE_PATH)
            sleep 0.1
        end
        # Create the lockfile
        File.open(LOCKFILE_PATH, 'w') {}
        end
    rescue Timeout::Error
        raise "Timeout while waiting for lockfile."
    end
end

#child_class_instanceObject

crate an instance of the profile type using the class defined in the ‘desc` attribute. override the base method



182
183
184
185
186
187
188
# File 'lib/base-line/profile.rb', line 182

def child_class_instance
    profile_type = self.desc['profile_type']
    key = self.class_name_from_profile_type
    raise "Source code of profile type #{profile_type} not found. Create a class #{key} in the folder `/lib` of your mass-sdk." unless Kernel.const_defined?(key)
    ret = Kernel.const_get(key).new(self.desc)
    return ret
end

#class_name_from_profile_typeObject

convert the profile_type into the ruby class to create an instance. example: Apollo –> Mass::ApolloAPI



175
176
177
178
# File 'lib/base-line/profile.rb', line 175

def class_name_from_profile_type
    profile_type = self.desc['profile_type']
    "Mass::#{profile_type}" 
end

#connectioncheck(limit: 100, logger: nil) ⇒ Object

Scrape the inbox of the profile. Return a an array of hash descriptors of outreach records.

Parameters:

  • limit: the maximum number of connections to scrape. Default: 100.

  • logger: a logger object to log the process. Default: nil.

Example of a hash descritor into the returned array: “‘

# a scraped message is always a :performed message
'status' => :performed,
# what is the outreach type?
# e.g.: :LinkedIn_ConnectionRequest
# decide this in the child class.
'outreach_type' => nil,
# hash descriptor of the profile who is scraping the inbox
'profile' => self.desc,
# hash descriptor of the lead who is the conversation partner
'lead' => nil,
# if the message has been sent by the profile, it is :outgoing.
# if the message has been sent by the lead, it is :incoming.
'direction' => :accepted,

“‘



261
262
263
# File 'lib/base-line/profile.rb', line 261

def connectioncheck(limit: 100, logger:nil)
    []
end

#create_s3_folder(folder_name) ⇒ Object

Function to create a folder in S3



44
45
46
47
48
49
50
# File 'lib/base-line/profile.rb', line 44

def create_s3_folder(folder_name)
    Mass.s3.put_object(
        bucket: Mass.s3_bucket, 
        key: "#{folder_name}/"
    )
    return true
end

#download_image(img, dropbox_folder = nil) ⇒ Object

download image from Selenium using JavaScript and upload to Dropbox return the URL of the screenshot

Parameters:

  • img: Selenium image element to download from the website and upload to dropbox.

  • dropbox_folder: Dropbox folder name to store the image.



157
158
159
# File 'lib/base-line/profile.rb', line 157

def download_image(img, dropbox_folder=nil)
    download_image_0(img.attribute('src'), dropbox_folder)
end

#download_image_0(url, dropbox_folder = nil) ⇒ Object

download image from Selenium using JavaScript and upload to Dropbox return the URL of the screenshot

Parameters:

  • url: Internet address of the image to download from the website and upload to dropbox.

  • dropbox_folder: Dropbox folder name to store the image.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/base-line/profile.rb', line 63

def download_image_0(url, dropbox_folder = nil)
    raise "Either dropbox_folder parameter or self.desc['id_account'] are required." if dropbox_folder.nil? && self.desc['id_account'].nil?
    dropbox_folder = self.desc['id_account'] if dropbox_folder.nil?
    
    # Parameters
    id = SecureRandom.uuid
    
    # JavaScript to get base64 image data
    js0 = "
        function getImageBase64(imageSrc) {
        return new Promise(async (resolve, reject) => {
            try {
            const response = await fetch(imageSrc);
            const blob = await response.blob();
            const reader = new FileReader();
            reader.onloadend = function() {
                resolve(reader.result);
            };
            reader.onerror = function(error) {
                reject(error);
            };
            reader.readAsDataURL(blob);
            } catch (error) {
            reject(error);
            }
        });
        }
        return getImageBase64('#{url}');
    "
    
    # Execute JavaScript and get base64 image data
    base64_image = driver.execute_script(js0)
    raise "Failed to retrieve image data from URL: #{url}" if base64_image.nil?
    
    # Extract MIME type and base64 data
    mime_type_match = base64_image.match(/^data:image\/([a-zA-Z0-9.+-]+);base64,/)
    if mime_type_match
        mime_subtype = mime_type_match[1] # e.g., 'png', 'jpeg', 'gif'
        # Map common MIME subtypes to file extensions
        extension = case mime_subtype
                    when 'jpeg' then 'jpg'
                    else mime_subtype
                    end
        # Remove the data URL prefix
        image_data = base64_image.sub(/^data:image\/[a-zA-Z0-9.+-]+;base64,/, '')
    else
        raise "Unsupported or invalid image data format."
    end
    
    # Update filename and paths
    filename = "#{id}.#{extension}"
    tmp_paths = if Mass.download_path.is_a?(String)
                    ["#{Mass.download_path}/#{filename}"]
                elsif Mass.download_path.is_a?(Array)
                    Mass.download_path.map { |s| "#{s}/#{filename}" }
                else
                    raise "Invalid Mass.download_path configuration."
                end
    
    # Save the image to the first available path
    tmp_path = tmp_paths.find { |path| File.writable?(File.dirname(path)) }
    raise "No writable path found in #{tmp_paths.join(', ')}." if tmp_path.nil?
    
    File.open(tmp_path, 'wb') do |file|
        file.write(Base64.decode64(image_data))
    end
    
    # Proceed with Dropbox operations
    year = Time.now.year.to_s.rjust(4, '0')
    month = Time.now.month.to_s.rjust(2, '0')
    folder = dropbox_folder #"/massprospecting.rpa/#{dropbox_folder}.#{year}.#{month}"
    path = "#{folder}/#{filename}"
    create_s3_folder(folder)

    # Upload the file to Dropbox
    ret = upload_file_to_s3(tmp_path, path)

    # Delete the local file
    File.delete(tmp_path)

    # Return the URL of the file in Dropbox
    # 
    # Add a timeout to wait the file is present in the cloud.
    # Reference: https://github.com/MassProspecting/docs/issues/320
    ret
end

#inboxcheck(limit: 100, only_unread: true, logger: nil) ⇒ Object

Scrape the inbox of the profile. Return a an array of hash descriptors of outreach records.

Parameters:

  • limit: the maximum number of messages to scrape. Default: 100.

  • only_unread: if true, then only the unread messages will be scraped. Default: true.

  • logger: a logger object to log the process. Default: nil.

Example of a hash descritor into the returned array: “‘

# a scraped message is always a :performed message
'status' => :performed,
# what is the outreach type?
# e.g.: :LinkedIn_DirectMessage
# decide this in the child class.
'outreach_type' => nil,
# hash descriptor of the profile who is scraping the inbox
'profile' => self.desc,
# hash descriptor of the lead who is the conversation partner
'lead' => nil,
# if the message has been sent by the profile, it is :outgoing.
# if the message has been sent by the lead, it is :incoming.
'direction' => nil, 
# the content of the message
'subject' => nil,
'body' => nil,

“‘



231
232
233
# File 'lib/base-line/profile.rb', line 231

def inboxcheck(limit: 100, only_unread:true, logger:nil)
    []
end

#release_lockObject



25
26
27
# File 'lib/base-line/profile.rb', line 25

def release_lock
    File.delete(LOCKFILE_PATH) if File.exist?(LOCKFILE_PATH)
end

#running?Boolean

return true of the profile is running if its profile type is rpa-access, then it will return true if the browser is running. else, it will return always true.

Returns:

  • (Boolean)


193
194
195
196
197
198
199
# File 'lib/base-line/profile.rb', line 193

def running?
    if self.type.desc['access'].to_sym == :rpa
        c = AdsPowerClient.new(key: ADSPOWER_API_KEY)
        return c.check(self.desc['ads_power_id'])
    end
    return true
end

#upload_file_to_s3(file_path, s3_key) ⇒ Object

Function to upload a file to Amazon S3 and get its public URL



30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/base-line/profile.rb', line 30

def upload_file_to_s3(file_path, s3_key)
    # Upload the file
    Mass.s3.put_object(
        bucket: Mass.s3_bucket, 
        key: s3_key, 
        body: File.open(file_path)
    )
    # Generate the public URL
    public_url = "https://#{Mass.s3_bucket}.s3.amazonaws.com/#{s3_key}"
    # return
    return public_url
end