forked from stud178875/tempora
		
	Добавлен README.md с списком группы
This commit is contained in:
		
						commit
						fe513ea8b0
					
				
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| .env | ||||
| *.log | ||||
| **/__pycache__/ | ||||
							
								
								
									
										0
									
								
								driver/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								driver/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										97
									
								
								driver/chrome_proxy.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								driver/chrome_proxy.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| import os | ||||
| import tempfile | ||||
| 
 | ||||
| class ChromeProxy: | ||||
|     def __init__(self, host: str, port: int, username: str = "", password: str = ""): | ||||
|         self.host = host | ||||
|         self.port = port | ||||
|         self.username = username | ||||
|         self.password = password | ||||
| 
 | ||||
|     def create_extension(self, name: str = "Chrome Proxy", version="1.0.0") -> str: | ||||
|         proxy_folder = tempfile.mkdtemp(prefix="chrome_proxy_") | ||||
| 
 | ||||
|         self._create_manifest( | ||||
|             name=name, | ||||
|             version=version, | ||||
|             proxy_folder=proxy_folder | ||||
|         ) | ||||
|          | ||||
|         self._create_background_js(proxy_folder) | ||||
| 
 | ||||
|         return proxy_folder | ||||
| 
 | ||||
|     def _create_manifest(self, name, version, proxy_folder):  | ||||
|         manifest = ChromeProxy.manifest_json | ||||
|         manifest = manifest.replace("<ext_name>", name) | ||||
|         manifest = manifest.replace("<ext_ver>", version) | ||||
| 
 | ||||
|         with open(f"{proxy_folder}/manifest.json", "w") as f: | ||||
|             f.write(manifest) | ||||
|      | ||||
|     def _create_background_js(self, proxy_folder):  | ||||
|         js = ChromeProxy.background_js | ||||
|         js = js.replace("<proxy_host>", self.host) | ||||
|         js = js.replace("<proxy_port>", str(self.port)) | ||||
|         js = js.replace("<proxy_username>", self.username) | ||||
|         js = js.replace("<proxy_password>", self.password) | ||||
| 
 | ||||
|         with open(f"{proxy_folder}/background.js", "w") as f: | ||||
|             f.write(js) | ||||
| 
 | ||||
|     manifest_json = """ | ||||
|     { | ||||
|         "version": "<ext_ver>", | ||||
|         "manifest_version": 3, | ||||
|         "name": "<ext_name>", | ||||
|         "permissions": [ | ||||
|             "proxy", | ||||
|             "tabs", | ||||
|             "storage", | ||||
|             "webRequest", | ||||
|             "webRequestAuthProvider" | ||||
|         ], | ||||
|         "host_permissions": [ | ||||
|             "<all_urls>" | ||||
|         ], | ||||
|         "background": { | ||||
|             "service_worker": "background.js" | ||||
|         }, | ||||
|         "minimum_chrome_version": "22.0.0" | ||||
|     } | ||||
|     """ | ||||
| 
 | ||||
|     background_js = """ | ||||
|     var config = { | ||||
|         mode: "fixed_servers", | ||||
|         rules: { | ||||
|             singleProxy: { | ||||
|                 scheme: "http", | ||||
|                 host: "<proxy_host>", | ||||
|                 port: parseInt("<proxy_port>") | ||||
|             }, | ||||
|             bypassList: ["localhost"] | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     chrome.proxy.settings.set({ | ||||
|         value: config, | ||||
|         scope: "regular" | ||||
|     }, function() {}); | ||||
| 
 | ||||
|     function callbackFn(details) { | ||||
|         return { | ||||
|             authCredentials: { | ||||
|                 username: "<proxy_username>", | ||||
|                 password: "<proxy_password>" | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     chrome.webRequest.onAuthRequired.addListener( | ||||
|         callbackFn, { | ||||
|             urls: ["<all_urls>"] | ||||
|         }, | ||||
|         ['blocking'] | ||||
|     ); | ||||
|     """ | ||||
							
								
								
									
										60
									
								
								driver/driver_creator.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								driver/driver_creator.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | ||||
