tempemail-sdk 1.2.0__tar.gz → 1.2.2.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/PKG-INFO +15 -12
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/README.md +14 -11
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/pyproject.toml +1 -1
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempemail_sdk.egg-info/PKG-INFO +15 -12
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempemail_sdk.egg-info/SOURCES.txt +6 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/client.py +44 -4
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/normalize.py +4 -2
- tempemail_sdk-1.2.2.dev0/tempmail_sdk/providers/moakt.py +225 -0
- tempemail_sdk-1.2.2.dev0/tempmail_sdk/providers/ta_easy.py +62 -0
- tempemail_sdk-1.2.2.dev0/tempmail_sdk/providers/tempmailg.py +197 -0
- tempemail_sdk-1.2.2.dev0/tempmail_sdk/providers/tenmail_wangtz.py +118 -0
- tempemail_sdk-1.2.2.dev0/tempmail_sdk/providers/tenminute_one.py +255 -0
- tempemail_sdk-1.2.2.dev0/tempmail_sdk/providers/tmpmails.py +158 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/types.py +6 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/setup.cfg +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempemail_sdk.egg-info/dependency_links.txt +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempemail_sdk.egg-info/requires.txt +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempemail_sdk.egg-info/top_level.txt +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/__init__.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/config.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/http.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/logger.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/__init__.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/anonbox.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/awamail.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/boomlify.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/chatgpt_org_uk.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/dropmail.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/emailnator.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/fake_legal.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/guerrillamail.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/linshi_email.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/linshi_token.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/linshiyou.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/mail_cx.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/mail_gw.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/mail_tm.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/maildrop.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/mffac.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/minmail.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/smail_pw.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/temp_mail_io.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/tempmail.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/tempmail_cn.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/tempmail_lol.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/temporary_email_org.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/providers/vip_215.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/retry.py +0 -0
- {tempemail_sdk-1.2.0 → tempemail_sdk-1.2.2.dev0}/tempmail_sdk/telemetry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tempemail-sdk
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2.dev0
|
|
4
4
|
Summary: 临时邮箱 SDK,所有渠道返回统一标准化格式
|
|
5
5
|
License: GPL-3.0
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -15,7 +15,7 @@ Requires-Dist: pygments>=2.19.2; extra == "dev"
|
|
|
15
15
|
|
|
16
16
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
17
17
|
|
|
18
|
-
临时邮箱 SDK(Python),支持 **
|
|
18
|
+
临时邮箱 SDK(Python),支持 **27** 个邮箱服务提供商,顺序与 `client.py` 中 `ALL_CHANNELS` 一致,返回格式与根目录 README 描述一致,并与 Go / npm / Rust / C 对齐。
|
|
19
19
|
|
|
20
20
|
## 安装
|
|
21
21
|
|
|
@@ -33,6 +33,11 @@ pip install https://github.com/XxxXTeam/tempmail-sdk/releases/latest/download/te
|
|
|
33
33
|
|------|--------|:----------:|------|
|
|
34
34
|
| `tempmail` | tempmail.ing | - | 支持自定义有效期 |
|
|
35
35
|
| `tempmail-cn` | tempmail.cn | - | Socket.IO:`request shortid` / `set shortid` / `mail`;`GenerateEmailOptions.domain` 可指定自定义接入域名 |
|
|
36
|
+
| `tmpmails` | tmpmails.com | ✅ | Next.js Server Action 收信;`domain` 可选语言路径 |
|
|
37
|
+
| `tempmailg` | tempmailg.com | ✅ | 独立 `requests.Session` 建邮;`GET /public/{locale}` + `POST /public/get_messages`;Token `tmg1:` + Base64(JSON);`domain` 可选语言路径 |
|
|
38
|
+
| `ta-easy` | ta-easy.com | ✅ | REST `api-endpoint.ta-easy.com` |
|
|
39
|
+
| `10mail-wangtz` | 10mail.wangtz.cn | - | REST `/api/tempMail`、`/api/emailList`;**默认跳过 TLS 证书校验** |
|
|
40
|
+
| `10minute-one` | 10minutemail.one | ✅ | SSR / JWT + Web API;`GenerateEmailOptions.domain` 可选 |
|
|
36
41
|
| `linshi-email` | linshi-email.com | - | |
|
|
37
42
|
| `linshiyou` | linshiyou.com | ✅ | `NEXUS_TOKEN` + Cookie;HTML 分段解析 |
|
|
38
43
|
| `mffac` | mffac.com | ✅ | mailbox `id`;REST 24h |
|
|
@@ -52,24 +57,22 @@ pip install https://github.com/XxxXTeam/tempmail-sdk/releases/latest/download/te
|
|
|
52
57
|
| `vip-215` | vip.215.im | ✅ | `POST` 建箱 + WebSocket;无正文时 synthetic 兜底 |
|
|
53
58
|
| `anonbox` | anonbox.net | ✅ | `GET /en/` 解析 HTML + mbox 收信 |
|
|
54
59
|
| `fake-legal` | fake.legal | - | `/api/domains` + `/api/inbox/new`;可选 `GenerateEmailOptions.domain` |
|
|
60
|
+
| `moakt` | moakt.com | ✅ | HTML 收件箱 + `tm_session`;`domain` 可选语言路径;独立 `requests` 请求避免污染全局 Session Cookie |
|
|
55
61
|
|
|
56
62
|
## 快速开始
|
|
57
63
|
|
|
58
64
|
```python
|
|
59
|
-
from tempmail_sdk import generate_email, get_emails, GenerateEmailOptions
|
|
65
|
+
from tempmail_sdk import generate_email, get_emails, GenerateEmailOptions
|
|
60
66
|
|
|
61
67
|
# 创建临时邮箱
|
|
62
68
|
info = generate_email(GenerateEmailOptions(channel="guerrillamail"))
|
|
63
|
-
|
|
69
|
+
if info:
|
|
70
|
+
print(f"邮箱: {info.email}")
|
|
64
71
|
|
|
65
|
-
#
|
|
66
|
-
result = get_emails(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
token=info.token,
|
|
70
|
-
))
|
|
71
|
-
if result.success:
|
|
72
|
-
print(f"收到 {len(result.emails)} 封邮件")
|
|
72
|
+
# 获取邮件(channel / email / token 由 SDK 从 EmailInfo 读取,无需手动传入)
|
|
73
|
+
result = get_emails(info)
|
|
74
|
+
if result.success:
|
|
75
|
+
print(f"收到 {len(result.emails)} 封邮件")
|
|
73
76
|
```
|
|
74
77
|
|
|
75
78
|
## 使用客户端类
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
4
4
|
|
|
5
|
-
临时邮箱 SDK(Python),支持 **
|
|
5
|
+
临时邮箱 SDK(Python),支持 **27** 个邮箱服务提供商,顺序与 `client.py` 中 `ALL_CHANNELS` 一致,返回格式与根目录 README 描述一致,并与 Go / npm / Rust / C 对齐。
|
|
6
6
|
|
|
7
7
|
## 安装
|
|
8
8
|
|
|
@@ -20,6 +20,11 @@ pip install https://github.com/XxxXTeam/tempmail-sdk/releases/latest/download/te
|
|
|
20
20
|
|------|--------|:----------:|------|
|
|
21
21
|
| `tempmail` | tempmail.ing | - | 支持自定义有效期 |
|
|
22
22
|
| `tempmail-cn` | tempmail.cn | - | Socket.IO:`request shortid` / `set shortid` / `mail`;`GenerateEmailOptions.domain` 可指定自定义接入域名 |
|
|
23
|
+
| `tmpmails` | tmpmails.com | ✅ | Next.js Server Action 收信;`domain` 可选语言路径 |
|
|
24
|
+
| `tempmailg` | tempmailg.com | ✅ | 独立 `requests.Session` 建邮;`GET /public/{locale}` + `POST /public/get_messages`;Token `tmg1:` + Base64(JSON);`domain` 可选语言路径 |
|
|
25
|
+
| `ta-easy` | ta-easy.com | ✅ | REST `api-endpoint.ta-easy.com` |
|
|
26
|
+
| `10mail-wangtz` | 10mail.wangtz.cn | - | REST `/api/tempMail`、`/api/emailList`;**默认跳过 TLS 证书校验** |
|
|
27
|
+
| `10minute-one` | 10minutemail.one | ✅ | SSR / JWT + Web API;`GenerateEmailOptions.domain` 可选 |
|
|
23
28
|
| `linshi-email` | linshi-email.com | - | |
|
|
24
29
|
| `linshiyou` | linshiyou.com | ✅ | `NEXUS_TOKEN` + Cookie;HTML 分段解析 |
|
|
25
30
|
| `mffac` | mffac.com | ✅ | mailbox `id`;REST 24h |
|
|
@@ -39,24 +44,22 @@ pip install https://github.com/XxxXTeam/tempmail-sdk/releases/latest/download/te
|
|
|
39
44
|
| `vip-215` | vip.215.im | ✅ | `POST` 建箱 + WebSocket;无正文时 synthetic 兜底 |
|
|
40
45
|
| `anonbox` | anonbox.net | ✅ | `GET /en/` 解析 HTML + mbox 收信 |
|
|
41
46
|
| `fake-legal` | fake.legal | - | `/api/domains` + `/api/inbox/new`;可选 `GenerateEmailOptions.domain` |
|
|
47
|
+
| `moakt` | moakt.com | ✅ | HTML 收件箱 + `tm_session`;`domain` 可选语言路径;独立 `requests` 请求避免污染全局 Session Cookie |
|
|
42
48
|
|
|
43
49
|
## 快速开始
|
|
44
50
|
|
|
45
51
|
```python
|
|
46
|
-
from tempmail_sdk import generate_email, get_emails, GenerateEmailOptions
|
|
52
|
+
from tempmail_sdk import generate_email, get_emails, GenerateEmailOptions
|
|
47
53
|
|
|
48
54
|
# 创建临时邮箱
|
|
49
55
|
info = generate_email(GenerateEmailOptions(channel="guerrillamail"))
|
|
50
|
-
|
|
56
|
+
if info:
|
|
57
|
+
print(f"邮箱: {info.email}")
|
|
51
58
|
|
|
52
|
-
#
|
|
53
|
-
result = get_emails(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
token=info.token,
|
|
57
|
-
))
|
|
58
|
-
if result.success:
|
|
59
|
-
print(f"收到 {len(result.emails)} 封邮件")
|
|
59
|
+
# 获取邮件(channel / email / token 由 SDK 从 EmailInfo 读取,无需手动传入)
|
|
60
|
+
result = get_emails(info)
|
|
61
|
+
if result.success:
|
|
62
|
+
print(f"收到 {len(result.emails)} 封邮件")
|
|
60
63
|
```
|
|
61
64
|
|
|
62
65
|
## 使用客户端类
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tempemail-sdk
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2.dev0
|
|
4
4
|
Summary: 临时邮箱 SDK,所有渠道返回统一标准化格式
|
|
5
5
|
License: GPL-3.0
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -15,7 +15,7 @@ Requires-Dist: pygments>=2.19.2; extra == "dev"
|
|
|
15
15
|
|
|
16
16
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
17
17
|
|
|
18
|
-
临时邮箱 SDK(Python),支持 **
|
|
18
|
+
临时邮箱 SDK(Python),支持 **27** 个邮箱服务提供商,顺序与 `client.py` 中 `ALL_CHANNELS` 一致,返回格式与根目录 README 描述一致,并与 Go / npm / Rust / C 对齐。
|
|
19
19
|
|
|
20
20
|
## 安装
|
|
21
21
|
|
|
@@ -33,6 +33,11 @@ pip install https://github.com/XxxXTeam/tempmail-sdk/releases/latest/download/te
|
|
|
33
33
|
|------|--------|:----------:|------|
|
|
34
34
|
| `tempmail` | tempmail.ing | - | 支持自定义有效期 |
|
|
35
35
|
| `tempmail-cn` | tempmail.cn | - | Socket.IO:`request shortid` / `set shortid` / `mail`;`GenerateEmailOptions.domain` 可指定自定义接入域名 |
|
|
36
|
+
| `tmpmails` | tmpmails.com | ✅ | Next.js Server Action 收信;`domain` 可选语言路径 |
|
|
37
|
+
| `tempmailg` | tempmailg.com | ✅ | 独立 `requests.Session` 建邮;`GET /public/{locale}` + `POST /public/get_messages`;Token `tmg1:` + Base64(JSON);`domain` 可选语言路径 |
|
|
38
|
+
| `ta-easy` | ta-easy.com | ✅ | REST `api-endpoint.ta-easy.com` |
|
|
39
|
+
| `10mail-wangtz` | 10mail.wangtz.cn | - | REST `/api/tempMail`、`/api/emailList`;**默认跳过 TLS 证书校验** |
|
|
40
|
+
| `10minute-one` | 10minutemail.one | ✅ | SSR / JWT + Web API;`GenerateEmailOptions.domain` 可选 |
|
|
36
41
|
| `linshi-email` | linshi-email.com | - | |
|
|
37
42
|
| `linshiyou` | linshiyou.com | ✅ | `NEXUS_TOKEN` + Cookie;HTML 分段解析 |
|
|
38
43
|
| `mffac` | mffac.com | ✅ | mailbox `id`;REST 24h |
|
|
@@ -52,24 +57,22 @@ pip install https://github.com/XxxXTeam/tempmail-sdk/releases/latest/download/te
|
|
|
52
57
|
| `vip-215` | vip.215.im | ✅ | `POST` 建箱 + WebSocket;无正文时 synthetic 兜底 |
|
|
53
58
|
| `anonbox` | anonbox.net | ✅ | `GET /en/` 解析 HTML + mbox 收信 |
|
|
54
59
|
| `fake-legal` | fake.legal | - | `/api/domains` + `/api/inbox/new`;可选 `GenerateEmailOptions.domain` |
|
|
60
|
+
| `moakt` | moakt.com | ✅ | HTML 收件箱 + `tm_session`;`domain` 可选语言路径;独立 `requests` 请求避免污染全局 Session Cookie |
|
|
55
61
|
|
|
56
62
|
## 快速开始
|
|
57
63
|
|
|
58
64
|
```python
|
|
59
|
-
from tempmail_sdk import generate_email, get_emails, GenerateEmailOptions
|
|
65
|
+
from tempmail_sdk import generate_email, get_emails, GenerateEmailOptions
|
|
60
66
|
|
|
61
67
|
# 创建临时邮箱
|
|
62
68
|
info = generate_email(GenerateEmailOptions(channel="guerrillamail"))
|
|
63
|
-
|
|
69
|
+
if info:
|
|
70
|
+
print(f"邮箱: {info.email}")
|
|
64
71
|
|
|
65
|
-
#
|
|
66
|
-
result = get_emails(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
token=info.token,
|
|
70
|
-
))
|
|
71
|
-
if result.success:
|
|
72
|
-
print(f"收到 {len(result.emails)} 封邮件")
|
|
72
|
+
# 获取邮件(channel / email / token 由 SDK 从 EmailInfo 读取,无需手动传入)
|
|
73
|
+
result = get_emails(info)
|
|
74
|
+
if result.success:
|
|
75
|
+
print(f"收到 {len(result.emails)} 封邮件")
|
|
73
76
|
```
|
|
74
77
|
|
|
75
78
|
## 使用客户端类
|
|
@@ -32,10 +32,16 @@ tempmail_sdk/providers/mail_tm.py
|
|
|
32
32
|
tempmail_sdk/providers/maildrop.py
|
|
33
33
|
tempmail_sdk/providers/mffac.py
|
|
34
34
|
tempmail_sdk/providers/minmail.py
|
|
35
|
+
tempmail_sdk/providers/moakt.py
|
|
35
36
|
tempmail_sdk/providers/smail_pw.py
|
|
37
|
+
tempmail_sdk/providers/ta_easy.py
|
|
36
38
|
tempmail_sdk/providers/temp_mail_io.py
|
|
37
39
|
tempmail_sdk/providers/tempmail.py
|
|
38
40
|
tempmail_sdk/providers/tempmail_cn.py
|
|
39
41
|
tempmail_sdk/providers/tempmail_lol.py
|
|
42
|
+
tempmail_sdk/providers/tempmailg.py
|
|
40
43
|
tempmail_sdk/providers/temporary_email_org.py
|
|
44
|
+
tempmail_sdk/providers/tenmail_wangtz.py
|
|
45
|
+
tempmail_sdk/providers/tenminute_one.py
|
|
46
|
+
tempmail_sdk/providers/tmpmails.py
|
|
41
47
|
tempmail_sdk/providers/vip_215.py
|
|
@@ -14,24 +14,29 @@ from .retry import with_retry, with_retry_with_attempts
|
|
|
14
14
|
from .telemetry import report_telemetry
|
|
15
15
|
from .logger import get_logger
|
|
16
16
|
from .providers import (
|
|
17
|
-
tempmail, tempmail_cn, linshi_email, linshiyou, mffac, tempmail_lol, chatgpt_org_uk,
|
|
17
|
+
tempmail, tempmail_cn, tmpmails, tempmailg, ta_easy, tenmail_wangtz, linshi_email, linshiyou, mffac, tempmail_lol, chatgpt_org_uk,
|
|
18
18
|
temp_mail_io, awamail, temporary_email_org, mail_tm, mail_cx,
|
|
19
19
|
dropmail, guerrillamail, maildrop, smail_pw,
|
|
20
|
-
boomlify, minmail, vip_215, anonbox, fake_legal,
|
|
20
|
+
boomlify, minmail, vip_215, anonbox, fake_legal, moakt, tenminute_one,
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
# 所有支持的渠道列表
|
|
24
24
|
ALL_CHANNELS = [
|
|
25
|
-
"tempmail", "tempmail-cn", "linshi-email", "linshiyou", "mffac", "tempmail-lol", "chatgpt-org-uk",
|
|
25
|
+
"tempmail", "tempmail-cn", "tmpmails", "tempmailg", "ta-easy", "10mail-wangtz", "10minute-one", "linshi-email", "linshiyou", "mffac", "tempmail-lol", "chatgpt-org-uk",
|
|
26
26
|
"temp-mail-io", "awamail", "temporary-email-org", "mail-tm", "mail-cx",
|
|
27
27
|
"dropmail", "guerrillamail", "maildrop", "smail-pw",
|
|
28
|
-
"boomlify", "minmail", "vip-215", "anonbox", "fake-legal",
|
|
28
|
+
"boomlify", "minmail", "vip-215", "anonbox", "fake-legal", "moakt",
|
|
29
29
|
]
|
|
30
30
|
|
|
31
31
|
# 渠道信息映射表
|
|
32
32
|
CHANNEL_INFO_MAP = {
|
|
33
33
|
"tempmail": ChannelInfo(channel="tempmail", name="TempMail", website="tempmail.ing"),
|
|
34
34
|
"tempmail-cn": ChannelInfo(channel="tempmail-cn", name="TempMail CN", website="tempmail.cn"),
|
|
35
|
+
"tmpmails": ChannelInfo(channel="tmpmails", name="TmpMails", website="tmpmails.com"),
|
|
36
|
+
"tempmailg": ChannelInfo(channel="tempmailg", name="TempMailG", website="tempmailg.com"),
|
|
37
|
+
"ta-easy": ChannelInfo(channel="ta-easy", name="TA Easy", website="ta-easy.com"),
|
|
38
|
+
"10mail-wangtz": ChannelInfo(channel="10mail-wangtz", name="10mail Wangtz", website="10mail.wangtz.cn"),
|
|
39
|
+
"10minute-one": ChannelInfo(channel="10minute-one", name="10 Minute Email", website="10minutemail.one"),
|
|
35
40
|
"linshi-email": ChannelInfo(channel="linshi-email", name="临时邮箱", website="linshi-email.com"),
|
|
36
41
|
"linshiyou": ChannelInfo(channel="linshiyou", name="临时邮", website="linshiyou.com"),
|
|
37
42
|
"mffac": ChannelInfo(channel="mffac", name="MFFAC", website="mffac.com"),
|
|
@@ -51,6 +56,7 @@ CHANNEL_INFO_MAP = {
|
|
|
51
56
|
"vip-215": ChannelInfo(channel="vip-215", name="VIP 215", website="vip.215.im"),
|
|
52
57
|
"anonbox": ChannelInfo(channel="anonbox", name="Anonbox", website="anonbox.net"),
|
|
53
58
|
"fake-legal": ChannelInfo(channel="fake-legal", name="Fake Legal", website="fake.legal"),
|
|
59
|
+
"moakt": ChannelInfo(channel="moakt", name="Moakt", website="moakt.com"),
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
|
|
@@ -130,6 +136,16 @@ def _generate_email_once(channel: str, options: GenerateEmailOptions) -> EmailIn
|
|
|
130
136
|
return tempmail.generate_email(options.duration)
|
|
131
137
|
elif channel == "tempmail-cn":
|
|
132
138
|
return tempmail_cn.generate_email(options.domain)
|
|
139
|
+
elif channel == "tmpmails":
|
|
140
|
+
return tmpmails.generate_email(options.domain)
|
|
141
|
+
elif channel == "tempmailg":
|
|
142
|
+
return tempmailg.generate_email(options.domain)
|
|
143
|
+
elif channel == "ta-easy":
|
|
144
|
+
return ta_easy.generate_email()
|
|
145
|
+
elif channel == "10mail-wangtz":
|
|
146
|
+
return tenmail_wangtz.generate_email(options.domain)
|
|
147
|
+
elif channel == "10minute-one":
|
|
148
|
+
return tenminute_one.generate_email(options.domain)
|
|
133
149
|
elif channel == "linshi-email":
|
|
134
150
|
return linshi_email.generate_email()
|
|
135
151
|
elif channel == "linshiyou":
|
|
@@ -168,6 +184,8 @@ def _generate_email_once(channel: str, options: GenerateEmailOptions) -> EmailIn
|
|
|
168
184
|
return anonbox.generate_email()
|
|
169
185
|
elif channel == "fake-legal":
|
|
170
186
|
return fake_legal.generate_email(options.domain)
|
|
187
|
+
elif channel == "moakt":
|
|
188
|
+
return moakt.generate_email(options.domain)
|
|
171
189
|
else:
|
|
172
190
|
raise ValueError(f"Unknown channel: {channel}")
|
|
173
191
|
|
|
@@ -234,6 +252,20 @@ def _get_emails_once(channel: str, email: str, token: Optional[str]) -> List[Ema
|
|
|
234
252
|
return tempmail.get_emails(email)
|
|
235
253
|
elif channel == "tempmail-cn":
|
|
236
254
|
return tempmail_cn.get_emails(email)
|
|
255
|
+
elif channel == "tmpmails":
|
|
256
|
+
if not token:
|
|
257
|
+
raise ValueError("token is required for tmpmails channel")
|
|
258
|
+
return tmpmails.get_emails(email, token)
|
|
259
|
+
elif channel == "tempmailg":
|
|
260
|
+
if not token:
|
|
261
|
+
raise ValueError("token is required for tempmailg channel")
|
|
262
|
+
return tempmailg.get_emails(email, token)
|
|
263
|
+
elif channel == "ta-easy":
|
|
264
|
+
if not token:
|
|
265
|
+
raise ValueError("token is required for ta-easy channel")
|
|
266
|
+
return ta_easy.get_emails(email, token)
|
|
267
|
+
elif channel == "10mail-wangtz":
|
|
268
|
+
return tenmail_wangtz.get_emails(email, token or "")
|
|
237
269
|
elif channel == "linshi-email":
|
|
238
270
|
if not token:
|
|
239
271
|
raise ValueError("token is required for linshi-email channel")
|
|
@@ -302,6 +334,14 @@ def _get_emails_once(channel: str, email: str, token: Optional[str]) -> List[Ema
|
|
|
302
334
|
return anonbox.get_emails(token, email)
|
|
303
335
|
elif channel == "fake-legal":
|
|
304
336
|
return fake_legal.get_emails(email)
|
|
337
|
+
elif channel == "moakt":
|
|
338
|
+
if not token:
|
|
339
|
+
raise ValueError("token is required for moakt channel")
|
|
340
|
+
return moakt.get_emails(email, token)
|
|
341
|
+
elif channel == "10minute-one":
|
|
342
|
+
if not token:
|
|
343
|
+
raise ValueError("token is required for 10minute-one channel")
|
|
344
|
+
return tenminute_one.get_emails(email, token)
|
|
305
345
|
else:
|
|
306
346
|
raise ValueError(f"Unknown channel: {channel}")
|
|
307
347
|
|
|
@@ -71,6 +71,7 @@ def _normalize_from(raw: Dict[str, Any]) -> str:
|
|
|
71
71
|
"from_addr",
|
|
72
72
|
"from_address",
|
|
73
73
|
"fromAddress",
|
|
74
|
+
"mail_sender",
|
|
74
75
|
"sender",
|
|
75
76
|
"address_from",
|
|
76
77
|
"from_email",
|
|
@@ -87,7 +88,7 @@ def _normalize_to(raw: Dict[str, Any], recipient_email: str) -> str:
|
|
|
87
88
|
|
|
88
89
|
def _normalize_subject(raw: Dict[str, Any]) -> str:
|
|
89
90
|
"""提取邮件主题"""
|
|
90
|
-
return _get_str(raw, "subject", "e_subject")
|
|
91
|
+
return _get_str(raw, "subject", "e_subject", "mail_title")
|
|
91
92
|
|
|
92
93
|
|
|
93
94
|
def _normalize_text(raw: Dict[str, Any]) -> str:
|
|
@@ -97,6 +98,7 @@ def _normalize_text(raw: Dict[str, Any]) -> str:
|
|
|
97
98
|
"text",
|
|
98
99
|
"text_body",
|
|
99
100
|
"preview_text",
|
|
101
|
+
"mail_body_text",
|
|
100
102
|
"body",
|
|
101
103
|
"content",
|
|
102
104
|
"body_text",
|
|
@@ -107,7 +109,7 @@ def _normalize_text(raw: Dict[str, Any]) -> str:
|
|
|
107
109
|
|
|
108
110
|
def _normalize_html(raw: Dict[str, Any]) -> str:
|
|
109
111
|
"""提取 HTML 内容"""
|
|
110
|
-
return _get_str(raw, "html", "html_body", "html_content", "body_html")
|
|
112
|
+
return _get_str(raw, "html", "html_body", "html_content", "body_html", "mail_body_html")
|
|
111
113
|
|
|
112
114
|
|
|
113
115
|
def _normalize_date(raw: Dict[str, Any]) -> str:
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""
|
|
2
|
+
moakt.com:GET 语言首页与收件箱 HTML;凭证为 tm_session 等 Cookie(序列化在 token 内);
|
|
3
|
+
列表解析 /{locale}/email/{uuid},正文 GET .../html 解析 .email-body。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import base64
|
|
7
|
+
import html
|
|
8
|
+
import json
|
|
9
|
+
import re
|
|
10
|
+
from typing import Dict, List, Optional, Tuple
|
|
11
|
+
|
|
12
|
+
import requests
|
|
13
|
+
|
|
14
|
+
from ..config import get_config
|
|
15
|
+
from ..normalize import normalize_email
|
|
16
|
+
from ..types import Email, EmailInfo
|
|
17
|
+
|
|
18
|
+
CHANNEL = "moakt"
|
|
19
|
+
ORIGIN = "https://www.moakt.com"
|
|
20
|
+
TOK_PREFIX = "mok1:"
|
|
21
|
+
|
|
22
|
+
_EMAIL_DIV_RE = re.compile(r'(?is)<div\s+id="email-address"\s*>([^<]+)</div>')
|
|
23
|
+
_HREF_EMAIL_RE = re.compile(
|
|
24
|
+
r'href="(/[^"]+/email/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})"'
|
|
25
|
+
)
|
|
26
|
+
_TITLE_RE = re.compile(r'(?is)<li\s+class="title"\s*>([^<]*)</li>')
|
|
27
|
+
_DATE_RE = re.compile(r'(?is)<li\s+class="date"[^>]*>[\s\S]*?<span[^>]*>([^<]+)</span>')
|
|
28
|
+
_SENDER_RE = re.compile(
|
|
29
|
+
r'(?is)<li\s+class="sender"[^>]*>[\s\S]*?<span[^>]*>([\s\S]*?)</span>\s*</li>'
|
|
30
|
+
)
|
|
31
|
+
_BODY_RE = re.compile(r'(?is)<div\s+class="email-body"\s*>([\s\S]*?)</div>')
|
|
32
|
+
_FROM_ADDR_RE = re.compile(
|
|
33
|
+
r"<([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>"
|
|
34
|
+
)
|
|
35
|
+
_TAG_RE = re.compile(r"<[^>]+>")
|
|
36
|
+
|
|
37
|
+
_DEFAULT_HEADERS = {
|
|
38
|
+
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
39
|
+
"Cache-Control": "no-cache",
|
|
40
|
+
"DNT": "1",
|
|
41
|
+
"Pragma": "no-cache",
|
|
42
|
+
"Upgrade-Insecure-Requests": "1",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _locale(domain: Optional[str]) -> str:
|
|
47
|
+
s = (domain or "").strip()
|
|
48
|
+
if not s or any(c in s for c in "/?#\\"):
|
|
49
|
+
return "zh"
|
|
50
|
+
return s
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _bare_get(url: str, headers: dict) -> requests.Response:
|
|
54
|
+
c = get_config()
|
|
55
|
+
kw: dict = {"timeout": c.timeout, "headers": headers, "verify": not c.insecure}
|
|
56
|
+
if c.proxy:
|
|
57
|
+
kw["proxies"] = {"http": c.proxy, "https": c.proxy}
|
|
58
|
+
return requests.get(url, **kw)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _parse_cookie_map(hdr: str) -> Dict[str, str]:
|
|
62
|
+
m: Dict[str, str] = {}
|
|
63
|
+
for part in hdr.split(";"):
|
|
64
|
+
part = part.strip()
|
|
65
|
+
if not part or "=" not in part:
|
|
66
|
+
continue
|
|
67
|
+
k, _, v = part.partition("=")
|
|
68
|
+
k, v = k.strip(), v.strip()
|
|
69
|
+
if k:
|
|
70
|
+
m[k] = v
|
|
71
|
+
return m
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _merge_cookie_hdr(prev: str, resp: requests.Response) -> str:
|
|
75
|
+
d = _parse_cookie_map(prev)
|
|
76
|
+
d.update(resp.cookies.get_dict())
|
|
77
|
+
return "; ".join(f"{k}={d[k]}" for k in sorted(d.keys()))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _page_headers(referer: str, ua: str) -> dict:
|
|
81
|
+
return {
|
|
82
|
+
**_DEFAULT_HEADERS,
|
|
83
|
+
"User-Agent": ua,
|
|
84
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
|
|
85
|
+
"Referer": referer,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _encode_sess(locale: str, cookie_hdr: str) -> str:
|
|
90
|
+
raw = json.dumps({"l": locale, "c": cookie_hdr}, separators=(",", ":")).encode("utf-8")
|
|
91
|
+
return TOK_PREFIX + base64.standard_b64encode(raw).decode("ascii")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _decode_sess(tok: str) -> Tuple[str, str]:
|
|
95
|
+
if not tok.startswith(TOK_PREFIX):
|
|
96
|
+
raise ValueError("moakt: invalid session token")
|
|
97
|
+
try:
|
|
98
|
+
data = base64.standard_b64decode(tok[len(TOK_PREFIX) :].encode("ascii"))
|
|
99
|
+
o = json.loads(data.decode("utf-8"))
|
|
100
|
+
except (json.JSONDecodeError, ValueError) as e:
|
|
101
|
+
raise ValueError("moakt: invalid session token") from e
|
|
102
|
+
loc = (o.get("l") or "").strip()
|
|
103
|
+
c = (o.get("c") or "").strip()
|
|
104
|
+
if not loc or not c:
|
|
105
|
+
raise ValueError("moakt: invalid session token")
|
|
106
|
+
return loc, c
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _parse_inbox_email(html_s: str) -> str:
|
|
110
|
+
m = _EMAIL_DIV_RE.search(html_s)
|
|
111
|
+
if not m:
|
|
112
|
+
raise RuntimeError("moakt: email-address not found")
|
|
113
|
+
addr = html.unescape(m.group(1).strip())
|
|
114
|
+
if not addr:
|
|
115
|
+
raise RuntimeError("moakt: empty email-address")
|
|
116
|
+
return addr
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _strip_tags(s: str) -> str:
|
|
120
|
+
return _TAG_RE.sub(" ", s).strip()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _list_mail_ids(html_s: str) -> List[str]:
|
|
124
|
+
seen = set()
|
|
125
|
+
out: List[str] = []
|
|
126
|
+
for m in _HREF_EMAIL_RE.finditer(html_s):
|
|
127
|
+
path = m.group(1)
|
|
128
|
+
if "/delete" in path:
|
|
129
|
+
continue
|
|
130
|
+
mid = path.rsplit("/", 1)[-1]
|
|
131
|
+
if len(mid) == 36 and mid not in seen:
|
|
132
|
+
seen.add(mid)
|
|
133
|
+
out.append(mid)
|
|
134
|
+
return out
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _parse_detail(page: str, mid: str, recipient: str) -> dict:
|
|
138
|
+
from_s = ""
|
|
139
|
+
sm = _SENDER_RE.search(page)
|
|
140
|
+
if sm:
|
|
141
|
+
inner = html.unescape(sm.group(1))
|
|
142
|
+
from_s = _strip_tags(inner)
|
|
143
|
+
em = _FROM_ADDR_RE.search(inner)
|
|
144
|
+
if em:
|
|
145
|
+
from_s = em.group(1).strip()
|
|
146
|
+
subj = ""
|
|
147
|
+
tm = _TITLE_RE.search(page)
|
|
148
|
+
if tm:
|
|
149
|
+
subj = html.unescape(tm.group(1).strip())
|
|
150
|
+
date_s = ""
|
|
151
|
+
dm = _DATE_RE.search(page)
|
|
152
|
+
if dm:
|
|
153
|
+
date_s = html.unescape(dm.group(1).strip())
|
|
154
|
+
body = ""
|
|
155
|
+
bm = _BODY_RE.search(page)
|
|
156
|
+
if bm:
|
|
157
|
+
body = bm.group(1).strip()
|
|
158
|
+
return {
|
|
159
|
+
"id": mid,
|
|
160
|
+
"to": recipient,
|
|
161
|
+
"from": from_s,
|
|
162
|
+
"subject": subj,
|
|
163
|
+
"date": date_s,
|
|
164
|
+
"html": body,
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def generate_email(domain: Optional[str] = None, **kwargs) -> EmailInfo:
|
|
169
|
+
loc = _locale(domain)
|
|
170
|
+
base = f"{ORIGIN}/{loc}"
|
|
171
|
+
inbox = f"{base}/inbox"
|
|
172
|
+
c = get_config()
|
|
173
|
+
ua = (c.headers or {}).get("User-Agent") or (
|
|
174
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
175
|
+
"Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
r1 = _bare_get(base, _page_headers(base, ua))
|
|
179
|
+
r1.raise_for_status()
|
|
180
|
+
cookie_hdr = _merge_cookie_hdr("", r1)
|
|
181
|
+
|
|
182
|
+
r2 = _bare_get(
|
|
183
|
+
inbox,
|
|
184
|
+
{**_page_headers(base, ua), "Cookie": cookie_hdr},
|
|
185
|
+
)
|
|
186
|
+
r2.raise_for_status()
|
|
187
|
+
cookie_hdr = _merge_cookie_hdr(cookie_hdr, r2)
|
|
188
|
+
html_s = r2.text
|
|
189
|
+
|
|
190
|
+
email = _parse_inbox_email(html_s)
|
|
191
|
+
if "tm_session" not in _parse_cookie_map(cookie_hdr):
|
|
192
|
+
raise RuntimeError("moakt: missing tm_session cookie")
|
|
193
|
+
tok = _encode_sess(loc, cookie_hdr)
|
|
194
|
+
return EmailInfo(channel=CHANNEL, email=email, _token=tok)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def get_emails(email: str, token: str, **kwargs) -> List[Email]:
|
|
198
|
+
loc, cookie_hdr = _decode_sess(token)
|
|
199
|
+
inbox = f"{ORIGIN}/{loc}/inbox"
|
|
200
|
+
c = get_config()
|
|
201
|
+
ua = (c.headers or {}).get("User-Agent") or (
|
|
202
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
203
|
+
"Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0"
|
|
204
|
+
)
|
|
205
|
+
base_ref = f"{ORIGIN}/{loc}"
|
|
206
|
+
|
|
207
|
+
r = _bare_get(
|
|
208
|
+
inbox,
|
|
209
|
+
{**_page_headers(base_ref, ua), "Cookie": cookie_hdr},
|
|
210
|
+
)
|
|
211
|
+
r.raise_for_status()
|
|
212
|
+
ids = _list_mail_ids(r.text)
|
|
213
|
+
out: List[Email] = []
|
|
214
|
+
for mid in ids:
|
|
215
|
+
detail = f"{ORIGIN}/{loc}/email/{mid}/html"
|
|
216
|
+
refer = f"{ORIGIN}/{loc}/email/{mid}"
|
|
217
|
+
rd = _bare_get(
|
|
218
|
+
detail,
|
|
219
|
+
{**_page_headers(refer, ua), "Cookie": cookie_hdr},
|
|
220
|
+
)
|
|
221
|
+
if rd.status_code != 200:
|
|
222
|
+
continue
|
|
223
|
+
raw = _parse_detail(rd.text, mid, email)
|
|
224
|
+
out.append(normalize_email(raw, email))
|
|
225
|
+
return out
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ta-easy.com 临时邮箱
|
|
3
|
+
API: https://api-endpoint.ta-easy.com/temp-email/
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .. import http as tm_http
|
|
7
|
+
from ..normalize import normalize_email
|
|
8
|
+
from ..types import EmailInfo
|
|
9
|
+
|
|
10
|
+
CHANNEL = "ta-easy"
|
|
11
|
+
API_BASE = "https://api-endpoint.ta-easy.com"
|
|
12
|
+
ORIGIN = "https://www.ta-easy.com"
|
|
13
|
+
|
|
14
|
+
_HEADERS = {
|
|
15
|
+
"Accept": "application/json",
|
|
16
|
+
"User-Agent": (
|
|
17
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
18
|
+
"Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0"
|
|
19
|
+
),
|
|
20
|
+
"origin": ORIGIN,
|
|
21
|
+
"referer": f"{ORIGIN}/",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def generate_email(**kwargs) -> EmailInfo:
|
|
26
|
+
"""POST /temp-email/address/new(空 body)"""
|
|
27
|
+
resp = tm_http.post(
|
|
28
|
+
f"{API_BASE}/temp-email/address/new",
|
|
29
|
+
headers={**_HEADERS, "Content-Length": "0"},
|
|
30
|
+
data=b"",
|
|
31
|
+
)
|
|
32
|
+
resp.raise_for_status()
|
|
33
|
+
data = resp.json()
|
|
34
|
+
if data.get("status") != "success" or not data.get("address") or not data.get("token"):
|
|
35
|
+
msg = data.get("message") or "create failed"
|
|
36
|
+
raise RuntimeError(f"ta-easy: {msg}")
|
|
37
|
+
exp = data.get("expiresAt")
|
|
38
|
+
expires_at = int(exp) if isinstance(exp, (int, float)) else None
|
|
39
|
+
return EmailInfo(
|
|
40
|
+
channel=CHANNEL,
|
|
41
|
+
email=data["address"],
|
|
42
|
+
_token=data["token"],
|
|
43
|
+
expires_at=expires_at,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_emails(email: str, token: str, **kwargs) -> list:
|
|
48
|
+
"""POST /temp-email/inbox/list"""
|
|
49
|
+
resp = tm_http.post(
|
|
50
|
+
f"{API_BASE}/temp-email/inbox/list",
|
|
51
|
+
headers={**_HEADERS, "Content-Type": "application/json"},
|
|
52
|
+
json={"token": token, "email": email},
|
|
53
|
+
)
|
|
54
|
+
resp.raise_for_status()
|
|
55
|
+
data = resp.json()
|
|
56
|
+
if data.get("status") != "success":
|
|
57
|
+
msg = data.get("message") or "inbox failed"
|
|
58
|
+
raise RuntimeError(f"ta-easy: {msg}")
|
|
59
|
+
raw_list = data.get("data")
|
|
60
|
+
if not isinstance(raw_list, list):
|
|
61
|
+
raw_list = []
|
|
62
|
+
return [normalize_email(raw, email) for raw in raw_list]
|