├── icloud ├── __init__.py └── hidemyemail.py ├── docs ├── icon.ico ├── example.png ├── header.png ├── export-cookies.png └── cookie-settings.png ├── requirements.txt ├── .gitignore ├── cookie.example.txt ├── LICENSE ├── cli.py ├── README.md └── main.py /icloud/__init__.py: -------------------------------------------------------------------------------- 1 | from .hidemyemail import HideMyEmail 2 | -------------------------------------------------------------------------------- /docs/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtunazzz/hidemyemail-generator/HEAD/docs/icon.ico -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.9.4 2 | rich==13.7.1 3 | click==8.1.7 4 | certifi==2024.2.2 5 | -------------------------------------------------------------------------------- /docs/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtunazzz/hidemyemail-generator/HEAD/docs/example.png -------------------------------------------------------------------------------- /docs/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtunazzz/hidemyemail-generator/HEAD/docs/header.png -------------------------------------------------------------------------------- /docs/export-cookies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtunazzz/hidemyemail-generator/HEAD/docs/export-cookies.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/__pycache__ 2 | venv/ 3 | build/ 4 | dist/ 5 | emails.txt 6 | cookie.txt 7 | **/.DS_Store 8 | *.spec 9 | -------------------------------------------------------------------------------- /docs/cookie-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rtunazzz/hidemyemail-generator/HEAD/docs/cookie-settings.png -------------------------------------------------------------------------------- /cookie.example.txt: -------------------------------------------------------------------------------- 1 | X_APPLE_WEB_KB-V5590FJFX4ZYGBSJEZRZBTFB9UU="xxxxxx";X-APPLE-DS-WEB-SESSION-TOKEN="xxxxxx";X-APPLE-UNIQUE-CLIENT-ID="xxxxxx";X-APPLE-WEB-ID=28672BD9012631BA3CBAE022A1DBAEE2D0AFD358;X-APPLE-WEBAUTH-HSA-TRUST="xxxxxx";X-APPLE-WEBAUTH-LOGIN="xxxxxx";X-APPLE-WEBAUTH-PCS-Cloudkit="xxxxxx";X-APPLE-WEBAUTH-PCS-Documents="xxxxxx";X-APPLE-WEBAUTH-PCS-Mail="xxxxxx";X-APPLE-WEBAUTH-PCS-News="xxxxxx";X-APPLE-WEBAUTH-PCS-Notes="xxxxxx";X-APPLE-WEBAUTH-PCS-Photos="xxxxxx";X-APPLE-WEBAUTH-PCS-Safari="xxxxxx";X-APPLE-WEBAUTH-PCS-Sharing="xxxxxx";X-APPLE-WEBAUTH-TOKEN="xxxxxx";X-APPLE-WEBAUTH-USER="xxxxxx";X-APPLE-WEBAUTH-VALIDATE="xxxxxx"; 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 rtunazzz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import asyncio 4 | import click 5 | 6 | from main import generate 7 | from main import list 8 | 9 | 10 | @click.group() 11 | def cli(): 12 | pass 13 | 14 | 15 | @click.command() 16 | @click.option( 17 | "--count", default=5, help="How many emails to generate", type=int 18 | ) 19 | def generatecommand(count: int): 20 | "Generate emails" 21 | loop = asyncio.new_event_loop() 22 | try: 23 | loop.run_until_complete(generate(count)) 24 | except KeyboardInterrupt: 25 | pass 26 | 27 | 28 | @click.command() 29 | @click.option( 30 | "--active/--inactive", default=True, help="Filter Active / Inactive emails" 31 | ) 32 | @click.option("--search", default=None, help="Search emails") 33 | def listcommand(active, search): 34 | "List emails" 35 | loop = asyncio.new_event_loop() 36 | try: 37 | loop.run_until_complete(list(active, search)) 38 | except KeyboardInterrupt: 39 | pass 40 | 41 | 42 | cli.add_command(listcommand, name="list") 43 | cli.add_command(generatecommand, name="generate") 44 | 45 | if __name__ == "__main__": 46 | cli() 47 | 48 | if __name__ == "__main__": 49 | loop = asyncio.new_event_loop() 50 | try: 51 | loop.run_until_complete(generate(None)) 52 | except KeyboardInterrupt: 53 | pass 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | > Automated generation of Apple's iCloud emails via HideMyEmail. 4 | 5 | _You do need to have an active iCloud+ subscription to be able to generate iCloud emails..._ 6 | 7 |