| import random | ||||
| from driver.chrome_proxy import ChromeProxy | ||||
| from urllib.parse import urlparse | ||||
| import undetected_chromedriver as uc | ||||
| import logging | ||||
| 
 | ||||
| 
 | ||||
| # В patcher.py библиотеки undetected_chromedriver | ||||
|     # import secrets  | ||||
|     # prefix = 'undetected' ----> prefix = secrets.token_hex(8) | ||||
| # Использование случайного токена в качестве префикса гарантирует,  | ||||
| # что каждый процесс будет иметь свою уникальную директорию,  | ||||
| # даже если несколько процессов работают одновременн | ||||
| 
 | ||||
| class DriverCreator: | ||||
|     def __init__(self, proxy_list): | ||||
|         self.proxy_list = proxy_list | ||||
| 
 | ||||
|     def _switch_proxy(self): | ||||
|         proxy = random.choice(self.proxy_list) | ||||
|         parsed_proxy = urlparse(proxy) | ||||
|         return ChromeProxy( | ||||
|             parsed_proxy.hostname, | ||||
|             parsed_proxy.port, | ||||
|             parsed_proxy.username, | ||||
|             parsed_proxy.password | ||||
|         ).create_extension() | ||||
| 
 | ||||
|     def get_driver(self): | ||||
|         ''' | ||||
|         Отключает JS | ||||
|         Каждый запрос получает `сырой` html | ||||
|         ''' | ||||
|         # extension_path = self._switch_proxy() | ||||
| 
 | ||||
|         options = uc.ChromeOptions() | ||||
|         # options.add_argument(f"--load-extension={extension_path}") # временно | ||||
|         options.add_argument("--headless=new") | ||||
|         options.add_argument("--disable-gpu") | ||||
|         options.add_argument("--disable-dev-shm-usage") | ||||
|         options.add_argument("--no-sandbox") | ||||
|         options.add_argument("--disable-webgl") | ||||
|         options.add_argument("--disable-software-rasterizer") | ||||
|         # options.add_argument("--disable-extensions") | ||||
|          | ||||
|         prefs = {"profile.managed_default_content_settings.javascript": 2} | ||||
|         options.experimental_options["prefs"] = prefs | ||||
|          | ||||
|         driver = uc.Chrome( | ||||
|             options=options, | ||||
|             version_main=132, | ||||
|             # user_multi_procs=True | ||||
|         ) | ||||
| 
 | ||||
|         driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { | ||||
|             "source": "Object.defineProperty(navigator, 'javaEnabled', {get: () => false});" | ||||
|         }) | ||||
|         driver.execute_cdp_cmd("Emulation.setScriptExecutionDisabled", {"value": True}) | ||||
|          | ||||
|         return driver | ||||
							
								
								
									
										402
									
								
								patcher.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										402
									
								
								patcher.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,402 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # this module is part of undetected_chromedriver | ||||
| 
 | ||||
| from distutils.version import LooseVersion | ||||
| import io | ||||
| import json | ||||
| import logging | ||||
| import os | ||||
| import pathlib | ||||
| import platform | ||||
| import random | ||||
| import re | ||||
| import shutil | ||||
| import string | ||||
| import sys | ||||
| import time | ||||
| from urllib.request import urlopen | ||||
| from urllib.request import urlretrieve | ||||
| import zipfile | ||||
| from multiprocessing import Lock | ||||
| import secrets | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| IS_POSIX = sys.platform.startswith(("darwin", "cygwin", "linux", "linux2")) | ||||
| 
 | ||||
| 
 | ||||
| class Patcher(object): | ||||
|     lock = Lock() | ||||
|     exe_name = "chromedriver%s" | ||||
| 
 | ||||
|     platform = sys.platform | ||||
|     if platform.endswith("win32"): | ||||
|         d = "~/appdata/roaming/undetected_chromedriver" | ||||
|     elif "LAMBDA_TASK_ROOT" in os.environ: | ||||
|         d = "/tmp/undetected_chromedriver" | ||||
|     elif platform.startswith(("linux", "linux2")): | ||||
|         d = "~/.local/share/undetected_chromedriver" | ||||
|     elif platform.endswith("darwin"): | ||||
|         d = "~/Library/Application Support/undetected_chromedriver" | ||||
|     else: | ||||
|         d = "~/.undetected_chromedriver" | ||||
|     data_path = os.path.abspath(os.path.expanduser(d)) | ||||
| 
 | ||||
