forked from mirrors/cursor-free-vip
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47e4a752a3 | ||
|
|
bd254cb43c | ||
|
|
b5d50ac15a | ||
|
|
dd190fac8e | ||
|
|
7971b6449e | ||
|
|
e58acce44e | ||
|
|
dc8cbca8ce | ||
|
|
2385cc2b3f | ||
|
|
52348d565d | ||
|
|
93f4f05ac4 | ||
|
|
869f1e5225 | ||
|
|
f6c958ccbb | ||
|
|
8fd4235fba |
44
CHANGELOG.md
44
CHANGELOG.md
@@ -1,5 +1,49 @@
|
||||
# Change Log
|
||||
|
||||
## v1.7.14
|
||||
1. Added a Russian locale to program, fixed a typo in readme.md. Also translated other files |
|
||||
為程式新增了俄語語系,修正了 readme.md 的拼寫錯誤,並翻譯了其他文件。
|
||||
|
||||
2. Changing the directory from ~/.config/Cursor to ~/.config/cursor |
|
||||
將目錄從 `~/.config/Cursor` 更改為 `~/.config/cursor`。
|
||||
|
||||
3. Changing the filename from machineId to machineid |
|
||||
將檔案名稱從 `machineId` 更改為 `machineid`。
|
||||
|
||||
4. Updated all related paths in: |
|
||||
更新了以下檔案中的相關路徑:
|
||||
- `reset_machine_manual.py`
|
||||
- `config.py`
|
||||
|
||||
5. Added Linux path note in README.md |
|
||||
在 `README.md` 中新增了 Linux 路徑的說明。
|
||||
|
||||
6. These changes align with Linux filesystem conventions and fix issues with Chrome/Chromium integration |
|
||||
這些變更符合 Linux 文件系統的規範,並修復了與 Chrome/Chromium 整合的問題。
|
||||
|
||||
**#337**
|
||||
|
||||
7. This PR adds retry logic to handle the 'Can't verify the user is human' error during registration |
|
||||
此 PR 新增了重試機制,以處理註冊時的「無法驗證用戶是否為人類」錯誤。
|
||||
|
||||
8. Added max 5 retries for form submission when human verification fails |
|
||||
當人類驗證失敗時,最多允許 5 次表單提交重試。
|
||||
|
||||
9. Added random delays between retries (2-4 seconds) |
|
||||
在重試之間隨機延遲 2-4 秒。
|
||||
|
||||
10. Enhanced browser fingerprint randomization to better bypass detection |
|
||||
增強了瀏覽器指紋的隨機性,以更好地繞過檢測。
|
||||
|
||||
11. Added new translation strings for retry status messages |
|
||||
新增了重試狀態訊息的翻譯字串。
|
||||
|
||||
12. Improved error handling and user feedback |
|
||||
改進了錯誤處理和用戶回饋機制。
|
||||
|
||||
13. The changes ensure a more robust registration process by automatically retrying when human verification fails, while maintaining human-like behavior through randomized delays and improved browser fingerprinting |
|
||||
這些變更確保了更穩定的註冊流程,透過自動重試機制處理人類驗證失敗的情況,同時透過隨機延遲與增強的瀏覽器指紋技術,維持類似人類的行為模式。
|
||||
|
||||
## v1.7.13
|
||||
1. Added _delete_current_account method to handle account deletion via API | 新增 _delete_current_account 方法,透過 API 處理帳號刪除
|
||||
|
||||
|
||||
@@ -121,6 +121,7 @@ storage_path = /Users/username/Library/Application Support/Cursor/User/globalSto
|
||||
sqlite_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/state.vscdb
|
||||
# Machine ID Path | 機器ID路徑
|
||||
machine_id_path = /Users/username/Library/Application Support/Cursor/machineId
|
||||
# For Linux users: ~/.config/cursor/machineid
|
||||
|
||||
[Timing]
|
||||
# Min Random Time | 最小隨機時間
|
||||
@@ -168,7 +169,7 @@ max_timeout = 160
|
||||
| 如果遇到權限問題,請確保: | 此腳本以管理員身份運行 |
|
||||
|:--------------------------------------------------:|:------------------------------------------------:|
|
||||
| If you encounter permission issues, please ensure: | This script is run with administrator privileges |
|
||||
|
||||
| Error 'User is not authorized' | This means your account was banned for using temporary (disposal) mail. Ensure using a non-temporary mail service |
|
||||
## 🤩 Contribution | 貢獻
|
||||
|
||||
歡迎提交 Issue 和 Pull Request!
|
||||
|
||||
@@ -64,9 +64,9 @@ def setup_config(translator=None):
|
||||
actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
|
||||
|
||||
default_config['LinuxPaths'] = {
|
||||
'storage_path': os.path.abspath(os.path.join(actual_home, ".config/Cursor/User/globalStorage/storage.json")),
|
||||
'sqlite_path': os.path.abspath(os.path.join(actual_home, ".config/Cursor/User/globalStorage/state.vscdb")),
|
||||
'machine_id_path': os.path.expanduser("~/.config/Cursor/machineId"),
|
||||
'storage_path': os.path.abspath(os.path.join(actual_home, ".config/cursor/User/globalStorage/storage.json")),
|
||||
'sqlite_path': os.path.abspath(os.path.join(actual_home, ".config/cursor/User/globalStorage/state.vscdb")),
|
||||
'machine_id_path': os.path.expanduser("~/.config/cursor/machineid"),
|
||||
'cursor_path': get_linux_cursor_path(),
|
||||
'updater_path': os.path.expanduser("~/.config/cursor-updater")
|
||||
}
|
||||
|
||||
@@ -19,14 +19,15 @@
|
||||
"totally_reset": "Cursor Vollständig Zurücksetzen"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
"vi": "Tiếng Việt",
|
||||
"nl": "Nederlands",
|
||||
"en": "Englisch",
|
||||
"zh_cn": "Vereinfachtes Chinesisch",
|
||||
"zh_tw": "Traditionelles Chinesisch",
|
||||
"vi": "Vietnamesisch",
|
||||
"nl": "Niederländisch",
|
||||
"de": "Deutsch",
|
||||
"fr": "Français",
|
||||
"pt": "Brazilian Portuguese"
|
||||
"fr": "Französisch",
|
||||
"pt": "Portugiesisch",
|
||||
"ru": "Russisch"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Beginne Cursor zu Beenden",
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
"vi": "Vietnamese",
|
||||
"nl": "Dutch",
|
||||
"de": "German",
|
||||
"fr": "French"
|
||||
"fr": "French",
|
||||
"pt": "Portuguese",
|
||||
"ru": "Russian"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Start Quitting Cursor",
|
||||
@@ -170,7 +172,9 @@
|
||||
"password_submitted": "Password Submitted",
|
||||
"total_usage": "Total Usage: {usage}",
|
||||
"setting_on_password": "Setting Password",
|
||||
"getting_code": "Getting Verification Code, Will Try in 60s"
|
||||
"getting_code": "Getting Verification Code, Will Try in 60s",
|
||||
"human_verify_error": "Can't verify the user is human. Retrying...",
|
||||
"max_retries_reached": "Maximum retry attempts reached. Registration failed."
|
||||
},
|
||||
"auth": {
|
||||
"title": "Cursor Auth Manager",
|
||||
|
||||
@@ -19,14 +19,15 @@
|
||||
"totally_reset": "Réinitialisation Complète de Cursor"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
"vi": "Tiếng Việt",
|
||||
"nl": "Nederlands",
|
||||
"de": "Deutsch",
|
||||
"en": "Anglais",
|
||||
"zh_cn": "Chinois simplifié",
|
||||
"zh_tw": "Chinois traditionnel",
|
||||
"vi": "Vietnamien",
|
||||
"nl": "Néerlandais",
|
||||
"de": "Allemand",
|
||||
"fr": "Français",
|
||||
"pt": "Brazilian Portuguese"
|
||||
"pt": "Portugais",
|
||||
"ru": "Russe"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Début de la Fermeture de Cursor",
|
||||
|
||||
@@ -19,14 +19,15 @@
|
||||
"totally_reset": "Cursor volledig resetten"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
"vi": "Tiếng Việt",
|
||||
"en": "Engels",
|
||||
"zh_cn": "Vereenvoudigd Chinees",
|
||||
"zh_tw": "Traditioneel Chinees",
|
||||
"vi": "Vietnamees",
|
||||
"nl": "Nederlands",
|
||||
"de": "Deutsch",
|
||||
"fr": "Français",
|
||||
"pt": "Brazilian Portuguese"
|
||||
"de": "Duits",
|
||||
"fr": "Frans",
|
||||
"pt": "Portugees",
|
||||
"ru": "Russisch"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Start met afsluiten van Cursor",
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
"nl": "Holandês",
|
||||
"de": "Alemão",
|
||||
"fr": "Francês",
|
||||
"pt": "Português do Brasil"
|
||||
"pt": "Português do Brasil",
|
||||
"ru": "Russo"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Iniciando fechamento do Cursor",
|
||||
|
||||
383
locales/ru.json
Normal file
383
locales/ru.json
Normal file
@@ -0,0 +1,383 @@
|
||||
{
|
||||
"menu": {
|
||||
"title": "Доступные опции",
|
||||
"exit": "Выйти из программы",
|
||||
"reset": "Сбросить ID машины",
|
||||
"register": "Зарегистрировать новый аккаунт Cursor",
|
||||
"register_google": "Зарегистрироваться через Google",
|
||||
"register_github": "Зарегистрироваться через GitHub",
|
||||
"register_manual": "Зарегистрировать Cursor используя свою почту",
|
||||
"quit": "Закрыть приложение Cursor",
|
||||
"select_language": "Выбрать язык",
|
||||
"input_choice": "Пожалуйста, введите ваш выбор ({choices})",
|
||||
"invalid_choice": "Неверный выбор. Пожалуйста, введите число из {choices}",
|
||||
"program_terminated": "Программа была завершена пользователем",
|
||||
"error_occurred": "Произошла ошибка: {error}. Пожалуйста, попробуйте снова",
|
||||
"press_enter": "Нажмите Enter для выхода",
|
||||
"disable_auto_update": "Отключить автоматическое обновление Cursor",
|
||||
"lifetime_access_enabled": "ВКЛЮЧЕН ПОЖИЗНЕННЫЙ ДОСТУП",
|
||||
"totally_reset": "Полностью сбросить Cursor"
|
||||
},
|
||||
"languages": {
|
||||
"en": "Английский",
|
||||
"zh_cn": "Упрощенный китайский",
|
||||
"zh_tw": "Традиционный китайский",
|
||||
"vi": "Вьетнамский",
|
||||
"nl": "Нидерландский",
|
||||
"de": "Немецкий",
|
||||
"fr": "Французский",
|
||||
"pt": "Бразильский португальский",
|
||||
"ru": "Русский"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Начало закрытия Cursor",
|
||||
"no_process": "Нет запущенных процессов Cursor",
|
||||
"terminating": "Завершение процесса {pid}",
|
||||
"waiting": "Ожидание завершения процесса",
|
||||
"success": "Все процессы Cursor закрыты",
|
||||
"timeout": "Таймаут процесса: {pids}",
|
||||
"error": "Произошла ошибка: {error}"
|
||||
},
|
||||
"reset": {
|
||||
"title": "Инструмент сброса ID машины Cursor",
|
||||
"checking": "Проверка конфигурационного файла",
|
||||
"not_found": "Конфигурационный файл не найден",
|
||||
"no_permission": "Невозможно прочитать или записать конфигурационный файл, проверьте права доступа",
|
||||
"reading": "Чтение текущей конфигурации",
|
||||
"creating_backup": "Создание резервной копии конфигурации",
|
||||
"backup_exists": "Резервный файл уже существует, пропускаем шаг резервного копирования",
|
||||
"generating": "Генерация нового ID машины",
|
||||
"saving_json": "Сохранение новой конфигурации в JSON",
|
||||
"success": "ID машины успешно сброшен",
|
||||
"new_id": "Новый ID машины",
|
||||
"permission_error": "Ошибка прав доступа: {error}",
|
||||
"run_as_admin": "Пожалуйста, запустите программу от имени администратора",
|
||||
"process_error": "Ошибка процесса сброса: {error}",
|
||||
"updating_sqlite": "Обновление базы данных SQLite",
|
||||
"updating_pair": "Обновление пары ключ-значение",
|
||||
"sqlite_success": "База данных SQLite успешно обновлена",
|
||||
"sqlite_error": "Ошибка обновления базы данных SQLite: {error}",
|
||||
"press_enter": "Нажмите Enter для выхода",
|
||||
"unsupported_os": "Неподдерживаемая ОС: {os}",
|
||||
"linux_path_not_found": "Путь Linux не найден",
|
||||
"updating_system_ids": "Обновление системных ID",
|
||||
"system_ids_updated": "Системные ID успешно обновлены",
|
||||
"system_ids_update_failed": "Ошибка обновления системных ID: {error}",
|
||||
"windows_guid_updated": "Windows GUID успешно обновлен",
|
||||
"windows_permission_denied": "Отказано в доступе Windows",
|
||||
"windows_guid_update_failed": "Ошибка обновления Windows GUID",
|
||||
"macos_uuid_updated": "macOS UUID успешно обновлен",
|
||||
"plutil_command_failed": "Ошибка команды plutil",
|
||||
"start_patching": "Начало патчинга getMachineId",
|
||||
"macos_uuid_update_failed": "Ошибка обновления macOS UUID",
|
||||
"current_version": "Текущая версия Cursor: {version}",
|
||||
"patch_completed": "Патчинг getMachineId завершен",
|
||||
"patch_failed": "Ошибка патчинга getMachineId: {error}",
|
||||
"version_check_passed": "Проверка версии Cursor пройдена",
|
||||
"file_modified": "Файл изменен",
|
||||
"version_less_than_0_45": "Версия Cursor < 0.45.0, пропускаем патчинг getMachineId",
|
||||
"detecting_version": "Определение версии Cursor",
|
||||
"patching_getmachineid": "Патчинг getMachineId",
|
||||
"version_greater_than_0_45": "Версия Cursor >= 0.45.0, патчинг getMachineId",
|
||||
"permission_denied": "Отказано в доступе: {error}",
|
||||
"backup_created": "Резервная копия создана",
|
||||
"update_success": "Обновление успешно",
|
||||
"update_failed": "Ошибка обновления: {error}",
|
||||
"windows_machine_guid_updated": "Windows Machine GUID успешно обновлен",
|
||||
"reading_package_json": "Чтение package.json {path}",
|
||||
"invalid_json_object": "Неверный JSON объект",
|
||||
"no_version_field": "Поле версии не найдено в package.json",
|
||||
"version_field_empty": "Поле версии пусто",
|
||||
"invalid_version_format": "Неверный формат версии: {version}",
|
||||
"found_version": "Найдена версия: {version}",
|
||||
"version_parse_error": "Ошибка разбора версии: {error}",
|
||||
"package_not_found": "Package.json не найден: {path}",
|
||||
"check_version_failed": "Ошибка проверки версии: {error}",
|
||||
"stack_trace": "Трассировка стека",
|
||||
"version_too_low": "Версия Cursor слишком низкая: {version} < 0.45.0"
|
||||
},
|
||||
"register": {
|
||||
"title": "Инструмент регистрации Cursor",
|
||||
"start": "Запуск процесса регистрации...",
|
||||
"handling_turnstile": "Обработка проверки безопасности...",
|
||||
"retry_verification": "Повторная попытка проверки...",
|
||||
"detect_turnstile": "Проверка безопасности...",
|
||||
"verification_success": "Проверка безопасности успешна",
|
||||
"starting_browser": "Открытие браузера...",
|
||||
"form_success": "Форма успешно отправлена",
|
||||
"browser_started": "Браузер успешно открыт",
|
||||
"waiting_for_second_verification": "Ожидание проверки email...",
|
||||
"waiting_for_verification_code": "Ожидание кода подтверждения...",
|
||||
"password_success": "Пароль успешно установлен",
|
||||
"password_error": "Не удалось установить пароль: {error}. Пожалуйста, попробуйте снова",
|
||||
"waiting_for_page_load": "Загрузка страницы...",
|
||||
"first_verification_passed": "Первичная проверка успешна",
|
||||
"mailbox": "Успешный доступ к почтовому ящику",
|
||||
"register_start": "Начать регистрацию",
|
||||
"form_submitted": "Форма отправлена, начало проверки...",
|
||||
"filling_form": "Заполнение формы",
|
||||
"visiting_url": "Переход по URL",
|
||||
"basic_info": "Основная информация отправлена",
|
||||
"handle_turnstile": "Обработка Turnstile",
|
||||
"no_turnstile": "Turnstile не обнаружен",
|
||||
"turnstile_passed": "Turnstile пройден",
|
||||
"verification_start": "Начало получения кода подтверждения",
|
||||
"verification_timeout": "Таймаут получения кода подтверждения",
|
||||
"verification_not_found": "Код подтверждения не найден",
|
||||
"try_get_code": "Попытка | {attempt} Получение кода подтверждения | Осталось времени: {time}с",
|
||||
"get_account": "Получение информации об аккаунте",
|
||||
"get_token": "Получение токена сессии Cursor",
|
||||
"token_success": "Токен успешно получен",
|
||||
"token_attempt": "Попытка | {attempt} раз получить токен | Повторная попытка через {time}с",
|
||||
"token_max_attempts": "Достигнуто максимальное количество попыток ({max}) | Не удалось получить токен",
|
||||
"token_failed": "Ошибка получения токена: {error}",
|
||||
"account_error": "Ошибка получения информации об аккаунте: {error}",
|
||||
"press_enter": "Нажмите Enter для выхода",
|
||||
"browser_start": "Запуск браузера",
|
||||
"open_mailbox": "Открытие страницы почтового ящика",
|
||||
"email_error": "Не удалось получить email адрес",
|
||||
"setup_error": "Ошибка настройки email: {error}",
|
||||
"start_getting_verification_code": "Начало получения кода подтверждения, попытка через 60с",
|
||||
"get_verification_code_timeout": "Таймаут получения кода подтверждения",
|
||||
"get_verification_code_success": "Код подтверждения успешно получен",
|
||||
"try_get_verification_code": "Попытка | {attempt} Получение кода подтверждения | Осталось времени: {remaining_time}с",
|
||||
"verification_code_filled": "Код подтверждения введен",
|
||||
"login_success_and_jump_to_settings_page": "Успешный вход и переход на страницу настроек",
|
||||
"detect_login_page": "Обнаружена страница входа, начало входа...",
|
||||
"cursor_registration_completed": "Регистрация Cursor завершена!",
|
||||
"set_password": "Установка пароля",
|
||||
"basic_info_submitted": "Основная информация отправлена",
|
||||
"cursor_auth_info_updated": "Информация авторизации Cursor обновлена",
|
||||
"cursor_auth_info_update_failed": "Ошибка обновления информации авторизации Cursor",
|
||||
"reset_machine_id": "Сброс ID машины",
|
||||
"account_info_saved": "Информация об аккаунте сохранена",
|
||||
"save_account_info_failed": "Ошибка сохранения информации об аккаунте",
|
||||
"get_email_address": "Получение email адреса",
|
||||
"update_cursor_auth_info": "Обновление информации авторизации Cursor",
|
||||
"register_process_error": "Ошибка процесса регистрации: {error}",
|
||||
"setting_password": "Установка пароля",
|
||||
"manual_code_input": "Ручной ввод кода",
|
||||
"manual_email_input": "Ручной ввод email",
|
||||
"password": "Пароль",
|
||||
"first_name": "Имя",
|
||||
"last_name": "Фамилия",
|
||||
"exit_signal": "Сигнал выхода",
|
||||
"email_address": "Email адрес",
|
||||
"config_created": "Конфигурация создана",
|
||||
"verification_failed": "Проверка не пройдена",
|
||||
"verification_error": "Ошибка проверки: {error}",
|
||||
"config_option_added": "Опция конфигурации добавлена: {option}",
|
||||
"config_updated": "Конфигурация обновлена",
|
||||
"password_submitted": "Пароль отправлен",
|
||||
"total_usage": "Общее использование: {usage}",
|
||||
"setting_on_password": "Установка пароля",
|
||||
"getting_code": "Получение кода подтверждения, попытка через 60с"
|
||||
},
|
||||
"auth": {
|
||||
"title": "Менеджер авторизации Cursor",
|
||||
"checking_auth": "Проверка файла авторизации",
|
||||
"auth_not_found": "Файл авторизации не найден",
|
||||
"auth_file_error": "Ошибка файла авторизации: {error}",
|
||||
"reading_auth": "Чтение файла авторизации",
|
||||
"updating_auth": "Обновление информации авторизации",
|
||||
"auth_updated": "Информация авторизации успешно обновлена",
|
||||
"auth_update_failed": "Ошибка обновления информации авторизации: {error}",
|
||||
"auth_file_created": "Файл авторизации создан",
|
||||
"auth_file_create_failed": "Ошибка создания файла авторизации: {error}",
|
||||
"press_enter": "Нажмите Enter для выхода",
|
||||
"reset_machine_id": "Сброс ID машины",
|
||||
"database_connection_closed": "Соединение с базой данных закрыто",
|
||||
"database_updated_successfully": "База данных успешно обновлена",
|
||||
"connected_to_database": "Подключено к базе данных",
|
||||
"updating_pair": "Обновление пары ключ-значение",
|
||||
"db_not_found": "Файл базы данных не найден по пути: {path}",
|
||||
"db_permission_error": "Невозможно получить доступ к файлу базы данных. Проверьте права доступа",
|
||||
"db_connection_error": "Ошибка подключения к базе данных: {error}"
|
||||
},
|
||||
"control": {
|
||||
"generate_email": "Генерация нового email",
|
||||
"blocked_domain": "Заблокированный домен",
|
||||
"select_domain": "Выбор случайного домена",
|
||||
"copy_email": "Копирование email адреса",
|
||||
"enter_mailbox": "Вход в почтовый ящик",
|
||||
"refresh_mailbox": "Обновление почтового ящика",
|
||||
"check_verification": "Проверка кода подтверждения",
|
||||
"verification_found": "Код подтверждения найден",
|
||||
"verification_not_found": "Код подтверждения не найден",
|
||||
"browser_error": "Ошибка управления браузером: {error}",
|
||||
"navigation_error": "Ошибка навигации: {error}",
|
||||
"email_copy_error": "Ошибка копирования email: {error}",
|
||||
"mailbox_error": "Ошибка почтового ящика: {error}",
|
||||
"token_saved_to_file": "Токен сохранен в cursor_tokens.txt",
|
||||
"navigate_to": "Переход на {url}",
|
||||
"generate_email_success": "Email успешно сгенерирован",
|
||||
"select_email_domain": "Выбор домена email",
|
||||
"select_email_domain_success": "Домен email успешно выбран",
|
||||
"get_email_name": "Получение имени email",
|
||||
"get_email_name_success": "Имя email успешно получено",
|
||||
"get_email_address": "Получение email адреса",
|
||||
"get_email_address_success": "Email адрес успешно получен",
|
||||
"enter_mailbox_success": "Успешный вход в почтовый ящик",
|
||||
"found_verification_code": "Найден код подтверждения",
|
||||
"get_cursor_session_token": "Получение токена сессии Cursor",
|
||||
"get_cursor_session_token_success": "Токен сессии Cursor успешно получен",
|
||||
"get_cursor_session_token_failed": "Ошибка получения токена сессии Cursor",
|
||||
"save_token_failed": "Ошибка сохранения токена",
|
||||
"database_updated_successfully": "База данных успешно обновлена",
|
||||
"database_connection_closed": "Соединение с базой данных закрыто",
|
||||
"no_valid_verification_code": "Нет действительного кода подтверждения"
|
||||
},
|
||||
"email": {
|
||||
"starting_browser": "Запуск браузера",
|
||||
"visiting_site": "Переход на сайты почтовых доменов",
|
||||
"create_success": "Email успешно создан",
|
||||
"create_failed": "Не удалось создать email",
|
||||
"create_error": "Ошибка создания email: {error}",
|
||||
"refreshing": "Обновление email",
|
||||
"refresh_success": "Email успешно обновлен",
|
||||
"refresh_error": "Ошибка обновления email: {error}",
|
||||
"refresh_button_not_found": "Кнопка обновления не найдена",
|
||||
"verification_found": "Проверка найдена",
|
||||
"verification_not_found": "Проверка не найдена",
|
||||
"verification_error": "Ошибка проверки: {error}",
|
||||
"verification_code_found": "Код подтверждения найден",
|
||||
"verification_code_not_found": "Код подтверждения не найден",
|
||||
"verification_code_error": "Ошибка кода подтверждения: {error}",
|
||||
"address": "Email адрес",
|
||||
"all_domains_blocked": "Все домены заблокированы, переключение сервиса",
|
||||
"no_available_domains_after_filtering": "Нет доступных доменов после фильтрации",
|
||||
"switching_service": "Переключение на сервис {service}",
|
||||
"domains_list_error": "Не удалось получить список доменов: {error}",
|
||||
"failed_to_get_available_domains": "Не удалось получить доступные домены",
|
||||
"domains_excluded": "Исключенные домены: {domains}",
|
||||
"failed_to_create_account": "Не удалось создать аккаунт",
|
||||
"account_creation_error": "Ошибка создания аккаунта: {error}",
|
||||
"blocked_domains": "Заблокированные домены: {domains}",
|
||||
"blocked_domains_loaded": "Загружены заблокированные домены: {count}",
|
||||
"blocked_domains_loaded_error": "Ошибка загрузки заблокированных доменов: {error}",
|
||||
"blocked_domains_loaded_success": "Заблокированные домены успешно загружены",
|
||||
"blocked_domains_loaded_timeout": "Таймаут загрузки заблокированных доменов: {timeout}с",
|
||||
"blocked_domains_loaded_timeout_error": "Ошибка таймаута загрузки заблокированных доменов: {error}",
|
||||
"available_domains_loaded": "Загружены доступные домены: {count}",
|
||||
"domains_filtered": "Отфильтрованы домены: {count}",
|
||||
"trying_to_create_email": "Попытка создания email: {email}",
|
||||
"domain_blocked": "Домен заблокирован: {domain}"
|
||||
},
|
||||
"update": {
|
||||
"title": "Отключение автоматического обновления Cursor",
|
||||
"disable_success": "Автоматическое обновление успешно отключено",
|
||||
"disable_failed": "Ошибка отключения автоматического обновления: {error}",
|
||||
"press_enter": "Нажмите Enter для выхода",
|
||||
"start_disable": "Начало отключения автоматического обновления",
|
||||
"killing_processes": "Завершение процессов",
|
||||
"processes_killed": "Процессы завершены",
|
||||
"removing_directory": "Удаление директории",
|
||||
"directory_removed": "Директория удалена",
|
||||
"creating_block_file": "Создание файла блокировки",
|
||||
"block_file_created": "Файл блокировки создан"
|
||||
},
|
||||
"updater": {
|
||||
"checking": "Проверка обновлений...",
|
||||
"new_version_available": "Доступна новая версия! (Текущая: {current}, Последняя: {latest})",
|
||||
"updating": "Обновление до последней версии. Программа перезапустится автоматически.",
|
||||
"up_to_date": "У вас установлена последняя версия.",
|
||||
"check_failed": "Не удалось проверить обновления: {error}",
|
||||
"continue_anyway": "Продолжение работы с текущей версией...",
|
||||
"update_confirm": "Хотите обновить до последней версии? (Y/n)",
|
||||
"update_skipped": "Обновление пропущено.",
|
||||
"invalid_choice": "Неверный выбор. Пожалуйста, введите 'Y' или 'n'.",
|
||||
"development_version": "Версия разработки {current} > {latest}",
|
||||
"changelog_title": "Журнал изменений"
|
||||
},
|
||||
"totally_reset": {
|
||||
"title": "Полный сброс Cursor",
|
||||
"checking_config": "Проверка конфигурационного файла",
|
||||
"config_not_found": "Конфигурационный файл не найден",
|
||||
"no_permission": "Невозможно прочитать или записать конфигурационный файл, проверьте права доступа",
|
||||
"reading_config": "Чтение текущей конфигурации",
|
||||
"creating_backup": "Создание резервной копии конфигурации",
|
||||
"backup_exists": "Резервный файл уже существует, пропускаем шаг резервного копирования",
|
||||
"generating_new_machine_id": "Генерация нового ID машины",
|
||||
"saving_new_config": "Сохранение новой конфигурации в JSON",
|
||||
"success": "Cursor успешно сброшен",
|
||||
"error": "Ошибка сброса Cursor: {error}",
|
||||
"press_enter": "Нажмите Enter для выхода",
|
||||
"reset_machine_id": "Сброс ID машины",
|
||||
"database_connection_closed": "Соединение с базой данных закрыто",
|
||||
"database_updated_successfully": "База данных успешно обновлена",
|
||||
"connected_to_database": "Подключено к базе данных",
|
||||
"updating_pair": "Обновление пары ключ-значение",
|
||||
"db_not_found": "Файл базы данных не найден по пути: {path}",
|
||||
"db_permission_error": "Невозможно получить доступ к файлу базы данных. Проверьте права доступа",
|
||||
"db_connection_error": "Ошибка подключения к базе данных: {error}",
|
||||
"feature_title": "ФУНКЦИИ",
|
||||
"feature_1": "Полное удаление настроек и конфигураций Cursor AI",
|
||||
"feature_2": "Очистка всех кэшированных данных, включая историю AI и промпты",
|
||||
"feature_3": "Сброс ID машины для обхода обнаружения пробной версии",
|
||||
"feature_4": "Создание новых случайных идентификаторов машины",
|
||||
"feature_5": "Удаление пользовательских расширений и настроек",
|
||||
"feature_6": "Сброс информации о пробной версии и данных активации",
|
||||
"feature_7": "Глубокий поиск скрытых файлов лицензии и пробной версии",
|
||||
"feature_8": "Безопасное сохранение файлов и приложений, не относящихся к Cursor",
|
||||
"feature_9": "Совместимость с Windows, macOS и Linux",
|
||||
"disclaimer_title": "ПРЕДУПРЕЖДЕНИЕ",
|
||||
"disclaimer_1": "Этот инструмент навсегда удалит все настройки Cursor AI,",
|
||||
"disclaimer_2": "конфигурации и кэшированные данные. Это действие нельзя отменить.",
|
||||
"disclaimer_3": "Ваши файлы кода НЕ будут затронуты, и инструмент разработан",
|
||||
"disclaimer_4": "только для файлов редактора Cursor AI и механизмов обнаружения пробной версии.",
|
||||
"disclaimer_5": "Другие приложения на вашей системе не будут затронуты.",
|
||||
"disclaimer_6": "После запуска этого инструмента вам нужно будет настроить Cursor AI заново.",
|
||||
"disclaimer_7": "Используйте на свой страх и риск",
|
||||
"confirm_title": "Вы уверены, что хотите продолжить?",
|
||||
"confirm_1": "Это действие удалит все настройки Cursor AI,",
|
||||
"confirm_2": "конфигурации и кэшированные данные. Это действие нельзя отменить.",
|
||||
"confirm_3": "Ваши файлы кода НЕ будут затронуты, и инструмент разработан",
|
||||
"confirm_4": "только для файлов редактора Cursor AI и механизмов обнаружения пробной версии.",
|
||||
"confirm_5": "Другие приложения на вашей системе не будут затронуты.",
|
||||
"confirm_6": "После запуска этого инструмента вам нужно будет настроить Cursor AI заново.",
|
||||
"confirm_7": "Используйте на свой страх и риск",
|
||||
"invalid_choice": "Пожалуйста, введите 'Y' или 'n'",
|
||||
"skipped_for_safety": "Пропущено для безопасности (не относится к Cursor): {path}",
|
||||
"deleted": "Удалено: {path}",
|
||||
"error_deleting": "Ошибка удаления {path}: {error}",
|
||||
"not_found": "Файл не найден: {path}",
|
||||
"resetting_machine_id": "Сброс идентификаторов машины для обхода обнаружения пробной версии...",
|
||||
"created_machine_id": "Создан новый ID машины: {path}",
|
||||
"error_creating_machine_id": "Ошибка создания файла ID машины {path}: {error}",
|
||||
"error_searching": "Ошибка поиска файлов в {path}: {error}",
|
||||
"created_extended_trial_info": "Создана новая расширенная информация о пробной версии: {path}",
|
||||
"error_creating_trial_info": "Ошибка создания файла информации о пробной версии {path}: {error}",
|
||||
"resetting_cursor_ai_editor": "Сброс редактора Cursor AI... Пожалуйста, подождите.",
|
||||
"reset_cancelled": "Сброс отменен. Выход без внесения изменений.",
|
||||
"windows_machine_id_modification_skipped": "Изменение ID машины Windows пропущено: {error}",
|
||||
"linux_machine_id_modification_skipped": "Изменение machine-id Linux пропущено: {error}",
|
||||
"note_complete_machine_id_reset_may_require_running_as_administrator": "Примечание: полный сброс ID машины может потребовать запуска от имени администратора",
|
||||
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Примечание: полный сброс системного machine-id может потребовать прав sudo",
|
||||
"windows_registry_instructions": "📝 ПРИМЕЧАНИЕ: Для полного сброса в Windows может потребоваться очистка записей реестра.",
|
||||
"windows_registry_instructions_2": " Запустите 'regedit' и найдите ключи, содержащие 'Cursor' или 'CursorAI' в HKEY_CURRENT_USER\\Software\\ и удалите их.\n",
|
||||
"reset_log_1": "Cursor AI полностью сброшен и обнаружение пробной версии обойдено!",
|
||||
"reset_log_2": "Пожалуйста, перезагрузите систему для применения изменений.",
|
||||
"reset_log_3": "Вам нужно будет переустановить Cursor AI, и теперь у вас должен быть новый пробный период.",
|
||||
"reset_log_4": "Для лучших результатов рекомендуется также:",
|
||||
"reset_log_5": "Использовать другой email адрес при регистрации нового пробного периода",
|
||||
"reset_log_6": "Если возможно, использовать VPN для изменения IP адреса",
|
||||
"reset_log_7": "Очистить куки и кэш браузера перед посещением сайта Cursor AI",
|
||||
"reset_log_8": "Если проблемы сохраняются, попробуйте установить Cursor AI в другое место",
|
||||
"reset_log_9": "Если вы столкнулись с проблемами, перейдите на Github Issue Tracker и создайте issue на https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"unexpected_error": "Произошла непредвиденная ошибка: {error}",
|
||||
"report_issue": "Пожалуйста, сообщите об этой проблеме на Github Issue Tracker на https://github.com/yeongpin/cursor-free-vip/issues",
|
||||
"keyboard_interrupt": "Процесс прерван пользователем. Выход...",
|
||||
"return_to_main_menu": "Возврат в главное меню...",
|
||||
"process_interrupted": "Процесс прерван. Выход...",
|
||||
"press_enter_to_return_to_main_menu": "Нажмите Enter для возврата в главное меню...",
|
||||
"removing_known": "Удаление известных файлов лицензии/пробной версии",
|
||||
"performing_deep_scan": "Выполнение глубокого поиска дополнительных файлов лицензии/пробной версии",
|
||||
"found_additional_potential_license_trial_files": "Найдено {count} дополнительных потенциальных файлов лицензии/пробной версии",
|
||||
"checking_for_electron_localstorage_files": "Проверка файлов localStorage Electron",
|
||||
"no_additional_license_trial_files_found_in_deep_scan": "Дополнительные файлы лицензии/пробной версии не найдены при глубоком поиске",
|
||||
"removing_electron_localstorage_files": "Удаление файлов localStorage Electron",
|
||||
"electron_localstorage_files_removed": "Файлы localStorage Electron удалены",
|
||||
"electron_localstorage_files_removal_error": "Ошибка удаления файлов localStorage Electron: {error}",
|
||||
"removing_electron_localstorage_files_completed": "Удаление файлов localStorage Electron завершено"
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,15 @@
|
||||
"totally_reset": "Đặt lại hoàn toàn Cursor"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
"en": "Tiếng Anh",
|
||||
"zh_cn": "Tiếng Trung Giản Thể",
|
||||
"zh_tw": "Tiếng Trung Phồn Thể",
|
||||
"vi": "Tiếng Việt",
|
||||
"nl": "Dutch",
|
||||
"de": "Germa",
|
||||
"fr": "French",
|
||||
"pt": "Brazilian Portuguese"
|
||||
"nl": "Tiếng Hà Lan",
|
||||
"de": "Tiếng Đức",
|
||||
"fr": "Tiếng Pháp",
|
||||
"pt": "Tiếng Bồ Đào Nha",
|
||||
"ru": "Tiếng Nga"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "Bắt Đầu Thoát Cursor",
|
||||
|
||||
@@ -19,14 +19,15 @@
|
||||
"totally_reset": "完全重置 Cursor"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"en": "英语",
|
||||
"zh_cn": "简体中文",
|
||||
"zh_tw": "繁體中文",
|
||||
"vi": "Vietnamese",
|
||||
"nl": "Dutch",
|
||||
"de": "German",
|
||||
"fr": "French",
|
||||
"pt": "Brazilian Portuguese"
|
||||
"zh_tw": "繁体中文",
|
||||
"vi": "越南语",
|
||||
"nl": "荷兰语",
|
||||
"de": "德语",
|
||||
"fr": "法语",
|
||||
"pt": "葡萄牙语",
|
||||
"ru": "俄语"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "开始退出 Cursor",
|
||||
|
||||
@@ -17,14 +17,15 @@
|
||||
"totally_reset": "完全重置 Cursor"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
"zh_cn": "简体中文",
|
||||
"en": "英文",
|
||||
"zh_cn": "簡體中文",
|
||||
"zh_tw": "繁體中文",
|
||||
"vi": "Vietnamese",
|
||||
"nl": "Dutch",
|
||||
"de": "German",
|
||||
"fr": "French",
|
||||
"pt": "Brazilian Portuguese"
|
||||
"vi": "越南文",
|
||||
"nl": "荷蘭文",
|
||||
"de": "德文",
|
||||
"fr": "法文",
|
||||
"pt": "葡萄牙文",
|
||||
"ru": "俄文"
|
||||
},
|
||||
"quit_cursor": {
|
||||
"start": "開始退出 Cursor",
|
||||
|
||||
2
logo.py
2
logo.py
@@ -30,7 +30,7 @@ CURSOR_LOGO = f"""
|
||||
|
||||
Github: https://github.com/yeongpin/cursor-free-vip
|
||||
{Fore.RED}
|
||||
Press 7 to change language | 按下 7 键切换语言 | Pressione 7 para alterar o idioma
|
||||
Press 7 to change language | 按下 7 键切换语言
|
||||
{Style.RESET_ALL}
|
||||
"""
|
||||
|
||||
|
||||
7
main.py
7
main.py
@@ -108,6 +108,7 @@ class Translator:
|
||||
0x0404: 'zh_tw', # Traditional Chinese
|
||||
0x0804: 'zh_cn', # Simplified Chinese
|
||||
0x0422: 'vi', # Vietnamese
|
||||
0x0419: 'ru', # Russian
|
||||
}
|
||||
|
||||
return language_map.get(layout_id, 'en')
|
||||
@@ -141,9 +142,9 @@ class Translator:
|
||||
return 'fr'
|
||||
elif system_locale.startswith('pt'):
|
||||
return 'pt'
|
||||
elif system_locale.startswith('ru'):
|
||||
return 'ru'
|
||||
|
||||
|
||||
|
||||
# Try to get language from LANG environment variable as fallback
|
||||
env_lang = os.getenv('LANG', '').lower()
|
||||
if 'tw' in env_lang or 'hk' in env_lang:
|
||||
@@ -160,6 +161,8 @@ class Translator:
|
||||
return 'fr'
|
||||
elif 'pt' in env_lang:
|
||||
return 'pt'
|
||||
elif 'ru' in env_lang:
|
||||
return 'ru'
|
||||
|
||||
return 'en'
|
||||
except:
|
||||
|
||||
316
new_signup.py
316
new_signup.py
@@ -39,7 +39,7 @@ def signal_handler(signum, frame):
|
||||
os._exit(0)
|
||||
|
||||
def simulate_human_input(page, url, config, translator=None):
|
||||
"""Visit URL"""
|
||||
"""Visit URL with human-like behavior"""
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🚀 {translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
|
||||
|
||||
@@ -47,53 +47,194 @@ def simulate_human_input(page, url, config, translator=None):
|
||||
page.get('about:blank')
|
||||
time.sleep(get_random_wait_time(config, 'page_load_wait'))
|
||||
|
||||
# Add random mouse movements before visiting target page
|
||||
try:
|
||||
page.run_js("""
|
||||
function simulateMouseMovement() {
|
||||
const points = [];
|
||||
const numPoints = Math.floor(Math.random() * 10) + 5;
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
points.push({
|
||||
x: Math.random() * window.innerWidth,
|
||||
y: Math.random() * window.innerHeight
|
||||
});
|
||||
}
|
||||
points.forEach((point, i) => {
|
||||
setTimeout(() => {
|
||||
const event = new MouseEvent('mousemove', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
clientX: point.x,
|
||||
clientY: point.y
|
||||
});
|
||||
document.dispatchEvent(event);
|
||||
}, i * (Math.random() * 200 + 50));
|
||||
});
|
||||
}
|
||||
simulateMouseMovement();
|
||||
""")
|
||||
except:
|
||||
pass # Ignore if JavaScript execution fails
|
||||
|
||||
# Visit target page
|
||||
page.get(url)
|
||||
time.sleep(get_random_wait_time(config, 'page_load_wait'))
|
||||
|
||||
# Add some random scrolling
|
||||
try:
|
||||
page.run_js("""
|
||||
function simulateHumanScroll() {
|
||||
const maxScroll = Math.max(
|
||||
document.body.scrollHeight,
|
||||
document.documentElement.scrollHeight,
|
||||
document.body.offsetHeight,
|
||||
document.documentElement.offsetHeight,
|
||||
document.body.clientHeight,
|
||||
document.documentElement.clientHeight
|
||||
);
|
||||
const scrollPoints = [];
|
||||
const numPoints = Math.floor(Math.random() * 3) + 2;
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
scrollPoints.push(Math.floor(Math.random() * maxScroll));
|
||||
}
|
||||
scrollPoints.sort((a, b) => a - b);
|
||||
scrollPoints.forEach((point, i) => {
|
||||
setTimeout(() => {
|
||||
window.scrollTo({
|
||||
top: point,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}, i * (Math.random() * 1000 + 500));
|
||||
});
|
||||
}
|
||||
simulateHumanScroll();
|
||||
""")
|
||||
except:
|
||||
pass # Ignore if JavaScript execution fails
|
||||
|
||||
def simulate_human_typing(element, text, config):
|
||||
"""Simulate human-like typing with random delays between keystrokes"""
|
||||
for char in text:
|
||||
element.input(char)
|
||||
# Random delay between keystrokes (30-100ms)
|
||||
time.sleep(random.uniform(0.03, 0.1))
|
||||
time.sleep(get_random_wait_time(config, 'input_wait'))
|
||||
|
||||
def fill_signup_form(page, first_name, last_name, email, config, translator=None):
|
||||
"""Fill signup form"""
|
||||
try:
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}📧 {translator.get('register.filling_form')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("\n正在填写注册表单...")
|
||||
|
||||
# Fill first name
|
||||
first_name_input = page.ele("@name=first_name")
|
||||
if first_name_input:
|
||||
first_name_input.input(first_name)
|
||||
time.sleep(get_random_wait_time(config, 'input_wait'))
|
||||
|
||||
# Fill last name
|
||||
last_name_input = page.ele("@name=last_name")
|
||||
if last_name_input:
|
||||
last_name_input.input(last_name)
|
||||
time.sleep(get_random_wait_time(config, 'input_wait'))
|
||||
|
||||
# Fill email
|
||||
email_input = page.ele("@name=email")
|
||||
if email_input:
|
||||
email_input.input(email)
|
||||
time.sleep(get_random_wait_time(config, 'input_wait'))
|
||||
|
||||
# Click submit button
|
||||
submit_button = page.ele("@type=submit")
|
||||
if submit_button:
|
||||
submit_button.click()
|
||||
time.sleep(get_random_wait_time(config, 'submit_wait'))
|
||||
"""Fill signup form with human-like behavior"""
|
||||
max_retries = 5
|
||||
retry_count = 0
|
||||
|
||||
while retry_count < max_retries:
|
||||
try:
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}📧 {translator.get('register.filling_form')} (Attempt {retry_count + 1}/{max_retries}){Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\n正在填写注册表单... (Attempt {retry_count + 1}/{max_retries})")
|
||||
|
||||
# Add random initial delay
|
||||
time.sleep(random.uniform(0.5, 1.5))
|
||||
|
||||
# Fill first name with human-like typing
|
||||
first_name_input = page.ele("@name=first_name")
|
||||
if first_name_input:
|
||||
simulate_human_typing(first_name_input, first_name, config)
|
||||
# Add random pause between fields
|
||||
time.sleep(random.uniform(0.3, 0.8))
|
||||
|
||||
# Fill last name with human-like typing
|
||||
last_name_input = page.ele("@name=last_name")
|
||||
if last_name_input:
|
||||
simulate_human_typing(last_name_input, last_name, config)
|
||||
# Add random pause between fields
|
||||
time.sleep(random.uniform(0.3, 0.8))
|
||||
|
||||
# Fill email with human-like typing
|
||||
email_input = page.ele("@name=email")
|
||||
if email_input:
|
||||
simulate_human_typing(email_input, email, config)
|
||||
# Add random pause before submitting
|
||||
time.sleep(random.uniform(0.5, 1.2))
|
||||
|
||||
# Move mouse to submit button with human-like movement
|
||||
submit_button = page.ele("@type=submit")
|
||||
if submit_button:
|
||||
try:
|
||||
# Simulate mouse movement to button
|
||||
page.run_js("""
|
||||
function moveToButton(button) {
|
||||
const rect = button.getBoundingClientRect();
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
|
||||
// Create a curved path to the button
|
||||
const startX = Math.random() * window.innerWidth;
|
||||
const startY = Math.random() * window.innerHeight;
|
||||
const controlX = (startX + centerX) / 2 + (Math.random() - 0.5) * 100;
|
||||
const controlY = (startY + centerY) / 2 + (Math.random() - 0.5) * 100;
|
||||
|
||||
const steps = 20;
|
||||
for (let i = 0; i <= steps; i++) {
|
||||
const t = i / steps;
|
||||
const x = Math.pow(1-t, 2) * startX + 2 * (1-t) * t * controlX + Math.pow(t, 2) * centerX;
|
||||
const y = Math.pow(1-t, 2) * startY + 2 * (1-t) * t * controlY + Math.pow(t, 2) * centerY;
|
||||
|
||||
setTimeout(() => {
|
||||
const event = new MouseEvent('mousemove', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
clientX: x,
|
||||
clientY: y
|
||||
});
|
||||
document.dispatchEvent(event);
|
||||
}, i * (Math.random() * 20 + 10));
|
||||
}
|
||||
}
|
||||
moveToButton(document.querySelector('button[type="submit"]'));
|
||||
""")
|
||||
time.sleep(random.uniform(0.3, 0.6))
|
||||
except:
|
||||
pass # Ignore if JavaScript execution fails
|
||||
|
||||
submit_button.click()
|
||||
time.sleep(get_random_wait_time(config, 'submit_wait'))
|
||||
|
||||
# Check for human verification error
|
||||
error_message = page.ele("text:Can't verify the user is human")
|
||||
if error_message:
|
||||
if translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {translator.get('register.human_verify_error')} (Attempt {retry_count + 1}/{max_retries}){Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"Can't verify the user is human. Retrying... (Attempt {retry_count + 1}/{max_retries})")
|
||||
retry_count += 1
|
||||
# Add longer random delay between retries
|
||||
time.sleep(random.uniform(2, 4))
|
||||
continue
|
||||
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.form_success')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("Form filled successfully")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.form_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"Error filling form: {e}")
|
||||
retry_count += 1
|
||||
if retry_count < max_retries:
|
||||
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
|
||||
continue
|
||||
return False
|
||||
|
||||
if retry_count >= max_retries:
|
||||
if translator:
|
||||
print(f"{Fore.GREEN}✅ {translator.get('register.form_success')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}❌ {translator.get('register.max_retries_reached')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print("Form filled successfully")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}❌ {translator.get('register.form_error', error=str(e))}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"Error filling form: {e}")
|
||||
print("Maximum retry attempts reached. Registration failed.")
|
||||
return False
|
||||
|
||||
def get_default_chrome_path():
|
||||
@@ -163,7 +304,7 @@ def get_random_wait_time(config, timing_type='page_load_wait'):
|
||||
return random.uniform(0.1, 0.8) # Return default value when error
|
||||
|
||||
def setup_driver(translator=None):
|
||||
"""Setup browser driver"""
|
||||
"""Setup browser driver with randomized fingerprint"""
|
||||
try:
|
||||
# Get config
|
||||
config = get_config(translator)
|
||||
@@ -185,10 +326,42 @@ def setup_driver(translator=None):
|
||||
# Use incognito mode
|
||||
co.set_argument("--incognito")
|
||||
|
||||
# Set random port
|
||||
co.set_argument("--no-sandbox")
|
||||
# Randomize browser fingerprint
|
||||
# Random screen resolution
|
||||
resolutions = [
|
||||
"1920,1080", "1366,768", "1536,864", "1440,900",
|
||||
"1280,720", "1600,900", "1024,768", "1680,1050"
|
||||
]
|
||||
window_size = random.choice(resolutions)
|
||||
co.set_argument(f"--window-size={window_size}")
|
||||
|
||||
# Random user agent
|
||||
user_agents = [
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
]
|
||||
co.set_argument(f"--user-agent={random.choice(user_agents)}")
|
||||
|
||||
# Random color depth
|
||||
color_depths = ["24", "30", "48"]
|
||||
co.set_argument(f"--color-depth={random.choice(color_depths)}")
|
||||
|
||||
# Additional fingerprint randomization
|
||||
co.set_argument("--disable-blink-features=AutomationControlled") # Hide automation
|
||||
co.set_argument("--disable-features=IsolateOrigins,site-per-process")
|
||||
|
||||
# Random platform
|
||||
platforms = ["Win32", "Win64", "MacIntel", "Linux x86_64"]
|
||||
co.set_argument(f"--platform={random.choice(platforms)}")
|
||||
|
||||
# Random language
|
||||
languages = ["en-US", "en-GB", "fr-FR", "de-DE", "es-ES", "it-IT"]
|
||||
co.set_argument(f"--lang={random.choice(languages)}")
|
||||
|
||||
# Set random port
|
||||
co.set_argument("--no-sandbox")
|
||||
co.auto_port()
|
||||
|
||||
# Use headless mode (must be set to False, simulate human operation)
|
||||
@@ -212,6 +385,63 @@ def setup_driver(translator=None):
|
||||
print("Starting browser...")
|
||||
|
||||
page = ChromiumPage(co)
|
||||
|
||||
# Additional JavaScript-based fingerprint randomization
|
||||
try:
|
||||
page.run_js("""
|
||||
// Override navigator properties
|
||||
const originalNavigator = window.navigator;
|
||||
const navigatorProxy = new Proxy(originalNavigator, {
|
||||
get: function(target, key) {
|
||||
switch (key) {
|
||||
case 'webdriver':
|
||||
return undefined;
|
||||
case 'plugins':
|
||||
return [
|
||||
{ name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer' },
|
||||
{ name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai' },
|
||||
{ name: 'Native Client', filename: 'internal-nacl-plugin' }
|
||||
];
|
||||
case 'languages':
|
||||
return ['en-US', 'en'];
|
||||
default:
|
||||
return target[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Override permissions
|
||||
const originalPermissions = window.Permissions;
|
||||
window.Permissions = new Proxy(originalPermissions, {
|
||||
get: function(target, key) {
|
||||
if (key === 'prototype') {
|
||||
return {
|
||||
query: async () => ({ state: 'prompt' })
|
||||
};
|
||||
}
|
||||
return target[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Random canvas fingerprint
|
||||
const originalGetContext = HTMLCanvasElement.prototype.getContext;
|
||||
HTMLCanvasElement.prototype.getContext = function() {
|
||||
const context = originalGetContext.apply(this, arguments);
|
||||
if (context && arguments[0] === '2d') {
|
||||
const originalFillText = context.fillText;
|
||||
context.fillText = function() {
|
||||
const noise = Math.random() * 0.1;
|
||||
context.rotate(noise);
|
||||
originalFillText.apply(this, arguments);
|
||||
context.rotate(-noise);
|
||||
};
|
||||
}
|
||||
return context;
|
||||
};
|
||||
""")
|
||||
except:
|
||||
pass # Ignore if JavaScript execution fails
|
||||
|
||||
return config, page
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -96,7 +96,7 @@ def get_cursor_machine_id_path(translator=None) -> str:
|
||||
if not config.has_section('LinuxPaths'):
|
||||
config.add_section('LinuxPaths')
|
||||
config.set('LinuxPaths', 'machine_id_path',
|
||||
os.path.expanduser("~/.config/Cursor/machineId"))
|
||||
os.path.expanduser("~/.config/cursor/machineid"))
|
||||
return config.get('LinuxPaths', 'machine_id_path')
|
||||
|
||||
elif sys.platform == "darwin": # macOS
|
||||
@@ -451,11 +451,11 @@ class MachineIDResetter:
|
||||
|
||||
config.set('LinuxPaths', 'storage_path', os.path.abspath(os.path.join(
|
||||
actual_home,
|
||||
".config/Cursor/User/globalStorage/storage.json"
|
||||
".config/cursor/User/globalStorage/storage.json"
|
||||
)))
|
||||
config.set('LinuxPaths', 'sqlite_path', os.path.abspath(os.path.join(
|
||||
actual_home,
|
||||
".config/Cursor/User/globalStorage/state.vscdb"
|
||||
".config/cursor/User/globalStorage/state.vscdb"
|
||||
)))
|
||||
|
||||
self.db_path = config.get('LinuxPaths', 'storage_path')
|
||||
|
||||
Reference in New Issue
Block a user