chromate-rb 0.0.1.pre → 0.0.3.pre
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/.rubocop.yml +2 -0
- data/CHANGELOG.md +72 -3
- data/README.md +33 -6
- data/Rakefile +48 -16
- data/docker_root/Gemfile +4 -0
- data/docker_root/Gemfile.lock +28 -0
- data/docker_root/TestInDocker.gif +0 -0
- data/docker_root/app.rb +87 -0
- data/dockerfiles/Dockerfile +21 -7
- data/dockerfiles/README.md +49 -0
- data/docs/BOT_BROWSER.md +74 -0
- data/docs/README.md +74 -0
- data/docs/browser.md +124 -102
- data/docs/client.md +126 -0
- data/docs/element.md +365 -0
- data/docs/elements/checkbox.md +69 -0
- data/docs/elements/radio.md +57 -0
- data/lib/bot_browser/downloader.rb +64 -0
- data/lib/bot_browser/installer.rb +99 -0
- data/lib/bot_browser.rb +43 -0
- data/lib/chromate/actions/dom.rb +35 -27
- data/lib/chromate/actions/navigate.rb +7 -5
- data/lib/chromate/actions/screenshot.rb +71 -14
- data/lib/chromate/actions/stealth.rb +62 -0
- data/lib/chromate/binary.rb +83 -0
- data/lib/chromate/browser.rb +120 -24
- data/lib/chromate/c_logger.rb +8 -0
- data/lib/chromate/client.rb +65 -26
- data/lib/chromate/configuration.rb +31 -14
- data/lib/chromate/element.rb +119 -16
- data/lib/chromate/elements/checkbox.rb +40 -0
- data/lib/chromate/elements/option.rb +43 -0
- data/lib/chromate/elements/radio.rb +37 -0
- data/lib/chromate/elements/select.rb +50 -6
- data/lib/chromate/elements/tags.rb +29 -0
- data/lib/chromate/exceptions.rb +2 -0
- data/lib/chromate/files/agents.json +11 -0
- data/lib/chromate/files/stealth.js +199 -0
- data/lib/chromate/hardwares/keyboard_controller.rb +45 -0
- data/lib/chromate/hardwares/keyboards/virtual_controller.rb +65 -0
- data/lib/chromate/hardwares/mouse_controller.rb +55 -11
- data/lib/chromate/hardwares/mouses/linux_controller.rb +124 -21
- data/lib/chromate/hardwares/mouses/mac_os_controller.rb +6 -6
- data/lib/chromate/hardwares/mouses/virtual_controller.rb +95 -7
- data/lib/chromate/hardwares/mouses/x11.rb +36 -0
- data/lib/chromate/hardwares.rb +19 -3
- data/lib/chromate/helpers.rb +22 -15
- data/lib/chromate/user_agent.rb +41 -15
- data/lib/chromate/version.rb +1 -1
- data/lib/chromate.rb +2 -0
- data/logo.png +0 -0
- data/results/bot.png +0 -0
- data/results/brotector.png +0 -0
- data/results/cloudflare.png +0 -0
- data/results/headers.png +0 -0
- data/results/pixelscan.png +0 -0
- metadata +45 -2
data/docs/element.md
ADDED
@@ -0,0 +1,365 @@
|
|
1
|
+
# Element
|
2
|
+
|
3
|
+
The `Chromate::Element` class represents a DOM element in a browser controlled via the Chrome DevTools Protocol (CDP). It provides methods to interact with the element, including manipulating its attributes, getting its text content, and simulating user actions.
|
4
|
+
|
5
|
+
### Initialization
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
element = Chromate::Element.new(selector, client, node_id: nil, object_id: nil, root_id: nil)
|
9
|
+
```
|
10
|
+
|
11
|
+
- **Parameters:**
|
12
|
+
- `selector` (String): The CSS selector used to locate the element.
|
13
|
+
- `client` (Chromate::Client): An instance of the CDP client.
|
14
|
+
- `node_id` (Integer, optional): The node ID of the element.
|
15
|
+
- `object_id` (String, optional): The object ID of the element.
|
16
|
+
- `root_id` (Integer, optional): The root node ID of the document.
|
17
|
+
|
18
|
+
### Attributes
|
19
|
+
|
20
|
+
#### `#selector`
|
21
|
+
|
22
|
+
Returns the CSS selector used to locate the element.
|
23
|
+
|
24
|
+
- **Returns:**
|
25
|
+
- `String`: The CSS selector.
|
26
|
+
|
27
|
+
#### `#client`
|
28
|
+
|
29
|
+
Returns the CDP client instance used to communicate with the browser.
|
30
|
+
|
31
|
+
- **Returns:**
|
32
|
+
- `Chromate::Client`: The CDP client instance.
|
33
|
+
|
34
|
+
#### `#root_id`
|
35
|
+
|
36
|
+
Returns the root node ID of the document.
|
37
|
+
|
38
|
+
- **Returns:**
|
39
|
+
- `Integer`: The root node ID.
|
40
|
+
|
41
|
+
#### `#object_id`
|
42
|
+
|
43
|
+
Returns the object ID of the element.
|
44
|
+
|
45
|
+
- **Returns:**
|
46
|
+
- `String`: The object ID.
|
47
|
+
|
48
|
+
#### `#node_id`
|
49
|
+
|
50
|
+
Returns the node ID of the element.
|
51
|
+
|
52
|
+
- **Returns:**
|
53
|
+
- `Integer`: The node ID.
|
54
|
+
|
55
|
+
### Public Methods
|
56
|
+
|
57
|
+
#### `#mouse`
|
58
|
+
|
59
|
+
Returns the mouse controller for interacting with the element.
|
60
|
+
|
61
|
+
- **Example:**
|
62
|
+
```ruby
|
63
|
+
element.mouse.click
|
64
|
+
```
|
65
|
+
|
66
|
+
#### `#keyboard`
|
67
|
+
|
68
|
+
Returns the keyboard controller for interacting with the element.
|
69
|
+
|
70
|
+
- **Example:**
|
71
|
+
```ruby
|
72
|
+
element.keyboard.type('Hello World')
|
73
|
+
```
|
74
|
+
|
75
|
+
#### `#inspect`
|
76
|
+
|
77
|
+
Returns a string representation of the element.
|
78
|
+
|
79
|
+
- **Example:**
|
80
|
+
```ruby
|
81
|
+
puts element.inspect
|
82
|
+
```
|
83
|
+
|
84
|
+
#### `#text`
|
85
|
+
|
86
|
+
Retrieves the inner text of the element.
|
87
|
+
|
88
|
+
- **Example:**
|
89
|
+
```ruby
|
90
|
+
text = element.text
|
91
|
+
puts "Element text: #{text}"
|
92
|
+
```
|
93
|
+
|
94
|
+
#### `#html`
|
95
|
+
|
96
|
+
Retrieves the outer HTML of the element.
|
97
|
+
|
98
|
+
- **Example:**
|
99
|
+
```ruby
|
100
|
+
html = element.html
|
101
|
+
puts "Element HTML: #{html}"
|
102
|
+
```
|
103
|
+
|
104
|
+
#### `#attributes`
|
105
|
+
|
106
|
+
Returns a hash of the element's attributes.
|
107
|
+
|
108
|
+
- **Example:**
|
109
|
+
```ruby
|
110
|
+
attributes = element.attributes
|
111
|
+
puts "Element attributes: #{attributes}"
|
112
|
+
```
|
113
|
+
|
114
|
+
#### `#tag_name`
|
115
|
+
|
116
|
+
Gets the HTML tag name of the element in lowercase.
|
117
|
+
|
118
|
+
- **Returns:**
|
119
|
+
- `String`: The HTML tag name.
|
120
|
+
|
121
|
+
- **Example:**
|
122
|
+
```ruby
|
123
|
+
tag = element.tag_name
|
124
|
+
puts "Tag name: #{tag}"
|
125
|
+
```
|
126
|
+
|
127
|
+
#### `#set_attribute(name, value)`
|
128
|
+
|
129
|
+
Sets an attribute on the element.
|
130
|
+
|
131
|
+
- **Parameters:**
|
132
|
+
- `name` (String): The name of the attribute.
|
133
|
+
- `value` (String): The value to set for the attribute.
|
134
|
+
|
135
|
+
- **Example:**
|
136
|
+
```ruby
|
137
|
+
element.set_attribute('class', 'highlighted')
|
138
|
+
```
|
139
|
+
|
140
|
+
#### `#bounding_box`
|
141
|
+
|
142
|
+
Returns a hash with the dimensions of the element's bounding box.
|
143
|
+
|
144
|
+
- **Example:**
|
145
|
+
```ruby
|
146
|
+
box = element.bounding_box
|
147
|
+
puts "Bounding box: #{box}"
|
148
|
+
```
|
149
|
+
|
150
|
+
#### `#x`
|
151
|
+
|
152
|
+
Returns the x-coordinate of the element's position.
|
153
|
+
|
154
|
+
- **Example:**
|
155
|
+
```ruby
|
156
|
+
x_position = element.x
|
157
|
+
puts "X Position: #{x_position}"
|
158
|
+
```
|
159
|
+
|
160
|
+
#### `#y`
|
161
|
+
|
162
|
+
Returns the y-coordinate of the element's position.
|
163
|
+
|
164
|
+
- **Example:**
|
165
|
+
```ruby
|
166
|
+
y_position = element.y
|
167
|
+
puts "Y Position: #{y_position}"
|
168
|
+
```
|
169
|
+
|
170
|
+
#### `#width`
|
171
|
+
|
172
|
+
Returns the width of the element.
|
173
|
+
|
174
|
+
- **Example:**
|
175
|
+
```ruby
|
176
|
+
width = element.width
|
177
|
+
puts "Element width: #{width}"
|
178
|
+
```
|
179
|
+
|
180
|
+
#### `#height`
|
181
|
+
|
182
|
+
Returns the height of the element.
|
183
|
+
|
184
|
+
- **Example:**
|
185
|
+
```ruby
|
186
|
+
height = element.height
|
187
|
+
puts "Element height: #{height}"
|
188
|
+
```
|
189
|
+
|
190
|
+
#### `#focus`
|
191
|
+
|
192
|
+
Sets focus on the element.
|
193
|
+
|
194
|
+
- **Example:**
|
195
|
+
```ruby
|
196
|
+
element.focus
|
197
|
+
```
|
198
|
+
|
199
|
+
#### `#click`
|
200
|
+
|
201
|
+
Simulates a click on the element.
|
202
|
+
|
203
|
+
- **Example:**
|
204
|
+
```ruby
|
205
|
+
element.click
|
206
|
+
```
|
207
|
+
|
208
|
+
#### `#hover`
|
209
|
+
|
210
|
+
Simulates a hover action over the element.
|
211
|
+
|
212
|
+
- **Example:**
|
213
|
+
```ruby
|
214
|
+
element.hover
|
215
|
+
```
|
216
|
+
|
217
|
+
#### `#type(text)`
|
218
|
+
|
219
|
+
Types the specified text into the element.
|
220
|
+
|
221
|
+
- **Parameters:**
|
222
|
+
- `text` (String): The text to type.
|
223
|
+
|
224
|
+
- **Example:**
|
225
|
+
```ruby
|
226
|
+
element.type('Hello, Chromate!')
|
227
|
+
```
|
228
|
+
|
229
|
+
#### `#press_enter`
|
230
|
+
|
231
|
+
Simulates pressing the Enter key and submits the form if the element is inside one.
|
232
|
+
|
233
|
+
- **Example:**
|
234
|
+
```ruby
|
235
|
+
element.press_enter
|
236
|
+
```
|
237
|
+
|
238
|
+
#### `#drop_to(element)`
|
239
|
+
|
240
|
+
Drag current element and drop to target
|
241
|
+
|
242
|
+
- **Example:**
|
243
|
+
```ruby
|
244
|
+
element.drop_to(target_element)
|
245
|
+
```
|
246
|
+
|
247
|
+
#### `#find_element(selector)`
|
248
|
+
|
249
|
+
Finds a single child element matching the given selector.
|
250
|
+
|
251
|
+
- **Parameters:**
|
252
|
+
- `selector` (String): The CSS selector to find the element.
|
253
|
+
|
254
|
+
- **Returns:**
|
255
|
+
- `Chromate::Element`: The found element.
|
256
|
+
|
257
|
+
- **Example:**
|
258
|
+
```ruby
|
259
|
+
child_element = element.find_element('.child')
|
260
|
+
puts child_element.text
|
261
|
+
```
|
262
|
+
|
263
|
+
#### `#find_elements(selector, max: 0)`
|
264
|
+
|
265
|
+
Finds all child elements matching the given selector.
|
266
|
+
|
267
|
+
- **Parameters:**
|
268
|
+
- `selector` (String): The CSS selector to find elements.
|
269
|
+
- `max` (Integer, optional): The maximum number of elements to find (0 for no limit).
|
270
|
+
|
271
|
+
- **Returns:**
|
272
|
+
- `Array<Chromate::Element>`: An array of found elements.
|
273
|
+
|
274
|
+
- **Example:**
|
275
|
+
```ruby
|
276
|
+
elements = element.find_elements('.item')
|
277
|
+
elements.each { |el| puts el.text }
|
278
|
+
```
|
279
|
+
|
280
|
+
#### `#shadow_root_id`
|
281
|
+
|
282
|
+
Returns the shadow root ID of the element if it has one.
|
283
|
+
|
284
|
+
- **Example:**
|
285
|
+
```ruby
|
286
|
+
shadow_id = element.shadow_root_id
|
287
|
+
puts "Shadow root ID: #{shadow_id}"
|
288
|
+
```
|
289
|
+
|
290
|
+
#### `#shadow_root?`
|
291
|
+
|
292
|
+
Checks if the element has a shadow root.
|
293
|
+
|
294
|
+
- **Returns:**
|
295
|
+
- `Boolean`: `true` if the element has a shadow root, otherwise `false`.
|
296
|
+
|
297
|
+
- **Example:**
|
298
|
+
```ruby
|
299
|
+
if element.shadow_root?
|
300
|
+
puts "Element has a shadow root."
|
301
|
+
end
|
302
|
+
```
|
303
|
+
|
304
|
+
#### `#find_shadow_child(selector)`
|
305
|
+
|
306
|
+
Finds a single child element inside the shadow root using the given selector.
|
307
|
+
|
308
|
+
- **Parameters:**
|
309
|
+
- `selector` (String): The CSS selector to find the shadow child element.
|
310
|
+
|
311
|
+
- **Returns:**
|
312
|
+
- `Chromate::Element` or `nil`: The found element or `nil` if not found.
|
313
|
+
|
314
|
+
- **Example:**
|
315
|
+
```ruby
|
316
|
+
shadow_child = element.find_shadow_child('.shadow-element')
|
317
|
+
puts shadow_child.text if shadow_child
|
318
|
+
```
|
319
|
+
|
320
|
+
#### `#find_shadow_children(selector)`
|
321
|
+
|
322
|
+
Finds all child elements inside the shadow root using the given selector.
|
323
|
+
|
324
|
+
- **Parameters:**
|
325
|
+
- `selector` (String): The CSS selector to find shadow child elements.
|
326
|
+
|
327
|
+
- **Returns:**
|
328
|
+
- `Array<Chromate::Element>`: An array of found elements.
|
329
|
+
|
330
|
+
- **Example:**
|
331
|
+
```ruby
|
332
|
+
shadow_elements = element.find_shadow_children('.shadow-item')
|
333
|
+
shadow_elements.each { |el| puts el.text }
|
334
|
+
```
|
335
|
+
|
336
|
+
#### `#value`
|
337
|
+
|
338
|
+
Gets the value of the element.
|
339
|
+
|
340
|
+
- **Returns:**
|
341
|
+
- `String`: The element's value.
|
342
|
+
|
343
|
+
- **Example:**
|
344
|
+
```ruby
|
345
|
+
value = element.value
|
346
|
+
puts "Element value: #{value}"
|
347
|
+
```
|
348
|
+
|
349
|
+
### Exceptions
|
350
|
+
|
351
|
+
- `NotFoundError`: Raised when an element cannot be found with the given selector.
|
352
|
+
- `InvalidSelectorError`: Raised when the selector cannot resolve to a valid element.
|
353
|
+
|
354
|
+
## Specialized Elements
|
355
|
+
|
356
|
+
Chromate provides specialized element classes for specific HTML elements that have unique behaviors and methods. When using `find_element`, Chromate automatically returns the appropriate specialized element based on the element type.
|
357
|
+
|
358
|
+
### Available Specialized Elements
|
359
|
+
|
360
|
+
- [Select Element](elements/select.md): For `<select>` elements
|
361
|
+
- [Option Element](elements/option.md): For `<option>` elements within select elements
|
362
|
+
- [Radio Element](elements/radio.md): For radio button inputs (`<input type="radio">`)
|
363
|
+
- [Checkbox Element](elements/checkbox.md): For checkbox inputs (`<input type="checkbox">`)
|
364
|
+
|
365
|
+
Each specialized element inherits from the base `Element` class and adds specific methods for interacting with that type of element. See the individual documentation files for details on the methods available for each element type.
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Checkbox Element
|
2
|
+
|
3
|
+
The `Chromate::Elements::Checkbox` class represents a checkbox input element in the browser. It extends the base `Element` class with specific functionality for checkboxes.
|
4
|
+
|
5
|
+
### Initialization
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
checkbox = Chromate::Elements::Checkbox.new(selector, client, **options)
|
9
|
+
```
|
10
|
+
|
11
|
+
- **Parameters:**
|
12
|
+
- `selector` (String): The CSS selector used to locate the checkbox.
|
13
|
+
- `client` (Chromate::Client): An instance of the CDP client.
|
14
|
+
- `options` (Hash): Additional options passed to the Element constructor.
|
15
|
+
- `object_id` (String): Optional. The object ID of a pre-searched element.
|
16
|
+
- `node_id` (Integer): Optional. The node ID of a pre-searched element.
|
17
|
+
- `root_id` (Integer): Optional. The root ID of a pre-searched element.
|
18
|
+
|
19
|
+
### Public Methods
|
20
|
+
|
21
|
+
#### `#checked?`
|
22
|
+
|
23
|
+
Returns whether the checkbox is currently checked.
|
24
|
+
|
25
|
+
- **Returns:**
|
26
|
+
- `Boolean`: `true` if the checkbox is checked, `false` otherwise.
|
27
|
+
|
28
|
+
- **Example:**
|
29
|
+
```ruby
|
30
|
+
if checkbox.checked?
|
31
|
+
puts "Checkbox is checked"
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
#### `#check`
|
36
|
+
|
37
|
+
Checks the checkbox if it's not already checked.
|
38
|
+
|
39
|
+
- **Returns:**
|
40
|
+
- `self`: Returns the checkbox element for method chaining.
|
41
|
+
|
42
|
+
- **Example:**
|
43
|
+
```ruby
|
44
|
+
checkbox.check
|
45
|
+
```
|
46
|
+
|
47
|
+
#### `#uncheck`
|
48
|
+
|
49
|
+
Unchecks the checkbox if it's currently checked.
|
50
|
+
|
51
|
+
- **Returns:**
|
52
|
+
- `self`: Returns the checkbox element for method chaining.
|
53
|
+
|
54
|
+
- **Example:**
|
55
|
+
```ruby
|
56
|
+
checkbox.uncheck
|
57
|
+
```
|
58
|
+
|
59
|
+
#### `#toggle`
|
60
|
+
|
61
|
+
Toggles the checkbox state (checks if unchecked, unchecks if checked).
|
62
|
+
|
63
|
+
- **Returns:**
|
64
|
+
- `self`: Returns the checkbox element for method chaining.
|
65
|
+
|
66
|
+
- **Example:**
|
67
|
+
```ruby
|
68
|
+
checkbox.toggle
|
69
|
+
```
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Radio Element
|
2
|
+
|
3
|
+
The `Chromate::Elements::Radio` class represents a radio button input element in the browser. It extends the base `Element` class with specific functionality for radio buttons.
|
4
|
+
|
5
|
+
### Initialization
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
radio = Chromate::Elements::Radio.new(selector, client, **options)
|
9
|
+
```
|
10
|
+
|
11
|
+
- **Parameters:**
|
12
|
+
- `selector` (String): The CSS selector used to locate the radio button.
|
13
|
+
- `client` (Chromate::Client): An instance of the CDP client.
|
14
|
+
- `options` (Hash): Additional options passed to the Element constructor.
|
15
|
+
- `object_id` (String): Optional. The object ID of a pre-searched element.
|
16
|
+
- `node_id` (Integer): Optional. The node ID of a pre-searched element.
|
17
|
+
- `root_id` (Integer): Optional. The root ID of a pre-searched element.
|
18
|
+
|
19
|
+
### Public Methods
|
20
|
+
|
21
|
+
#### `#checked?`
|
22
|
+
|
23
|
+
Returns whether the radio button is currently checked.
|
24
|
+
|
25
|
+
- **Returns:**
|
26
|
+
- `Boolean`: `true` if the radio button is checked, `false` otherwise.
|
27
|
+
|
28
|
+
- **Example:**
|
29
|
+
```ruby
|
30
|
+
if radio.checked?
|
31
|
+
puts "Radio button is checked"
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
#### `#check`
|
36
|
+
|
37
|
+
Checks the radio button if it's not already checked.
|
38
|
+
|
39
|
+
- **Returns:**
|
40
|
+
- `self`: Returns the radio element for method chaining.
|
41
|
+
|
42
|
+
- **Example:**
|
43
|
+
```ruby
|
44
|
+
radio.check
|
45
|
+
```
|
46
|
+
|
47
|
+
#### `#uncheck`
|
48
|
+
|
49
|
+
Unchecks the radio button if it's currently checked.
|
50
|
+
|
51
|
+
- **Returns:**
|
52
|
+
- `self`: Returns the radio element for method chaining.
|
53
|
+
|
54
|
+
- **Example:**
|
55
|
+
```ruby
|
56
|
+
radio.uncheck
|
57
|
+
```
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Special thanks to the BotBrowser project (https://github.com/MiddleSchoolStudent/BotBrowser)
|
4
|
+
# for providing an amazing foundation for browser automation and making this work possible.
|
5
|
+
|
6
|
+
require 'chromate/binary'
|
7
|
+
|
8
|
+
module BotBrowser
|
9
|
+
class Downloader
|
10
|
+
class << self
|
11
|
+
def download(version = nil, profile = nil, platform = :mac)
|
12
|
+
version ||= versions.keys.first
|
13
|
+
profile ||= profiles[version].keys.first
|
14
|
+
version = version.to_sym
|
15
|
+
binary_path = download_file(versions[version][platform], "/tmp/botbrowser_#{version}_#{platform}.dmg")
|
16
|
+
profile_path = download_file(profiles[version][profile], "/tmp/botbrowser_#{version}_#{platform}.json")
|
17
|
+
|
18
|
+
[binary_path, profile_path]
|
19
|
+
end
|
20
|
+
|
21
|
+
def download_file(url, path)
|
22
|
+
Chromate::CLogger.log("Downloading #{url} to #{path}")
|
23
|
+
Chromate::Binary.run('curl', ['-L', url, '-o', path])
|
24
|
+
|
25
|
+
path
|
26
|
+
end
|
27
|
+
|
28
|
+
def versions
|
29
|
+
{
|
30
|
+
v132: {
|
31
|
+
mac: 'https://github.com/MiddleSchoolStudent/BotBrowser/releases/download/20250204/botbrowser_132.0.6834.84_mac_arm64.dmg',
|
32
|
+
linux: 'https://github.com/MiddleSchoolStudent/BotBrowser/releases/download/20250204/botbrowser_132.0.6834.84_amd64.deb',
|
33
|
+
windows: 'https://github.com/MiddleSchoolStudent/BotBrowser/releases/download/20250204/botbrowser_132.0.6834.84_win_x86_64.7z'
|
34
|
+
},
|
35
|
+
v130: {
|
36
|
+
mac: 'https://github.com/MiddleSchoolStudent/BotBrowser/releases/download/v130/botbrowser_130.0.6723.92_mac_arm64.dmg',
|
37
|
+
linux: 'https://github.com/MiddleSchoolStudent/BotBrowser/releases/download/v130/botbrowser_130.0.6723.117_amd64.deb',
|
38
|
+
windows: 'https://github.com/MiddleSchoolStudent/BotBrowser/releases/download/v130/botbrowser_130.0.6723.117_win_x86_64.7z'
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def profiles
|
44
|
+
{
|
45
|
+
v128: {
|
46
|
+
mac: 'https://raw.githubusercontent.com/MiddleSchoolStudent/BotBrowser/refs/heads/main/profiles/v128/chrome128_mac_arm64.enc',
|
47
|
+
win: 'https://raw.githubusercontent.com/MiddleSchoolStudent/BotBrowser/refs/heads/main/profiles/v128/chrome128_win10_x86_64.enc'
|
48
|
+
},
|
49
|
+
v129: {
|
50
|
+
mac: 'https://raw.githubusercontent.com/MiddleSchoolStudent/BotBrowser/refs/heads/main/profiles/v129/chrome129_mac_arm64.enc'
|
51
|
+
},
|
52
|
+
v130: {
|
53
|
+
mac: 'https://raw.githubusercontent.com/MiddleSchoolStudent/BotBrowser/refs/heads/main/profiles/v130/chrome130_mac_arm64.enc',
|
54
|
+
iphone: 'https://raw.githubusercontent.com/MiddleSchoolStudent/BotBrowser/refs/heads/main/profiles/v130/chrome130_iphone.enc'
|
55
|
+
},
|
56
|
+
v132: {
|
57
|
+
mac: 'https://github.com/MiddleSchoolStudent/BotBrowser/blob/main/profiles/v132/chrome132_mac_arm64.enc',
|
58
|
+
win: 'https://github.com/MiddleSchoolStudent/BotBrowser/blob/main/profiles/v132/chrome132_win10_x86_64.enc'
|
59
|
+
}
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'chromate/helpers'
|
5
|
+
require 'chromate/c_logger'
|
6
|
+
require 'bot_browser/downloader'
|
7
|
+
|
8
|
+
module BotBrowser
|
9
|
+
class Installer
|
10
|
+
class NotInstalledError < StandardError; end
|
11
|
+
class << self
|
12
|
+
include Chromate::Helpers
|
13
|
+
|
14
|
+
def install(version = nil)
|
15
|
+
create_config_dir
|
16
|
+
binary_path, profile_path = Downloader.download(version)
|
17
|
+
bot_browser_path = install_binary(binary_path)
|
18
|
+
bot_browser_profile_path = install_profile(profile_path)
|
19
|
+
|
20
|
+
write_config(bot_browser_path, bot_browser_profile_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
def config_dir
|
24
|
+
"#{Dir.home}/.botbrowser"
|
25
|
+
end
|
26
|
+
|
27
|
+
def installed?
|
28
|
+
File.exist?("#{config_dir}/config.yml")
|
29
|
+
end
|
30
|
+
|
31
|
+
def uninstall
|
32
|
+
raise NotInstalledError, 'BotBrowser is not installed' unless installed?
|
33
|
+
|
34
|
+
config = YAML.load_file("#{config_dir}/config.yml")
|
35
|
+
Chromate::CLogger.log("Uninstalling binary at #{config["bot_browser_path"]}")
|
36
|
+
FileUtils.rm_rf(config['bot_browser_path'])
|
37
|
+
Chromate::CLogger.log("Uninstalling profile at #{config["profile"]}")
|
38
|
+
FileUtils.rm_rf(config['profile'])
|
39
|
+
FileUtils.rm_rf(config_dir)
|
40
|
+
Chromate::CLogger.log('Uninstalled')
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def install_binary(binary_path)
|
46
|
+
Chromate::CLogger.log("Installing binary from #{binary_path}")
|
47
|
+
return install_binary_mac(binary_path) if mac?
|
48
|
+
return install_binary_linux(binary_path) if linux?
|
49
|
+
return install_binary_windows(binary_path) if windows?
|
50
|
+
|
51
|
+
raise 'Unsupported platform'
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_config_dir
|
55
|
+
Chromate::CLogger.log("Creating config directory at #{config_dir}")
|
56
|
+
FileUtils.mkdir_p(config_dir)
|
57
|
+
end
|
58
|
+
|
59
|
+
def install_profile(profile_path)
|
60
|
+
Chromate::CLogger.log("Installing profile from #{profile_path}")
|
61
|
+
`cp #{profile_path} #{config_dir}/`
|
62
|
+
|
63
|
+
"#{config_dir}/#{File.basename(profile_path)}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def install_binary_mac(binary_path)
|
67
|
+
Chromate::Binary.run('hdiutil', ['attach', binary_path])
|
68
|
+
Chromate::Binary.run('cp', ['-r', '/Volumes/Chromium/Chromium.app', '/Applications/'])
|
69
|
+
Chromate::Binary.run('hdiutil', ['detach', '/Volumes/Chromium'])
|
70
|
+
Chromate::Binary.run('xattr', ['-rd', 'com.apple.quarantine', '/Applications/Chromium.app'])
|
71
|
+
Chromate::Binary.run('codesign', ['--force', '--deep', '--sign', '-', '/Applications/Chromium.app'], need_success: false)
|
72
|
+
|
73
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium'
|
74
|
+
end
|
75
|
+
|
76
|
+
def install_binary_linux(binary_path)
|
77
|
+
Chromate::Binary.run('sudo', ['dpkg', '-i', binary_path])
|
78
|
+
Chromate::Binary.run('sudo', ['apt-get', 'install', '-f'])
|
79
|
+
|
80
|
+
'/usr/bin/chromium'
|
81
|
+
end
|
82
|
+
|
83
|
+
def install_binary_windows(binary_path)
|
84
|
+
Chromate::Binary.run('7z', ['x', binary_path])
|
85
|
+
|
86
|
+
'chromium.exe'
|
87
|
+
end
|
88
|
+
|
89
|
+
def write_config(bot_browser_path, bot_browser_profile_path)
|
90
|
+
Chromate::CLogger.log("Writing config to #{config_dir}/config.yml")
|
91
|
+
File.write(File.expand_path("#{config_dir}/config.yml"), <<~YAML)
|
92
|
+
---
|
93
|
+
bot_browser_path: #{bot_browser_path}
|
94
|
+
profile: #{bot_browser_profile_path}
|
95
|
+
YAML
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/bot_browser.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'chromate/c_logger'
|
5
|
+
require 'bot_browser/installer'
|
6
|
+
|
7
|
+
module BotBrowser
|
8
|
+
class << self
|
9
|
+
def install(version = nil)
|
10
|
+
Installer.install(version)
|
11
|
+
end
|
12
|
+
|
13
|
+
def uninstall
|
14
|
+
Installer.uninstall
|
15
|
+
end
|
16
|
+
|
17
|
+
def installed?
|
18
|
+
Installer.installed?
|
19
|
+
end
|
20
|
+
|
21
|
+
def load
|
22
|
+
yaml = YAML.load_file("#{Dir.home}/.botbrowser/config.yml")
|
23
|
+
|
24
|
+
Chromate.configure do |config|
|
25
|
+
ENV['CHROME_BIN'] = yaml['bot_browser_path']
|
26
|
+
config.args = [
|
27
|
+
"--bot-profile=#{yaml["profile"]}",
|
28
|
+
'--no-sandbox'
|
29
|
+
]
|
30
|
+
config.startup_patch = false
|
31
|
+
end
|
32
|
+
|
33
|
+
Chromate::CLogger.log('BotBrowser loaded', level: :debug)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Usage
|
39
|
+
# require 'bot_browser'
|
40
|
+
|
41
|
+
# BotBrowser.install
|
42
|
+
# BotBrowser.load
|
43
|
+
# browser = Chromate::Browser.new
|