|     def __init__( | ||||
|         self, | ||||
|         executable_path=None, | ||||
|         force=False, | ||||
|         version_main: int = 0, | ||||
|         user_multi_procs=False, | ||||
|     ): | ||||
|         """ | ||||
|         Args: | ||||
|             executable_path: None = automatic | ||||
|                              a full file path to the chromedriver executable | ||||
|             force: False | ||||
|                     terminate processes which are holding lock | ||||
|             version_main: 0 = auto | ||||
|                 specify main chrome version (rounded, ex: 82) | ||||
|         """ | ||||
|         self.force = force | ||||
|         self._custom_exe_path = False | ||||
|         prefix = secrets.token_hex(8) | ||||
|         self.user_multi_procs = user_multi_procs | ||||
| 
 | ||||
|         self.is_old_chromedriver = version_main and version_main <= 114 | ||||
|         # Needs to be called before self.exe_name is accessed | ||||
|         self._set_platform_name() | ||||
| 
 | ||||
|         if not os.path.exists(self.data_path): | ||||
|             os.makedirs(self.data_path, exist_ok=True) | ||||
| 
 | ||||
|         if not executable_path: | ||||
|             self.executable_path = os.path.join( | ||||
|                 self.data_path, "_".join([prefix, self.exe_name]) | ||||
|             ) | ||||
| 
 | ||||
|         if not IS_POSIX: | ||||
|             if executable_path: | ||||
|                 if not executable_path[-4:] == ".exe": | ||||
|                     executable_path += ".exe" | ||||
| 
 | ||||
|         self.zip_path = os.path.join(self.data_path, prefix) | ||||
| 
 | ||||
|         if not executable_path: | ||||
|             if not self.user_multi_procs: | ||||
|                 self.executable_path = os.path.abspath( | ||||
|                     os.path.join(".", self.executable_path) | ||||
|                 ) | ||||
| 
 | ||||
|         if executable_path: | ||||
|             self._custom_exe_path = True | ||||
|             self.executable_path = executable_path | ||||
| 
 | ||||
|         # Set the correct repository to download the Chromedriver from | ||||
|         if self.is_old_chromedriver: | ||||
|             self.url_repo = "https://chromedriver.storage.googleapis.com" | ||||
|         else: | ||||
|             self.url_repo = "https://googlechromelabs.github.io/chrome-for-testing" | ||||
| 
 | ||||
|         self.version_main = version_main | ||||
|         self.version_full = None | ||||
| 
 | ||||
|     def _set_platform_name(self): | ||||
|         """ | ||||
|         Set the platform and exe name based on the platform undetected_chromedriver is running on | ||||
|         in order to download the correct chromedriver. | ||||
|         """ | ||||
|         if self.platform.endswith("win32"): | ||||
|             self.platform_name = "win32" | ||||
|             self.exe_name %= ".exe" | ||||
|         if self.platform.endswith(("linux", "linux2")): | ||||
|             self.platform_name = "linux64" | ||||
|             self.exe_name %= "" | ||||
|         if self.platform.endswith("darwin"): | ||||
|             if self.is_old_chromedriver: | ||||
|                 self.platform_name = "mac64" | ||||
|             else: | ||||
|                 self.platform_name = "mac-x64" | ||||
|             self.exe_name %= "" | ||||
| 
 | ||||
|     def auto(self, executable_path=None, force=False, version_main=None, _=None): | ||||
|         """ | ||||
| 
 | ||||
|         Args: | ||||
|             executable_path: | ||||
|             force: | ||||
|             version_main: | ||||
| 
 | ||||
|         Returns: | ||||
| 
 | ||||
|         """ | ||||
|         p = pathlib.Path(self.data_path) | ||||
|         if self.user_multi_procs: | ||||
|             with Lock(): | ||||
|                 files = list(p.rglob("*chromedriver*")) | ||||
|                 most_recent = max(files, key=lambda f: f.stat().st_mtime) | ||||
|                 files.remove(most_recent) | ||||
|                 list(map(lambda f: f.unlink(), files)) | ||||
|                 if self.is_binary_patched(most_recent): | ||||
|                     self.executable_path = str(most_recent) | ||||
|                     return True | ||||
| 
 | ||||
