├── 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 |
--------------------------------------------------------------------------------