├── .gitignore ├── README.md ├── example.config.py ├── generateEmails.js ├── generate_emails_rq.py ├── parseEmails.js └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | config.py 2 | venv/ 3 | .idea/ 4 | logs/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚀 Icloud scripts 2 | [Вишня](https://t.me/micro_vyshnya) рассказал о том, как генерить iCloud почты для фермы. Вот [видео](https://www.youtube.com/watch?v=A0QcWOUivoI). Я подготовил несколько простеньких скриптов, которые сильно упрощают процесс. 3 | 4 | 🚀 Канал с разъебами: [@CryptoKiddiesClub](https://t.me/CryptoKiddiesClub) 5 | 💬 Чат для комьюнити: [@CryptoKiddiesChat](https://t.me/CryptoKiddiesChat) 6 | 🔥 Связь с создателем: [@CryptoBusher](https://t.me/CryptoBusher) 7 | 8 | ## ⚙️ Описание скриптов 9 | 1. generateEmails.js - генерирует почты. Проблема в том, что iCloud разрешает создавать максимум 5 почт, после чего лочит эту функцию на 1 час. Скрипт создает 5 почт, ждет 1.1 часа, продолжает. Никаких настроек, скрипт сам ищет пропущенные номера (названия почт) и регистрирует их, когда пропущенных нет - генерит дальше оп возрастанию. 10 | 2. parseEmails.js - парсит все почты (название:адрес) и копирует их в буфер. 11 | 3. generate_emails_rq.py - все то же самое, что и generateEmails.js, только на запросах, поддерживает много iCloud аккаунтов (привет, Давн). Геморно настраивается, но я сюда не выебываться пришел. 12 | 13 | ## 📚 Первый запуск (generateEmails.js, parseEmails.js) 14 | 1. Логинимся в свой iCloud аккаунт. 15 | 2. Открываем модалку "Hide My Email", в которой будет отображаться список почт. Через нее можно добавлять новые почты. 16 | 3. Открываем консоль (CTRL+SHIFT+J) 17 | 4. Пишем руками в консоль 'allow pasting' и жмем ENTER. 18 | 5. Вставляем нужный скрипт и жмем ENTER. 19 | 20 | ## 📚 Первый запуск (generate_emails_rq.py) 21 | 1. Устанавливаем [Python 3.12](https://www.python.org/downloads/). 22 | 2. Скачиваем проект, в терминале, находясь в папке проекта, вписываем команду ```pip install -r requirements.txt``` для установки всех зависимостей. 23 | 3. Логинимся в свой iCloud аккаунт. 24 | 4. Открываем модалку "Hide My Email", в которой будет отображаться список почт. Через нее можно добавлять новые почты. 25 | 5. Смотрим в DevTools запросы, уходящие на ссылку, содержащую "maildomainws" (можно создать одну почту руками, точно стриггерится), достаем оттуда (проще всего это сделать, скопировав правой кнопкой мыши запрос): 26 | 1. все cookies 27 | 2. clientBuildNumber 28 | 3. clientMasteringNumber 29 | 4. clientId 30 | 5. dsid 31 | 6. все headers 32 | 6. Переименовываем файл "example.config.py" на "config.py", вставляем данные от каждого аккаунта в config.py, по желанию можно добавить proxy. 33 | 7. В терминале, находясь в папке проекта, вписываем команду ```python3 generate_emails_rq.py``` и жмем ENTER. 34 | 35 | -------------------------------------------------------------------------------- /example.config.py: -------------------------------------------------------------------------------- 1 | config = [ 2 | { 3 | "name": "account1", 4 | "proxy": "", 5 | "clientBuildNumber": "your_data", 6 | "clientMasteringNumber": "your_data", 7 | "clientId": "your_data", 8 | "dsid": "your_data", 9 | "cookies": { 10 | 'X_APPLE_WEB_KB-': 'your_data', 11 | 'X-APPLE-WEBAUTH-USER': 'your_data', 12 | 'X-APPLE-WEBAUTH-HSA-TRUST': 'your_data', 13 | 'X-APPLE-DS-WEB-SESSION-TOKEN': 'your_data', 14 | 'X-APPLE-WEBAUTH-PCS-Events': 'your_data', 15 | 'X-APPLE-WEBAUTH-PCS-Documents': 'your_data', 16 | 'X-APPLE-WEBAUTH-PCS-Photos': 'your_data', 17 | 'X-APPLE-WEBAUTH-PCS-Cloudkit': 'your_data', 18 | 'X-APPLE-WEBAUTH-PCS-Safari': 'your_data', 19 | 'X-APPLE-WEBAUTH-PCS-Mail': 'your_data', 20 | 'X-APPLE-WEBAUTH-PCS-Notes': 'your_data', 21 | 'X-APPLE-WEBAUTH-PCS-News': 'your_data', 22 | 'X-APPLE-WEBAUTH-PCS-Sharing': 'your_data', 23 | 'X-APPLE-WEB-ID': 'your_data', 24 | 'X-APPLE-WEBAUTH-VALIDATE': 'your_data', 25 | 'X-APPLE-WEBAUTH-TOKEN': 'your_data', 26 | }, 27 | "headers": { 28 | 'Accept': '*/*', 29 | 'Accept-Language': 'en-US,en;q=0.9,ru;q=0.8', 30 | 'Connection': 'keep-alive', 31 | 'Content-Type': 'text/plain', 32 | 'Origin': 'https://www.icloud.com', 33 | 'Referer': 'https://www.icloud.com/', 34 | 'Sec-Fetch-Dest': 'empty', 35 | 'Sec-Fetch-Mode': 'cors', 36 | 'Sec-Fetch-Site': 'same-site', 37 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36', 38 | 'sec-ch-ua': '"Not A(Brand";v="8", "Chromium";v="132", "Google Chrome";v="132"', 39 | 'sec-ch-ua-mobile': '?0', 40 | 'sec-ch-ua-platform': '"Windows"', 41 | } 42 | }, 43 | { 44 | "name": "account2", 45 | "proxy": "", 46 | "clientBuildNumber": "your_data", 47 | "clientMasteringNumber": "your_data", 48 | "clientId": "your_data", 49 | "dsid": "your_data", 50 | "cookies": { 51 | 'X_APPLE_WEB_KB-': 'your_data', 52 | 'X-APPLE-WEBAUTH-USER': 'your_data', 53 | 'X-APPLE-WEBAUTH-HSA-TRUST': 'your_data', 54 | 'X-APPLE-DS-WEB-SESSION-TOKEN': 'your_data', 55 | 'X-APPLE-WEBAUTH-PCS-Events': 'your_data', 56 | 'X-APPLE-WEBAUTH-PCS-Documents': 'your_data', 57 | 'X-APPLE-WEBAUTH-PCS-Photos': 'your_data', 58 | 'X-APPLE-WEBAUTH-PCS-Cloudkit': 'your_data', 59 | 'X-APPLE-WEBAUTH-PCS-Safari': 'your_data', 60 | 'X-APPLE-WEBAUTH-PCS-Mail': 'your_data', 61 | 'X-APPLE-WEBAUTH-PCS-Notes': 'your_data', 62 | 'X-APPLE-WEBAUTH-PCS-News': 'your_data', 63 | 'X-APPLE-WEBAUTH-PCS-Sharing': 'your_data', 64 | 'X-APPLE-WEB-ID': 'your_data', 65 | 'X-APPLE-WEBAUTH-VALIDATE': 'your_data', 66 | 'X-APPLE-WEBAUTH-TOKEN': 'your_data', 67 | }, 68 | "headers": { 69 | 'Accept': '*/*', 70 | 'Accept-Language': 'en-US,en;q=0.9,ru;q=0.8', 71 | 'Connection': 'keep-alive', 72 | 'Content-Type': 'text/plain', 73 | 'Origin': 'https://www.icloud.com', 74 | 'Referer': 'https://www.icloud.com/', 75 | 'Sec-Fetch-Dest': 'empty', 76 | 'Sec-Fetch-Mode': 'cors', 77 | 'Sec-Fetch-Site': 'same-site', 78 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36', 79 | 'sec-ch-ua': '"Not A(Brand";v="8", "Chromium";v="132", "Google Chrome";v="132"', 80 | 'sec-ch-ua-mobile': '?0', 81 | 'sec-ch-ua-platform': '"Windows"', 82 | } 83 | } 84 | ] 85 | -------------------------------------------------------------------------------- /generateEmails.js: -------------------------------------------------------------------------------- 1 | const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); 2 | 3 | const getTimestamp = () => { 4 | return new Date(Date.now()).toLocaleString(); 5 | }; 6 | 7 | const fillInput = (input, value) => { 8 | if (input) { 9 | input.setAttribute('value', value); 10 | input.dispatchEvent(new Event('input', { bubbles: true })); 11 | } else { 12 | throw new Error('fillInput: input element not found'); 13 | } 14 | }; 15 | 16 | const getEmailsData = () => { 17 | let emailsLines = document.querySelector('iframe')?.contentWindow?.document 18 | ?.querySelector(".CardList")?.querySelectorAll("li"); 19 | 20 | if (!emailsLines || emailsLines.length === 0) { 21 | emailsLines = document.querySelectorAll('.CardList > li'); 22 | } 23 | 24 | const emailsData = {}; 25 | const numericNames = new Set(); 26 | 27 | for (const data of emailsLines) { 28 | const emailName = data.querySelector("h2")?.innerText.trim(); 29 | const emailAddress = data.querySelector("span.searchable-card-subtitle")?.innerText.trim(); 30 | 31 | if (emailName && emailAddress) { 32 | emailsData[emailName] = emailAddress; 33 | 34 | if (/^\d+$/.test(emailName)) { 35 | numericNames.add(parseInt(emailName, 10)); 36 | } 37 | } 38 | } 39 | 40 | return { emailsData, numericNames }; 41 | }; 42 | 43 | const getNextName = () => { 44 | const { numericNames } = getEmailsData(); 45 | 46 | if (numericNames.size === 0) return 1; 47 | 48 | const sortedNumbers = [...numericNames].sort((a, b) => a - b); 49 | 50 | for (let i = 1; i <= sortedNumbers[sortedNumbers.length - 1]; i++) { 51 | if (!numericNames.has(i)) { 52 | return i; 53 | } 54 | } 55 | 56 | return sortedNumbers[sortedNumbers.length - 1] + 1; 57 | }; 58 | 59 | const createEmail = async (emailName) => { 60 | const iframeDoc = document.querySelector('iframe')?.contentWindow?.document || document; 61 | 62 | let addBtn = iframeDoc.querySelector('button[title="Add"]') || document.querySelector('.AddButton > button'); 63 | if (addBtn) { 64 | addBtn.click(); 65 | await sleep(1000); 66 | } else { 67 | throw new Error('add button not found'); 68 | } 69 | 70 | let labelInput = iframeDoc.querySelector("input"); 71 | if (labelInput) { 72 | fillInput(labelInput, emailName); 73 | await sleep(1000); 74 | } else { 75 | throw new Error('label input not found'); 76 | } 77 | 78 | let createNewEmailBtn = iframeDoc.querySelectorAll("button")[2]; 79 | if (createNewEmailBtn) { 80 | createNewEmailBtn.click(); 81 | await sleep(5000); 82 | } else { 83 | throw new Error('create new email button not found'); 84 | } 85 | 86 | let backBtn = iframeDoc.querySelectorAll("button")[1]; 87 | if (backBtn) { 88 | backBtn.click(); 89 | await sleep(5000); 90 | } else { 91 | throw new Error('back button not found'); 92 | } 93 | }; 94 | 95 | const start = async () => { 96 | while (true) { 97 | for (let i = 0; i < 5; i++) { 98 | try { 99 | const name = getNextName(); 100 | console.log(`${getTimestamp()}: ${name} - registering`); 101 | await createEmail(name); 102 | console.log(`${getTimestamp()}: ${name} - email registered`); 103 | } catch (e) { 104 | console.error(`${getTimestamp()}: failed to register email, reason: ${e.message}`); 105 | } 106 | } 107 | 108 | console.log(`${getTimestamp()} - sleeping 1.1 hours...`); 109 | await sleep(1000 * 60 * 60 * 1.1); // 1.1 hours 110 | } 111 | }; 112 | 113 | await start(); 114 | -------------------------------------------------------------------------------- /generate_emails_rq.py: -------------------------------------------------------------------------------- 1 | import json 2 | from sys import stderr 3 | import asyncio 4 | from pathlib import Path 5 | from random import randint 6 | from dataclasses import dataclass 7 | 8 | from loguru import logger 9 | import httpx 10 | 11 | from config import config 12 | 13 | 14 | def set_logger(): 15 | logs_path = Path("logs") 16 | logs_path.mkdir(parents=True, exist_ok=True) 17 | 18 | logger.remove() 19 | log_format = "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {message}" 20 | logger.add(stderr, 21 | level="INFO", 22 | format=log_format) 23 | 24 | logger.add(logs_path / "app_{time:YYYY-MM-DD}.log", 25 | level="DEBUG", 26 | format=log_format, 27 | rotation="00:00", 28 | retention="7 days", 29 | compression="zip") 30 | 31 | 32 | class EmailsLimitReachedException(Exception): 33 | pass 34 | 35 | class BatchLimitReachedException(Exception): 36 | pass 37 | 38 | 39 | @dataclass 40 | class IcloudAccount: 41 | name: str 42 | proxy: str 43 | clientBuildNumber: str 44 | clientMasteringNumber: str 45 | clientId: str 46 | dsid: str 47 | cookies: dict[str, str] 48 | headers: dict[str, str] 49 | 50 | 51 | class IcloudEmailManager: 52 | BASE_URL = 'https://icloud-account.icloudapp.net/' 53 | MAX_EMAILS = 750 54 | BATCH_SIZE = 5 55 | BATCH_DELAY_HOURS = 1 56 | API_CALL_DELAYS_SEC = [5, 15] 57 | 58 | def __init__(self, account: IcloudAccount): 59 | self.account = account 60 | self.client = None 61 | 62 | async def create_client(self): 63 | self.client = httpx.AsyncClient( 64 | headers=self.account.headers, 65 | cookies=self.account.cookies, 66 | proxy=self.account.proxy if self.account.proxy else None, 67 | timeout=30 68 | ) 69 | 70 | async def _generate_new_email_domain(self) -> str: 71 | url = 'https://p57-maildomainws.icloud.com/v1/hme/generate' 72 | data = {"langCode": "en-us"} 73 | params = { 74 | 'clientBuildNumber': self.account.clientBuildNumber, 75 | 'clientMasteringNumber': self.account.clientMasteringNumber, 76 | 'clientId': self.account.clientId, 77 | 'dsid': self.account.dsid, 78 | } 79 | 80 | await asyncio.sleep(randint(*self.API_CALL_DELAYS_SEC)) 81 | response = await self.client.post(url, json=data, params=params) 82 | response.raise_for_status() 83 | 84 | json_response = response.json() 85 | if not json_response["success"]: 86 | raise Exception(f'Failed to get new domain, reason: {json_response}') 87 | 88 | return json_response["result"]["hme"] 89 | 90 | @staticmethod 91 | def find_available_email_labels(email_list: list[tuple[str, str]]) -> list[int]: 92 | available_labels = list(range(1, IcloudEmailManager.MAX_EMAILS + 1)) 93 | existing_numeric_labels = sorted(int(name) for name, email in email_list if name.isdigit()) 94 | 95 | available_labels = list(set(available_labels) - set(existing_numeric_labels)) 96 | available_labels.sort() 97 | 98 | if not available_labels: 99 | raise EmailsLimitReachedException() 100 | 101 | available_emails_count = IcloudEmailManager.MAX_EMAILS - len(email_list) 102 | if available_emails_count == 0: 103 | raise EmailsLimitReachedException() 104 | 105 | return available_labels[:available_emails_count] 106 | 107 | async def fetch_registered_emails(self) -> list[tuple[str, str]]: 108 | url = 'https://p57-maildomainws.icloud.com/v2/hme/list' 109 | params = { 110 | 'clientBuildNumber': self.account.clientBuildNumber, 111 | 'clientMasteringNumber': self.account.clientMasteringNumber, 112 | 'clientId': self.account.clientId, 113 | 'dsid': self.account.dsid, 114 | } 115 | 116 | await asyncio.sleep(randint(*self.API_CALL_DELAYS_SEC)) 117 | response = await self.client.get(url, params=params) 118 | response.raise_for_status() 119 | 120 | json_response = response.json() 121 | if not json_response["success"]: 122 | raise Exception(f'Failed to get emails list, reason: {json_response["error"]["errorMessage"]}') 123 | 124 | email_objects = json_response["result"]["hmeEmails"] 125 | return [(e["label"], e["hme"]) for e in email_objects] 126 | 127 | async def register_new_email(self, label: str) -> None: 128 | domain = await self._generate_new_email_domain() 129 | 130 | url = 'https://p57-maildomainws.icloud.com/v1/hme/reserve' 131 | data = { 132 | "hme": domain, 133 | "label": label, 134 | "note": "" 135 | } 136 | params = { 137 | 'clientBuildNumber': self.account.clientBuildNumber, 138 | 'clientMasteringNumber': self.account.clientMasteringNumber, 139 | 'clientId': self.account.clientId, 140 | 'dsid': self.account.dsid, 141 | } 142 | 143 | await asyncio.sleep(randint(*self.API_CALL_DELAYS_SEC)) 144 | response = await self.client.post(url, json=data, params=params) 145 | response.raise_for_status() 146 | 147 | json_response = response.json() 148 | if not json_response["success"]: 149 | if json_response["error"]["errorCode"] == "-41015": 150 | raise BatchLimitReachedException() 151 | else: 152 | raise Exception(f'Failed to register domain: {domain}, reason: {json_response["error"]["errorMessage"]}') 153 | 154 | logger.success(f'{self.account.name} - registered domain: {domain}') 155 | 156 | async def close(self): 157 | await self.client.aclose() 158 | 159 | 160 | async def register_accounts(icloud_manager: IcloudEmailManager): 161 | await icloud_manager.create_client() 162 | 163 | try: 164 | while True: 165 | try: 166 | registered_emails = await icloud_manager.fetch_registered_emails() 167 | 168 | if len(registered_emails) == icloud_manager.MAX_EMAILS: 169 | break 170 | 171 | available_labels = icloud_manager.find_available_email_labels(registered_emails) 172 | logger.info(f'{icloud_manager.account.name} - total available labels: {len(available_labels)}') 173 | 174 | registered_count = len(registered_emails) 175 | available_count = len(available_labels) 176 | 177 | for label in available_labels[:icloud_manager.BATCH_SIZE]: 178 | try: 179 | await icloud_manager.register_new_email(str(label)) 180 | registered_count += 1 181 | available_count -= 1 182 | continue 183 | except EmailsLimitReachedException: 184 | return 185 | except BatchLimitReachedException: 186 | logger.warning(f'{icloud_manager.account.name} - batch limit reached') 187 | break 188 | except Exception as e: 189 | logger.error(f'{icloud_manager.account.name} - {e}') 190 | continue 191 | 192 | logger.info(f'{icloud_manager.account.name} - registered {registered_count}, available {available_count}') 193 | 194 | except Exception as e: 195 | logger.error(f'{icloud_manager.account.name} - {e}') 196 | continue 197 | finally: 198 | delay_sec = (icloud_manager.BATCH_DELAY_HOURS * 60 * 60) + randint(10, 300) 199 | logger.info(f'{icloud_manager.account.name} - sleeping {delay_sec} seconds') 200 | await asyncio.sleep(delay_sec) 201 | finally: 202 | await icloud_manager.close() 203 | 204 | 205 | async def main(): 206 | tasks = [] 207 | for acc_config_raw in config: 208 | icloud_account = IcloudAccount(**acc_config_raw) 209 | manager = IcloudEmailManager(icloud_account) 210 | tasks.append(register_accounts(manager)) 211 | 212 | await asyncio.gather(*tasks) 213 | 214 | 215 | if __name__ == '__main__': 216 | set_logger() 217 | asyncio.run(main()) 218 | -------------------------------------------------------------------------------- /parseEmails.js: -------------------------------------------------------------------------------- 1 | const getEmailsData = () => { 2 | let emailsLines = document.querySelector('iframe')?.contentWindow?.document 3 | ?.querySelector(".CardList")?.querySelectorAll("li"); 4 | 5 | if (!emailsLines || emailsLines.length === 0) { 6 | emailsLines = document.querySelectorAll('.CardList > li'); 7 | } 8 | 9 | const emailsData = new Map(); 10 | 11 | for (const data of emailsLines) { 12 | const emailName = data.querySelector("h2")?.innerText.trim(); 13 | const emailAddress = data.querySelector("span.searchable-card-subtitle")?.innerText.trim(); 14 | 15 | if (emailName && emailAddress) { 16 | if (!emailsData.has(emailName)) { 17 | emailsData.set(emailName, []); 18 | } 19 | emailsData.get(emailName).push(emailAddress); 20 | } 21 | } 22 | 23 | return emailsData; 24 | }; 25 | 26 | const emailsData = getEmailsData(); 27 | 28 | const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }); 29 | 30 | if (emailsData.size > 0) { 31 | const emailsDataString = [...emailsData.entries()] 32 | .sort(([a], [b]) => collator.compare(a, b)) 33 | .map(([label, emails]) => emails.map(email => `${label}:${email}`).join("\n")) 34 | .join('\n'); 35 | 36 | copy(emailsDataString); 37 | console.log("Emails copied to clipboard!"); 38 | } else { 39 | console.warn("No email data found."); 40 | } 41 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | httpx==0.28.1 2 | loguru==0.6.0 3 | --------------------------------------------------------------------------------