|         if executable_path: | ||||
|             self.executable_path = executable_path | ||||
|             self._custom_exe_path = True | ||||
| 
 | ||||
|         if self._custom_exe_path: | ||||
|             ispatched = self.is_binary_patched(self.executable_path) | ||||
|             if not ispatched: | ||||
|                 return self.patch_exe() | ||||
|             else: | ||||
|                 return | ||||
| 
 | ||||
|         if version_main: | ||||
|             self.version_main = version_main | ||||
|         if force is True: | ||||
|             self.force = force | ||||
| 
 | ||||
|         try: | ||||
|             os.unlink(self.executable_path) | ||||
|         except PermissionError: | ||||
|             if self.force: | ||||
|                 self.force_kill_instances(self.executable_path) | ||||
|                 return self.auto(force=not self.force) | ||||
|             try: | ||||
|                 if self.is_binary_patched(): | ||||
|                     # assumes already running AND patched | ||||
|                     return True | ||||
|             except PermissionError: | ||||
|                 pass | ||||
|             # return False | ||||
|         except FileNotFoundError: | ||||
|             pass | ||||
| 
 | ||||
|         release = self.fetch_release_number() | ||||
|         self.version_main = release.version[0] | ||||
|         self.version_full = release | ||||
|         self.unzip_package(self.fetch_package()) | ||||
|         return self.patch() | ||||
| 
 | ||||
|     def driver_binary_in_use(self, path: str = None) -> bool: | ||||
|         """ | ||||
|         naive test to check if a found chromedriver binary is | ||||
|         currently in use | ||||
| 
 | ||||
|         Args: | ||||
|             path: a string or PathLike object to the binary to check. | ||||
|                   if not specified, we check use this object's executable_path | ||||
|         """ | ||||
|         if not path: | ||||
|             path = self.executable_path | ||||
|         p = pathlib.Path(path) | ||||
| 
 | ||||
|         if not p.exists(): | ||||
|             raise OSError("file does not exist: %s" % p) | ||||
|         try: | ||||
|             with open(p, mode="a+b") as fs: | ||||
|                 exc = [] | ||||
|                 try: | ||||
| 
 | ||||
|                     fs.seek(0, 0) | ||||
|                 except PermissionError as e: | ||||
|                     exc.append(e)  # since some systems apprently allow seeking | ||||
|                     # we conduct another test | ||||
|                 try: | ||||
|                     fs.readline() | ||||
|                 except PermissionError as e: | ||||
|                     exc.append(e) | ||||
| 
 | ||||
|                 if exc: | ||||
| 
 | ||||
|                     return True | ||||
|                 return False | ||||
|             # ok safe to assume this is in use | ||||
|         except Exception as e: | ||||
|             # logger.exception("whoops ", e) | ||||
|             pass | ||||
| 
 | ||||
|     def cleanup_unused_files(self): | ||||
|         p = pathlib.Path(self.data_path) | ||||
|         items = list(p.glob("*undetected*")) | ||||
|         for item in items: | ||||
|             try: | ||||
|                 item.unlink() | ||||
|             except: | ||||
|                 pass | ||||
| 
 | ||||
|     def patch(self): | ||||
|         self.patch_exe() | ||||
|         return self.is_binary_patched() | ||||
| 
 | ||||
|     def fetch_release_number(self): | ||||
|         """ | ||||
|         Gets the latest major version available, or the latest major version of self.target_version if set explicitly. | ||||
|         :return: version string | ||||
|         :rtype: LooseVersion | ||||
|         """ | ||||
|         # Endpoint for old versions of Chromedriver (114 and below) | ||||
|         if self.is_old_chromedriver: | ||||
|             path = f"/latest_release_{self.version_main}" | ||||
|             path = path.upper() | ||||
|             logger.debug("getting release number from %s" % path) | ||||
|             return LooseVersion(urlopen(self.url_repo + path).read().decode()) | ||||
| 
 | ||||