8 | 9 | ## Usage 10 | 11 | You can get prebuild binaries for Windows & ARM Macs from the [releases page](https://github.com/rtunazzz/hidemyemail-generator/releases). Follow the guide steps 1 & 2 below if you'd like to run it from source, otherwise you can skip to the 3rd step - set your cookie and run. 12 | 13 | Apple allows you to create 5 * # of people in your iCloud familly emails every 30 mins or so. From my experience, they cap the amount of iCloud emails you can generate at ~700. 14 | 15 | ## Setup 16 | > Python 3.12+ is required! 17 | 18 | 1. Clone this repository 19 | 20 | ```bash 21 | git clone https://github.com/rtunazzz/hidemyemail-generator 22 | ``` 23 | 24 | 2. Install requirements 25 | 26 | ```bash 27 | pip install -r requirements.txt 28 | ``` 29 | 30 | 3. [Save your cookie string](https://github.com/rtunazzz/hidemyemail-generator#getting-icloud-cookie-string) 31 | 32 | > You only need to do this once 🙂 33 | 34 | 4. You can now run the gen with: 35 | 36 | 37 | **on Mac:** 38 | 39 | ```bash 40 | python3 main.py 41 | ``` 42 | 43 | **on Windows:** 44 | 45 | ```bash 46 | python main.py 47 | ``` 48 | 49 | ## Getting iCloud cookie string 50 | 51 | > There is more than one way how you can get the required cookie string but this one is _imo_ the simplest... 52 | 53 | 1. Download [EditThisCookie](https://chrome.google.com/webstore/detail/editthiscookie/fngmhnnpilhplaeedifhccceomclgfbg) Chrome extension 54 | 55 | 2. Go to [EditThisCookie settings page](chrome-extension://fngmhnnpilhplaeedifhccceomclgfbg/options_pages/user_preferences.html) and set the preferred export format to `Semicolon separated name=value pairs` 56 | 57 |

