aazzrrooddeell commited on
Commit
308cf3b
·
verified ·
1 Parent(s): e81c93c

Upload 67 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. .gitignore +4 -0
  3. Commands.txt +1 -0
  4. img/account_run_win.png +0 -0
  5. img/account_run_win_custom_email_api.png +0 -0
  6. img/activated_href_business.png +0 -0
  7. img/activated_href_esethome.png +0 -0
  8. img/activated_href_protecthub.png +0 -0
  9. img/activation_with_account_1.png +0 -0
  10. img/activation_with_account_2.png +0 -0
  11. img/activation_with_account_3.png +0 -0
  12. img/activation_with_account_5.png +3 -0
  13. img/activation_with_account_6.png +0 -0
  14. img/activation_with_account_7.png +0 -0
  15. img/activation_with_key.png +0 -0
  16. img/custom_settings_menu.png +0 -0
  17. img/default_settings_menu.png +0 -0
  18. img/delete_eset_home_account.png +0 -0
  19. img/endpoint_key_run_win.png +0 -0
  20. img/endpoint_key_run_win_custom_email_api.png +0 -0
  21. img/installation_from_source.png +0 -0
  22. img/installer_permission_error.png +0 -0
  23. img/key_run_win.png +0 -0
  24. img/key_run_win_custom_email_api.png +0 -0
  25. img/logo_alt.png +0 -0
  26. img/main_menu.png +0 -0
  27. img/protecthub_account_run_win.png +0 -0
  28. img/protecthub_account_run_win_custom_email_api.png +0 -0
  29. img/protecthub_license_data_message.png +0 -0
  30. img/reinstallation_attempt.png +0 -0
  31. img/reset_eset_vpn.png +0 -0
  32. img/small_business_key_run_win.png +0 -0
  33. img/small_business_key_run_win_custom_email_api.png +0 -0
  34. img/successfully_installed.png +0 -0
  35. img/unbinding_protecthub_key.png +0 -0
  36. img/updater_available.png +0 -0
  37. img/updater_binary_update.png +0 -0
  38. img/updater_src_update.png +0 -0
  39. img/updater_uptodate.png +0 -0
  40. img/vpn_codes_message.png +0 -0
  41. img/vpn_codes_run_win.png +0 -0
  42. img/vpn_codes_run_win_custom_email_api.png +0 -0
  43. main.py +735 -0
  44. modules/EmailAPIs.py +384 -0
  45. modules/EsetTools.py +515 -0
  46. modules/MBCI.py +122 -0
  47. modules/ProgressBar.py +55 -0
  48. modules/SharedTools.py +539 -0
  49. modules/Updater.py +235 -0
  50. modules/WebDriverInstaller.py +410 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ img/activation_with_account_5.png filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ *.exe
2
+ *.pyc
3
+ *.txt
4
+ .DS_STORE
Commands.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ python .\main.py --skip-webdriver-menu --chrome --vpn-codes --email-api fakemail --custom-browser-location "C:\Program Files\BraveSoftware\Brave-Browser\Application\brave.exe"
img/account_run_win.png ADDED
img/account_run_win_custom_email_api.png ADDED
img/activated_href_business.png ADDED
img/activated_href_esethome.png ADDED
img/activated_href_protecthub.png ADDED
img/activation_with_account_1.png ADDED
img/activation_with_account_2.png ADDED
img/activation_with_account_3.png ADDED
img/activation_with_account_5.png ADDED

Git LFS Details

  • SHA256: dee62c4fbd3559477dfbc49c1da1b20c186541bd1ef9e1469ba57809e3d3617b
  • Pointer size: 131 Bytes
  • Size of remote file: 163 kB