|         # Endpoint for new versions of Chromedriver (115+) | ||||
|         if not self.version_main: | ||||
|             # Fetch the latest version | ||||
|             path = "/last-known-good-versions-with-downloads.json" | ||||
|             logger.debug("getting release number from %s" % path) | ||||
|             with urlopen(self.url_repo + path) as conn: | ||||
|                 response = conn.read().decode() | ||||
| 
 | ||||
|             last_versions = json.loads(response) | ||||
|             return LooseVersion(last_versions["channels"]["Stable"]["version"]) | ||||
| 
 | ||||
|         # Fetch the latest minor version of the major version provided | ||||
|         path = "/latest-versions-per-milestone-with-downloads.json" | ||||
|         logger.debug("getting release number from %s" % path) | ||||
|         with urlopen(self.url_repo + path) as conn: | ||||
|             response = conn.read().decode() | ||||
| 
 | ||||
|         major_versions = json.loads(response) | ||||
|         return LooseVersion(major_versions["milestones"][str(self.version_main)]["version"]) | ||||
| 
 | ||||
|     def parse_exe_version(self): | ||||
|         with io.open(self.executable_path, "rb") as f: | ||||
|             for line in iter(lambda: f.readline(), b""): | ||||
|                 match = re.search(rb"platform_handle\x00content\x00([0-9.]*)", line) | ||||
|                 if match: | ||||
|                     return LooseVersion(match[1].decode()) | ||||
| 
 | ||||
|     def fetch_package(self): | ||||
|         """ | ||||
|         Downloads ChromeDriver from source | ||||
| 
 | ||||
|         :return: path to downloaded file | ||||
|         """ | ||||
|         zip_name = f"chromedriver_{self.platform_name}.zip" | ||||
|         if self.is_old_chromedriver: | ||||
|             download_url = "%s/%s/%s" % (self.url_repo, self.version_full.vstring, zip_name) | ||||
|         else: | ||||
|             zip_name = zip_name.replace("_", "-", 1) | ||||
|             download_url = "https://storage.googleapis.com/chrome-for-testing-public/%s/%s/%s" | ||||
|             download_url %= (self.version_full.vstring, self.platform_name, zip_name) | ||||
| 
 | ||||
|         logger.debug("downloading from %s" % download_url) | ||||
|         return urlretrieve(download_url)[0] | ||||
| 
 | ||||
|     def unzip_package(self, fp): | ||||
|         """ | ||||
|         Does what it says | ||||
| 
 | ||||
|         :return: path to unpacked executable | ||||
|         """ | ||||
|         exe_path = self.exe_name | ||||
|         if not self.is_old_chromedriver: | ||||
|             # The new chromedriver unzips into its own folder | ||||
|             zip_name = f"chromedriver-{self.platform_name}" | ||||
|             exe_path = os.path.join(zip_name, self.exe_name) | ||||
| 
 | ||||
|         logger.debug("unzipping %s" % fp) | ||||
|         try: | ||||
|             os.unlink(self.zip_path) | ||||
|         except (FileNotFoundError, OSError): | ||||
|             pass | ||||
| 
 | ||||
|         os.makedirs(self.zip_path, mode=0o755, exist_ok=True) | ||||
|         with zipfile.ZipFile(fp, mode="r") as zf: | ||||
|             zf.extractall(self.zip_path) | ||||
|         os.rename(os.path.join(self.zip_path, exe_path), self.executable_path) | ||||
|         os.remove(fp) | ||||
|         shutil.rmtree(self.zip_path) | ||||
|         os.chmod(self.executable_path, 0o755) | ||||
|         return self.executable_path | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def force_kill_instances(exe_name): | ||||
|         """ | ||||
|         kills running instances. | ||||
|         :param: executable name to kill, may be a path as well | ||||
| 
 | ||||
|         :return: True on success else False | ||||
|         """ | ||||
|         exe_name = os.path.basename(exe_name) | ||||
|         if IS_POSIX: | ||||
|             r = os.system("kill -f -9 $(pidof %s)" % exe_name) | ||||
|         else: | ||||
|             r = os.system("taskkill /f /im %s" % exe_name) | ||||
|         return not r | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def gen_random_cdc(): | ||||
|         cdc = random.choices(string.ascii_letters, k=27) | ||||
|         return "".join(cdc).encode() | ||||
| 
 | ||||