58 | 59 | 3. Navigate to [iCloud settings](https://www.icloud.com/settings/) in your browser and log in 60 | 61 | 4. Click on the EditThisCookie extension and export cookies 62 | 63 |

64 | 65 | 5. Paste the exported cookies into a file named `cookie.txt` 66 | 67 | # License 68 | 69 | Licensed under the MIT License - see the [LICENSE file](./LICENSE) for more details. 70 | 71 | Made by **[rtuna](https://twitter.com/rtunazzz)**. 72 | -------------------------------------------------------------------------------- /icloud/hidemyemail.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import aiohttp 3 | import ssl 4 | import certifi 5 | 6 | 7 | class HideMyEmail: 8 | base_url_v1 = "https://p68-maildomainws.icloud.com/v1/hme" 9 | base_url_v2 = "https://p68-maildomainws.icloud.com/v2/hme" 10 | params = { 11 | "clientBuildNumber": "2536Project32", 12 | "clientMasteringNumber": "2536B20", 13 | "clientId": "", 14 | "dsid": "", # Directory Services Identifier (DSID) is a method of identifying AppleID accounts 15 | } 16 | 17 | def __init__(self, label: str = "rtuna's gen", cookies: str = ""): 18 | """Initializes the HideMyEmail class. 19 | 20 | Args: 21 | label (str) Label that will be set for all emails generated, defaults to `rtuna's gen` 22 | cookies (str) Cookie string to be used with requests. Required for authorization. 23 | """ 24 | # Label that will be set for all emails generated, defaults to `rtuna's gen` 25 | self.label = label 26 | 27 | # Cookie string to be used with requests. Required for authorization. 28 | self.cookies = cookies 29 | 30 | async def __aenter__(self): 31 | connector = aiohttp.TCPConnector(ssl_context=ssl.create_default_context(cafile=certifi.where())) 32 | self.s = aiohttp.ClientSession( 33 | headers={ 34 | "Connection": "keep-alive", 35 | "Pragma": "no-cache", 36 | "Cache-Control": "no-cache", 37 | "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36", 38 | "Content-Type": "text/plain", 39 | "Accept": "*/*", 40 | "Sec-GPC": "1", 41 | "Origin": "https://www.icloud.com", 42 | "Sec-Fetch-Site": "same-site", 43 | "Sec-Fetch-Mode": "cors", 44 | "Sec-Fetch-Dest": "empty", 45 | "Referer": "https://www.icloud.com/", 46 | "Accept-Language": "en-US,en-GB;q=0.9,en;q=0.8,cs;q=0.7", 47 | "sec-ch-ua": '"Brave";v="141", "Not?A_Brand";v="8", "Chromium";v="141"', 48 | "sec-ch-ua-mobile": "?0", 49 | "sec-ch-ua-platform": '"macOS"', 50 | "Cookie": self.__cookies.strip(), 51 | }, 52 | timeout=aiohttp.ClientTimeout(total=10), 53 | connector=connector, 54 | ) 55 | 56 | return self 57 | 58 | async def __aexit__(self, exc_t, exc_v, exc_tb): 59 | await self.s.close() 60 | 61 | @property 62 | def cookies(self) -> str: 63 | return self.__cookies 64 | 65 | @cookies.setter 66 | def cookies(self, cookies: str): 67 | # remove new lines/whitespace for security reasons 68 | self.__cookies = cookies.strip() 69 | 70 | async def generate_email(self) -> dict: 71 | """Generates an email""" 72 | try: 73 | async with self.s.post( 74 | f"{self.base_url_v1}/generate", params=self.params, json={"langCode": "en-us"} 75 | ) as resp: 76 | res = await resp.json() 77 | return res 78 | except asyncio.TimeoutError: 79 | return {"error": 1, "reason": "Request timed out"} 80 | except Exception as e: 81 | return {"error": 1, "reason": str(e)} 82 | 83 | async def reserve_email(self, email: str) -> dict: 84 | """Reserves an email and registers it for forwarding""" 85 | try: 86 | payload = { 87 | "hme": email, 88 | "label": self.label, 89 | "note": "Generated by rtuna's iCloud email generator", 90 | } 91 | async with self.s.post( 92 | f"{self.base_url_v1}/reserve", params=self.params, json=payload 93 | ) as resp: 94 | res = await resp.json() 95 | return res 96 | except asyncio.TimeoutError: 97 | return {"error": 1, "reason": "Request timed out"} 98 | except Exception as e: 99 | return {"error": 1, "reason": str(e)} 100 | 101 | async def list_email(self) -> dict: 102 | """List all HME""" 103 | try: 104 | async with self.s.get(f"{self.base_url_v2}/list", params=self.params) as resp: 105 | res = await resp.json() 106 | return res 107 | except asyncio.TimeoutError: 108 | return {"error": 1, "reason": "Request timed out"} 109 | except Exception as e: 110 | return {"error": 1, "reason": str(e)} 111 | 112 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import datetime 3 | import os 4 | from typing import Union, List, Optional 5 | import re 6 | 7 | from rich.text import Text 8 | from rich.prompt import IntPrompt 9 | from rich.console import Console 10 | from rich.table import Table 11 | 12 | from icloud import HideMyEmail 13 | 14 | 15 | MAX_CONCURRENT_TASKS = 10 16 | 17 | 18 | class RichHideMyEmail(HideMyEmail): 19 | _cookie_file = "cookie.txt" 20 | 21 | def __init__(self): 22 | super().__init__() 23 | self.console = Console() 24 | self.table = Table() 25 | 26 | if os.path.exists(self._cookie_file): 27 | # load in a cookie string from file 28 | with open(self._cookie_file, "r") as f: 29 | self.cookies = [line for line in f if not line.startswith("//")][0] 30 | else: 31 | self.console.log( 32 | '[bold yellow][WARN][/] No "cookie.txt" file found! Generation might not work due to unauthorized access.' 33 | ) 34 | 35 | async def _generate_one(self) -> Union[str, None]: 36 | # First, generate an email 37 | gen_res = await self.generate_email() 38 | 39 | if not gen_res: 40 | return 41 | elif "success" not in gen_res or not gen_res["success"]: 42 | error = gen_res["error"] if "error" in gen_res else {} 43 | err_msg = "Unknown" 44 | if type(error) == int and "reason" in gen_res: 45 | err_msg = gen_res["reason"] 46 | elif type(error) == dict and "errorMessage" in error: 47 | err_msg = error["errorMessage"] 48 | self.console.log( 49 | f"[bold red][ERR][/] - Failed to generate email. Reason: {err_msg}" 50 | ) 51 | return 52 | 53 | email = gen_res["result"]["hme"] 54 | self.console.log(f'[50%] "{email}" - Successfully generated') 55 | 56 | # Then, reserve it 57 | reserve_res = await self.reserve_email(email) 58 | 59 | if not reserve_res: 60 | return 61 | elif "success" not in reserve_res or not reserve_res["success"]: 62 | error = reserve_res["error"] if "error" in reserve_res else {} 63 | err_msg = "Unknown" 64 | if type(error) == int and "reason" in reserve_res: 65 | err_msg = reserve_res["reason"] 66 | elif type(error) == dict and "errorMessage" in error: 67 | err_msg = error["errorMessage"] 68 | self.console.log( 69 | f'[bold red][ERR][/] "{email}" - Failed to reserve email. Reason: {err_msg}' 70 | ) 71 | return 72 | 73 | self.console.log(f'[100%] "{email}" - Successfully reserved') 74 | return email 75 | 76 | async def _generate(self, num: int): 77 | tasks = [] 78 | for _ in range(num): 79 | task = asyncio.ensure_future(self._generate_one()) 80 | tasks.append(task) 81 | 82 | return filter(lambda e: e is not None, await asyncio.gather(*tasks)) 83 | 84 | async def generate(self, count: Optional[int]) -> List[str]: 85 | try: 86 | emails = [] 87 | self.console.rule() 88 | if count is None: 89 | s = IntPrompt.ask( 90 | Text.assemble(("How many iCloud emails you want to generate?")), 91 | console=self.console, 92 | ) 93 | 94 | count = int(s) 95 | self.console.log(f"Generating {count} email(s)...") 96 | self.console.rule() 97 | 98 | with self.console.status(f"[bold green]Generating iCloud email(s)..."): 99 | while count > 0: 100 | batch = await self._generate( 101 | count if count < MAX_CONCURRENT_TASKS else MAX_CONCURRENT_TASKS 102 | ) 103 | count -= MAX_CONCURRENT_TASKS 104 | emails += batch 105 | 106 | if len(emails) > 0: 107 | with open("emails.txt", "a+") as f: 108 | f.write(os.linesep.join(emails) + os.linesep) 109 | 110 | self.console.rule() 111 | self.console.log( 112 | f':star: Emails have been saved into the "emails.txt" file' 113 | ) 114 | 115 | self.console.log( 116 | f"[bold green]All done![/] Successfully generated [bold green]{len(emails)}[/] email(s)" 117 | ) 118 | 119 | return emails 120 | except KeyboardInterrupt: 121 | return [] 122 | 123 | async def list(self, active: bool, search: str) -> None: 124 | gen_res = await self.list_email() 125 | if not gen_res: 126 | return 127 | 128 | if "success" not in gen_res or not gen_res["success"]: 129 | error = gen_res["error"] if "error" in gen_res else {} 130 | err_msg = "Unknown" 131 | if type(error) == int and "reason" in gen_res: 132 | err_msg = gen_res["reason"] 133 | elif type(error) == dict and "errorMessage" in error: 134 | err_msg = error["errorMessage"] 135 | self.console.log( 136 | f"[bold red][ERR][/] - Failed to generate email. Reason: {err_msg}" 137 | ) 138 | return 139 | 140 | self.table.add_column("Label") 141 | self.table.add_column("Hide my email") 142 | self.table.add_column("Created Date Time") 143 | self.table.add_column("IsActive") 144 | 145 | for row in gen_res["result"]["hmeEmails"]: 146 | if row["isActive"] == active: 147 | if search is not None and re.search(search, row["label"]): 148 | self.table.add_row( 149 | row["label"], 150 | row["hme"], 151 | str( 152 | datetime.datetime.fromtimestamp( 153 | row["createTimestamp"] / 1000 154 | ) 155 | ), 156 | str(row["isActive"]), 157 | ) 158 | else: 159 | self.table.add_row( 160 | row["label"], 161 | row["hme"], 162 | str( 163 | datetime.datetime.fromtimestamp( 164 | row["createTimestamp"] / 1000 165 | ) 166 | ), 167 | str(row["isActive"]), 168 | ) 169 | 170 | self.console.print(self.table) 171 | 172 | 173 | async def generate(count: Optional[int]) -> None: 174 | async with RichHideMyEmail() as hme: 175 | await hme.generate(count) 176 | 177 | 178 | async def list(active: bool, search: str) -> None: 179 | async with RichHideMyEmail() as hme: 180 | await hme.list(active, search) 181 | 182 | 183 | if __name__ == "__main__": 184 | loop = asyncio.new_event_loop() 185 | try: 186 | loop.run_until_complete(generate(None)) 187 | except KeyboardInterrupt: 188 | pass 189 | --------------------------------------------------------------------------------