img/activation_with_account_6.png ADDED
img/activation_with_account_7.png ADDED
img/activation_with_key.png ADDED
img/custom_settings_menu.png ADDED
img/default_settings_menu.png ADDED
img/delete_eset_home_account.png ADDED
img/endpoint_key_run_win.png ADDED
img/endpoint_key_run_win_custom_email_api.png ADDED
img/installation_from_source.png ADDED
img/installer_permission_error.png ADDED
img/key_run_win.png ADDED
img/key_run_win_custom_email_api.png ADDED
img/logo_alt.png ADDED
img/main_menu.png ADDED
img/protecthub_account_run_win.png ADDED
img/protecthub_account_run_win_custom_email_api.png ADDED
img/protecthub_license_data_message.png ADDED
img/reinstallation_attempt.png ADDED
img/reset_eset_vpn.png ADDED
img/small_business_key_run_win.png ADDED
img/small_business_key_run_win_custom_email_api.png ADDED
img/successfully_installed.png ADDED
img/unbinding_protecthub_key.png ADDED
img/updater_available.png ADDED
img/updater_binary_update.png ADDED
img/updater_src_update.png ADDED
img/updater_uptodate.png ADDED
img/vpn_codes_message.png ADDED
img/vpn_codes_run_win.png ADDED
img/vpn_codes_run_win_custom_email_api.png ADDED
main.py ADDED
@@ -0,0 +1,735 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import contextlib
2
+ import logging
3
+ import pathlib
4
+ import json
5
+ import sys
6
+ import io
7
+
8
+ I_AM_EXECUTABLE = (True if (getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS')) else False)
9
+ PATH_TO_SELF = sys.executable if I_AM_EXECUTABLE else __file__
10
+ CONFIG_PATH = pathlib.Path(PATH_TO_SELF).parent.resolve().joinpath('eset-keygen-config.json')
11
+ LOG_PATH = pathlib.Path(PATH_TO_SELF).parent.resolve().joinpath('ESET-KeyGen.log')
12
+ SILENT_MODE = '--silent' in sys.argv
13
+ MBCI_MODE = len(sys.argv) == 1
14
+
15
+ def enable_logging():
16
+ logging.basicConfig(
17
+ level=logging.INFO,
18
+ filemode='w',
19
+ filename=LOG_PATH,
20
+ format='%(asctime)s - %(levelname)s - %(message)s'
21
+ )
22
+
23
+ if ('--disable-logging' not in sys.argv and not MBCI_MODE) or ('--disable-logging' in sys.argv and SILENT_MODE):
24
+ enable_logging()
25
+
26
+ from modules.EmailAPIs import *
27
+
28
+ # ---- Quick settings ----
29
+ VERSION = ['v1.5.5.3', 1553]
30
+ LOGO = f"""
31
+ ███████╗███████╗███████╗████████╗ ██╗ ██╗███████╗██╗ ██╗ ██████╗ ███████╗███╗ ██╗
32
+ ██╔════╝██╔════╝██╔════╝╚══██╔══╝ ██║ ██╔╝██╔════╝╚██╗ ██╔╝██╔════╝ ██╔════╝████╗ ██║
33
+ █████╗ ███████╗█████╗ ██║ █████╔╝ █████╗ ╚████╔╝ ██║ ███╗█████╗ ██╔██╗ ██║
34
+ ██╔══╝ ╚════██║██╔══╝ ██║ ██╔═██╗ ██╔══╝ ╚██╔╝ ██║ ██║██╔══╝ ██║╚██╗██║
35
+ ███████╗███████║███████╗ ██║ ██║ ██╗███████╗ ██║ ╚██████╔╝███████╗██║ ╚████║
36
+ ╚══════╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝
37
+ Project Version: {VERSION[0]}
38
+ Project Dev: Parv Ashwani (ZARWIN)
39
+ Dev Website: https://zarwin.is-a.dev
40
+ """
41
+ if '--no-logo' in sys.argv:
42
+ LOGO = f"ESET KeyGen {VERSION[0]} by rzc0d3r\n"
43
+
44
+ DEFAULT_PATH_TO_PROXY_FILE = 'proxies.txt'
45
+ DEFAULT_EMAIL_API = 'guerrillamail'
46
+ AVAILABLE_EMAIL_APIS = ('1secmail', 'guerrillamail', 'developermail', 'mailticking', 'fakemail', 'inboxes', 'incognitomail')
47
+ WEB_WRAPPER_EMAIL_APIS = ('guerrillamail', 'mailticking', 'fakemail', 'inboxes', 'incognitomail')
48
+ EMAIL_API_CLASSES = {
49
+ 'guerrillamail': GuerRillaMailAPI,
50
+ '1secmail': OneSecEmailAPI,
51
+ 'developermail': DeveloperMailAPI,
52
+ 'mailticking': MailTickingAPI,
53
+ 'fakemail': FakeMailAPI,
54
+ 'inboxes': InboxesAPI,
55
+ 'incognitomail': IncognitoMailAPI
56
+ }
57
+
58
+ args = {
59
+ 'auto_detect_browser': True,
60
+ 'chrome': False,
61
+ 'firefox': False,
62
+ 'edge': False,
63
+ 'safari': False,
64
+
65
+ 'key': True,
66
+ 'small_business_key': False,
67
+ 'advanced_key': False,
68
+ 'vpn_codes': False,
69
+ 'account': False,
70
+ 'protecthub_account': False,
71
+ 'only_webdriver_update': False,
72
+ 'update': False,
73
+ 'reset_eset_vpn': False,
74
+ 'install': False,
75
+ 'return_exit_code': 0,
76
+
77
+ 'skip_webdriver_menu': False,
78
+ 'no_headless': False,
79
+ 'custom_browser_location': '',
80
+ 'email_api': DEFAULT_EMAIL_API,
81
+ 'custom_email_api': False,
82
+ 'skip_update_check': False,
83
+ 'no_logo': False,
84
+ 'disable_progress_bar': False,
85
+ 'disable_output_file': False,
86
+ 'repeat': 1,
87
+ 'proxy_file': DEFAULT_PATH_TO_PROXY_FILE,
88
+
89
+ 'silent': False,
90
+ 'disable_logging': False
91
+ }
92
+
93
+ MBCI_BROWSERS_ARGS = ['auto-detect-browser', 'chrome', 'firefox', 'edge', 'safari']
94
+ MBCI_MODES_OF_OPERATION_ARGS = [
95
+ 'key', 'small-business-key', 'advanced-key', 'vpn-codes', 'account',
96
+ 'protecthub-account', 'only-webdriver-update', 'reset-eset-vpn', 'update', 'install'
97
+ ]
98
+ MBCI_OTHER_ARGS = [
99
+ 'skip_webdriver_menu', 'no_headless', 'custom_browser_location', 'custom_email_api',
100
+ 'skip_update_check', 'disable_progress_bar', 'disable_output_file', 'repeat', 'disable_logging',
101
+ 'proxy_file'
102
+ ]
103
+ MBCI_ARGS = MBCI_BROWSERS_ARGS + MBCI_MODES_OF_OPERATION_ARGS + MBCI_OTHER_ARGS
104
+
105
+ from modules.WebDriverInstaller import *
106
+ from modules.EsetTools import EsetRegister as ER
107
+ from modules.EsetTools import EsetKeygen as EK
108
+ from modules.EsetTools import EsetVPN as EV
109
+ from modules.EsetTools import EsetProtectHubRegister as EPHR
110
+ from modules.EsetTools import EsetProtectHubKeygen as EPHK
111
+ from modules.EsetTools import EsetVPNResetWindows as EVRW
112
+ from modules.EsetTools import EsetVPNResetMacOS as EVRM
113
+ from modules.EsetTools import IPBlockedException
114
+ from modules.SharedTools import *
115
+ from modules.MBCI import *
116
+ from modules.Updater import Updater
117
+
118
+ import traceback
119
+ import colorama
120
+ import platform
121
+ import datetime
122
+ import argparse
123
+ import re
124
+ import os
125
+
126
+ PATH_TO_SELF = sys.executable if I_AM_EXECUTABLE else __file__
127
+ DRIVER = None
128
+ PROXIES = []
129
+ PROXIES_LEN = 0
130
+ PROXY_COUNTER = 1
131
+ PROXY_ERROR_COUNTER = 0
132
+ PROXY_ERROR_COUNTER_LIMIT = 3
133
+ CHROME_PROXY_EXTENSION_PATH = ""
134
+
135
+ class MBCIConfigManager:
136
+ def __init__(self, path=CONFIG_PATH):
137
+ self.path = path
138
+
139
+ def save(self, args):
140
+ config = {
141
+ 'Browser': [key for key in MBCI_BROWSERS_ARGS if args[key.replace('-', '_')]][0],
142
+ 'Mode of operation': [key for key in MBCI_MODES_OF_OPERATION_ARGS if args[key.replace('-', '_')]][0],
143
+ 'Email API': args['email_api']
144
+ }
145
+
146
+ for key in MBCI_OTHER_ARGS:
147
+ config[key] = args[key]
148
+
149
+ json.dump(config, open(CONFIG_PATH, 'w'), indent=4)
150
+
151
+ def load(self):
152
+ config = json.load(open(self.path))
153
+ try:
154
+ filtered_config = {}
155
+ browser = config.pop('Browser')
156
+ mode_of_operation = config.pop('Mode of operation')
157
+ email_api = config.pop('Email API')
158
+ if browser in MBCI_BROWSERS_ARGS:
159
+ filtered_config[browser] = True
160
+ if mode_of_operation in MBCI_MODES_OF_OPERATION_ARGS:
161
+ filtered_config[mode_of_operation] = True
162
+ if email_api in AVAILABLE_EMAIL_APIS:
163
+ filtered_config['email_api'] = email_api
164
+ for key in config:
165
+ if key in MBCI_OTHER_ARGS:
166
+ filtered_config[key] = config[key]
167
+ return filtered_config
168
+ except:
169
+ return False
170
+
171
+ @property
172
+ def is_exists(self):
173
+ return os.path.isfile(self.path)
174
+
175
+ def RunMenu():
176
+ MainMenu = ViewMenu(LOGO+'\n---- Main Menu ----')
177
+
178
+ SettingMenu = ViewMenu(LOGO+'\n---- Settings Menu ----')
179
+ SettingMenu.add_item(
180
+ OptionAction(
181
+ args,
182
+ title='Browsers',
183
+ action='store_true',
184
+ args_names=MBCI_BROWSERS_ARGS,
185
+ default_value=[key for key in MBCI_BROWSERS_ARGS if args[key.replace('-', '_')]][0]
186
+ )
187
+ )
188
+ SettingMenu.add_item(
189
+ OptionAction(
190
+ args,
191
+ title='Modes of operation',
192
+ action='store_true',
193
+ args_names=MBCI_MODES_OF_OPERATION_ARGS,
194
+ default_value=[key for key in MBCI_MODES_OF_OPERATION_ARGS if args[key.replace('-', '_')]][0]
195
+ )
196
+ )
197
+ SettingMenu.add_item(
198
+ OptionAction(
199
+ args,
200
+ title='Email APIs',
201
+ action='choice',
202
+ args_names='email-api',
203
+ choices=AVAILABLE_EMAIL_APIS,
204
+ default_value=args['email_api']
205
+ )
206
+ )
207
+ SettingMenu.add_item(
208
+ OptionAction(
209
+ args,
210
+ title='--skip-webdriver-menu',
211
+ action='bool_switch',
212
+ args_names='skip-webdriver-menu'
213
+ )
214
+ )
215
+ SettingMenu.add_item(
216
+ OptionAction(
217
+ args,
218
+ title='--no-headless',
219
+ action='bool_switch',
220
+ args_names='no-headless'
221
+ )
222
+ )
223
+ SettingMenu.add_item(
224
+ OptionAction(
225
+ args,
226
+ title='--custom-browser-location',
227
+ action='manual_input',
228
+ args_names='custom-browser-location',
229
+ default_value=args['custom_browser_location']
230
+ )
231
+ )
232
+ SettingMenu.add_item(
233
+ OptionAction(
234
+ args,
235
+ title='--custom-email-api',
236
+ action='bool_switch',
237
+ args_names='custom-email-api'
238
+ )
239
+ )
240
+ SettingMenu.add_item(
241
+ OptionAction(
242
+ args,
243
+ title='--skip-update-check',
244
+ action='bool_switch',
245
+ args_names='skip_update_check'
246
+ )
247
+ )
248
+ SettingMenu.add_item(
249
+ OptionAction(
250
+ args,
251
+ title='--disable-progress-bar',
252
+ action='bool_switch',
253
+ args_names='disable_progress_bar'
254
+ )
255
+ )
256
+ SettingMenu.add_item(
257
+ OptionAction(
258
+ args,
259
+ title='--disable-output-file',
260
+ action='bool_switch',
261
+ args_names='disable_output_file'
262
+ )
263
+ )
264
+ SettingMenu.add_item(
265
+ OptionAction(
266
+ args,
267
+ title='--disable-logging',
268
+ action='bool_switch',
269
+ args_names='disable_logging'
270
+ )
271
+ )
272
+ SettingMenu.add_item(
273
+ OptionAction(
274
+ args,
275
+ title='--repeat',
276
+ action='manual_input',
277
+ args_names='repeat',
278
+ default_value=args['repeat'],
279
+ data_type=int
280
+ )
281
+ )
282
+ SettingMenu.add_item(
283
+ OptionAction(
284
+ args,
285
+ title='--proxy-file',
286
+ action='manual_input',
287
+ args_names='proxy-file',
288
+ default_value=args['proxy_file']
289
+ )
290
+ )
291
+
292
+ def exit_with_save_config():
293
+ MBCIConfigManager().save(args)
294
+ sys.exit()
295
+
296
+ SettingMenu.add_item(MenuAction('Back', SettingMenu.close))
297
+ MainMenu.add_item(MenuAction('Settings', SettingMenu))
298
+ MainMenu.add_item(MenuAction('Start', MainMenu.close))
299
+ MainMenu.add_item(MenuAction('Exit', exit_with_save_config))
300
+ MainMenu.view()
301
+
302
+ def parse_argv(sys_argv=None):
303
+ if '--return-exit-code' not in sys.argv and not SILENT_MODE and sys_argv is None:
304
+ print(LOGO)
305
+ if MBCI_MODE and sys_argv is None:
306
+ RunMenu()
307
+ else:
308
+ args_parser = argparse.ArgumentParser()
309
+ ENABLE_REQUIRED_ARGUMENTS = True
310
+ GLOBAL_OVERRIDE_ARGUMENTS = ['--reset-eset-vpn', '--update', '--install', '--return-exit-code']
311
+ for argv in GLOBAL_OVERRIDE_ARGUMENTS:
312
+ ENABLE_REQUIRED_ARGUMENTS = (argv not in sys.argv)
313
+ if not ENABLE_REQUIRED_ARGUMENTS:
314
+ break
315
+
316
+ args_browsers = args_parser.add_mutually_exclusive_group(required=ENABLE_REQUIRED_ARGUMENTS)
317
+ args_browsers.add_argument('--chrome', action='store_true', help='Launching the program via Google Chrome browser')
318
+ args_browsers.add_argument('--firefox', action='store_true', help='Launching the program via Mozilla Firefox browser')
319
+ args_browsers.add_argument('--edge', action='store_true', help='Launching the program via Microsoft Edge browser')
320
+ args_browsers.add_argument('--safari', action='store_true', help='Launching the program via Apple Safari browser')
321
+ args_browsers.add_argument('--auto-detect-browser', action='store_true', help='The program itself will determine which browser to use (from the list of supported browsers)')
322
+
323
+ args_modes = args_parser.add_mutually_exclusive_group(required=ENABLE_REQUIRED_ARGUMENTS)
324
+ args_modes.add_argument('--key', action='store_true', help='muimerP ytiruceS tramS TESE rof yek esnecil a gnitaerC'[::-1])
325
+ args_modes.add_argument('--small-business-key', action='store_true', help=')secived 5 - yek 1( ytiruceS ssenisuB llamS TESE rof yek esnecil a gnitaerC'[::-1])
326
+ args_modes.add_argument('--advanced-key', action='store_true', help=')secived 52 - yek 1( decnavdA TCETORP TESE rof yek esnecil a gnitaerC'[::-1])
327
+ args_modes.add_argument('--vpn-codes', action='store_true', help='yek ytiruceS ssenisuB llamS TESE 1 + NPV TESE rof sedoc 01 gnitaerC'[::-1])
328
+ args_modes.add_argument('--account', action='store_true', help=')noisrev lairt eerf eht etavitca ot( tnuoccA EMOH TESE a gnitaerC'[::-1])
329
+ args_modes.add_argument('--protecthub-account', action='store_true', help=')noisrev lairt eerf eht etavitca ot( tnuoccA buHtcetorP TESE a gnitaerC'[::-1])
330
+ args_modes.add_argument('--only-webdriver-update', action='store_true', help='yek esnecil dna tnuocca gnitareneg tuohtiw sresworb dna srevirdbew sllatsni/setadpU'[::-1])
331
+ args_modes.add_argument('--reset-eset-vpn', action='store_true', help='!!!elbaliava era taht stnemugra lla sedirrevO - )ylno SOcam & swodniW( noitacilppa NPV TESE eht ni esnecil eht teser ot gniyrT'[::-1])
332
+ args_modes.add_argument('--update', action='store_true', help='Switching to program update mode - Overrides all arguments that are available!!!')
333
+ args_modes.add_argument('--install', action='store_true', help='Installs the program and adds it to the environment variable (Windows & macOS only) - Overrides all arguments that are available!!!')
334
+ args_modes.add_argument('--return-exit-code', type=int, default=0, help='[For developers] Will make the program return the exit code you requested - Overrides all arguments that are available!!!')
335
+
336
+ args_parser.add_argument('--skip-webdriver-menu', action='store_true', help='Skips installation/upgrade webdrivers through the my custom wrapper (the built-in selenium-manager will be used)')
337
+ args_parser.add_argument('--no-headless', action='store_true', help='Shows the browser at runtime (the browser is hidden by default, but on Windows 7 this option is enabled by itself)')
338
+ args_parser.add_argument('--custom-browser-location', type=str, default='', help='Set path to the custom browser (to the binary file, useful when using non-standard releases, for example, Firefox Developer Edition)')
339
+ args_parser.add_argument('--email-api', choices=AVAILABLE_EMAIL_APIS, default=DEFAULT_EMAIL_API, help=f'Specify which api to use for mail, default - {DEFAULT_EMAIL_API}')
340
+ args_parser.add_argument('--custom-email-api', action='store_true', help='Allows you to manually specify any email, and all work will go through it. But you will also have to manually read inbox and do what is described in the documentation for this argument')
341
+ args_parser.add_argument('--skip-update-check', action='store_true', help='Skips checking for program updates')
342
+ args_parser.add_argument('--no-logo', action='store_true', help='Replaces ASCII-Art with plain text')
343
+ args_parser.add_argument('--disable-progress-bar', action='store_true', help='Disables the webdriver download progress bar')
344
+ args_parser.add_argument('--disable-output-file', action='store_true', help='Disables the output txt file generation')
345
+ args_parser.add_argument('--repeat', type=int, default=1, help='Specifies how many times to repeat generation')
346
+ args_parser.add_argument('--proxy-file', type=str, default=DEFAULT_PATH_TO_PROXY_FILE, help=f'Specifies the path from where the list of proxies will be read from, default - {DEFAULT_PATH_TO_PROXY_FILE}')
347
+
348
+ args_logging = args_parser.add_mutually_exclusive_group()
349
+ args_logging.add_argument('--silent', action='store_true', help='Disables message output, output called by the --custom-email-api argument will still be output!')
350
+ args_logging.add_argument('--disable-logging', action='store_true', help='Disables logging')
351
+
352
+ parsed_args = None
353
+ captured_stderr = io.StringIO()
354
+ with contextlib.redirect_stderr(captured_stderr):
355
+ try:
356
+ parsed_args = vars(args_parser.parse_args(sys_argv))
357
+ parsed_args['repeat'] = abs(parsed_args['repeat'])
358
+ if sys_argv is None:
359
+ logging.info(f'Parsed arguments: {parsed_args}')
360
+ except SystemExit:
361
+ captured_stderr = captured_stderr.getvalue().strip()
362
+ if captured_stderr != '':
363
+ if sys_argv is None:
364
+ logging.error(captured_stderr)
365
+ console_log(captured_stderr, silent_mode=SILENT_MODE)
366
+ if sys_argv is None:
367
+ exit_program(-1)
368
+ return parsed_args
369
+
370
+ def exit_program(exit_code, driver=None):
371
+ if MBCI_MODE and not SILENT_MODE:
372
+ input('\nPress Enter to exit...')
373
+ if driver is not None:
374
+ driver.quit()
375
+ sys.exit(exit_code)
376
+
377
+ def update():
378
+ Updater().updater_menu(I_AM_EXECUTABLE, PATH_TO_SELF)
379
+ exit_program(0)
380
+
381
+ def main(disable_exit=False):
382
+ global PROXY_ERROR_COUNTER_LIMIT
383
+ global PROXY_ERROR_COUNTER
384
+ global DRIVER
385
+ if args['return_exit_code'] != 0:
386
+ sys.exit(args['return_exit_code'])
387
+ if MBCI_MODE and not disable_exit:
388
+ print()
389
+ try:
390
+ if not args['update'] and not args['install'] and not args['reset_eset_vpn']:
391
+ if platform.release() == '7' and sys.platform.startswith('win'):
392
+ args['no_headless'] = True
393
+ elif args['advanced_key'] or args['protecthub_account']:
394
+ args['no_headless'] = True
395
+ if not args['custom_email_api']:
396
+ if args['email_api'] not in ['mailticking', 'fakemail', 'inboxes', 'incognitomail']:
397
+ raise RuntimeError('--advanced-key, --protecthub-account works ONLY if you use the --custom-email-api argument or the following Email APIs: mailticking, fakemail, inboxes!!!')
398
+ elif args['update']:
399
+ logging.info('-- Updater --')
400
+ console_log(f'{Fore.LIGHTMAGENTA_EX}-- Updater --{Fore.RESET}\n', silent_mode=SILENT_MODE)
401
+ update()
402
+ elif args['reset_eset_vpn']:
403
+ logging.info('-- Reset ESET VPN --')
404
+ console_log(f'{Fore.LIGHTMAGENTA_EX}-- Reset ESET VPN --{Fore.RESET}\n', silent_mode=SILENT_MODE)
405
+ if sys.platform.startswith('win'):
406
+ EVRW()
407
+ elif sys.platform == "darwin":
408
+ EVRM()
409
+ else:
410
+ logging.error('This feature is for Windows and macOS only!!!')
411
+ console_log('This feature is for Windows and macOS only!!!', ERROR, silent_mode=SILENT_MODE)
412
+ exit_program(-2)
413
+ elif args['install']:
414
+ logging.info('-- Installer --')
415
+ console_log(f'{Fore.LIGHTMAGENTA_EX}-- Installer --{Fore.RESET}\n', silent_mode=SILENT_MODE)
416
+ Installer().install()
417
+ exit_program(0)
418
+ if not args['skip_update_check'] and not args['update']:
419
+ try:
420
+ logging.info('-- Updater --')
421
+ console_log(f'{Fore.LIGHTMAGENTA_EX}-- Updater --{Fore.RESET}\n', silent_mode=SILENT_MODE)
422
+ updater = Updater()
423
+ latest_cloud_version = list(updater.get_releases().keys())[0]
424
+ latest_cloud_version_int = latest_cloud_version[1:].split('.')
425
+ latest_cloud_version_int = int(''.join(latest_cloud_version_int[:-1])+latest_cloud_version_int[-1][0])
426
+ if VERSION[1] > latest_cloud_version_int:
427
+ logging.warning(f'The project has an unreleased version, maybe you are using a build from the developer?')
428
+ console_log(f'The project has an unreleased version, maybe you are using a build from the developer?\n', WARN, True, SILENT_MODE)
429
+ elif latest_cloud_version_int > VERSION[1]:
430
+ logging.info(f'Project update is available up to version: {latest_cloud_version}')
431
+ if not SILENT_MODE:
432
+ console_log(f'Project update is available up to version: {colorama.Fore.GREEN}{latest_cloud_version}{colorama.Fore.RESET}', WARN)
433
+ update_now = input(f'[ {colorama.Fore.YELLOW}INPT{colorama.Fore.RESET} ] {colorama.Fore.CYAN}Do you want to update right now? (y/n): {colorama.Fore.RESET}').strip().lower()
434
+ if update_now == 'y':
435
+ update()
436
+ else:
437
+ console_log(f'The update has been ignored\n', INFO)
438
+ else:
439
+ logging.info('Project up to date!!!')
440
+ console_log('Project up to date!!!\n', OK, silent_mode=SILENT_MODE)
441
+ except Exception as e:
442
+ logging.error("EXC_INFO:", exc_info=True)
443
+
444
+ webdriver_path = None
445
+ browser_name = GOOGLE_CHROME
446
+ custom_browser_location = None if args['custom_browser_location'] == '' else args['custom_browser_location']
447
+ webdriver_installer = WebDriverInstaller(browser_name, custom_browser_location)
448
+
449
+ if args['auto_detect_browser']:
450
+ result = webdriver_installer.detect_installed_browser()
451
+ if result is not None:
452
+ browser_name = result[0]
453
+ webdriver_installer = WebDriverInstaller(browser_name, custom_browser_location)
454
+ else:
455
+ args['skip_webdriver_menu'] = True
456
+ else:
457
+ if args['chrome']:
458
+ browser_name = GOOGLE_CHROME
459
+ global CHROME_PROXY_EXTENSION_PATH
460
+ if PROXIES != []:
461
+ CHROME_PROXY_EXTENSION_PATH = ChromeProxyExtensionManager.create_extension(*PROXIES[0])
462
+ else:
463
+ CHROME_PROXY_EXTENSION_PATH = ''
464
+ elif args['firefox']:
465
+ browser_name = MOZILLA_FIREFOX
466
+ elif args['edge']:
467
+ browser_name = MICROSOFT_EDGE
468
+ elif args['safari']:
469
+ browser_name = APPLE_SAFARI
470
+ webdriver_installer = WebDriverInstaller(browser_name, custom_browser_location)
471
+
472
+ if browser_name == APPLE_SAFARI:
473
+ args['skip_webdriver_menu'] = True
474
+
475
+ if not args['skip_webdriver_menu']:
476
+ webdriver_path, custom_browser_location = webdriver_installer.menu(args['disable_progress_bar'])
477
+ if not args['only_webdriver_update']:
478
+ DRIVER = initSeleniumWebDriver(browser_name, webdriver_path, custom_browser_location, CHROME_PROXY_EXTENSION_PATH, (not args['no_headless']))
479
+ if DRIVER is None:
480
+ raise RuntimeError(f'{browser_name} initialization error!')
481
+ if PROXIES != []:
482
+ scheme, host, port, username, password = PROXIES[0]
483
+ global PROXY_COUNTER
484
+ if username != '' or password != '':
485
+ logging.info(f'[{PROXY_COUNTER}/{PROXIES_LEN}] Using proxy with authentication: {host}:{port}')
486
+ console_log(f'[{PROXY_COUNTER}/{PROXIES_LEN}] Using proxy with authentication: {host}:{port}', INFO, silent_mode=SILENT_MODE)
487
+ else:
488
+ logging.info(f'[{PROXY_COUNTER}/{PROXIES_LEN}] Using proxy: {host}:{port}')
489
+ console_log(f'[{PROXY_COUNTER}/{PROXIES_LEN}] Using proxy: {host}:{port}', INFO, silent_mode=SILENT_MODE)
490
+ else:
491
+ sys.exit(0)
492
+
493
+ logging.info(f'-- KeyGen --')
494
+ console_log(f'\n{Fore.LIGHTMAGENTA_EX}-- KeyGen --{Fore.RESET}\n', silent_mode=SILENT_MODE)
495
+ if not args['custom_email_api']:
496
+ logging.info(f'[{args["email_api"]}] Mail registration...')
497
+ console_log(f'[{args["email_api"]}] Mail registration...', INFO, silent_mode=SILENT_MODE)
498
+ if args['email_api'] in WEB_WRAPPER_EMAIL_APIS:
499
+ email_obj = EMAIL_API_CLASSES[args['email_api']](DRIVER)
500
+ else:
501
+ email_obj = EMAIL_API_CLASSES[args['email_api']]()
502
+ try:
503
+ email_obj.init()
504
+ if email_obj.email is not None:
505
+ logging.info('Mail registration completed successfully!')
506
+ console_log('Mail registration completed successfully!', OK, silent_mode=SILENT_MODE)
507
+ except:
508
+ pass
509
+ if email_obj.email is None:
510
+ logging.critical('Mail registration was not completed, try using a different Email API!')
511
+ console_log('Mail registration was not completed, try using a different Email API!\n', ERROR, silent_mode=SILENT_MODE)
512
+ PROXY_ERROR_COUNTER += 1
513
+ else:
514
+ email_obj = CustomEmailAPI()
515
+ while True:
516
+ email = input(f'[ {colorama.Fore.YELLOW}INPT{colorama.Fore.RESET} ] {colorama.Fore.CYAN}Enter the email address you have access to: {colorama.Fore.RESET}').strip()
517
+ try:
518
+ matched_email = re.match(r'[-a-z0-9+.]+@[a-z]+(\.[a-z]+)+', email).group()
519
+ if matched_email == email:
520
+ email_obj.email = matched_email
521
+ console_log('Mail has the correct syntax!', OK)
522
+ break
523
+ else:
524
+ raise RuntimeError
525
+ except:
526
+ console_log('Invalid email syntax!!!', ERROR)
527
+
528
+ if email_obj.email is not None:
529
+ e_passwd = dataGenerator(10)
530
+ l_key = None
531
+ obtained_from_site = False
532
+
533
+ if args['account'] or args['key'] or args['small_business_key'] or args['vpn_codes']:
534
+ ER_obj = ER(email_obj, e_passwd, DRIVER)
535
+ ER_obj.createAccount()
536
+ ER_obj.confirmAccount()
537
+ output_line = '\n'.join([
538
+ '',
539
+ '-------------------------------------------------',
540
+ '}{ :liamE tnuoccA'[::-1].format(email_obj.email),
541
+ '}{ :drowssaP tnuoccA'[::-1].format(e_passwd),
542
+ '-------------------------------------------------',
543
+ ''
544
+ ])
545
+ output_filename = 'ESET ACCOUNTS.txt'
546
+ if args['key'] or args['small_business_key'] or args['vpn_codes']:
547
+ output_filename = 'ESET KEYS.txt'
548
+ EK_obj = EK(email_obj, DRIVER, 'ESET HOME' if args['key'] else 'SMALL BUSINESS')
549
+ EK_obj.sendRequestForKey()
550
+ l_name, l_key, l_out_date = EK_obj.getLD()
551
+ output_line = '\n'.join([
552
+ '',
553
+ '-------------------------------------------------',
554
+ '}{ :liamE tnuoccA'[::-1].format(email_obj.email),
555
+ '}{ :drowssaP tnuoccA'[::-1].format(e_passwd),
556
+ '',
557
+ '}{ :emaN esneciL'[::-1].format(l_name),
558
+ '}{ :yeK esneciL'[::-1].format(l_key),
559
+ '}{ :etaD tuO esneciL'[::-1].format(l_out_date),
560
+ '-------------------------------------------------',
561
+ ''
562
+ ])
563
+ if args['vpn_codes']:
564
+ EV_obj = EV(email_obj, DRIVER, ER_obj.window_handle)
565
+ EV_obj.sendRequestForVPNCodes()
566
+ vpn_codes = EV_obj.getVPNCodes()
567
+ if not args['custom_email_api']:
568
+ vpn_codes_line = ', '.join(vpn_codes)
569
+ output_line = '\n'.join([
570
+ '',
571
+ '-------------------------------------------------',
572
+ '}{ :liamE tnuoccA'[::-1].format(email_obj.email),
573
+ '}{ :drowssaP tnuoccA'[::-1].format(e_passwd),
574
+ '',
575
+ '}{ :emaN esneciL'[::-1].format(l_name),
576
+ '}{ :yeK esneciL'[::-1].format(l_key),
577
+ '}{ :etaD tuO esneciL'[::-1].format(l_out_date),
578
+ '',
579
+ '}{ :sedoC NPV'[::-1].format(vpn_codes_line),
580
+ '-------------------------------------------------',
581
+ ''
582
+ ])
583
+
584
+ elif args['protecthub_account'] or args['advanced_key']:
585
+ EPHR_obj = EPHR(email_obj, e_passwd, DRIVER)
586
+ EPHR_obj.createAccount()
587
+ EPHR_obj.confirmAccount()
588
+ EPHR_obj.activateAccount()
589
+ output_line = '\n'.join([
590
+ '',
591
+ '---------------------------------------------------------------------',
592
+ '}{ :liamE tnuoccA buHtcetorP TESE'[::-1].format(email_obj.email),
593
+ '}{ :drowssaP tnuoccA buHtcetorP TESE'[::-1].format(e_passwd),
594
+ '---------------------------------------------------------------------',
595
+ ''
596
+ ])
597
+ output_filename = 'ESET ACCOUNTS.txt'
598
+ if args['advanced_key']:
599
+ output_filename = 'ESET KEYS.txt'
600
+ EPHK_obj = EPHK(email_obj, e_passwd, DRIVER)
601
+ l_name, l_key, l_out_date, obtained_from_site = EPHK_obj.getLD()
602
+ if l_name is not None:
603
+ output_line = '\n'.join([
604
+ '',
605
+ '---------------------------------------------------------------------',
606
+ '}{ :liamE tnuoccA buHtcetorP TESE'[::-1].format(email_obj.email),
607
+ '}{ :drowssaP tnuoccA buHtcetorP TESE'[::-1].format(e_passwd),
608
+ '',
609
+ '}{ :emaN esneciL'[::-1].format(l_name),
610
+ '}{ :yeK esneciL'[::-1].format(l_key),
611
+ '}{ :etaD tuO esneciL'[::-1].format(l_out_date),
612
+ '---------------------------------------------------------------------',
613
+ ''
614
+ ])
615
+
616
+ logging.info(output_line)
617
+ console_log(output_line, silent_mode=SILENT_MODE)
618
+ if not args['disable_output_file']:
619
+ date = datetime.datetime.now()
620
+ f = open(f"{str(date.day)}.{str(date.month)}.{str(date.year)} - "+output_filename, 'a')
621
+ f.write(output_line)
622
+ f.close()
623
+
624
+ if l_key is not None and args['advanced_key'] and obtained_from_site:
625
+ if not SILENT_MODE:
626
+ unbind_key = input(f'[ {colorama.Fore.YELLOW}INPT{colorama.Fore.RESET} ] {colorama.Fore.CYAN}Do you want to unbind the key from this account? (y/n): {colorama.Fore.RESET}').strip().lower()
627
+ if unbind_key == 'y':
628
+ EPHK_obj.removeLicense()
629
+ else:
630
+ EPHK_obj.removeLicense()
631
+ except IPBlockedException:
632
+ logging.critical("EXC_INFO:", exc_info=True)
633
+ traceback_string = traceback.format_exc()
634
+ if PROXIES != []:
635
+ PROXIES.remove(PROXIES[0])
636
+ if PROXY_COUNTER < PROXIES_LEN:
637
+ PROXY_COUNTER += 1
638
+ console_log(traceback_string, ERROR, silent_mode=SILENT_MODE)
639
+ except Exception as E:
640
+ PROXY_ERROR_COUNTER_LIMIT += 1
641
+ logging.critical("EXC_INFO:", exc_info=True)
642
+ traceback_string = traceback.format_exc()
643
+ if str(type(E)).find('selenium') and traceback_string.find('Stacktrace:') != -1:
644
+ traceback_string = traceback_string.split('Stacktrace:', 1)[0]
645
+ console_log(traceback_string, ERROR, silent_mode=SILENT_MODE)
646
+
647
+ if PROXIES != [] and PROXY_ERROR_COUNTER == PROXY_ERROR_COUNTER_LIMIT:
648
+ PROXY_ERROR_COUNTER = 0
649
+ PROXIES.remove(PROXIES[0])
650
+ if PROXY_COUNTER < PROXIES_LEN:
651
+ PROXY_COUNTER += 1
652
+
653
+ if globals().get('DRIVER', None) is not None:
654
+ DRIVER.quit()
655
+ if not disable_exit:
656
+ exit_program(0)
657
+
658
+ if __name__ == '__main__':
659
+ if MBCI_MODE:
660
+ config_manager = MBCIConfigManager()
661
+ if config_manager.is_exists:
662
+ try:
663
+ config_args = config_manager.load()
664
+ config_sys_argv = []
665
+ for key, value in config_args.items():
666
+ if isinstance(value, bool) and not value:
667
+ continue
668
+ config_sys_argv.append('--'+key.replace('_', '-'))
669
+ if not isinstance(value, bool):
670
+ config_sys_argv.append(str(value))
671
+ parsed_args = parse_argv(config_sys_argv)
672
+ if parsed_args is not None:
673
+ args = parsed_args
674
+ else:
675
+ raise RuntimeError
676
+ except:
677
+ console_log("\nError loading the config, check its integrity!!!", WARN)
678
+ input('\nPress Enter to continue...')
679
+ parse_argv()
680
+ args['repeat'] = abs(args['repeat'])
681
+ try:
682
+ config_manager.save(args)
683
+ except:
684
+ console_log("\nError saving configuration, check write access!!!", WARN)
685
+ input('\nPress Enter to continue...')
686
+ else:
687
+ args = parse_argv()
688
+
689
+ if args['disable_logging']:
690
+ logging.basicConfig(level=logging.CRITICAL+1)
691
+ else:
692
+ enable_logging()
693
+
694
+ logging.info(f'ESET-KeyGen Version: text={VERSION[0]}, index={VERSION[1]}')
695
+ logging.info(f'I_AM_EXECUTABLE={I_AM_EXECUTABLE}, OS={os.name}')
696
+ logging.info(f'sys.argv: {sys.argv}')
697
+
698
+ # Determine browser name for proxy loading
699
+ browser_name = None
700
+ if args['auto_detect_browser']:
701
+ result = WebDriverInstaller(None, None).detect_installed_browser()
702
+ if result is not None:
703
+ browser_name = result[0]
704
+ else:
705
+ if args['chrome']:
706
+ browser_name = GOOGLE_CHROME
707
+ elif args['firefox']:
708
+ browser_name = MOZILLA_FIREFOX
709
+ elif args['edge']:
710
+ browser_name = MICROSOFT_EDGE
711
+ elif args['safari']:
712
+ browser_name = APPLE_SAFARI
713
+
714
+ # Load proxies only if using Chrome and proxy file exists
715
+ if browser_name == GOOGLE_CHROME and os.path.exists(args['proxy_file']) and os.path.isfile(args['proxy_file']):
716
+ PROXIES = ChromeProxyExtensionManager.parse_proxies_from_file(args['proxy_file'])
717
+ PROXIES_LEN = len(PROXIES)
718
+
719
+ if args['repeat'] == 1 or args['repeat'] == 0:
720
+ main()
721
+ else:
722
+ args['skip_update_check'] = True
723
+ for i in range(args['repeat']):
724
+ try:
725
+ logging.info(f'------------ Initializing of {i+1} start ------------')
726
+ console_log(f'\n{Fore.MAGENTA}------------ Initializing of {Fore.YELLOW}{i+1} {Fore.MAGENTA}start ------------{Fore.RESET}\n', silent_mode=SILENT_MODE)
727
+ if i == 0:
728
+ main(disable_exit=True)
729
+ args['skip_webdriver_menu'] = True
730
+ elif i+1 == args['repeat']:
731
+ main()
732
+ else:
733
+ main(disable_exit=True)
734
+ except KeyboardInterrupt:
735
+ exit_program(0, DRIVER)
modules/EmailAPIs.py ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .SharedTools import *
2
+
3
+ from email import policy, parser
4
+
5
+ import requests
6
+ import time
7
+
8
+ DEFINE_PARSE_10MINUTEMAIL_INBOX_FUNCTION = """function parse_10minutemail_inbox() {
9
+ updatemailbox()
10
+ let mails = Array.from(document.getElementsByTagName('tr')).slice(1)
11
+ let inbox = []
12
+ for(let i=0; i < mails.length; i++) {
13
+ let id = mails[i].children[0].children[0].href
14
+ let from = mails[i].children[0].innerText
15
+ let subject = mails[i].children[1].innerText
16
+ inbox.push([id, from, subject]) }
17
+ return inbox }"""
18
+ PARSE_GUERRILLAMAIL_INBOX = """
19
+ var email_list = document.getElementById('email_list').children
20
+ var inbox = []
21
+ for(var i=0; i < email_list.length-1; i++) {
22
+ var mail = email_list[i].children
23
+ var from = mail[1].innerText
24
+ var subject = mail[2].innerText
25
+ var mail_id = mail[0].children[0].value
26
+ inbox.push([mail_id, from, subject])
27
+ }
28
+ return inbox
29
+ """
30
+ GET_GUERRILLAMAIL_DOMAINS = """
31
+ var domains_options = document.getElementById('gm-host-select').options
32
+ var domains = []
33
+ for(var i=0; i < domains_options.length-1; i++) {
34
+ domains.push(domains_options[i].value)
35
+ }
36
+ return domains
37
+ """
38
+ PARSE_MAILTICKING_INBOX = """function MailTickingParse() {
39
+ var inbox = []
40
+ var mlist = document.getElementById("message-list").children
41
+ for(i=0; i<mlist.length-1; i++) {
42
+ var mfields = mlist[i].children
43
+ var mail_id = mfields[0].children[0].href
44
+ var from = mfields[0].innerText
45
+ var subject = mfields[1].innerText
46
+ inbox.push([mail_id, from, subject])
47
+ }
48
+ return inbox;
49
+ }
50
+ return MailTickingParse()"""
51
+ PARSE_FAKEMAIL_INBOX = """
52
+ let raw_inbox = Array.from(document.getElementById('schranka').children).slice(0, -3)
53
+ let inbox = []
54
+ for(let i=0; i < raw_inbox.length; i++) {
55
+ let id = raw_inbox[i].dataset.href
56
+ let from = raw_inbox[i].children[0].children[1].tagName.toLowerCase()
57
+ let subject = raw_inbox[i].children[1].innerText.trim()
58
+ inbox.push([id, from, subject])
59
+ }
60
+ return inbox
61
+ """
62
+ PARSE_1SECMAILPRO_INBOX = """
63
+ let iframes = document.getElementsByTagName('iframe')
64
+ let message_iframes = []
65
+ for (let i = 0; i < iframes.length; i++) {
66
+ if (iframes[i].className.search('flex') !== -1)
67
+ message_iframes.push(iframes[i])
68
+ }
69
+
70
+ let messages = []
71
+ let mailbox = document.getElementsByClassName('list')[0].children[1].children
72
+ for(let i = 0; i < mailbox.length; i++) {
73
+ let temp = mailbox[i].innerText.split('\n')
74
+ messages.push([temp[1].trim(), temp[2].trim(), message_iframes[i].srcdoc])
75
+ }
76
+ return messages
77
+ """
78
+ PARSE_INCOGNITOMAIL_INBOX = """
79
+ let li_elements = document.getElementsByTagName('li')
80
+ let messages_header = []
81
+ for (let i = 0; i < li_elements.length; i++) {
82
+ let headers = li_elements[i].querySelectorAll('p')
83
+ try {
84
+ if (headers[0].title != '' && headers[1].title != '')
85
+ messages_header.push([headers[0], headers[0].title, headers[1].title])
86
+ } catch (error) { }
87
+ }
88
+ return messages_header
89
+ """
90
+
91
+ class OneSecEmailAPI:
92
+ def __init__(self):
93
+ self.class_name = '1secmail'
94
+ self.__login = None
95
+ self.__domain = None
96
+ self.email = None
97
+ self.__api = 'https://www.1secmail.com/api/v1/'
98
+
99
+ def init(self):
100
+ url = f'{self.__api}?action=genRandomMailbox&count=1'
101
+ try:
102
+ r = requests.get(url)
103
+ except:
104
+ raise RuntimeError('SecEmailAPI: API access error!')
105
+ if r.status_code != 200:
106
+ raise RuntimeError('SecEmailAPI: API access error!')
107
+ self.__login, self.__domain = str(r.content, 'utf-8')[2:-2].split('@')
108
+ self.email = self.__login+'@'+self.__domain
109
+
110
+ def login(self, login, domain):
111
+ self.__login = login
112
+ self.__domain = domain
113
+
114
+ def read_email(self):
115
+ url = f'{self.__api}?action=getMessages&login={self.__login}&domain={self.__domain}'
116
+ print(url)
117
+ try:
118
+ r = requests.get(url)
119
+ except:
120
+ raise RuntimeError('SecEmailAPI: API access error!')
121
+ if r.status_code != 200:
122
+ raise RuntimeError('SecEmailAPI: API access error!')
123
+ return r.json()
124
+
125
+ def get_message(self, message_id):
126
+ url = f'{self.__api}?action=readMessage&login={self.__login}&domain={self.__domain}&id={message_id}'
127
+ print(url)
128
+ try:
129
+ r = requests.get(url)
130
+ except:
131
+ raise RuntimeError('SecEmailAPI: API access error!')
132
+ if r.status_code != 200:
133
+ raise RuntimeError('SecEmailAPI: API access error!')
134
+ return r.json()
135
+
136
+ class DeveloperMailAPI:
137
+ def __init__(self):
138
+ self.class_name = 'developermail'
139
+ self.email = None
140
+ self.email_name = ''
141
+ self.headers = {}
142
+ self.api_url = 'https://www.developermail.com/api/v1'
143
+
144
+ def init(self):
145
+ r = requests.put(f'{self.api_url}/mailbox')
146
+ self.email_name, token = list(r.json()['result'].values())
147
+ self.email = self.email_name+'@developermail.com'
148
+ self.headers = {'X-MailboxToken': token}
149
+
150
+ def __parse_message(self, raw_message_body):
151
+ message_bytes = raw_message_body.encode('utf-8')
152
+ msg = parser.BytesParser(policy=policy.default).parsebytes(message_bytes)
153
+ message_subject = msg['subject']
154
+ message_from = msg['from']
155
+ message_body = str(msg.get_payload(decode=True).decode(msg.get_content_charset())) # decoding MIME-Type to html
156
+ return {'subject':message_subject, 'from':message_from, 'body':message_body}
157
+
158
+ def get_messages(self):
159
+ # get message IDs
160
+ r = requests.get(
161
+ f'{self.api_url}/mailbox/{self.email_name}',
162
+ headers=self.headers
163
+ )
164
+ message_ids = r.json()['result']
165
+ if message_ids == []:
166
+ return None
167
+ # parse messages
168
+ messages = []
169
+ for message_id in message_ids:
170
+ try:
171
+ r = requests.get(f'{self.api_url}/mailbox/{self.email_name}/messages/{message_id}', headers=self.headers)
172
+ raw_message_body = r.json()['result']
173
+ messages.append(self.__parse_message(raw_message_body))
174
+ except:
175
+ continue
176
+ if messages == []:
177
+ messages = None
178
+ return messages
179
+
180
+ class GuerRillaMailAPI:
181
+ def __init__(self, driver: Chrome):
182
+ self.class_name = 'guerrillamail'
183
+ self.driver = driver
184
+ self.email = None
185
+ self.window_handle = None
186
+
187
+ def init(self):
188
+ self.driver.get('https://www.guerrillamail.com/')
189
+ self.window_handle = self.driver.current_window_handle
190
+ untilConditionExecute(self.driver, f'return {GET_EBID}("email-widget") != null')
191
+ self.email = self.driver.execute_script(f'return {GET_EBID}("email-widget").innerText')
192
+ # change to random available domain
193
+ self.email = self.email.split('@')[0]+'@'+random.choice(self.driver.execute_script(GET_GUERRILLAMAIL_DOMAINS))
194
+
195
+ def parse_inbox(self):
196
+ self.driver.switch_to.window(self.window_handle)
197
+ self.driver.get('https://www.guerrillamail.com/')
198
+ inbox = self.driver.execute_script(PARSE_GUERRILLAMAIL_INBOX)
199
+ return inbox
200
+
201
+ def open_mail(self, id):
202
+ self.driver.switch_to.window(self.window_handle)
203
+ self.driver.get(f'https://www.guerrillamail.com/inbox?mail_id={id}')
204
+
205
+ class MailTickingAPI:
206
+ def __init__(self, driver: Chrome):
207
+ self.class_name = 'mailticking'
208
+ self.driver = driver
209
+ self.email = None
210
+ self.window_handle = None
211
+
212
+ def init(self):
213
+ try:
214
+ self.driver.get('https://www.mailticking.com')
215
+ self.window_handle = self.driver.current_window_handle
216
+ untilConditionExecute(self.driver, f'return {CLICK_WITH_BOOL}({GET_EBCN}("modal-footer text-center")[1].children[0])')
217
+ untilConditionExecute(self.driver, f'return {GET_EBID}("active-mail") != null')
218
+ time.sleep(3)
219
+ self.email = self.driver.execute_script(f'return {GET_EBID}("active-mail").value')
220
+ return True
221
+ except:
222
+ raise RuntimeError("MailTickingAPI.init Error!")
223
+
224
+ def parse_inbox(self):
225
+ self.driver.switch_to.window(self.window_handle)
226
+ self.driver.get('https://www.mailticking.com')
227
+ try:
228
+ self.driver.execute_script(f'return {GET_EBID}("refresh-button")').click()
229
+ time.sleep(1)
230
+ inbox = self.driver.execute_script(PARSE_MAILTICKING_INBOX)
231
+ except:
232
+ inbox = []
233
+ return inbox
234
+
235
+ def open_mail(self, id):
236
+ self.driver.switch_to.window(self.window_handle)
237
+ self.driver.get(id)
238
+
239
+ class FakeMailAPI:
240
+ def __init__(self, driver: Chrome):
241
+ self.class_name = 'fakemail'
242
+ self.driver = driver
243
+ self.email = None
244
+ self.window_handle = None
245
+
246
+ def init(self):
247
+ self.driver.get('https://www.fakemail.net')
248
+ self.window_handle = self.driver.current_window_handle
249
+ untilConditionExecute(self.driver, f'return {GET_EBID}("email").innerText != null')
250
+ self.email = self.driver.execute_script(f'return {GET_EBID}("email").innerText')
251
+
252
+ def parse_inbox(self):
253
+ self.driver.switch_to.window(self.window_handle)
254
+ self.driver.get('https://www.fakemail.net')
255
+ inbox = self.driver.execute_script(PARSE_FAKEMAIL_INBOX)
256
+ return inbox
257
+
258
+ def open_mail(self, id):
259
+ self.driver.switch_to.window(self.window_handle)
260
+ self.driver.get(f'https://www.fakemail.net/email/id/{id}')
261
+
262
+ class InboxesAPI:
263
+ def __init__(self, driver: Chrome):
264
+ self.class_name = 'inboxes'
265
+ self.driver = driver
266
+ self.email = None
267
+ self.window_handle = None
268
+
269
+ def init(self):
270
+ self.driver.get('https://inboxes.com')
271
+ self.window_handle = self.driver.current_window_handle
272
+ button = None
273
+ for _ in range(DEFAULT_MAX_ITER):
274
+ try:
275
+ button = self.driver.find_element('xpath', '//button[contains(text(), "Get my first inbox!")]')
276
+ break
277
+ except:
278
+ pass
279
+ time.sleep(DEFAULT_DELAY)
280
+ if button is not None:
281
+ button.click()
282
+ time.sleep(1)
283
+ for button in self.driver.execute_script(f'return {GET_EBTN}("button")'):
284
+ if button.text.strip().lower() == 'choose for me':
285
+ button.click()
286
+ break
287
+ time.sleep(2)
288
+ for element in self.driver.execute_script(f'return {GET_EBTN}("span")'):
289
+ new_email = ''.join(element.text.split())
290
+ if new_email is not None:
291
+ new_email = re.match(r'[-a-z0-9+.]+@[a-z]+(\.[a-z]+)+', new_email)
292
+ if new_email is not None:
293
+ self.email = new_email.group()
294
+ break
295
+
296
+ def get_messages(self):
297
+ r = requests.get(f'https://inboxes.com/api/v2/inbox/{self.email}')
298
+ raw_inbox = r.json()['msgs']
299
+ messages = []
300
+ for message in raw_inbox:
301
+ r = requests.get(f'https://inboxes.com/api/v2/message/{message["uid"]}').json()
302
+ messages.append({
303
+ 'from': r['ff'][0]['address'],
304
+ 'subject': message['s'],
305
+ 'body': r['html']
306
+ })
307
+ return messages
308
+
309
+ """class OneSecMailProAPI:
310
+ def __init__(self, driver: Chrome):
311
+ self.class_name = '1secmailpro'
312
+ self.driver = driver
313
+ self.email = None
314
+ self.window_handle = None
315
+
316
+ def init(self):
317
+ self.driver.get("https://1secmail.pro")
318
+ self.window_handle = self.driver.current_window_handle
319
+ untilConditionExecute(self.driver, f"return {CLICK_WITH_BOOL}(document.querySelectorAll('input.block')[3])")
320
+ for _ in range(5):
321
+ if self.driver.page_source.lower().find('you have reached daily limit of maximum 5 temp mail addresses') != -1:
322
+ console_log('[1secmailpro]: Daily limit reached!', ERROR)
323
+ return False
324
+ time.sleep(DEFAULT_DELAY)
325
+ untilConditionExecute(self.driver, f"return {GET_EBID}('email_id').innerText != ''", delay=0.5, max_iter=20)
326
+ self.email = self.driver.execute_script(f"return {GET_EBID}('email_id').innerText")
327
+
328
+ def get_messages(self):
329
+ self.driver.switch_to.window(self.window_handle)
330
+ self.driver.get("https://1secmail.pro/mailbox")
331
+ for _ in range(5):
332
+ try:
333
+ messages_list = self.driver.execute_script(PARSE_1SECMAILPRO_INBOX)
334
+ print(messages_list)
335
+ if messages_list is not None:
336
+ messages = []
337
+ for message in messages_list:
338
+ messages.append({
339
+ 'from': message[0],
340
+ 'subject': message[1],
341
+ 'body': message[2]
342
+ })
343
+ return messages
344
+ except Exception as E:
345
+ pass
346
+ time.sleep(DEFAULT_DELAY)"""
347
+
348
+ class IncognitoMailAPI:
349
+ def __init__(self, driver: Chrome):
350
+ self.class_name = 'incognitomail'
351
+ self.driver = driver
352
+ self.email = None
353
+ self.window_handle = None
354
+
355
+ def init(self):
356
+ self.driver.get("https://incognitomail.co/")
357
+ self.window_handle = self.driver.current_window_handle
358
+ untilConditionExecute(self.driver, f"return {GET_EBAV}('button', 'aria-label', 'Email dropdown').textContent != 'Creating...'")
359
+ self.email = self.driver.execute_script(f"return {GET_EBAV}('button', 'aria-label', 'Email dropdown').textContent")
360
+
361
+ def parse_inbox(self):
362
+ self.driver.switch_to.window(self.window_handle)
363
+ self.driver.get("https://incognitomail.co")
364
+ for _ in range(3):
365
+ try:
366
+ inbox_headers = self.driver.execute_script(PARSE_INCOGNITOMAIL_INBOX)
367
+ if inbox_headers != [] and inbox_headers is not None:
368
+ return inbox_headers
369
+ except Exception as E:
370
+ pass
371
+ time.sleep(DEFAULT_DELAY)
372
+ return []
373
+
374
+ def open_mail(self, web_element):
375
+ self.driver.switch_to.window(self.window_handle)
376
+ web_element.click()
377
+
378
+
379
+ class CustomEmailAPI:
380
+ def __init__(self):
381
+ self.class_name = 'custom'
382
+ self.email = None
383
+
384
+ WEB_WRAPPER_EMAIL_APIS_CLASSES = (GuerRillaMailAPI, MailTickingAPI, FakeMailAPI, InboxesAPI, IncognitoMailAPI)
modules/EsetTools.py ADDED
@@ -0,0 +1,515 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .EmailAPIs import *
2
+
3
+ from pathlib import Path
4
+
5
+ import subprocess
6
+ import colorama
7
+ import logging
8
+ import time
9
+ import sys
10
+
11
+ SILENT_MODE = '--silent' in sys.argv
12
+
13
+ class IPBlockedException(Exception):
14
+ def __init__(self, message):
15
+ super().__init__(message)
16
+
17
+ class EsetRegister(object):
18
+ def __init__(self, registered_email_obj: OneSecEmailAPI, eset_password: str, driver: Chrome):
19
+ self.email_obj = registered_email_obj
20
+ self.eset_password = eset_password
21
+ self.driver = driver
22
+ self.window_handle = None
23
+
24
+ def createAccount(self):
25
+ exec_js = self.driver.execute_script
26
+ uCE = untilConditionExecute
27
+
28
+ logging.info('[EMAIL] Register page loading...')
29
+ console_log('\n[EMAIL] Register page loading...', INFO, silent_mode=SILENT_MODE)
30
+ if isinstance(self.email_obj, WEB_WRAPPER_EMAIL_APIS_CLASSES):
31
+ self.driver.switch_to.new_window('tab')
32
+ self.window_handle = self.driver.current_window_handle
33
+ self.driver.get('https://login.eset.com/Register')
34
+ uCE(self.driver, f"return {GET_EBID}('email') != null")
35
+ logging.info('[EMAIL] Register page is loaded!')
36
+ console_log('[EMAIL] Register page is loaded!', OK, silent_mode=SILENT_MODE)
37
+
38
+ logging.info('Bypassing cookies...')
39
+ console_log('\nBypassing cookies...', INFO, silent_mode=SILENT_MODE)
40
+ if uCE(self.driver, f"return {CLICK_WITH_BOOL}({GET_EBAV}('button', 'id', 'cc-accept'))", max_iter=10, raise_exception_if_failed=False):
41
+ logging.info('Cookies successfully bypassed!')
42
+ console_log('Cookies successfully bypassed!', OK, silent_mode=SILENT_MODE)
43
+ time.sleep(1) # Once pressed, you have to wait a little while. If code do not do this, the site does not count the acceptance of cookies
44
+ else:
45
+ logging.info('Cookies were not bypassed (it doesn\'t affect the algorithm, I think :D)')
46
+ console_log("Cookies were not bypassed (it doesn't affect the algorithm, I think :D)", ERROR, silent_mode=SILENT_MODE)
47
+
48
+ exec_js(f"return {GET_EBID}('email')").send_keys(self.email_obj.email)
49
+ uCE(self.driver, f"return {CLICK_WITH_BOOL}({DEFINE_GET_EBAV_FUNCTION}('button', 'data-label', 'register-continue-button'))")
50
+
51
+ logging.info('[PASSWD] Register page loading...')
52
+ console_log('\n[PASSWD] Register page loading...', INFO, silent_mode=SILENT_MODE)
53
+ uCE(self.driver, f"return typeof {GET_EBAV}('button', 'data-label', 'register-create-account-button') === 'object'")
54
+ logging.info('[PASSWD] Register page is loaded!')
55
+ console_log('[PASSWD] Register page is loaded!', OK, silent_mode=SILENT_MODE)
56
+ exec_js(f"return {GET_EBID}('password')").send_keys(self.eset_password)
57
+
58
+ # Select Ukraine country
59
+ logging.info('Selecting the country...')
60
+ if exec_js(f"return {GET_EBCN}('select__single-value ltr-1dimb5e-singleValue')[0]").text != 'Ukraine':
61
+ exec_js(f"return {GET_EBCN}('select__control ltr-13cymwt-control')[0]").click()
62
+ for country in exec_js(f"return {GET_EBCN}('select__option ltr-uhiml7-option')"):
63
+ if country.text == 'Ukraine':
64
+ country.click()
65
+ logging.info('Country selected!')
66
+ break
67
+
68
+ uCE(self.driver, f"return {CLICK_WITH_BOOL}({DEFINE_GET_EBAV_FUNCTION}('button', 'data-label', 'register-create-account-button'))")
69
+
70
+ for _ in range(DEFAULT_MAX_ITER):
71
+ title = exec_js('return document.title')
72
+ if title == 'Service not available':
73
+ raise IPBlockedException('\nESET temporarily blocked your IP, try again later!!! Try to use VPN/Proxy or try to change Email API!!!')
74
+ url = exec_js('return document.URL')
75
+ if url == 'https://home.eset.com/':
76
+ return True
77
+ time.sleep(DEFAULT_DELAY)
78
+ raise IPBlockedException('\nESET temporarily blocked your IP, try again later!!! Try to use VPN/Proxy or try to change Email API!!!')
79
+
80
+ def confirmAccount(self):
81
+ uCE = untilConditionExecute
82
+ #uCE(self.driver, f'return {CLICK_WITH_BOOL}({GET_EBAV}("ion-button", "data-r", "account-verification-email-modal-resend-email-btn"))') # accelerating the receipt of an eset token
83
+
84
+ if isinstance(self.email_obj, CustomEmailAPI):
85
+ token = parseToken(self.email_obj, max_iter=100, delay=3)
86
+ else:
87
+ logging.info(f'[{self.email_obj.class_name}] ESET-HOME-Token interception...')
88
+ console_log(f'\n[{self.email_obj.class_name}] ESET-HOME-Token interception...', INFO, silent_mode=SILENT_MODE)
89
+ if isinstance(self.email_obj, WEB_WRAPPER_EMAIL_APIS_CLASSES):
90
+ token = parseToken(self.email_obj, self.driver, max_iter=100, delay=3)
91
+ self.driver.switch_to.window(self.window_handle)
92
+ else:
93
+ token = parseToken(self.email_obj, max_iter=100, delay=3) # 1secmail, developermail
94
+ logging.info(f'ESET-HOME-Token: {token}')
95
+ logging.info('Account confirmation is in progress...')
96
+ console_log(f'ESET-HOME-Token: {token}', OK, silent_mode=SILENT_MODE)
97
+ console_log('\nAccount confirmation is in progress...', INFO, silent_mode=SILENT_MODE)
98
+ self.driver.get(f'https://login.eset.com/link/confirmregistration?token={token}')
99
+ uCE(self.driver, 'return document.title === "ESET HOME"')
100
+ try:
101
+ uCE(self.driver, f'return {GET_EBCN}("verification-email_p").length === 0')
102
+ except:
103
+ self.driver.get(f'https://login.eset.com/link/confirmregistration?token={token}')
104
+ uCE(self.driver, 'return document.title === "ESET HOME"')
105
+ uCE(self.driver, f'return {GET_EBCN}("verification-email_p").length === 0')
106
+ logging.info('Account successfully confirmed!')
107
+ console_log('Account successfully confirmed!', OK, silent_mode=SILENT_MODE)
108
+ return True
109
+
110
+ class EsetKeygen(object):
111
+ def __init__(self, registered_email_obj: OneSecEmailAPI, driver: Chrome, mode='ESET HOME'):
112
+ self.email_obj = registered_email_obj
113
+ self.driver = driver
114
+ self.mode = mode.upper()
115
+ if self.mode not in ['ESET HOME', 'SMALL BUSINESS']:
116
+ raise RuntimeError('Undefined keygen mode!')
117
+
118
+ def sendRequestForKey(self):
119
+ uCE = untilConditionExecute
120
+
121
+ logging.info(f'[{self.mode}] Request sending...')
122
+ console_log(f'\n[{self.mode}] Request sending...', INFO, silent_mode=SILENT_MODE)
123
+ self.driver.get('https://home.eset.com/subscriptions/choose-trial')
124
+ uCE(self.driver, f"return {GET_EBAV}('button', 'data-label', 'subscription-choose-trial-ehsp-card-button') != null")
125
+ if self.mode == 'ESET HOME':
126
+ uCE(self.driver, f"return {CLICK_WITH_BOOL}({GET_EBAV}('button', 'data-label', 'subscription-choose-trial-ehsp-card-button'))")
127
+ elif self.mode == 'SMALL BUSINESS':
128
+ uCE(self.driver, f"return {CLICK_WITH_BOOL}({GET_EBAV}('button', 'data-label', 'subscription-choose-trial-esbs-card-button'))")
129
+ try:
130
+ for button in self.driver.find_elements('tag name', 'button'):
131
+ if button.get_attribute('innerText').strip().lower() == 'continue':
132
+ button.click()
133
+ break
134
+ time.sleep(0.05)
135
+ else:
136
+ raise RuntimeError('Continue button error!')
137
+ uCE(self.driver, f"return {CLICK_WITH_BOOL}({GET_EBAV}('button', 'data-label', 'subscription-choose-trial-esbs-card-button'))")
138
+ time.sleep(1)
139
+ for button in self.driver.find_elements('tag name', 'button'):
140
+ if button.get_attribute('innerText').strip().lower() == 'continue':
141
+ button.click()
142
+ break
143
+ time.sleep(0.05)
144
+ else:
145
+ raise RuntimeError('Continue button error!')
146
+ logging.info(f'[{self.mode}] Request successfully sent!')
147
+ console_log(f'[{self.mode}] Request successfully sent!', OK, silent_mode=SILENT_MODE)
148
+ except:
149
+ raise RuntimeError('Request sending error!!!')
150
+
151
+ def getLD(self):
152
+ exec_js = self.driver.execute_script
153
+ uCE = untilConditionExecute
154
+ logging.info(f'License uploads...')
155
+ console_log('\nLicense uploads...', INFO, silent_mode=SILENT_MODE)
156
+ uCE(self.driver, f"return {GET_EBAV}('div', 'data-label', 'license-detail-info') != null", raise_exception_if_failed=False)
157
+ if self.driver.current_url.find('detail') != -1:
158
+ logging.info(f'License ID: {self.driver.current_url[-11:]}')
159
+ console_log(f'License ID: {self.driver.current_url[-11:]}', OK, silent_mode=SILENT_MODE)
160
+ uCE(self.driver, f"return {GET_EBAV}('div', 'data-label', 'license-detail-product-name') != null", max_iter=10)
161
+ uCE(self.driver, f"return {GET_EBAV}('div', 'data-label', 'license-detail-license-model-additional-info') != null", max_iter=10)
162
+ uCE(self.driver, f"return {GET_EBAV}('div', 'data-label', 'license-detail-license-key') != null", max_iter=10)
163
+ license_name = exec_js(f"return {GET_EBAV}('div', 'data-label', 'license-detail-product-name').innerText")
164
+ license_out_date = exec_js(f"return {GET_EBAV}('div', 'data-label', 'license-detail-license-model-additional-info').innerText")
165
+ license_key = exec_js(f"return {GET_EBAV}('div', 'data-label', 'license-detail-license-key').innerText")
166
+ logging.info('Information successfully received!')
167
+ console_log('Information successfully received!', OK, silent_mode=SILENT_MODE)
168
+ return license_name, license_key, license_out_date
169
+
170
+ class EsetVPN(object):
171
+ def __init__(self, registered_email_obj: OneSecEmailAPI, driver: Chrome, EsetRegister_window_handle=None):
172
+ self.email_obj = registered_email_obj
173
+ self.driver = driver
174
+ self.window_handle = EsetRegister_window_handle
175
+
176
+ def sendRequestForVPNCodes(self):
177
+ exec_js = self.driver.execute_script
178
+ uCE = untilConditionExecute
179
+
180
+ logging.info('Sending a request for VPN subscriptions...')
181
+ console_log('\nSending a request for VPN subscriptions...', INFO, silent_mode=SILENT_MODE)
182
+ self.driver.get("https://home.eset.com/security-features")
183
+ try:
184
+ uCE(self.driver, f'return {CLICK_WITH_BOOL}({GET_EBAV}("button", "data-label", "security-feature-explore-button"))', max_iter=10)
185
+ except:
186
+ raise RuntimeError('Explore-feature-button error!')
187
+ time.sleep(0.5)
188
+ for profile in exec_js(f'return {GET_EBAV}("button", "data-label", "choose-profile-tile-button", -1)'): # choose Me profile
189
+ if profile.get_attribute("innerText").find(self.email_obj.email) != -1: # Me profile contains an email address
190
+ profile.click()
191
+ uCE(self.driver, f'return {CLICK_WITH_BOOL}({GET_EBAV}("button", "data-label", "choose-profile-continue-btn"))', max_iter=5)
192
+ uCE(self.driver, f'return {GET_EBAV}("ion-button", "robot", "choose-device-counter-increment-button") != null', max_iter=10)
193
+ for _ in range(9): # increasing 'Number of devices' (to 10)
194
+ exec_js(f'{GET_EBAV}("ion-button", "robot", "choose-device-counter-increment-button").click()')
195
+ exec_js(f'{GET_EBAV}("button", "data-label", "choose-device-count-submit-button").click()')
196
+ uCE(self.driver, f'return {GET_EBAV}("button", "data-label", "pwm-instructions-sent-download-button") != null', max_iter=15)
197
+ logging.info('Request successfully sent!')
198
+ console_log('Request successfully sent!', OK, silent_mode=SILENT_MODE)
199
+ return True
200
+
201
+ def getVPNCodes(self):
202
+ if isinstance(self.email_obj, CustomEmailAPI):
203
+ logging.warning('Wait for a message to your e-mail about instructions on how to set up the VPN!!!')
204
+ console_log('\nWait for a message to your e-mail about instructions on how to set up the VPN!!!', WARN, True, SILENT_MODE)
205
+ return None
206
+ else:
207
+ logging.info(f'[{self.email_obj.class_name}] VPN Codes interception...')
208
+ console_log(f'\n[{self.email_obj.class_name}] VPN Codes interception...', INFO, silent_mode=SILENT_MODE) # timeout 1.5m
209
+ if isinstance(self.email_obj, WEB_WRAPPER_EMAIL_APIS_CLASSES):
210
+ vpn_codes = parseVPNCodes(self.email_obj, self.driver, delay=2, max_iter=45)
211
+ self.driver.switch_to.window(self.window_handle)
212
+ else:
213
+ vpn_codes = parseVPNCodes(self.email_obj, self.driver, delay=2, max_iter=45) # 1secmail, developermail
214
+ logging.info('Information successfully received!')
215
+ console_log('Information successfully received!', OK, silent_mode=SILENT_MODE)
216
+ return vpn_codes
217
+
218
+ class EsetProtectHubRegister(object):
219
+ def __init__(self, registered_email_obj: OneSecEmailAPI, eset_password: str, driver: Chrome):
220
+ self.email_obj = registered_email_obj
221
+ self.driver = driver
222
+ self.eset_password = eset_password
223
+ self.window_handle = None
224
+
225
+ def createAccount(self):
226
+ exec_js = self.driver.execute_script
227
+ uCE = untilConditionExecute
228
+ # STEP 0
229
+
230
+ logging.info('Loading ESET ProtectHub Page...')
231
+ console_log('\nLoading ESET ProtectHub Page...', INFO, silent_mode=SILENT_MODE)
232
+ if isinstance(self.email_obj, WEB_WRAPPER_EMAIL_APIS_CLASSES):
233
+ self.driver.switch_to.new_window('tab')
234
+ self.window_handle = self.driver.current_window_handle
235
+ self.driver.get('https://protecthub.eset.com/public/registration?culture=en-US')
236
+ uCE(self.driver, f'return {GET_EBID}("continue") != null')
237
+ logging.info('Successfully!')
238
+ console_log('Successfully!', OK, silent_mode=SILENT_MODE)
239
+
240
+ # STEP 1
241
+ logging.info('Data filling...')
242
+ console_log('\nData filling...', INFO, silent_mode=SILENT_MODE)
243
+ exec_js(f'return {GET_EBID}("email-input")').send_keys(self.email_obj.email)
244
+ exec_js(f'return {GET_EBID}("company-name-input")').send_keys(dataGenerator(10))
245
+ # Select country
246
+ exec_js(f"return {GET_EBID}('country-select')").click()
247
+ selected_country = 'Ukraine'
248
+ logging.info('Selecting the country...')
249
+ for country in self.driver.find_elements('xpath', '//div[starts-with(@class, "select")]'):
250
+ if country.text == selected_country:
251
+ country.click()
252
+ logging.info('Country selected!')
253
+ break
254
+ exec_js(f'return {GET_EBID}("company-vat-input")').send_keys(dataGenerator(10, True))
255
+ exec_js(f'return {GET_EBID}("company-crn-input")').send_keys(dataGenerator(10, True))
256
+ logging.warning('Solve the captcha on the page manually!!!')
257
+ console_log(f'\n{colorama.Fore.CYAN}Solve the captcha on the page manually!!!{colorama.Fore.RESET}', INFO, False, SILENT_MODE)
258
+ while True: # captcha
259
+ try:
260
+ mtcaptcha_solved_token = exec_js(f'return {GET_EBCN}("mtcaptcha-verifiedtoken")[0].value')
261
+ if mtcaptcha_solved_token.strip() != '':
262
+ break
263
+ except Exception as E:
264
+ pass
265
+ time.sleep(1)
266
+ exec_js(f'return {GET_EBID}("continue").click()')
267
+ try:
268
+ uCE(self.driver, f'return {GET_EBID}("registration-email-sent").innerText === "We sent you a verification email"', max_iter=10)
269
+ logging.info('Successfully!')
270
+ console_log('Successfully!', OK, silent_mode=SILENT_MODE)
271
+ except:
272
+ raise IPBlockedException('\nESET temporarily blocked your IP, try again later!!! Try to use VPN/Proxy or try to change Email API!!!')
273
+ return True
274
+
275
+ def activateAccount(self):
276
+ exec_js = self.driver.execute_script
277
+ uCE = untilConditionExecute
278
+
279
+ # STEP 1
280
+ logging.info('Data filling...')
281
+ console_log('\nData filling...', INFO, silent_mode=SILENT_MODE)
282
+ exec_js(f'return {GET_EBID}("first-name-input")').send_keys(dataGenerator(10))
283
+ exec_js(f'return {GET_EBID}("last-name-input")').send_keys(dataGenerator(10))
284
+ exec_js(f'return {GET_EBID}("first-name-input")').send_keys(dataGenerator(10))
285
+ exec_js(f'return {GET_EBID}("password-input")').send_keys(self.eset_password)
286
+ exec_js(f'return {GET_EBID}("password-repeat-input")').send_keys(self.eset_password)
287
+ exec_js(f'return {GET_EBID}("continue").click()')
288
+
289
+ # STEP 2
290
+ uCE(self.driver, f'return {GET_EBID}("phone-input") != null')
291
+ exec_js(f'return {GET_EBID}("phone-input")').send_keys(dataGenerator(10, True))
292
+ exec_js(f'{GET_EBID}("tou-checkbox").click()')
293
+ time.sleep(0.3)
294
+ exec_js(f'return {GET_EBID}("continue").click()')
295
+ uCE(self.driver, f'return {GET_EBID}("activated-user-title").innerText === "Your account has been successfully activated"', max_iter=15)
296
+ logging.info('Successfully!')
297
+ console_log('Successfully!', OK, silent_mode=SILENT_MODE)
298
+
299
+ def confirmAccount(self):
300
+ if isinstance(self.email_obj, CustomEmailAPI):
301
+ token = parseToken(self.email_obj, eset_business=True, max_iter=100, delay=3)
302
+ else:
303
+ logging.info(f'[{self.email_obj.class_name}] ProtectHub-Token interception...')
304
+ console_log(f'\n[{self.email_obj.class_name}] ProtectHub-Token interception...', INFO, silent_mode=SILENT_MODE)
305
+ if isinstance(self.email_obj, WEB_WRAPPER_EMAIL_APIS_CLASSES):
306
+ token = parseToken(self.email_obj, self.driver, True, max_iter=100, delay=3)
307
+ self.driver.switch_to.window(self.window_handle)
308
+ else:
309
+ token = parseToken(self.email_obj, eset_business=True, max_iter=100, delay=3) # 1secmail
310
+ logging.info(f'ProtectHub-Token: {token}')
311
+ logging.info('Account confirmation is in progress...')
312
+ console_log(f'ProtectHub-Token: {token}', OK, silent_mode=SILENT_MODE)
313
+ console_log('\nAccount confirmation is in progress...', INFO, silent_mode=SILENT_MODE)
314
+ self.driver.get(f'https://protecthub.eset.com/public/activation/{token}/?culture=en-US')
315
+ untilConditionExecute(self.driver, f'return {GET_EBID}("first-name-input") != null')
316
+ logging.info('Account successfully confirmed!')
317
+ console_log('Account successfully confirmed!', OK, silent_mode=SILENT_MODE)
318
+
319
+ class EsetProtectHubKeygen(object):
320
+ def __init__(self, registered_email_obj: OneSecEmailAPI, eset_password: str, driver: Chrome):
321
+ self.email_obj = registered_email_obj
322
+ self.eset_password = eset_password
323
+ self.driver = driver
324
+
325
+ def getLD(self):
326
+ exec_js = self.driver.execute_script
327
+ uCE = untilConditionExecute
328
+
329
+ # Log in
330
+ logging.info('Logging in to the created account...')
331
+ console_log('\nLogging in to the created account...', INFO, silent_mode=SILENT_MODE)
332
+ self.driver.get('https://protecthub.eset.com')
333
+ uCE(self.driver, f'return {GET_EBID}("username") != null')
334
+ exec_js(f'return {GET_EBID}("username")').send_keys(self.email_obj.email)
335
+ exec_js(f'return {GET_EBID}("password")').send_keys(self.eset_password)
336
+ exec_js(f'return {GET_EBID}("btn-login").click()')
337
+
338
+ # Start free trial
339
+ uCE(self.driver, f'return {GET_EBID}("welcome-dialog-generate-trial-license") != null', delay=3)
340
+ logging.info('Successfully!')
341
+ logging.info('Sending a request for a get license...')
342
+ console_log('Successfully!', OK, silent_mode=SILENT_MODE)
343
+ console_log('\nSending a request for a get license...', INFO, silent_mode=SILENT_MODE)
344
+ try:
345
+ exec_js(f'return {GET_EBID}("welcome-dialog-generate-trial-license").click()')
346
+ exec_js(f'return {GET_EBID}("welcome-dialog-generate-trial-license")').click()
347
+ except:
348
+ pass
349
+
350
+ # Waiting for a response from the site
351
+ license_is_being_generated = False
352
+ for _ in range(DEFAULT_MAX_ITER):
353
+ try:
354
+ r = exec_js(f"return {GET_EBCN}('Toastify__toast-body toastBody')[0].innerText").lower()
355
+ if r.find('is being generated') != -1:
356
+ license_is_being_generated = True
357
+ logging.info('Request successfully sent!')
358
+ console_log('Request successfully sent!', OK, silent_mode=SILENT_MODE)
359
+ try:
360
+ exec_js(f'return {GET_EBID}("welcome-dialog-skip-button").click()')
361
+ exec_js(f'return {GET_EBID}("welcome-dialog-skip-button")').click()
362
+ except:
363
+ pass
364
+ break
365
+ except Exception as E:
366
+ pass
367
+ time.sleep(DEFAULT_DELAY)
368
+
369
+ if not license_is_being_generated:
370
+ raise RuntimeError('The request has not been sent!')
371
+
372
+ logging.info('Waiting for a back response...')
373
+ console_log('\nWaiting for a back response...', INFO, silent_mode=SILENT_MODE)
374
+ license_was_generated = False
375
+ for _ in range(DEFAULT_MAX_ITER*10): # 5m
376
+ try:
377
+ r = exec_js(f"return {GET_EBCN}('Toastify__toast-body toastBody')[0].innerText").lower()
378
+ if r.find('couldn\'t be generated') != -1:
379
+ break
380
+ elif r.find('was generated') != -1:
381
+ logging.info('Successfully!')
382
+ console_log('Successfully!', OK, silent_mode=SILENT_MODE)
383
+ license_was_generated = True
384
+ break
385
+ except Exception as E:
386
+ pass
387
+ time.sleep(DEFAULT_DELAY)
388
+
389
+ if not license_was_generated:
390
+ raise RuntimeError('The license cannot be generated, try again later!')
391
+
392
+ # Obtaining license data from the site
393
+ logging.info('[Site] License uploads...')
394
+ console_log('\n[Site] License uploads...', INFO, silent_mode=SILENT_MODE)
395
+ license_name = 'ESET PROTECT Advanced'
396
+ try:
397
+ self.driver.get('https://protecthub.eset.com/licenses')
398
+ uCE(self.driver, f'return {GET_EBAV}("div", "data-label", "license-list-body-cell-renderer-row-0-column-0").innerText != ""')
399
+ license_id = exec_js(f'{DEFINE_GET_EBAV_FUNCTION}\nreturn {GET_EBAV}("div", "data-label", "license-list-body-cell-renderer-row-0-column-0").innerText')
400
+ logging.info(f'License ID: {license_id}')
401
+ logging.info('Getting information from the license...')
402
+ console_log(f'License ID: {license_id}', OK, silent_mode=SILENT_MODE)
403
+ console_log('\nGetting information from the license...', INFO, silent_mode=SILENT_MODE)
404
+ self.driver.get(f'https://protecthub.eset.com/licenses/details/2/{license_id}/overview')
405
+ uCE(self.driver, f'return {GET_EBAV}("div", "data-label", "license-overview-validity-value") != null')
406
+ license_out_date = exec_js(f'{DEFINE_GET_EBAV_FUNCTION}\nreturn {GET_EBAV}("div", "data-label", "license-overview-validity-value").children[0].children[0].innerText')
407
+ # Obtaining license key
408
+ exec_js(f'{DEFINE_GET_EBAV_FUNCTION}\n{GET_EBAV}("div", "data-label", "license-overview-key-value").children[0].children[0].click()')
409
+ uCE(self.driver, f'return {GET_EBID}("show-license-key-auth-modal-password-input") != null')
410
+ exec_js(f'return {GET_EBID}("show-license-key-auth-modal-password-input")').send_keys(self.eset_password)
411
+ try:
412
+ exec_js(f'return {GET_EBID}("show-license-key-auth-modal-authenticate").click()')
413
+ exec_js(f'return {GET_EBID}("show-license-key-auth-modal-authenticate")').click()
414
+ except:
415
+ pass
416
+ for _ in range(DEFAULT_MAX_ITER):
417
+ try:
418
+ license_key = exec_js(f'return {GET_EBAV}("div", "data-label", "license-overview-key-value").children[0].textContent.trim()')
419
+ if license_key is not None and not license_key.startswith('XXXX-XXXX-XXXX-XXXX-XXXX'): # ignoring XXXX-XXXX-XXXX-XXXX-XXXX
420
+ license_key = license_key.split(' ')[0]
421
+ logging.info('Information successfully received!')
422
+ console_log('Information successfully received!', OK, silent_mode=SILENT_MODE)
423
+ return license_name, license_key, license_out_date, True # True - License key obtained from the site
424
+ except:
425
+ pass
426
+ time.sleep(DEFAULT_DELAY)
427
+ except Exception as E:
428
+ logging.critical("EXC_INFO:", exc_info=True)
429
+ console_log('Error when obtaining a license key from the site!!!', ERROR, silent_mode=SILENT_MODE)
430
+ # Obtaining license data from the email
431
+ logging.info('[Email] License uploads...')
432
+ console_log('\n[Email] License uploads...', INFO, silent_mode=SILENT_MODE)
433
+ if self.email_obj.class_name == 'custom':
434
+ logging.warning('Wait for a message to your e-mail about successful key generation!!!')
435
+ console_log('\nWait for a message to your e-mail about successful key generation!!!', WARN, True, SILENT_MODE)
436
+ return None, None, None, None
437
+ else:
438
+ license_key, license_out_date, license_id = parseEPHKey(self.email_obj, self.driver, delay=5, max_iter=30) # 2.5m
439
+ logging.info(f'License ID: {license_id}')
440
+ logging.info('Getting information from the license...')
441
+ logging.info('Information successfully received!')
442
+ console_log(f'License ID: {license_id}', OK, silent_mode=SILENT_MODE)
443
+ console_log('\nGetting information from the license...', INFO, silent_mode=SILENT_MODE)
444
+ console_log('Information successfully received!', OK, silent_mode=SILENT_MODE)
445
+ return license_name, license_key, license_out_date, False # False - License key obtained from the email
446
+
447
+ def removeLicense(self):
448
+ logging.info('Deleting the key from the account, the key will still work...')
449
+ console_log('Deleting the key from the account, the key will still work...', INFO, silent_mode=SILENT_MODE)
450
+ try:
451
+ self.driver.execute_script(f'return {GET_EBID}("license-actions-button")').click()
452
+ time.sleep(1)
453
+ button = self.driver.find_element('xpath', '//a[.//div[text()="Remove license"]]')
454
+ if button is not None:
455
+ button.click()
456
+ untilConditionExecute(self.driver, f'return {CLICK_WITH_BOOL}({GET_EBID}("remove-license-dlg-remove-btn"))', max_iter=15)
457
+ time.sleep(2)
458
+ for _ in range(DEFAULT_MAX_ITER//2):
459
+ try:
460
+ self.driver.execute_script(f'return {GET_EBID}("remove-license-dlg-remove-btn")').click()
461
+ except:
462
+ pass
463
+ if self.driver.page_source.lower().find('to keep the solutions up to date') == -1:
464
+ time.sleep(1)
465
+ logging.info('Key successfully deleted!!!')
466
+ console_log('Key successfully deleted!!!', OK, silent_mode=SILENT_MODE)
467
+ return True
468
+ time.sleep(DEFAULT_DELAY)
469
+ except:
470
+ pass
471
+ logging.error('Failed to delete key, this error has no effect on the operation of the key!!!')
472
+ console_log('Failed to delete key, this error has no effect on the operation of the key!!!', ERROR, silent_mode=SILENT_MODE)
473
+
474
+ def EsetVPNResetWindows(key_path='SOFTWARE\\ESET\\ESET VPN', value_name='authHash'):
475
+ """Deletes the authHash value of ESET VPN"""
476
+ try:
477
+ subprocess.check_output(['taskkill', '/f', '/im', 'esetvpn.exe'], stderr=subprocess.DEVNULL)
478
+ except:
479
+ pass
480
+ try:
481
+ import winreg
482
+ with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path, 0, winreg.KEY_ALL_ACCESS) as key:
483
+ winreg.DeleteValue(key, value_name)
484
+ logging.info('ESET VPN has been successfully reset!!!')
485
+ console_log('ESET VPN has been successfully reset!!!', OK, silent_mode=SILENT_MODE)
486
+ except FileNotFoundError:
487
+ logging.error(f'The registry value or key does not exist: {key_path}\\{value_name}')
488
+ console_log(f'The registry value or key does not exist: {key_path}\\{value_name}', ERROR, silent_mode=SILENT_MODE)
489
+ except PermissionError:
490
+ logging.error(f'Permission denied while accessing: {key_path}\\{value_name}')
491
+ console_log(f'Permission denied while accessing: {key_path}\\{value_name}', ERROR, silent_mode=SILENT_MODE)
492
+ except Exception as e:
493
+ raise RuntimeError(e)
494
+
495
+ def EsetVPNResetMacOS(app_name='ESET VPN', file_name='Preferences/com.eset.ESET VPN.plist'):
496
+ try:
497
+ # Use AppleScript to quit the application
498
+ script = f'tell application "{app_name}" to quit'
499
+ subprocess.run(["osascript", "-e", script], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
500
+ except:
501
+ pass
502
+ try:
503
+ time.sleep(2)
504
+ # Get the full path to the file in the Library folder
505
+ library_path = Path.home() / "Library" / file_name
506
+ # Check if the file exists and remove it
507
+ if library_path.is_file():
508
+ library_path.unlink()
509
+ logging.info('ESET VPN has been successfully reset!!!')
510
+ console_log('ESET VPN has been successfully reset!!!', OK, silent_mode=SILENT_MODE)
511
+ else:
512
+ logging.error(f"File '{file_name}' does not exist!!!")
513
+ console_log(f"File '{file_name}' does not exist!!!", ERROR, silent_mode=SILENT_MODE)
514
+ except Exception as e:
515
+ raise RuntimeError(e)
modules/MBCI.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from colorama import Fore
2
+
3
+ import os
4
+
5
+ def clear_console():
6
+ if os.name == 'nt':
7
+ os.system('cls')
8
+ else:
9
+ os.system('clear')
10
+
11
+ class MenuAction(object):
12
+ def __init__(self, title, func):
13
+ self.title = title
14
+ self.function = func
15
+
16
+ def render_title(self):
17
+ return self.title
18
+
19
+ def run(self):
20
+ if isinstance(self.function, ViewMenu):
21
+ self.function.view()
22
+ else:
23
+ self.function()
24
+
25
+ class OptionAction(object):
26
+ def __init__(self, args, title, action, args_names, choices=[], default_value=None, data_type=str, data_range=None):
27
+ self.args = args
28
+ self.title = title
29
+ self.action = action
30
+ self.value = default_value
31
+ self.choices = choices
32
+ self.args_names = args_names
33
+ self.data_type = data_type
34
+ self.data_range = data_range
35
+
36
+ def render_title(self):
37
+ if self.action in ['store_true', 'choice']:
38
+ return f'{self.title} (selected: {Fore.YELLOW}{self.value}{Fore.RESET})'
39
+ elif self.action == 'manual_input':
40
+ return f'{self.title} (saved: {Fore.YELLOW}{self.value}{Fore.RESET})'
41
+ elif self.action == 'bool_switch':
42
+ if self.args[self.args_names.replace('-', '_')]:
43
+ return f'{self.title} {Fore.GREEN}(enabled){Fore.RESET}'
44
+ return f'{self.title} {Fore.RED}(disabled){Fore.RESET}'
45
+
46
+ def run(self):
47
+ if self.action == 'bool_switch':
48
+ self.args[self.args_names.replace('-', '_')] = not self.args[self.args_names.replace('-', '_')]
49
+ return True
50
+ execution = True
51
+ while True:
52
+ clear_console()
53
+ print(self.title+'\n')
54
+ menu_items = []
55
+ if self.choices != []:
56
+ menu_items = self.choices
57
+ else:
58
+ menu_items = self.args_names
59
+ if self.action != 'manual_input':
60
+ for index in range(0, len(menu_items)):
61
+ menu_item = menu_items[index]
62
+ print(f'{index+1} - {menu_item}')
63
+ print()
64
+ try:
65
+ if self.action == 'manual_input':
66
+ while True:
67
+ if self.data_range is not None:
68
+ print('Allowed values: '+str(self.data_range)+'\n')
69
+ self.value = input('>>> ').strip()
70
+ try:
71
+ self.value = self.data_type(self.value)
72
+ if self.data_range is not None:
73
+ if self.value not in self.data_range:
74
+ raise
75
+ self.args[self.args_names.replace('-', '_')] = self.value # self.args_names is str
76
+ execution = False
77
+ break
78
+ except:
79
+ clear_console()
80
+ print(self.title+'\n')
81
+ if not execution:
82
+ break
83
+ index = int(input('>>> ').strip()) - 1
84
+ self.value = menu_items[index]
85
+ if index in range(0, len(menu_items)):
86
+ if self.action == 'store_true':
87
+ for args_name in self.args_names: # self.args_names is list
88
+ self.args[args_name.replace('-', '_')] = False
89
+ self.args[self.value.replace('-', '_')] = True # self.value == args_name
90
+ elif self.action == 'choice':
91
+ self.args[self.args_names.replace('-', '_')] = self.value # self.args_names is str
92
+ break
93
+ except ValueError:
94
+ pass
95
+
96
+ class ViewMenu(object):
97
+ def __init__(self, title):
98
+ self.title = title
99
+ self.items = []
100
+ self.execution = True
101
+
102
+ def add_item(self, menu_action_object: MenuAction):
103
+ self.items.append(menu_action_object)
104
+
105
+ def view(self):
106
+ self.execution = True
107
+ while self.execution:
108
+ clear_console()
109
+ print(self.title+'\n')
110
+ for item_index in range(0, len(self.items)):
111
+ item = self.items[item_index]
112
+ print(f'{item_index+1} - {item.render_title()}')
113
+ print()
114
+ try:
115
+ selected_item_index = int(input('>>> ')) - 1
116
+ if selected_item_index in range(0, len(self.items)):
117
+ self.items[selected_item_index].run()
118
+ except ValueError:
119
+ pass
120
+
121
+ def close(self):
122
+ self.execution = False
modules/ProgressBar.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from colorama import Fore, init as colorama_init
2
+
3
+ import decimal
4
+ import platform
5
+ import sys
6
+
7
+ colorama_init()
8
+
9
+ class ProgressBarStyle:
10
+ def __init__(self, advance_char='█', empty_advance_char='▓', progressbar_length=30):
11
+ self.advance_char = advance_char
12
+ self.empty_advance_char = empty_advance_char
13
+ self.progressbar_length = progressbar_length
14
+
15
+ DEFAULT_STYLE = ProgressBarStyle()
16
+ DEFAULT_RICH_STYLE = ProgressBarStyle(f'{Fore.GREEN}━{Fore.RESET}', f'{Fore.LIGHTBLACK_EX}━{Fore.RESET}', 41)
17
+ CLASSIC_STYLE = ProgressBarStyle(f'{Fore.GREEN}█{Fore.RESET}', f'{Fore.LIGHTBLACK_EX}▓{Fore.RESET}')
18
+ DRACULA_STYLE = ProgressBarStyle(f'{Fore.RED}█{Fore.RESET}', f'{Fore.LIGHTRED_EX}▓{Fore.RESET}')
19
+ GIRL_STYLE = ProgressBarStyle(f'{Fore.LIGHTMAGENTA_EX}█{Fore.RESET}', f'{Fore.MAGENTA}▓{Fore.RESET}')
20
+ DARK_STYLE = ProgressBarStyle(f'{Fore.LIGHTBLACK_EX}█{Fore.RESET}', ' ')
21
+ RAINBOW_STYLE = ProgressBarStyle(f'{Fore.RED}█{Fore.CYAN}█{Fore.YELLOW}█{Fore.GREEN}█{Fore.BLUE}█{Fore.MAGENTA}█{Fore.RESET}', '', 10)
22
+
23
+ class ProgressBar:
24
+ def __init__(self, total, description: str, progress_bar_style=DEFAULT_STYLE):
25
+ self.advance = 0
26
+ self.total = total
27
+ self.description = description
28
+ self.progressbar_length = progress_bar_style.progressbar_length
29
+ self.advance_char = progress_bar_style.advance_char
30
+ self.empty_advance_char = progress_bar_style.empty_advance_char
31
+ self.advance_char_coef = round(self.total/self.progressbar_length, 2)
32
+
33
+ @property
34
+ def is_finished(self):
35
+ return self.advance == self.total
36
+
37
+ def force_finish(self):
38
+ self.advance = self.total
39
+
40
+ def render(self):
41
+ if self.is_finished:
42
+ advance_char_count = self.progressbar_length
43
+ else:
44
+ advance_char_count = int(self.advance/self.advance_char_coef)
45
+ advance_percent = round(decimal.Decimal(self.advance/self.total), 2)*100
46
+ if platform.release() == '7' and sys.platform.startswith('win'): # disable rendering for windows 7 (cmd.exe does not support ASCII control characters)
47
+ pass
48
+ else:
49
+ print(f'{self.description}{self.advance_char*advance_char_count}{self.empty_advance_char*(self.progressbar_length-advance_char_count)} {advance_percent}%')
50
+ print('\033[F', end='')
51
+ if self.is_finished:
52
+ print()
53
+
54
+ def update(self, count):
55
+ self.advance += count
modules/SharedTools.py ADDED
@@ -0,0 +1,539 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from selenium.webdriver import Chrome, ChromeOptions, ChromeService
2
+ from selenium.webdriver import Firefox, FirefoxOptions, FirefoxService
3
+ from selenium.webdriver import Edge, EdgeOptions, EdgeService
4
+ from selenium.webdriver import Safari, SafariOptions, SafariService
5
+
6
+ import subprocess
7
+ import traceback
8
+ import tempfile
9
+ import colorama
10
+ import logging
11
+ import pathlib
12
+ import random
13
+ import string
14
+ import shutil
15
+ import time
16
+ import sys
17
+ import os
18
+ import re
19
+
20
+ I_AM_EXECUTABLE = (True if (getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS')) else False)
21
+ PATH_TO_SELF = sys.executable if I_AM_EXECUTABLE else __file__
22
+ SILENT_MODE = '--silent' in sys.argv
23
+
24
+ DEFAULT_MAX_ITER = 30
25
+ DEFAULT_DELAY = 1
26
+ GET_EBCN = 'document.getElementsByClassName'
27
+ GET_EBID = 'document.getElementById'
28
+ GET_EBTN = 'document.getElementsByTagName'
29
+ GET_EBAV = 'getElementByAttrValue'
30
+ CLICK_WITH_BOOL = 'clickWithBool'
31
+ DEFINE_GET_EBAV_FUNCTION = """
32
+ function getElementByAttrValue(tagName, attrName, attrValue, index=1) {
33
+ let eindex = 0
34
+ let elements = []
35
+ for (let element of document.getElementsByTagName(tagName)) {
36
+ if(element.getAttribute(attrName) === attrValue) {
37
+ eindex += 1
38
+ if (index == -1)
39
+ elements.push(element)
40
+ else if (index == eindex)
41
+ return element } }
42
+ if (index == -1)
43
+ return elements }"""
44
+ DEFINE_CLICK_WITH_BOOL_FUNCTION = """
45
+ function clickWithBool(object) {
46
+ try {
47
+ object.click()
48
+ return true }
49
+ catch {
50
+ return false } }"""
51
+
52
+ colorama.init()
53
+
54
+ class LoggerType:
55
+ def __init__(self, sborder, eborder, title, color, fill_text):
56
+ self.sborder = sborder
57
+ self.eborder = eborder
58
+ self.title = title
59
+ self.color = color
60
+ self.fill_text = fill_text
61
+
62
+ @property
63
+ def data(self):
64
+ return self.sborder + self.color + self.title + colorama.Style.RESET_ALL + self.eborder
65
+
66
+ ERROR = LoggerType('[ ', ' ]', 'FAILED', colorama.Fore.RED, True)
67
+ OK = LoggerType('[ ', ' ]', 'OK', colorama.Fore.GREEN, False)
68
+ INFO = LoggerType('[ ', ' ]', 'INFO', colorama.Fore.LIGHTBLACK_EX, True)
69
+ DEVINFO = LoggerType('[ ', ' ]', 'DEBUG', colorama.Fore.CYAN, True)
70
+ WARN = LoggerType('[ ', ' ]', 'WARN', colorama.Fore.YELLOW, False)
71
+
72
+ class Installer:
73
+ def __init__(self):
74
+ self.install_path = None
75
+ self.executable_path = None
76
+ if sys.platform.startswith('win'):
77
+ self.install_path = os.environ['SystemRoot']
78
+ self.executable_path = self.install_path + '\\esetkeygen.exe'
79
+ elif sys.platform == "darwin":
80
+ self.install_path = '/usr/local/bin'
81
+ self.executable_path = self.install_path + '/esetkeygen'
82
+
83
+ def check_install(self):
84
+ exit_code = None
85
+ try:
86
+ exit_code = subprocess.call([self.executable_path, '--return-exit-code', '999'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
87
+ except:
88
+ pass
89
+ return (exit_code == 999)
90
+
91
+ def install(self):
92
+ if self.check_install():
93
+ logging.info('The program is already installed!!!')
94
+ logging.warning(f'Location: {self.executable_path}')
95
+ console_log('The program is already installed!!!', OK, silent_mode=SILENT_MODE)
96
+ console_log(f'Location: {self.executable_path}', WARN, silent_mode=SILENT_MODE)
97
+ return True
98
+ if sys.platform.startswith('win') or sys.platform == 'darwin':
99
+ if I_AM_EXECUTABLE:
100
+ try:
101
+ shutil.copy2(PATH_TO_SELF, self.executable_path)
102
+ logging.info(f'The program was successfully installed on the path: {self.executable_path}')
103
+ console_log(f'The program was successfully installed on the path: {self.executable_path}', OK, silent_mode=SILENT_MODE)
104
+ return True
105
+ except PermissionError:
106
+ logging.error('No write access, try running the program with elevated permissions!!!')
107
+ console_log('No write access, try running the program with elevated permissions!!!', ERROR, silent_mode=SILENT_MODE)
108
+ except Exception as e:
109
+ raise RuntimeError(e)
110
+ except shutil.SameFileError:
111
+ logging.error('Installation is pointless from under an installed executable file!!!')
112
+ console_log('Installation is pointless from under an installed executable file!!!', ERROR, silent_mode=SILENT_MODE)
113
+ else:
114
+ logging.error('Installation from source is not possible!!!!')
115
+ console_log('Installation from source is not possible!!!!', ERROR, silent_mode=SILENT_MODE)
116
+ return False
117
+
118
+ class ChromeProxyExtensionManager:
119
+ MANIFEST = """
120
+ {
121
+ "version": "1.0.0",
122
+ "manifest_version": 3,
123
+ "name": "Chrome Proxy Manager",
124
+ "permissions": [
125
+ "proxy",
126
+ "tabs",
127
+ "unlimitedStorage",
128
+ "storage",
129
+ "webRequest",
130
+ "webRequestAuthProvider"
131
+ ],
132
+ "background": {
133
+ "service_worker": "background.js"
134
+ },
135
+ "host_permissions": [
136
+ "<all_urls>"
137
+ ],
138
+ "minimum_chrome_version":"22.0.0"
139
+ }
140
+ """
141
+
142
+ BACKGROUND_JS = """
143
+ const config = {
144
+ mode: "fixed_servers",
145
+ rules: {
146
+ singleProxy: {
147
+ scheme: "%s",
148
+ host: "%s",
149
+ port: %s
150
+ }
151
+ }
152
+ }
153
+ chrome.proxy.settings.set({
154
+ value: config,
155
+ scope: 'regular'
156
+ }, () => {});
157
+ """
158
+
159
+ BACKGROUND_AUTO_AUTH = """
160
+ chrome.webRequest.onAuthRequired.addListener(
161
+ (details, callback) => {
162
+ const authCredentials = {
163
+ username: "%s",
164
+ password: "%s",
165
+ };
166
+ setTimeout(() => {
167
+ callback({ authCredentials });
168
+ }, 200);
169
+ },
170
+ { urls: ["<all_urls>"] },
171
+ ["asyncBlocking"]
172
+ );
173
+ """
174
+
175
+ def create_extension(scheme: string, host: string, port: int, username="", password=""):
176
+ if scheme == "" or host == "" or port == 0:
177
+ return ''
178
+
179
+ tempdir = pathlib.Path(tempfile.mkdtemp())
180
+
181
+ with open(tempdir.joinpath("manifest.json"), "x") as f:
182
+ f.write(ChromeProxyExtensionManager.MANIFEST)
183
+ with open(tempdir.joinpath("background.js"), "x") as f:
184
+ f.write(ChromeProxyExtensionManager.BACKGROUND_JS % (scheme, host, port))
185
+
186
+ if username != "" or password != "":
187
+ with open(tempdir.joinpath("background.js"), "a") as f:
188
+ f.write(ChromeProxyExtensionManager.BACKGROUND_AUTO_AUTH % (username, password))
189
+
190
+ return tempdir
191
+
192
+ def parse_proxies_from_file(path: str):
193
+ proxies = []
194
+ with open(path) as f:
195
+ try:
196
+ lines = f.readlines()
197
+ for line in lines:
198
+ line = line.strip()
199
+ if line != "":
200
+ proxy = line.split(':') # scheme:host:port:username:password
201
+ if len(proxy) == 5:
202
+ proxies.append(proxy)
203
+ except:
204
+ pass
205
+ return proxies
206
+
207
+ def console_log(text='', logger_type=None, fill_text=None, silent_mode=False):
208
+ if silent_mode:
209
+ return
210
+ if isinstance(logger_type, LoggerType):
211
+ ni = 0
212
+ for i in range(0, len(text)):
213
+ if text[i] != '\n':
214
+ ni = i
215
+ break
216
+ print()
217
+ if fill_text is None:
218
+ fill_text = logger_type.fill_text
219
+ if fill_text:
220
+ print(logger_type.data + ' ' + logger_type.color + text[ni:] + colorama.Style.RESET_ALL)
221
+ else:
222
+ print(logger_type.data + ' ' + text[ni:])
223
+ else:
224
+ print(text)
225
+
226
+ from .WebDriverInstaller import GOOGLE_CHROME, MICROSOFT_EDGE, MOZILLA_FIREFOX, APPLE_SAFARI
227
+
228
+ def clear_console():
229
+ if os.name == 'nt':
230
+ os.system('cls')
231
+ else:
232
+ os.system('clear')
233
+
234
+ def untilConditionExecute(driver_obj, js: str, delay=DEFAULT_DELAY, max_iter=DEFAULT_MAX_ITER, positive_result=True, raise_exception_if_failed=True, return_js_result=False):
235
+ driver_obj.execute_script(f'window.{GET_EBAV} = {DEFINE_GET_EBAV_FUNCTION}')
236
+ driver_obj.execute_script(f'window.{CLICK_WITH_BOOL} = {DEFINE_CLICK_WITH_BOOL_FUNCTION}')
237
+ pre_js = [
238
+ DEFINE_GET_EBAV_FUNCTION,
239
+ DEFINE_CLICK_WITH_BOOL_FUNCTION
240
+ ]
241
+ js = '\n'.join(pre_js+[js])
242
+ for _ in range(max_iter):
243
+ try:
244
+ result = driver_obj.execute_script(js)
245
+ if return_js_result and result is not None:
246
+ return result
247
+ elif result == positive_result:
248
+ return True
249
+ except Exception as E:
250
+ pass
251
+ time.sleep(delay)
252
+ if raise_exception_if_failed:
253
+ raise RuntimeError('untilConditionExecute: the code did not return the desired value! TRY VPN!')
254
+
255
+ def dataGenerator(length, only_numbers=False):
256
+ """generates a password by default. If only_numbers=True - phone number"""
257
+ data = []
258
+ if only_numbers: # phone number
259
+ data = [random.choice(string.digits) for _ in range(length)]
260
+ else: # password
261
+ length += random.randint(1, 10)
262
+ data = [ # 1 uppercase & lowercase letter, 1 number, 1 special character
263
+ random.choice(string.ascii_uppercase),
264
+ random.choice(string.ascii_lowercase),
265
+ random.choice(string.digits),
266
+ random.choice("""!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~""")
267
+ ]
268
+ characters = string.ascii_letters + string.digits + string.punctuation
269
+ data += [random.choice(characters) for _ in range(length-3)]
270
+ random.shuffle(data)
271
+ return ''.join(data)
272
+
273
+ def initSeleniumWebDriver(browser_name: str, webdriver_path = None, browser_path = '', chrome_proxy_extension_path = '', headless=True):
274
+ if browser_path is None:
275
+ browser_path = ''
276
+ logging.info('-- Browsers Initializer --')
277
+ console_log(f'{colorama.Fore.LIGHTMAGENTA_EX}-- Browsers Initializer --{colorama.Fore.RESET}\n', silent_mode=SILENT_MODE)
278
+ if os.name == 'posix': # For Linux
279
+ if sys.platform.startswith('linux'):
280
+ logging.info(f'Initializing {browser_name} for Linux')
281
+ console_log(f'Initializing {browser_name} for Linux', INFO, silent_mode=SILENT_MODE)
282
+ elif sys.platform == "darwin":
283
+ logging.info(f'Initializing {browser_name} for macOS')
284
+ console_log(f'Initializing {browser_name} for macOS', INFO, silent_mode=SILENT_MODE)
285
+ elif os.name == 'nt':
286
+ logging.info(f'Initializing {browser_name} for Windows')
287
+ console_log(f'Initializing {browser_name} for Windows', INFO, silent_mode=SILENT_MODE)
288
+ driver_options = None
289
+ driver = None
290
+ if browser_name == GOOGLE_CHROME:
291
+ driver_options = ChromeOptions()
292
+ driver_options.page_load_strategy = "eager"
293
+ driver_options.binary_location = browser_path
294
+ driver_options.debugger_address = ''
295
+ driver_options.add_experimental_option('excludeSwitches', ['enable-logging'])
296
+ driver_options.add_argument("--log-level=3")
297
+ driver_options.add_argument("--lang=en-US")
298
+ driver_options.add_argument(f"--load-extension={chrome_proxy_extension_path}")
299
+ if headless:
300
+ driver_options.add_argument('--headless')
301
+ if os.name == 'posix': # For Linux
302
+ driver_options.add_argument('--no-sandbox')
303
+ driver_options.add_argument('--disable-dev-shm-usage')
304
+ try:
305
+ service = ChromeService(executable_path=webdriver_path)
306
+ if os.name == 'nt' and headless:
307
+ service.creation_flags = 0x08000000 # CREATE_NO_WINDOW (Process Creation Flags, WinBase.h) -> 'DevTools listening on' is not visible!!!
308
+ driver = Chrome(options=driver_options, service=service)
309
+ except Exception as e:
310
+ logging.critical("EXC_INFO:", exc_info=True)
311
+ if traceback.format_exc().find('only supports') != -1: # Fix for downloaded chrome update
312
+ browser_path = traceback.format_exc().split('path')[-1].split('Stacktrace')[0].strip()
313
+ if 'new_chrome.exe' in os.listdir(browser_path[:-10]):
314
+ logging.info('Downloaded Google Chrome update is detected! Using new chrome executable file!')
315
+ console_log('Downloaded Google Chrome update is detected! Using new chrome executable file!', INFO, silent_mode=SILENT_MODE)
316
+ browser_path = browser_path[:-10]+'new_chrome.exe'
317
+ driver_options.binary_location = browser_path
318
+ driver = Chrome(options=driver_options, service=ChromeService(executable_path=webdriver_path))
319
+ else:
320
+ raise e
321
+ elif browser_name == MICROSOFT_EDGE:
322
+ driver_options = EdgeOptions()
323
+ driver_options.page_load_strategy = "eager"
324
+ driver_options.use_chromium = True
325
+ driver_options.binary_location = browser_path
326
+ driver_options.add_experimental_option('excludeSwitches', ['enable-logging'])
327
+ driver_options.add_argument("--log-level=3")
328
+ driver_options.add_argument("--lang=en-US")
329
+ if headless:
330
+ driver_options.add_argument('--headless')
331
+ if os.name == 'posix': # For Linux
332
+ driver_options.add_argument('--no-sandbox')
333
+ driver_options.add_argument('--disable-dev-shm-usage')
334
+ service = EdgeService(executable_path=webdriver_path)
335
+ if os.name == 'nt' and headless:
336
+ service.creation_flags = 0x08000000 # CREATE_NO_WINDOW (Process Creation Flags, WinBase.h) -> 'DevTools listening on' is not visible!!!
337
+ try:
338
+ driver = Edge(options=driver_options, service=service)
339
+ except Exception as e:
340
+ logging.critical("EXC_INFO:", exc_info=True)
341
+ if traceback.format_exc().find('--user-data-dir') != -1: # Fix for probably user data directory is already in use
342
+ driver_options.add_argument("--user-data-dir=./edge_tmp")
343
+ try:
344
+ shutil.rmtree("edge_tmp")
345
+ except:
346
+ pass
347
+ os.makedirs('edge_tmp', exist_ok=True)
348
+ driver = Edge(options=driver_options, service=EdgeService(executable_path=webdriver_path))
349
+ else:
350
+ raise e
351
+ elif browser_name == MOZILLA_FIREFOX:
352
+ driver_options = FirefoxOptions()
353
+ driver_options.page_load_strategy = "eager"
354
+ if browser_path.strip() != '':
355
+ driver_options.binary_location = browser_path
356
+ driver_options.set_preference('intl.accept_languages', 'en-US')
357
+ if headless:
358
+ driver_options.add_argument('--headless')
359
+ if os.name == 'posix': # For Linux
360
+ driver_options.add_argument('--no-sandbox')
361
+ driver_options.add_argument("--disable-dev-shm-usage")
362
+ service = FirefoxService(executable_path=webdriver_path)
363
+ if os.name == 'nt' and headless:
364
+ service.creation_flags = 0x08000000 # CREATE_NO_WINDOW (Process Creation Flags, WinBase.h) -> 'DevTools listening on' is not visible!!!
365
+ # Fix for: Your firefox profile cannot be loaded. it may be missing or inaccessible
366
+ os.makedirs('firefox_tmp', exist_ok=True)
367
+ os.environ['TMPDIR'] = (os.getcwd()+'/firefox_tmp').replace('\\', '/')
368
+ driver = Firefox(options=driver_options, service=service)
369
+ elif browser_name == APPLE_SAFARI:
370
+ driver_options = SafariOptions()
371
+ try:
372
+ if os.name == 'nt':
373
+ console_log('Apple Safari is not supported on Windows!!!', ERROR)
374
+ return None
375
+ elif os.name == 'posix' and sys.platform.startswith('linux'):
376
+ console_log('Apple Safari is not supported on Linux!!!', ERROR)
377
+ return None
378
+ driver = Safari(options=driver_options, service=SafariService(executable_path=webdriver_path))
379
+ except Exception as e:
380
+ logging.critical("EXC_INFO:", exc_info=True)
381
+ if traceback.format_exc().find("Allow Remote Automation") != -1:
382
+ console_log(traceback.format_exc().split('Message: ')[-1].strip(), ERROR)
383
+ else:
384
+ raise e
385
+ return driver
386
+
387
+ def parseToken(email_obj, driver=None, eset_business=False, delay=DEFAULT_DELAY, max_iter=DEFAULT_MAX_ITER):
388
+ activated_href = None
389
+ if email_obj.class_name == 'custom':
390
+ while True:
391
+ activated_href = input(f'\n[ {colorama.Fore.YELLOW}INPT{colorama.Fore.RESET} ] {colorama.Fore.CYAN}Enter the link to activate your account, it will come to the email address you provide: {colorama.Fore.RESET}').strip()
392
+ logging.info(f'[ INPT ] Enter the link to activate your account, it will come to the email address you provide: {activated_href}')
393
+ if activated_href != '':
394
+ if eset_business:
395
+ match = re.search(r'activation\/[a-zA-Z0-9-]+', activated_href)
396
+ else:
397
+ match = re.search(r'token=[a-zA-Z\d:/-]*', activated_href)
398
+ if match is not None:
399
+ if eset_business:
400
+ token = match.group()[11:]
401
+ else:
402
+ token = match.group()[6:]
403
+ if len(token) == 36:
404
+ return token
405
+ logging.error('Incorrect link syntax')
406
+ console_log('Incorrect link syntax', ERROR)
407
+ for _ in range(max_iter):
408
+ if email_obj.class_name == '1secmail':
409
+ json = email_obj.read_email()
410
+ if json != []:
411
+ for message in json:
412
+ if eset_business and message['subject'].find('ESET PROTECT Hub') != -1:
413
+ activated_href = email_obj.get_message(message['id'])['body']
414
+ elif message['from'].find('product.eset.com') != -1:
415
+ activated_href = email_obj.get_message(message['id'])['body']
416
+ elif email_obj.class_name in ['developermail', 'inboxes']:
417
+ messages = email_obj.get_messages()
418
+ if messages is not None:
419
+ for message in messages:
420
+ if eset_business and message['subject'].find('ESET PROTECT Hub') != -1:
421
+ activated_href = message['body']
422
+ elif message['from'].find('product.eset.com') != -1:
423
+ activated_href = message['body']
424
+ elif email_obj.class_name in ['guerrillamail', 'mailticking', 'fakemail', 'incognitomail']:
425
+ inbox = email_obj.parse_inbox()
426
+ for mail in inbox:
427
+ mail_id, mail_from, mail_subject = mail
428
+ if mail_from.find('product.eset.com') != -1 or mail_from.find('ESET HOME') != -1 or mail_subject.find('ESET PROTECT Hub') != -1:
429
+ email_obj.open_mail(mail_id)
430
+ if email_obj.class_name in ['mailticking', 'incognitomail']:
431
+ time.sleep(2)
432
+ try:
433
+ if email_obj.class_name == 'mailticking':
434
+ driver.switch_to.frame(driver.find_element('id', 'email-iframe'))
435
+ if eset_business:
436
+ activated_href = driver.find_element('xpath', "//a[starts-with(@href, 'https://protecthub.eset.com')]").get_attribute('href')
437
+ else:
438
+ activated_href = driver.find_element('xpath', "//a[starts-with(@href, 'https://login.eset.com')]").get_attribute('href')
439
+ driver.switch_to.default_content()
440
+ except:
441
+ pass
442
+ if activated_href is not None:
443
+ if eset_business:
444
+ match = re.search(r'activation\/[a-zA-Z0-9-]+', activated_href)
445
+ else:
446
+ match = re.search(r'token=[a-zA-Z\d:/-]*', activated_href)
447
+ if match is not None:
448
+ if eset_business:
449
+ token = match.group()[11:]
450
+ else:
451
+ token = match.group()[6:]
452
+ if len(token) == 36:
453
+ return token
454
+ time.sleep(delay)
455
+ raise RuntimeError('Token retrieval error, try again later or change the Email API!!!')
456
+
457
+ def parseEPHKey(email_obj, driver=None, delay=DEFAULT_DELAY, max_iter=DEFAULT_MAX_ITER):
458
+ for _ in range(max_iter):
459
+ license_data = None
460
+ if email_obj.class_name in ['developermail', 'inboxes']:
461
+ messages = email_obj.get_messages()
462
+ if messages is not None:
463
+ for message in messages:
464
+ if message['subject'].find('Thank you for purchasing') != -1:
465
+ license_data = message['body']
466
+ break
467
+ elif email_obj.class_name in ['mailticking', 'fakemail', 'incognitomail']:
468
+ inbox = email_obj.parse_inbox()
469
+ for mail in inbox:
470
+ mail_id, mail_from, mail_subject = mail
471
+ if mail_subject.find('Thank you for purchasing') != -1:
472
+ try:
473
+ email_obj.open_mail(mail_id)
474
+ time.sleep(3)
475
+ if email_obj.class_name == 'mailticking':
476
+ license_data = driver.find_element('id', 'email-iframe').srcdoc
477
+ else:
478
+ license_data = email_obj.driver.page_source
479
+ break
480
+ except:
481
+ pass
482
+ if license_data is not None:
483
+ license_data = str(license_data)
484
+ license_key = re.search(r'([A-Z0-9]){4}-([A-Z0-9]){4}-([A-Z0-9]){4}-([A-Z0-9]){4}-([A-Z0-9]){4}', license_data).group()
485
+ license_id = re.search(r'[A-Z0-9]{3}-[A-Z0-9]{3}-[A-Z0-9]{3}', license_data).group()
486
+ license_out_date = re.findall(r'\d\d.\d\d.\d\d\d\d', license_data)[-1]
487
+ return license_key, license_out_date, license_id
488
+ time.sleep(delay)
489
+ raise RuntimeError('ESET ProtectHub data waiting time has been exceeded!!!')
490
+
491
+ def parseVPNCodes(email_obj, driver=None, delay=DEFAULT_DELAY, max_iter=DEFAULT_MAX_ITER):
492
+ for _ in range(max_iter):
493
+ data = None
494
+ try:
495
+ if email_obj.class_name == '1secmail':
496
+ json = email_obj.read_email()
497
+ if json != []:
498
+ for message in json:
499
+ if message['subject'].find('VPN - Setup instructions') != -1:
500
+ data = email_obj.get_message(message['id'])['body']
501
+ elif email_obj.class_name in ['developermail', 'inboxes']:
502
+ messages = email_obj.get_messages()
503
+ if messages is not None:
504
+ for message in messages:
505
+ if message['subject'].find('VPN - Setup instructions') != -1:
506
+ data = message['body']
507
+ elif email_obj.class_name in ['guerrillamail', 'mailticking', 'fakemail', 'incognitomail']:
508
+ inbox = email_obj.parse_inbox()
509
+ for mail in inbox:
510
+ mail_id, mail_from, mail_subject = mail
511
+ if mail_subject.find('VPN - Setup instructions') != -1:
512
+ email_obj.open_mail(mail_id)
513
+ if email_obj.class_name == 'mailticking':
514
+ time.sleep(1.5)
515
+ try:
516
+ data = str(driver.find_element('id', 'email-iframe').get_attribute('srcdoc'))
517
+ except:
518
+ pass
519
+ elif email_obj.class_name == 'guerrillamail':
520
+ try:
521
+ data = str(driver.execute_script(f'return {GET_EBCN}("email_body")[0].innerHTML'))
522
+ except:
523
+ pass
524
+ elif email_obj.class_name == 'incognitomail':
525
+ time.sleep(1.5)
526
+ try:
527
+ data = str(driver.execute_script(f'return {GET_EBCN}("MuiBox-root joy-1v8x7dw")[0].innerHTML'))
528
+ except:
529
+ pass
530
+ else:
531
+ data = driver.page_source
532
+ except:
533
+ pass
534
+ if data is not None:
535
+ match = re.findall(r'[A-Z0-9]{10}', data)
536
+ if match is not None and len(match) == 10:
537
+ return match
538
+ time.sleep(delay)
539
+ raise RuntimeError('VPN Codes retrieval error, try again later or change the Email API!!!')
modules/Updater.py ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .SharedTools import console_log, INFO, OK, ERROR, WARN
2
+ from .ProgressBar import ProgressBar, DEFAULT_RICH_STYLE
3
+
4
+ import subprocess
5
+ import requests
6
+ import zipfile
7
+ import logging
8
+ import pathlib
9
+ import sys
10
+ import os
11
+
12
+ SILENT_MODE = '--silent' in sys.argv
13
+
14
+ WINDOWS_EXTERNAL_UPDATER = """
15
+ @echo off
16
+
17
+ timeout 1 >nul 2>&1
18
+
19
+ echo.
20
+ echo --- ESET-KeyGen External-Updater ---
21
+ echo.
22
+
23
+ echo !!! Make sure you are running the program with elevated permissions, else update will fail !!!
24
+ echo.
25
+ echo !!! Do not interrupt the update, if you interrupt the update, the executable file will be corrupted !!!
26
+ echo.
27
+
28
+ where curl >nul 2>&1
29
+ if %ERRORLEVEL%==0 (
30
+ curl -#L %1 -o %2
31
+ ) else (
32
+ powershell Invoke-WebRequest -Uri %1 -OutFile %2
33
+ )
34
+
35
+ echo.
36
+ echo Press Enter to exit the updater ...
37
+ pause >nul
38
+ """
39
+
40
+ WINDOWS_EXTERNAL_UPDATER_SILENT = """
41
+ @echo off
42
+
43
+ timeout 1 >nul 2>&1
44
+
45
+ where curl >nul 2>&1
46
+ if %ERRORLEVEL%==0 (
47
+ curl -#L %1 -o %2 -s
48
+ ) else (
49
+ powershell Invoke-WebRequest -Uri %1 -OutFile %2
50
+ )
51
+ """
52
+
53
+ MACOS_EXTERNAL_UPDATER = """
54
+ #!/bin/bash
55
+
56
+ sleep 1
57
+
58
+ echo -e '\n--- ESET-KeyGen External-Updater ---\n'
59
+ echo -e '!!! Make sure you are running the program with elevated permissions, else update will fail !!!\n'
60
+ echo -e '!!! Do not interrupt the update, if you interrupt the update, the executable file will be corrupted !!!\n'
61
+
62
+ curl -#L $1 -o $2
63
+ chmod 755 $2
64
+ echo -e "\nPress Enter to exit the updater ..."
65
+
66
+ exit
67
+ """
68
+
69
+ MACOS_EXTERNAL_UPDATER_SILENT = """
70
+ #!/bin/bash
71
+
72
+ sleep 1
73
+
74
+ curl -#L $1 -o $2 -s
75
+ chmod 755 $2
76
+ exit
77
+ """
78
+
79
+ class Updater:
80
+ def __init__(self, disable_logging=False):
81
+ self.disable_logging = disable_logging
82
+ if SILENT_MODE:
83
+ self.disable_logging = True
84
+ self.arch = None
85
+ if sys.platform.startswith('win'):
86
+ self.arch = 'win32'
87
+ if sys.maxsize > 2**32: # 64bit
88
+ self.arch = 'win64'
89
+ elif sys.platform == 'darwin':
90
+ self.arch = 'macos' # prefix for universal macOS builds (arm64 + x86_64)
91
+ #arch = 'macos_arm64'
92
+ #if platform.machine() == "x86_64":
93
+ # arch = 'macos_amd64'
94
+ self.releases = None
95
+
96
+ def get_releases(self, version='latest'):
97
+ url = 'https://api.github.com/repos/shadowcopyrz/ESET-KGEN-COPY/releases'
98
+ if version == 'latest':
99
+ url = 'https://api.github.com/repos/shadowcopyrz/ESET-KGEN-COPY/releases/latest'
100
+ try:
101
+ response = requests.get(url, timeout=5)
102
+ update_json = response.json()
103
+ try:
104
+ if update_json.get('message') is not None:
105
+ logging.error('Your IP address has been blocked. try again later or use a VPN!')
106
+ console_log('Your IP address has been blocked. try again later or use a VPN!', ERROR, silent_mode=self.disable_logging)
107
+ return None
108
+ except AttributeError:
109
+ pass
110
+ if version == 'latest': # when requesting the latest version, the site returns json without a list.
111
+ update_json = [update_json]
112
+ f_update_json = {}
113
+ for release in update_json:
114
+ f_update_json[release['name']] = {
115
+ 'version': release['name'],
116
+ 'src': release['zipball_url'],
117
+ 'assets': {}
118
+ }
119
+ for asset in release['assets']:
120
+ if asset['name'] == 'src.zip':
121
+ f_update_json[release['name']]['src'] = asset['browser_download_url']
122
+ else:
123
+ f_update_json[release['name']]['assets'][asset['name']] = asset['browser_download_url']
124
+ self.releases = f_update_json
125
+ return f_update_json
126
+ except:
127
+ return None
128
+
129
+ def find_suitable_data(self, datatype='source_code', version='latest'): # datatype: source_code OR executable_file
130
+ if self.releases is None:
131
+ self.releases = self.get_releases(version)
132
+ if version == 'latest':
133
+ if datatype == 'source_code':
134
+ return self.releases[list(self.releases.keys())[0]]['src']
135
+ elif datatype == 'executable_file':
136
+ assets = self.releases[list(self.releases.keys())[0]]['assets']
137
+ else:
138
+ for release_name in self.releases:
139
+ if release_name == version:
140
+ if datatype == 'source_code':
141
+ return self.releases[release_name]['src']
142
+ elif datatype == 'executable_file':
143
+ assets = self.releases[release_name]['assets']
144
+ break
145
+ if datatype == 'executable_file':
146
+ for asset_name, asset_url in assets.items():
147
+ if asset_name.find(self.arch) != -1:
148
+ return asset_url
149
+
150
+ def download_file(self, url):
151
+ try:
152
+ response = requests.get(url, stream=True)
153
+ try:
154
+ filename = response.headers.get('content-disposition').split('filename=')[1]
155
+ logging.info(f'Downloading {filename}...')
156
+ console_log(f'Downloading {filename}...', INFO, silent_mode=self.disable_logging)
157
+ except:
158
+ pass
159
+ total_length = response.headers.get('content-length')
160
+ if total_length is None or self.disable_logging: # No content length header
161
+ with open(filename, 'wb') as f:
162
+ f.write(response.content)
163
+ else:
164
+ task = ProgressBar(int(total_length), ' ', DEFAULT_RICH_STYLE)
165
+ with open(filename, 'wb') as f:
166
+ for chunk in response.iter_content(chunk_size=8192):
167
+ if chunk: # filter out keep-alive new chunks
168
+ f.write(chunk)
169
+ task.update(len(chunk))
170
+ task.render()
171
+ return str(pathlib.Path(filename).resolve())
172
+ except Exception as e:
173
+ logging.critical("EXC_INFO:", exc_info=True)
174
+ console_log(f"Error downloading file: {e}", ERROR, silent_mode=self.disable_logging)
175
+ return False
176
+
177
+ def extract_data(self, data_path: str, new_name=None):
178
+ extracted_data_path = None
179
+ if data_path.endswith('.zip'): # source code
180
+ try:
181
+ with zipfile.ZipFile(data_path, 'r') as zipf:
182
+ extracted_folder_name = zipf.filelist[0].filename[0:-1] # rzc0d3r-ESET-KeyGen-56a2c5b/ -> rzc0d3r-ESET-KeyGen-56a2c5b
183
+ zipf.extractall()
184
+ logging.info("Extraction completed successfully!")
185
+ console_log("Extraction completed successfully!", OK, silent_mode=self.disable_logging)
186
+ extracted_data_path = str(pathlib.Path(extracted_folder_name).resolve())
187
+ if new_name is not None:
188
+ os.rename(extracted_folder_name, new_name)
189
+ extracted_data_path = str(pathlib.Path(new_name).resolve())
190
+ else:
191
+ os.rename(extracted_folder_name, 'ESET-KeyGen-'+list(self.releases.keys())[0])
192
+ extracted_data_path = str(pathlib.Path('ESET-KeyGen-'+list(self.releases.keys())[0]))
193
+ except Exception as e:
194
+ logging.critical("EXC_INFO:", exc_info=True)
195
+ console_log(str(e), ERROR, silent_mode=self.disable_logging)
196
+ if not data_path.endswith('.zip'): # executable file
197
+ extracted_data_path = str(pathlib.Path(data_path).resolve())
198
+ if new_name is not None:
199
+ os.rename(data_path, new_name)
200
+ extracted_data_path = str(pathlib.Path(new_name).resolve())
201
+ logging.warning(f"Location of update: {extracted_data_path}")
202
+ console_log(f"Location of update: {extracted_data_path}", WARN, silent_mode=self.disable_logging)
203
+ return extracted_data_path
204
+
205
+ def updater_menu(self, i_am_executable, path_to_main_file):
206
+ executable_file_url = self.find_suitable_data(datatype='executable_file')
207
+ if i_am_executable: # run from the build [supported platform]
208
+ logging.info('Transferring control to an external script...')
209
+ if sys.platform.startswith('win'):
210
+ updater_path = os.environ['TEMP']+'\\updater.bat'
211
+ with open(updater_path, 'w') as f:
212
+ if SILENT_MODE:
213
+ f.write(WINDOWS_EXTERNAL_UPDATER_SILENT)
214
+ else:
215
+ f.write(WINDOWS_EXTERNAL_UPDATER)
216
+ subprocess.Popen([updater_path, executable_file_url, path_to_main_file], shell=True)
217
+ elif sys.platform == 'darwin':
218
+ updater_path = r'/tmp/updater.sh'
219
+ with open(updater_path, 'w') as f:
220
+ if SILENT_MODE:
221
+ f.write(MACOS_EXTERNAL_UPDATER_SILENT)
222
+ else:
223
+ f.write(MACOS_EXTERNAL_UPDATER)
224
+ os.chmod(updater_path, 0o755)
225
+ subprocess.Popen(['bash', updater_path, executable_file_url, path_to_main_file])
226
+ sys.exit(0)
227
+ elif executable_file_url is not None: # run from source [supported platform]
228
+ executable_file_url = self.find_suitable_data(datatype='executable_file')
229
+ self.extract_data(self.download_file(executable_file_url))
230
+ else: # run from source [unsupported platform]
231
+ logging.error('No suitable executable file was found for your platform!!!')
232
+ logging.info('Downloading the latest release source code...')
233
+ console_log('No suitable executable file was found for your platform!!!', ERROR, silent_mode=SILENT_MODE)
234
+ console_log('Downloading the latest release source code...', INFO, silent_mode=SILENT_MODE)
235
+ self.extract_data(self.download_file(self.find_suitable_data()))
modules/WebDriverInstaller.py ADDED
@@ -0,0 +1,410 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GOOGLE_CHROME = 'Google Chrome'
2
+ MICROSOFT_EDGE = 'Microsoft Edge'
3
+ MOZILLA_FIREFOX = 'Mozilla Firefox'
4
+ APPLE_SAFARI = 'Apple Safari'
5
+
6
+ GOOGLE_CHROME_RE = r'(\d+\.\d+\.\d+\.\d+)'
7
+ MICROSOFT_EDGE_RE = r'(\d+\.\d+\.\d+\.\d+)'
8
+ MOZILLA_FIREFOX_RE = r'(\d+\.\d+\.\d+)|(\d+\.\d+)'
9
+ APPLE_SAFARI_RE = r'\d+.\d+.\d+'
10
+
11
+ from .SharedTools import console_log, INFO, OK, ERROR, WARN
12
+ from .ProgressBar import ProgressBar, DEFAULT_RICH_STYLE
13
+
14
+ from pathlib import Path
15
+
16
+ from colorama import Fore, init
17
+ init()
18
+
19
+ import subprocess
20
+ import platform
21
+ import requests
22
+ import logging
23
+ import zipfile
24
+ import tarfile
25
+ import shutil
26
+ import sys
27
+ import re
28
+ import os
29
+
30
+ SILENT_MODE = '--silent' in sys.argv
31
+
32
+ class WebDriverInstaller(object):
33
+ def __init__(self, browser_name: str, custom_browser_location=None):
34
+ self.browsers_data = {
35
+ GOOGLE_CHROME: [self.get_chromedriver_url, 'chromedriver.exe' if sys.platform.startswith('win') else 'chromedriver', self.get_chrome_version, GOOGLE_CHROME_RE],
36
+ MICROSOFT_EDGE: [self.get_msedgedriver_url, 'msedgedriver.exe' if sys.platform.startswith('win') else 'msedgedriver', self.get_edge_version, MICROSOFT_EDGE_RE],
37
+ MOZILLA_FIREFOX: [self.get_geckodriver_url, 'geckodriver.exe' if sys.platform.startswith('win') else 'geckodriver', self.get_firefox_version, MOZILLA_FIREFOX_RE],
38
+ APPLE_SAFARI: []
39
+ }
40
+ self.browser_name = browser_name
41
+ self.custom_browser_location = custom_browser_location
42
+ if self.browser_name not in self.browsers_data:
43
+ raise RuntimeError('WebDriverInstaller: invalid browser_name!')
44
+ self.browser_data = self.browsers_data[self.browser_name]
45
+ self.platform = ['', []] # [OC name, [webdriver architectures]]
46
+ if sys.platform.startswith('win'):
47
+ self.platform[0] = 'win'
48
+ if sys.maxsize > 2**32:
49
+ self.platform[1] = ['win64', 'win32']
50
+ else:
51
+ self.platform[1] = ['win32']
52
+ elif sys.platform.startswith('linux'):
53
+ self.platform[0] = 'linux'
54
+ if sys.maxsize > 2**32:
55
+ self.platform[1].append('linux64')
56
+ else:
57
+ self.platform[1].append('linux32')
58
+ elif sys.platform == "darwin":
59
+ self.platform[0] = 'mac'
60
+ if self.browser_name == MOZILLA_FIREFOX:
61
+ self.platform[1] = ['macos']
62
+ elif platform.processor() == "arm":
63
+ self.platform[1] = ['mac-arm64', 'mac_arm64', 'mac64_m1']
64
+ if self.browser_name == MOZILLA_FIREFOX:
65
+ self.platform[1] = ['macos-aarch64']
66
+ elif platform.processor() == "i386":
67
+ self.platform[1] = ['mac64', 'mac-x64']
68
+
69
+ def get_browser_version_from_cmd(self, path: str, re_pattern: str):
70
+ try:
71
+ with subprocess.Popen([path, "--version"], stdout=subprocess.PIPE) as proc:
72
+ return re.search(re_pattern, proc.communicate()[0].decode("utf-8")).group()
73
+ except:
74
+ pass
75
+
76
+ def get_chrome_version(self):
77
+ browser_version = None
78
+ browser_path = None
79
+ if self.platform[0] == "linux":
80
+ if self.custom_browser_location is not None:
81
+ browser_version = self.get_browser_version_from_cmd(self.custom_browser_location, GOOGLE_CHROME_RE)
82
+ browser_path = self.custom_browser_location
83
+ else:
84
+ for executable in ["google-chrome", "google-chrome-stable", "google-chrome-beta", "google-chrome-dev", "chromium-browser", "chromium"]:
85
+ browser_version = self.get_browser_version_from_cmd(shutil.which(executable), GOOGLE_CHROME_RE)
86
+ if browser_version is not None:
87
+ browser_path = shutil.which(executable)
88
+ break
89
+ elif self.platform[0] == "mac":
90
+ if self.custom_browser_location is not None:
91
+ browser_version = self.get_browser_version_from_cmd(self.custom_browser_location, GOOGLE_CHROME_RE)
92
+ browser_path = self.custom_browser_location
93
+ else:
94
+ browser_version = self.get_browser_version_from_cmd('/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', GOOGLE_CHROME_RE)
95
+ browser_path = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
96
+ elif self.platform[0] == "win":
97
+ paths = [
98
+ f'{os.environ.get("SYSTEMDRIVE")}\\Program Files\\Google\\Chrome\\Application',
99
+ f'{os.environ.get("SYSTEMDRIVE")}\\Program Files (x86)\\Google\\Chrome\\Application',
100
+ f'{os.environ.get("LOCALAPPDATA")}\\Google\\Chrome\\Application'
101
+ ]
102
+ if self.custom_browser_location is not None:
103
+ paths = [str(Path(self.custom_browser_location).parent)]
104
+ for path in paths:
105
+ try:
106
+ with open(path+'\\chrome.VisualElementsManifest.xml', 'r') as f:
107
+ browser_version = re.search(GOOGLE_CHROME_RE, f.read()).group()
108
+ browser_path = path+'\\chrome.exe'
109
+ break
110
+ except:
111
+ pass
112
+ return [browser_version, browser_path]
113
+
114
+ def get_chromedriver_url(self, chrome_major_version=None):
115
+ if chrome_major_version is None:
116
+ chrome_major_version = self.get_chrome_version()[0]
117
+ if chrome_major_version is None:
118
+ return None
119
+ chrome_major_version = self.get_chrome_version()[0].split('.')[0]
120
+ if int(chrome_major_version) >= 115: # for new drivers ( [115.0.0000.0, ...] )
121
+ drivers_data = requests.get('https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json')
122
+ drivers_data = drivers_data.json()['versions'][::-1] # start with the latest version
123
+ for driver_data in drivers_data:
124
+ driver_major_version = driver_data['version'].split('.')[0] # major, _, minor, micro
125
+ if driver_major_version == chrome_major_version: # return latest driver version for current major chrome version
126
+ for driver_url in driver_data['downloads'].get('chromedriver', []):
127
+ if driver_url['platform'] in self.platform[1]:
128
+ return driver_url['url']
129
+ else: # for old drivers ( [..., 115.0.0000.0) )
130
+ latest_old_driver_version = requests.get(f'https://chromedriver.storage.googleapis.com/LATEST_RELEASE_{chrome_major_version}')
131
+ if latest_old_driver_version.status_code == 200:
132
+ latest_old_driver_version = latest_old_driver_version.text
133
+ driver_url = f'https://chromedriver.storage.googleapis.com/{latest_old_driver_version}/chromedriver_'
134
+ for arch in self.platform[1]:
135
+ current_driver_url = driver_url+arch+'.zip'
136
+ driver_size = requests.head(current_driver_url).headers.get('x-goog-stored-content-length', None)
137
+ if driver_size is not None and int(driver_size) > 1024**2:
138
+ return current_driver_url
139
+
140
+ def get_edge_version(self):
141
+ browser_version = None
142
+ browser_path = None
143
+ if self.platform[0] == 'linux':
144
+ if self.custom_browser_location is not None:
145
+ browser_version = self.get_browser_version_from_cmd(self.custom_browser_location, MICROSOFT_EDGE_RE)
146
+ browser_path = self.custom_browser_location
147
+ else:
148
+ for executable in ['microsoft-edge']:
149
+ browser_version = self.get_browser_version_from_cmd(shutil.which(executable), MICROSOFT_EDGE_RE)
150
+ if browser_version is not None:
151
+ browser_path = shutil.which(executable)
152
+ break
153
+ elif self.platform[0] == "mac":
154
+ if self.custom_browser_location is not None:
155
+ browser_version = self.get_browser_version_from_cmd(self.custom_browser_location, MICROSOFT_EDGE_RE)
156
+ browser_path = self.custom_browser_location
157
+ else:
158
+ browser_version = self.get_browser_version_from_cmd('/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge', MICROSOFT_EDGE_RE)
159
+ browser_path = '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge'
160
+ elif self.platform[0] == 'win':
161
+ paths = [
162
+ f'{os.environ.get("SYSTEMDRIVE")}\\Program Files\\Microsoft\\Edge\\Application',
163
+ f'{os.environ.get("SYSTEMDRIVE")}\\Program Files (x86)\\Microsoft\\Edge\\Application'
164
+ ]
165
+ if self.custom_browser_location is not None:
166
+ paths = [str(Path(self.custom_browser_location).parent)]
167
+ for path in paths:
168
+ try:
169
+ with open(path+'\\msedge.VisualElementsManifest.xml', 'r') as f:
170
+ browser_version = re.search(MICROSOFT_EDGE_RE, f.read()).group()
171
+ browser_path = path+'\\msedge.exe'
172
+ break
173
+ except:
174
+ pass
175
+ return [browser_version, browser_path]
176
+
177
+ def get_msedgedriver_url(self, edge_version=None):
178
+ archs = self.platform[1]
179
+ if edge_version is None:
180
+ edge_version, _ = self.get_edge_version()
181
+ major_version = edge_version.split('.')[0]
182
+ if self.platform[0] == 'win':
183
+ r = requests.get(f'https://msedgedriver.azureedge.net/LATEST_RELEASE_{major_version}_WINDOWS')
184
+ elif self.platform[0] == 'linux':
185
+ r = requests.get(f'https://msedgedriver.azureedge.net/LATEST_RELEASE_{major_version}_LINUX')
186
+ elif self.platform[0] == 'mac':
187
+ r = requests.get(f'https://msedgedriver.azureedge.net/LATEST_RELEASE_{major_version}_MACOS')
188
+ if r.status_code == 200:
189
+ webdriver_version = r.text.strip()
190
+ for arch in archs:
191
+ driver_url = f'https://msedgedriver.azureedge.net/{webdriver_version}/edgedriver_{arch}.zip'
192
+ driver_size = requests.head(driver_url).headers.get('Content-Length', None)
193
+ if driver_size is not None and int(driver_size) > 1024**2:
194
+ return driver_url
195
+
196
+ def get_firefox_version(self):
197
+ browser_version = None
198
+ browser_path = None
199
+ if self.platform[0] == 'linux':
200
+ if self.custom_browser_location is not None:
201
+ browser_version = self.get_browser_version_from_cmd(self.custom_browser_location, MOZILLA_FIREFOX_RE)
202
+ browser_path = self.custom_browser_location
203
+ else:
204
+ for executable in ['firefox']:
205
+ browser_version = self.get_browser_version_from_cmd(shutil.which(executable), MOZILLA_FIREFOX_RE)
206
+ if browser_version is not None:
207
+ browser_path = shutil.which(executable)
208
+ break
209
+ elif self.platform[0] == "mac":
210
+ if self.custom_browser_location is not None:
211
+ browser_version = self.get_browser_version_from_cmd(self.custom_browser_location, MOZILLA_FIREFOX_RE)
212
+ browser_path = self.custom_browser_location
213
+ else:
214
+ for path in ['/Applications/Firefox.app/Contents/MacOS/firefox', '/application/firefox.app']:
215
+ if browser_version is not None:
216
+ browser_version = self.get_browser_version_from_cmd(path, MOZILLA_FIREFOX_RE)
217
+ browser_path = path
218
+ break
219
+ elif self.platform[0] == 'win':
220
+ paths = [
221
+ f'{os.environ.get("SYSTEMDRIVE")}\\Program Files\\Mozilla Firefox',
222
+ f'{os.environ.get("SYSTEMDRIVE")}\\Program Files (x86)\\Mozilla Firefox',
223
+ ]
224
+ if self.custom_browser_location is not None:
225
+ paths = [str(Path(self.custom_browser_location).parent)]
226
+ for path in paths:
227
+ try:
228
+ with open(path+'\\application.ini', 'r') as f:
229
+ browser_version = re.search(MOZILLA_FIREFOX_RE, f.read()).group()
230
+ browser_path = path+'\\firefox.exe'
231
+ break
232
+ except:
233
+ pass
234
+ return [browser_version, browser_path]
235
+
236
+ def get_geckodriver_url(self, only_version=False):
237
+ r = requests.get("https://api.github.com/repos/mozilla/geckodriver/releases/latest")
238
+ r_json = r.json()
239
+ api_rate_limit = (True if r_json.get('name', None) is None else False)
240
+ if api_rate_limit: # bypass for API rate limit exceeded for your IP
241
+ r = requests.head("https://github.com/mozilla/geckodriver/releases/latest", allow_redirects=True)
242
+ geckodriver_version = r.url.split('/')[-1][1:]
243
+ else:
244
+ geckodriver_version = r_json['name']
245
+ if only_version:
246
+ return geckodriver_version
247
+ if not api_rate_limit:
248
+ #https://github.com/mozilla/geckodriver/releases/download/v0.34.0/geckodriver-v0.34.0-macos.tar.gz
249
+ # note for: r_json['assets'][::-1]
250
+ # in the initialization of WebDriverInstaller for 64bit is also suitable for 32bit, but
251
+ # in the list of assets first go 32bit and it comes out that for 64bit gives a 32bit release, turning the list fixes it
252
+ for asset in r_json['assets'][::-1]:
253
+ if asset['name'].find('asc') == -1: # ignoring GPG Keys
254
+ asset_arch = asset['name'].split('-', 2)[-1].split('.')[0] # package architecture parsing; geckodriver-v0.34.0-win32.zip -> ['geckodriver', 'v0.34.0', 'win32.zip'] -> ['win32', 'zip'] -> win32
255
+ if asset_arch in self.platform[1]:
256
+ return asset['browser_download_url']
257
+ else:
258
+ # bypass for API rate limit exceeded for your IP
259
+ extension = '.zip' if self.platform[0] == 'win' else '.tar.gz'
260
+ for arch in self.platform[1]:
261
+ url = f'https://github.com/mozilla/geckodriver/releases/download/v{geckodriver_version}/geckodriver-v{geckodriver_version}-{arch}{extension}'
262
+ r = requests.get(url, stream=True)
263
+ if int(r.headers.get('Content-Length', 0)) > 1024**2:
264
+ return url
265
+
266
+ def get_safari_version(self):
267
+ if self.platform[0] == "mac":
268
+ cmd = ['/usr/libexec/PlistBuddy', '-c', "print :CFBundleShortVersionString", '/Applications/Safari.app/Contents/Info.plist']
269
+ try:
270
+ with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc:
271
+ return re.search(APPLE_SAFARI_RE, proc.communicate()[0].decode("utf-8")).group()
272
+ except:
273
+ pass
274
+
275
+ def detect_installed_browser(self):
276
+ for browser_name in self.browsers_data:
277
+ if browser_name == APPLE_SAFARI:
278
+ browser_version = self.get_safari_version()
279
+ if browser_version is not None:
280
+ return [APPLE_SAFARI, browser_version]
281
+ else:
282
+ browser_version, browser_path = self.browsers_data[browser_name][2]()
283
+ if browser_version is not None:
284
+ return [browser_name, browser_version, browser_path]
285
+
286
+ def download_webdriver(self, url=None, path='.', disable_progress_bar=False):
287
+ # init
288
+ webdriver_name = self.browser_data[1]
289
+ file_extension = '.zip'
290
+ if url is None:
291
+ url = self.browser_data[0]()
292
+ if url is None:
293
+ return None
294
+ if url.split('.')[-1] == 'gz':
295
+ file_extension = '.tar.gz'
296
+ # downloading
297
+ archive_path = str(Path(f'{path}/data{file_extension}').resolve())
298
+ response = requests.get(url, stream=True)
299
+ if not disable_progress_bar:
300
+ total_length = int(response.headers.get('Content-Length', 0))
301
+ total_length = int(response.headers.get('content-length', total_length))
302
+ else:
303
+ total_length = 0
304
+ if total_length == 0 or SILENT_MODE: # No content length header
305
+ with open(archive_path, 'wb') as f:
306
+ f.write(response.content)
307
+ else:
308
+ task = ProgressBar(int(total_length), ' ', DEFAULT_RICH_STYLE)
309
+ with open(archive_path, 'wb') as f:
310
+ for chunk in response.iter_content(chunk_size=8192):
311
+ if chunk: # filter out keep-alive new chunks
312
+ f.write(chunk)
313
+ task.update(len(chunk))
314
+ task.render()
315
+ # extracting
316
+ archive, info = None, []
317
+ if file_extension == '.zip':
318
+ archive = zipfile.ZipFile(archive_path)
319
+ archive_info = archive.infolist()
320
+ elif file_extension == '.tar.gz':
321
+ archive = tarfile.open(archive_path)
322
+ archive_info = archive.getnames()
323
+ if archive is not None:
324
+ for info in archive_info:
325
+ archive_filename, archive_filepath = None, None
326
+ if file_extension == '.zip':
327
+ archive_filename, archive_filepath = info.filename.split('/')[-1], info.filename
328
+ else:
329
+ archive_filename, archive_filepath = info.split('/')[-1], info
330
+ if archive_filename is not None and archive_filename == webdriver_name:
331
+ try:
332
+ archive.extract(info)
333
+ archive.close()
334
+ webdriver_path = str(Path(archive_filepath).resolve())
335
+ if Path(archive_filepath).resolve().parent != Path(os.getcwd()):
336
+ webdriver_path = shutil.copy2(str(Path(archive_filepath).resolve()), os.getcwd())
337
+ try:
338
+ shutil.rmtree(archive_filepath.split('/')[0], ignore_errors=True)
339
+ os.remove(archive_path)
340
+ except:
341
+ pass
342
+ os.chmod(webdriver_path, 0o777)
343
+ return webdriver_path
344
+ except:
345
+ return None
346
+
347
+ def menu(self, disable_progress_bar=False): # auto updating or installing webdrivers
348
+ def download():
349
+ driver_url = self.browser_data[0]()
350
+ if driver_url is not None:
351
+ logging.info('Found a suitable version for your system!')
352
+ logging.info('Downloading...')
353
+ console_log('\nFound a suitable version for your system!', OK, silent_mode=SILENT_MODE)
354
+ console_log('Downloading...', INFO, silent_mode=SILENT_MODE)
355
+ if self.download_webdriver(driver_url, disable_progress_bar=disable_progress_bar):
356
+ logging.info(f'{self.browser_name} webdriver was successfully downloaded and unzipped!')
357
+ console_log(f'{self.browser_name} webdriver was successfully downloaded and unzipped!\n', OK, silent_mode=SILENT_MODE)
358
+ return os.path.join(os.getcwd(), webdriver_name)
359
+ else:
360
+ logging.info('Error downloading or unpacking!')
361
+ console_log('Error downloading or unpacking!\n', ERROR, silent_mode=SILENT_MODE)
362
+ else:
363
+ logging.info('A suitable version for your system was not found!')
364
+ console_log('\nA suitable version for your system was not found!\n', ERROR, silent_mode=SILENT_MODE)
365
+ logging.info('-- WebDriver Auto-Installer --')
366
+ console_log(f'{Fore.LIGHTMAGENTA_EX}-- WebDriver Auto-Installer --{Fore.RESET}\n', silent_mode=SILENT_MODE)
367
+ browser_version, browser_path = self.browser_data[2]()
368
+ if browser_version is None:
369
+ if self.custom_browser_location is None or self.custom_browser_location == '':
370
+ raise RuntimeError(f'{self.browser_name} was not found in the standard catalogs!')
371
+ raise RuntimeError(f'{self.custom_browser_location} is not a valid executable file of {self.browser_name}!')
372
+ webdriver_name = self.browser_data[1]
373
+ current_webdriver_version = None
374
+ webdriver_path = None
375
+ if os.path.exists(webdriver_name):
376
+ try:
377
+ out = subprocess.check_output([os.path.join(os.getcwd(), webdriver_name), "--version"], stderr=subprocess.PIPE)
378
+ out = re.search(self.browser_data[3], out.decode('utf-8'))
379
+ if out is not None:
380
+ current_webdriver_version = out.group()
381
+ webdriver_path = os.path.join(os.getcwd(), webdriver_name)
382
+ except:
383
+ pass
384
+ logging.info(f'{self.browser_name} version: {browser_version}')
385
+ logging.info(f'{self.browser_name} webdriver version: {current_webdriver_version}')
386
+ console_log(f'{self.browser_name} version: {browser_version}', INFO, False, SILENT_MODE)
387
+ console_log(f'{self.browser_name} webdriver version: {current_webdriver_version}', INFO, False, SILENT_MODE)
388
+ if self.browser_name == MOZILLA_FIREFOX:
389
+ latest_geckodriver_version = self.browser_data[0](True)
390
+ if current_webdriver_version == latest_geckodriver_version:
391
+ logging.info('The webdriver has already been updated to the latest version!')
392
+ console_log('The webdriver has already been updated to the latest version!\n', OK, silent_mode=SILENT_MODE)
393
+ webdriver_path = os.path.join(os.getcwd(), webdriver_name)
394
+ else:
395
+ logging.info(f'Updating the webdriver from {current_webdriver_version} to {latest_geckodriver_version} version...')
396
+ console_log(f'Updating the webdriver from {current_webdriver_version} to {latest_geckodriver_version} version...', INFO, silent_mode=SILENT_MODE)
397
+ webdriver_path = download()
398
+ else:
399
+ if current_webdriver_version is None or (current_webdriver_version.split('.')[0] != browser_version.split('.')[0]): # major version match
400
+ logging.warning(f'{self.browser_name} webdriver version doesn\'t match version of the installed {self.browser_name}, trying to download...')
401
+ console_log(f'{self.browser_name} webdriver version doesn\'t match version of the installed {self.browser_name}, trying to download...', WARN, True, SILENT_MODE)
402
+ webdriver_path = download()
403
+ else:
404
+ logging.info('The webdriver has already been updated to the browser version!')
405
+ console_log('The webdriver has already been updated to the browser version!\n', OK, silent_mode=SILENT_MODE)
406
+ try:
407
+ os.chmod(webdriver_path, 0o755)
408
+ except:
409
+ pass
410
+ return [str(Path(webdriver_path).resolve()), str(Path(browser_path).resolve())]