|     def is_binary_patched(self, executable_path=None): | ||||
|         executable_path = executable_path or self.executable_path | ||||
|         try: | ||||
|             with io.open(executable_path, "rb") as fh: | ||||
|                 return fh.read().find(b"undetected chromedriver") != -1 | ||||
|         except FileNotFoundError: | ||||
|             return False | ||||
| 
 | ||||
|     def patch_exe(self): | ||||
|         start = time.perf_counter() | ||||
|         logger.info("patching driver executable %s" % self.executable_path) | ||||
|         with io.open(self.executable_path, "r+b") as fh: | ||||
|             content = fh.read() | ||||
|             # match_injected_codeblock = re.search(rb"{window.*;}", content) | ||||
|             match_injected_codeblock = re.search(rb"\{window\.cdc.*?;\}", content) | ||||
|             if match_injected_codeblock: | ||||
|                 target_bytes = match_injected_codeblock[0] | ||||
|                 new_target_bytes = ( | ||||
|                     b'{console.log("undetected chromedriver 1337!")}'.ljust( | ||||
|                         len(target_bytes), b" " | ||||
|                     ) | ||||
|                 ) | ||||
|                 new_content = content.replace(target_bytes, new_target_bytes) | ||||
|                 if new_content == content: | ||||
|                     logger.warning( | ||||
|                         "something went wrong patching the driver binary. could not find injection code block" | ||||
|                     ) | ||||
|                 else: | ||||
|                     logger.debug( | ||||
|                         "found block:\n%s\nreplacing with:\n%s" | ||||
|                         % (target_bytes, new_target_bytes) | ||||
|                     ) | ||||
|                 fh.seek(0) | ||||
|                 fh.write(new_content) | ||||
|         logger.debug( | ||||
|             "patching took us {:.2f} seconds".format(time.perf_counter() - start) | ||||
|         ) | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         return "{0:s}({1:s})".format( | ||||
|             self.__class__.__name__, | ||||
|             self.executable_path, | ||||
|         ) | ||||
| 
 | ||||
|     def __del__(self): | ||||
|         if self._custom_exe_path: | ||||
|             # if the driver binary is specified by user | ||||
|             # we assume it is important enough to not delete it | ||||
|             return | ||||
|         else: | ||||
|             timeout = 3  # stop trying after this many seconds | ||||
|             t = time.monotonic() | ||||
|             now = lambda: time.monotonic() | ||||
|             while now() - t > timeout: | ||||
|                 # we don't want to wait until the end of time | ||||
|                 try: | ||||
|                     if self.user_multi_procs: | ||||
|                         break | ||||
|                     os.unlink(self.executable_path) | ||||
|                     logger.debug("successfully unlinked %s" % self.executable_path) | ||||
|                     break | ||||
|                 except (OSError, RuntimeError, PermissionError): | ||||
|                     time.sleep(0.01) | ||||
|                     continue | ||||
|                 except FileNotFoundError: | ||||
|                     break | ||||
							
								
								
									
										7
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| requests | ||||
| setuptools | ||||
| python-dotenv | ||||
| tls-client | ||||
| undetected-chromedriver | ||||
| selenium | ||||
| grpc_interceptor_headers | ||||
							
								
								
									
										14
									
								
								runner.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								runner.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| #!/bin/bash | ||||
| 
 | ||||
| TARGET_DIR=$(python -c "import undetected_chromedriver as uc; print(uc.__path__[0])" 2>/dev/null) | ||||
| 
 | ||||
| if [ -d "$TARGET_DIR" ]; then | ||||
|     echo "patcher.py в $TARGET_DIR" | ||||
|     cp patcher.py "$TARGET_DIR" | ||||
| else | ||||
|     echo "undetected_chromedriver не найден" | ||||
| fi | ||||
| 
 | ||||
| pip install -r requirements.txt | ||||
| 
 | ||||
| python initiator.py | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user