tempmail-sdk 1.1.2 → 1.1.4
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.
- package/README.md +43 -38
- package/demo/poll-emails.ts +290 -28
- package/dist/config.d.ts +16 -0
- package/dist/config.js +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +33 -16
- package/dist/providers/awamail.js +4 -3
- package/dist/providers/chatgpt-org-uk.d.ts +1 -1
- package/dist/providers/chatgpt-org-uk.js +188 -20
- package/dist/providers/dropmail.js +135 -3
- package/dist/providers/guerrillamail.js +4 -3
- package/dist/providers/linshi-email.d.ts +1 -1
- package/dist/providers/linshi-email.js +19 -7
- package/dist/providers/linshi-token.d.ts +25 -0
- package/dist/providers/linshi-token.js +69 -0
- package/dist/providers/mail-tm.js +43 -25
- package/dist/providers/maildrop.js +3 -2
- package/dist/providers/smail-pw.d.ts +9 -0
- package/dist/providers/smail-pw.js +356 -0
- package/dist/providers/temp-mail-io.js +5 -4
- package/dist/providers/tempmail-lol.js +4 -3
- package/dist/providers/tempmail.js +4 -3
- package/dist/retry.d.ts +2 -10
- package/dist/retry.js +41 -10
- package/dist/types.d.ts +6 -1
- package/dist/types.js +1 -1
- package/package.json +1 -1
- package/src/config.ts +16 -0
- package/src/index.ts +31 -14
- package/src/providers/awamail.ts +3 -2
- package/src/providers/chatgpt-org-uk.ts +213 -22
- package/src/providers/dropmail.ts +162 -2
- package/src/providers/guerrillamail.ts +3 -2
- package/src/providers/linshi-email.ts +24 -7
- package/src/providers/linshi-token.ts +86 -0
- package/src/providers/mail-tm.ts +43 -24
- package/src/providers/maildrop.ts +2 -1
- package/src/providers/smail-pw.ts +382 -0
- package/src/providers/temp-mail-io.ts +4 -3
- package/src/providers/tempmail-lol.ts +3 -2
- package/src/providers/tempmail.ts +3 -2
- package/src/retry.ts +42 -9
- package/src/types.ts +6 -1
- package/test/example.ts +183 -4
- package/dist/providers/tempmail-la.d.ts +0 -15
- package/dist/providers/tempmail-la.js +0 -89
- package/src/providers/tempmail-la.ts +0 -99
package/src/retry.ts
CHANGED
|
@@ -32,8 +32,13 @@ const DEFAULT_RETRY_OPTIONS: Required<RetryOptions> = {
|
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* 默认重试判断
|
|
35
|
-
*
|
|
36
|
-
*
|
|
35
|
+
* 以下错误类型会触发重试:
|
|
36
|
+
* - 网络连接错误(fetch failed, ECONNREFUSED, ECONNRESET 等)
|
|
37
|
+
* - 超时错误(timeout, abort)
|
|
38
|
+
* - DNS 解析失败
|
|
39
|
+
* - HTTP 429 限流
|
|
40
|
+
* - HTTP 4xx/5xx 服务端错误(含状态码的错误消息)
|
|
41
|
+
* 仅 SDK 内部的参数校验类错误不重试
|
|
37
42
|
*/
|
|
38
43
|
function defaultShouldRetry(error: any): boolean {
|
|
39
44
|
if (!error) return false;
|
|
@@ -58,7 +63,7 @@ function defaultShouldRetry(error: any): boolean {
|
|
|
58
63
|
return true;
|
|
59
64
|
}
|
|
60
65
|
|
|
61
|
-
/* HTTP 4xx/5xx
|
|
66
|
+
/* HTTP 4xx/5xx 错误(含状态码的错误消息)→ 重试 */
|
|
62
67
|
const statusMatch = message.match(/:\s*(\d{3})/);
|
|
63
68
|
if (statusMatch) {
|
|
64
69
|
const status = parseInt(statusMatch[1], 10);
|
|
@@ -77,9 +82,9 @@ function sleep(ms: number): Promise<void> {
|
|
|
77
82
|
|
|
78
83
|
/**
|
|
79
84
|
* 带重试的异步操作执行器
|
|
80
|
-
* - 自动重试可恢复的错误(网络错误、超时、5xx)
|
|
85
|
+
* - 自动重试可恢复的错误(网络错误、超时、HTTP 4xx/5xx)
|
|
81
86
|
* - 指数退避避免过度请求
|
|
82
|
-
* - 不可恢复的错误(
|
|
87
|
+
* - 不可恢复的错误(SDK 内部参数校验错误等)直接抛出不重试
|
|
83
88
|
*
|
|
84
89
|
* @param fn 要执行的异步操作
|
|
85
90
|
* @param options 重试配置
|
|
@@ -127,18 +132,46 @@ export async function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions)
|
|
|
127
132
|
* @param init fetch 选项
|
|
128
133
|
* @param timeoutMs 超时时间(毫秒)
|
|
129
134
|
*/
|
|
135
|
+
/**
|
|
136
|
+
* 缓存的全局配置快照,避免每次请求都读取
|
|
137
|
+
* 仅在 setConfig 被调用时失效(通过 configVersion 比对)
|
|
138
|
+
*/
|
|
139
|
+
let _cachedFetchConfig: { fetchFn: typeof fetch; timeout: number; version: number } | null = null;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 获取缓存的 fetch 配置
|
|
143
|
+
*/
|
|
144
|
+
function getFetchConfig(): { fetchFn: typeof fetch; timeout: number } {
|
|
145
|
+
const config = getConfig();
|
|
146
|
+
/* 简单的引用比对即可,getConfig 在未变更时返回同一对象 */
|
|
147
|
+
if (!_cachedFetchConfig || _cachedFetchConfig.fetchFn !== (config.customFetch || fetch) || _cachedFetchConfig.timeout !== (config.timeout ?? DEFAULT_RETRY_OPTIONS.timeout)) {
|
|
148
|
+
_cachedFetchConfig = {
|
|
149
|
+
fetchFn: config.customFetch || fetch,
|
|
150
|
+
timeout: config.timeout ?? DEFAULT_RETRY_OPTIONS.timeout,
|
|
151
|
+
version: 0,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return _cachedFetchConfig;
|
|
155
|
+
}
|
|
156
|
+
|
|
130
157
|
export async function fetchWithTimeout(
|
|
131
158
|
url: string,
|
|
132
159
|
init?: RequestInit,
|
|
133
160
|
timeoutMs?: number,
|
|
134
161
|
): Promise<Response> {
|
|
135
|
-
const
|
|
136
|
-
const effectiveTimeout = timeoutMs ??
|
|
162
|
+
const { fetchFn, timeout: defaultTimeout } = getFetchConfig();
|
|
163
|
+
const effectiveTimeout = timeoutMs ?? defaultTimeout;
|
|
137
164
|
const controller = new AbortController();
|
|
138
165
|
const timeoutId = setTimeout(() => controller.abort(), effectiveTimeout);
|
|
139
166
|
|
|
140
|
-
/*
|
|
141
|
-
|
|
167
|
+
/*
|
|
168
|
+
* 如果调用方已提供 signal,需要同时监听两个信号(调用方 + 超时)
|
|
169
|
+
* 任一触发则中断请求
|
|
170
|
+
*/
|
|
171
|
+
const externalSignal = init?.signal;
|
|
172
|
+
if (externalSignal) {
|
|
173
|
+
externalSignal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
174
|
+
}
|
|
142
175
|
|
|
143
176
|
try {
|
|
144
177
|
const response = await fetchFn(url, {
|
package/src/types.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 支持的临时邮箱渠道标识
|
|
3
3
|
* 每个渠道对应一个第三方临时邮箱服务商
|
|
4
4
|
*/
|
|
5
|
-
export type Channel = 'tempmail' | 'linshi-email' | 'tempmail-lol' | 'chatgpt-org-uk' | '
|
|
5
|
+
export type Channel = 'tempmail' | 'linshi-email' | 'tempmail-lol' | 'chatgpt-org-uk' | 'temp-mail-io' | 'awamail' | 'mail-tm' | 'dropmail' | 'guerrillamail' | 'maildrop' | 'smail-pw';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* 创建临时邮箱后返回的邮箱信息
|
|
@@ -123,6 +123,11 @@ export interface RetryConfig {
|
|
|
123
123
|
export interface GenerateEmailOptions {
|
|
124
124
|
/** 指定渠道,不传则随机选择 */
|
|
125
125
|
channel?: Channel;
|
|
126
|
+
/**
|
|
127
|
+
* 为 false 时仅尝试 `channel` 指定的渠道,失败即返回 null,不 Fallback 到其他渠道。
|
|
128
|
+
* 用于按渠道探测可用性。默认 true(保持原有「优先指定渠道、失败后试其他」行为)。
|
|
129
|
+
*/
|
|
130
|
+
channelFallback?: boolean;
|
|
126
131
|
/** 邮箱有效时长 */
|
|
127
132
|
duration?: number;
|
|
128
133
|
/** 指定邮箱域名 */
|
package/test/example.ts
CHANGED
|
@@ -1,18 +1,188 @@
|
|
|
1
1
|
import { generateEmail, getEmails, TempEmailClient, Channel } from '../src';
|
|
2
|
+
import nodemailer from 'nodemailer';
|
|
3
|
+
|
|
4
|
+
type SmtpConfig = {
|
|
5
|
+
host: string;
|
|
6
|
+
port: number;
|
|
7
|
+
user?: string;
|
|
8
|
+
pass?: string;
|
|
9
|
+
from: string;
|
|
10
|
+
secure: boolean;
|
|
11
|
+
timeoutMs: number;
|
|
12
|
+
subject: string;
|
|
13
|
+
body: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type PollConfig = {
|
|
17
|
+
intervalMs: number;
|
|
18
|
+
max: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function parseBoolean(value?: string): boolean {
|
|
22
|
+
if (!value) return false;
|
|
23
|
+
return value === '1' || value.toLowerCase() === 'true';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parsePositiveNumber(value: string | undefined, fallback: number): number {
|
|
27
|
+
if (!value) return fallback;
|
|
28
|
+
const parsed = Number(value);
|
|
29
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getSmtpConfig(): SmtpConfig | null {
|
|
34
|
+
const enabled = parseBoolean(process.env.TEMPMAIL_SMTP_ENABLED);
|
|
35
|
+
if (!enabled) return null;
|
|
36
|
+
|
|
37
|
+
const host = process.env.TEMPMAIL_SMTP_HOST?.trim();
|
|
38
|
+
const portRaw = process.env.TEMPMAIL_SMTP_PORT?.trim();
|
|
39
|
+
const port = portRaw ? Number(portRaw) : 0;
|
|
40
|
+
|
|
41
|
+
if (!host || !port || !Number.isFinite(port)) {
|
|
42
|
+
console.warn('SMTP 已启用,但 TEMPMAIL_SMTP_HOST/TEMPMAIL_SMTP_PORT 未正确配置,跳过发送。');
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const user = process.env.TEMPMAIL_SMTP_USER?.trim();
|
|
47
|
+
const pass = process.env.TEMPMAIL_SMTP_PASS?.trim();
|
|
48
|
+
const from = (process.env.TEMPMAIL_SMTP_FROM?.trim() || user || '').trim();
|
|
49
|
+
|
|
50
|
+
if (!from) {
|
|
51
|
+
console.warn('SMTP 已启用,但 TEMPMAIL_SMTP_FROM 未配置且 USER 为空,跳过发送。');
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const secureEnv = process.env.TEMPMAIL_SMTP_SECURE?.trim();
|
|
56
|
+
const secure = secureEnv ? parseBoolean(secureEnv) : port === 465;
|
|
57
|
+
|
|
58
|
+
const timeoutMs = parsePositiveNumber(process.env.TEMPMAIL_SMTP_TIMEOUT, 10000);
|
|
59
|
+
const subject = process.env.TEMPMAIL_SMTP_SUBJECT?.trim() || 'TempMail SDK SMTP Test';
|
|
60
|
+
const body = process.env.TEMPMAIL_SMTP_BODY?.trim() || 'This is a tempmail SDK SMTP test email.';
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
host,
|
|
64
|
+
port,
|
|
65
|
+
user,
|
|
66
|
+
pass,
|
|
67
|
+
from,
|
|
68
|
+
secure,
|
|
69
|
+
timeoutMs,
|
|
70
|
+
subject,
|
|
71
|
+
body,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getPollConfig(): PollConfig {
|
|
76
|
+
return {
|
|
77
|
+
intervalMs: parsePositiveNumber(process.env.TEMPMAIL_POLL_INTERVAL_MS, 3000),
|
|
78
|
+
max: parsePositiveNumber(process.env.TEMPMAIL_POLL_MAX, 10),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function createTraceId(): string {
|
|
83
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function sleep(ms: number) {
|
|
87
|
+
return new Promise<void>((resolve) => setTimeout(resolve, ms));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function sendSmtpTestEmail(targetEmail: string): Promise<{ marker: string } | null> {
|
|
91
|
+
const smtp = getSmtpConfig();
|
|
92
|
+
if (!smtp) return null;
|
|
93
|
+
|
|
94
|
+
const marker = `tempmail-sdk:${createTraceId()}`;
|
|
95
|
+
const subject = `${smtp.subject} [${marker}]`;
|
|
96
|
+
const text = `${smtp.body}\n\n[${marker}]`;
|
|
97
|
+
const html = `${smtp.body}<br/><br/><strong>[${marker}]</strong>`;
|
|
98
|
+
|
|
99
|
+
const transport = nodemailer.createTransport({
|
|
100
|
+
host: smtp.host,
|
|
101
|
+
port: smtp.port,
|
|
102
|
+
secure: smtp.secure,
|
|
103
|
+
auth: smtp.user && smtp.pass ? { user: smtp.user, pass: smtp.pass } : undefined,
|
|
104
|
+
connectionTimeout: smtp.timeoutMs,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
await transport.sendMail({
|
|
109
|
+
from: smtp.from,
|
|
110
|
+
to: targetEmail,
|
|
111
|
+
subject,
|
|
112
|
+
text,
|
|
113
|
+
html,
|
|
114
|
+
});
|
|
115
|
+
console.log(`SMTP 测试邮件已发送到 ${targetEmail}`);
|
|
116
|
+
return { marker };
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.warn(`SMTP 发送失败:${error}`);
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function emailHasMarker(content: string | undefined, marker: string): boolean {
|
|
124
|
+
return !!content && content.includes(marker);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function pollForEmail(client: TempEmailClient, marker: string): Promise<boolean> {
|
|
128
|
+
const { intervalMs, max } = getPollConfig();
|
|
129
|
+
|
|
130
|
+
for (let attempt = 1; attempt <= max; attempt++) {
|
|
131
|
+
try {
|
|
132
|
+
const result = await client.getEmails();
|
|
133
|
+
const found = result.emails.some((email) => {
|
|
134
|
+
return (
|
|
135
|
+
emailHasMarker(email.subject, marker) ||
|
|
136
|
+
emailHasMarker(email.text, marker) ||
|
|
137
|
+
emailHasMarker(email.html, marker)
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
if (found) {
|
|
141
|
+
console.log(`轮询成功:已收到包含标识 ${marker} 的邮件。`);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.warn(`轮询第 ${attempt} 次失败:${error}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (attempt < max) {
|
|
149
|
+
await sleep(intervalMs);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
console.warn(`轮询超时:未收到包含标识 ${marker} 的邮件。`);
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
2
156
|
|
|
3
157
|
async function testGenerateEmail() {
|
|
4
158
|
console.log('=== Test Generate Email ===\n');
|
|
5
159
|
|
|
6
|
-
// Test each channel
|
|
7
|
-
const channels: Channel[] = [
|
|
8
|
-
|
|
160
|
+
// Test each channel(与 src/index.ts allChannels 一致)
|
|
161
|
+
const channels: Channel[] = [
|
|
162
|
+
'tempmail',
|
|
163
|
+
'linshi-email',
|
|
164
|
+
'tempmail-lol',
|
|
165
|
+
'chatgpt-org-uk',
|
|
166
|
+
'temp-mail-io',
|
|
167
|
+
'awamail',
|
|
168
|
+
'mail-tm',
|
|
169
|
+
'dropmail',
|
|
170
|
+
'guerrillamail',
|
|
171
|
+
'maildrop',
|
|
172
|
+
'smail-pw',
|
|
173
|
+
];
|
|
174
|
+
|
|
9
175
|
for (const channel of channels) {
|
|
10
176
|
try {
|
|
11
177
|
console.log(`Testing channel: ${channel}`);
|
|
12
178
|
const emailInfo = await generateEmail({ channel });
|
|
179
|
+
if (!emailInfo) {
|
|
180
|
+
console.log(' Failed to generate email');
|
|
181
|
+
console.log();
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
13
184
|
console.log(` Channel: ${emailInfo.channel}`);
|
|
14
185
|
console.log(` Email: ${emailInfo.email}`);
|
|
15
|
-
if (emailInfo.token) console.log(` Token: ${emailInfo.token}`);
|
|
16
186
|
if (emailInfo.expiresAt) console.log(` Expires: ${emailInfo.expiresAt}`);
|
|
17
187
|
if (emailInfo.createdAt) console.log(` Created: ${emailInfo.createdAt}`);
|
|
18
188
|
console.log();
|
|
@@ -31,6 +201,10 @@ async function testGetEmails() {
|
|
|
31
201
|
|
|
32
202
|
try {
|
|
33
203
|
const emailInfo = await client.generate({ channel: 'tempmail' });
|
|
204
|
+
if (!emailInfo) {
|
|
205
|
+
console.log('Generated: null');
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
34
208
|
console.log(`Generated: ${emailInfo.channel} - ${emailInfo.email}`);
|
|
35
209
|
|
|
36
210
|
const result = await client.getEmails();
|
|
@@ -50,6 +224,11 @@ async function testGetEmails() {
|
|
|
50
224
|
console.log();
|
|
51
225
|
}
|
|
52
226
|
console.log();
|
|
227
|
+
|
|
228
|
+
const smtpResult = await sendSmtpTestEmail(emailInfo.email);
|
|
229
|
+
if (smtpResult) {
|
|
230
|
+
await pollForEmail(client, smtpResult.marker);
|
|
231
|
+
}
|
|
53
232
|
} catch (error) {
|
|
54
233
|
console.error(`Error: ${error}`);
|
|
55
234
|
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { InternalEmailInfo, Email } from '../types';
|
|
2
|
-
/**
|
|
3
|
-
* 创建临时邮箱
|
|
4
|
-
* API: POST /api/mail/create
|
|
5
|
-
* 返回: { code: 0, data: { mailId, address, type, startAt, endAt } }
|
|
6
|
-
*/
|
|
7
|
-
export declare function generateEmail(): Promise<InternalEmailInfo>;
|
|
8
|
-
/**
|
|
9
|
-
* 获取邮件列表
|
|
10
|
-
* API: POST /api/mail/box
|
|
11
|
-
* 请求: { address, cursor }
|
|
12
|
-
* 返回: { code: 0, data: { rows: [...], cursor, hasMore } }
|
|
13
|
-
* 每封邮件: { mailboxId, messageFrom, subject, createdAt, html, attachments, read }
|
|
14
|
-
*/
|
|
15
|
-
export declare function getEmails(email: string): Promise<Email[]>;
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateEmail = generateEmail;
|
|
4
|
-
exports.getEmails = getEmails;
|
|
5
|
-
const normalize_1 = require("../normalize");
|
|
6
|
-
const CHANNEL = 'tempmail-la';
|
|
7
|
-
const BASE_URL = 'https://tempmail.la/api';
|
|
8
|
-
const DEFAULT_HEADERS = {
|
|
9
|
-
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0',
|
|
10
|
-
'Accept': 'application/json, text/plain, */*',
|
|
11
|
-
'Content-Type': 'application/json',
|
|
12
|
-
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
|
|
13
|
-
'cache-control': 'no-cache',
|
|
14
|
-
'dnt': '1',
|
|
15
|
-
'locale': 'zh-CN',
|
|
16
|
-
'origin': 'https://tempmail.la',
|
|
17
|
-
'platform': 'PC',
|
|
18
|
-
'pragma': 'no-cache',
|
|
19
|
-
'product': 'TEMP_MAIL',
|
|
20
|
-
'referer': 'https://tempmail.la/zh-CN/tempmail',
|
|
21
|
-
'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Microsoft Edge";v="144"',
|
|
22
|
-
'sec-ch-ua-mobile': '?0',
|
|
23
|
-
'sec-ch-ua-platform': '"Windows"',
|
|
24
|
-
'sec-fetch-dest': 'empty',
|
|
25
|
-
'sec-fetch-mode': 'cors',
|
|
26
|
-
'sec-fetch-site': 'same-origin',
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* 创建临时邮箱
|
|
30
|
-
* API: POST /api/mail/create
|
|
31
|
-
* 返回: { code: 0, data: { mailId, address, type, startAt, endAt } }
|
|
32
|
-
*/
|
|
33
|
-
async function generateEmail() {
|
|
34
|
-
const response = await fetch(`${BASE_URL}/mail/create`, {
|
|
35
|
-
method: 'POST',
|
|
36
|
-
headers: DEFAULT_HEADERS,
|
|
37
|
-
body: JSON.stringify({ turnstile: '' }),
|
|
38
|
-
});
|
|
39
|
-
if (!response.ok) {
|
|
40
|
-
throw new Error(`Failed to generate email: ${response.status}`);
|
|
41
|
-
}
|
|
42
|
-
const data = await response.json();
|
|
43
|
-
if (data.code !== 0 || !data.data) {
|
|
44
|
-
throw new Error('Failed to generate email');
|
|
45
|
-
}
|
|
46
|
-
return {
|
|
47
|
-
channel: CHANNEL,
|
|
48
|
-
email: data.data.address,
|
|
49
|
-
expiresAt: data.data.endAt,
|
|
50
|
-
createdAt: data.data.startAt,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* 获取邮件列表
|
|
55
|
-
* API: POST /api/mail/box
|
|
56
|
-
* 请求: { address, cursor }
|
|
57
|
-
* 返回: { code: 0, data: { rows: [...], cursor, hasMore } }
|
|
58
|
-
* 每封邮件: { mailboxId, messageFrom, subject, createdAt, html, attachments, read }
|
|
59
|
-
*/
|
|
60
|
-
async function getEmails(email) {
|
|
61
|
-
const allEmails = [];
|
|
62
|
-
let cursor = null;
|
|
63
|
-
let hasMore = true;
|
|
64
|
-
// 支持分页,循环获取所有邮件
|
|
65
|
-
while (hasMore) {
|
|
66
|
-
const response = await fetch(`${BASE_URL}/mail/box`, {
|
|
67
|
-
method: 'POST',
|
|
68
|
-
headers: DEFAULT_HEADERS,
|
|
69
|
-
body: JSON.stringify({ address: email, cursor }),
|
|
70
|
-
});
|
|
71
|
-
if (!response.ok) {
|
|
72
|
-
throw new Error(`Failed to get emails: ${response.status}`);
|
|
73
|
-
}
|
|
74
|
-
const data = await response.json();
|
|
75
|
-
if (data.code !== 0 || !data.data) {
|
|
76
|
-
throw new Error('Failed to get emails');
|
|
77
|
-
}
|
|
78
|
-
const rows = data.data.rows || [];
|
|
79
|
-
allEmails.push(...rows);
|
|
80
|
-
if (data.data.hasMore && data.data.cursor) {
|
|
81
|
-
cursor = data.data.cursor;
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
hasMore = false;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return allEmails.map((raw) => (0, normalize_1.normalizeEmail)(raw, email));
|
|
88
|
-
}
|
|
89
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVtcG1haWwtbGEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcHJvdmlkZXJzL3RlbXBtYWlsLWxhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBZ0NBLHNDQXVCQztBQVNELDhCQWtDQztBQWpHRCw0Q0FBOEM7QUFFOUMsTUFBTSxPQUFPLEdBQVksYUFBYSxDQUFDO0FBQ3ZDLE1BQU0sUUFBUSxHQUFHLHlCQUF5QixDQUFDO0FBRTNDLE1BQU0sZUFBZSxHQUEyQjtJQUM5QyxZQUFZLEVBQUUsK0hBQStIO0lBQzdJLFFBQVEsRUFBRSxtQ0FBbUM7SUFDN0MsY0FBYyxFQUFFLGtCQUFrQjtJQUNsQyxpQkFBaUIsRUFBRSxpREFBaUQ7SUFDcEUsZUFBZSxFQUFFLFVBQVU7SUFDM0IsS0FBSyxFQUFFLEdBQUc7SUFDVixRQUFRLEVBQUUsT0FBTztJQUNqQixRQUFRLEVBQUUscUJBQXFCO0lBQy9CLFVBQVUsRUFBRSxJQUFJO0lBQ2hCLFFBQVEsRUFBRSxVQUFVO0lBQ3BCLFNBQVMsRUFBRSxXQUFXO0lBQ3RCLFNBQVMsRUFBRSxvQ0FBb0M7SUFDL0MsV0FBVyxFQUFFLG1FQUFtRTtJQUNoRixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLG9CQUFvQixFQUFFLFdBQVc7SUFDakMsZ0JBQWdCLEVBQUUsT0FBTztJQUN6QixnQkFBZ0IsRUFBRSxNQUFNO0lBQ3hCLGdCQUFnQixFQUFFLGFBQWE7Q0FDaEMsQ0FBQztBQUVGOzs7O0dBSUc7QUFDSSxLQUFLLFVBQVUsYUFBYTtJQUNqQyxNQUFNLFFBQVEsR0FBRyxNQUFNLEtBQUssQ0FBQyxHQUFHLFFBQVEsY0FBYyxFQUFFO1FBQ3RELE1BQU0sRUFBRSxNQUFNO1FBQ2QsT0FBTyxFQUFFLGVBQWU7UUFDeEIsSUFBSSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFLENBQUM7S0FDeEMsQ0FBQyxDQUFDO0lBRUgsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNqQixNQUFNLElBQUksS0FBSyxDQUFDLDZCQUE2QixRQUFRLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUNsRSxDQUFDO0lBRUQsTUFBTSxJQUFJLEdBQUcsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7SUFFbkMsSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNsQyxNQUFNLElBQUksS0FBSyxDQUFDLDBCQUEwQixDQUFDLENBQUM7SUFDOUMsQ0FBQztJQUVELE9BQU87UUFDTCxPQUFPLEVBQUUsT0FBTztRQUNoQixLQUFLLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPO1FBQ3hCLFNBQVMsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUs7UUFDMUIsU0FBUyxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTztLQUM3QixDQUFDO0FBQ0osQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNJLEtBQUssVUFBVSxTQUFTLENBQUMsS0FBYTtJQUMzQyxNQUFNLFNBQVMsR0FBVSxFQUFFLENBQUM7SUFDNUIsSUFBSSxNQUFNLEdBQWtCLElBQUksQ0FBQztJQUNqQyxJQUFJLE9BQU8sR0FBRyxJQUFJLENBQUM7SUFFbkIsZ0JBQWdCO0lBQ2hCLE9BQU8sT0FBTyxFQUFFLENBQUM7UUFDZixNQUFNLFFBQVEsR0FBYSxNQUFNLEtBQUssQ0FBQyxHQUFHLFFBQVEsV0FBVyxFQUFFO1lBQzdELE1BQU0sRUFBRSxNQUFNO1lBQ2QsT0FBTyxFQUFFLGVBQWU7WUFDeEIsSUFBSSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxDQUFDO1NBQ2pELENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDakIsTUFBTSxJQUFJLEtBQUssQ0FBQyx5QkFBeUIsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDOUQsQ0FBQztRQUVELE1BQU0sSUFBSSxHQUFRLE1BQU0sUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO1FBRXhDLElBQUksSUFBSSxDQUFDLElBQUksS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDbEMsTUFBTSxJQUFJLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1FBQzFDLENBQUM7UUFFRCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksSUFBSSxFQUFFLENBQUM7UUFDbEMsU0FBUyxDQUFDLElBQUksQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDO1FBRXhCLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUMxQyxNQUFNLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7UUFDNUIsQ0FBQzthQUFNLENBQUM7WUFDTixPQUFPLEdBQUcsS0FBSyxDQUFDO1FBQ2xCLENBQUM7SUFDSCxDQUFDO0lBRUQsT0FBTyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBUSxFQUFFLEVBQUUsQ0FBQyxJQUFBLDBCQUFjLEVBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUM7QUFDakUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEludGVybmFsRW1haWxJbmZvLCBFbWFpbCwgQ2hhbm5lbCB9IGZyb20gJy4uL3R5cGVzJztcbmltcG9ydCB7IG5vcm1hbGl6ZUVtYWlsIH0gZnJvbSAnLi4vbm9ybWFsaXplJztcblxuY29uc3QgQ0hBTk5FTDogQ2hhbm5lbCA9ICd0ZW1wbWFpbC1sYSc7XG5jb25zdCBCQVNFX1VSTCA9ICdodHRwczovL3RlbXBtYWlsLmxhL2FwaSc7XG5cbmNvbnN0IERFRkFVTFRfSEVBREVSUzogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcbiAgJ1VzZXItQWdlbnQnOiAnTW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzE0NC4wLjAuMCBTYWZhcmkvNTM3LjM2IEVkZy8xNDQuMC4wLjAnLFxuICAnQWNjZXB0JzogJ2FwcGxpY2F0aW9uL2pzb24sIHRleHQvcGxhaW4sICovKicsXG4gICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXG4gICdhY2NlcHQtbGFuZ3VhZ2UnOiAnemgtQ04semg7cT0wLjksZW47cT0wLjgsZW4tR0I7cT0wLjcsZW4tVVM7cT0wLjYnLFxuICAnY2FjaGUtY29udHJvbCc6ICduby1jYWNoZScsXG4gICdkbnQnOiAnMScsXG4gICdsb2NhbGUnOiAnemgtQ04nLFxuICAnb3JpZ2luJzogJ2h0dHBzOi8vdGVtcG1haWwubGEnLFxuICAncGxhdGZvcm0nOiAnUEMnLFxuICAncHJhZ21hJzogJ25vLWNhY2hlJyxcbiAgJ3Byb2R1Y3QnOiAnVEVNUF9NQUlMJyxcbiAgJ3JlZmVyZXInOiAnaHR0cHM6Ly90ZW1wbWFpbC5sYS96aC1DTi90ZW1wbWFpbCcsXG4gICdzZWMtY2gtdWEnOiAnXCJOb3QoQTpCcmFuZFwiO3Y9XCI4XCIsIFwiQ2hyb21pdW1cIjt2PVwiMTQ0XCIsIFwiTWljcm9zb2Z0IEVkZ2VcIjt2PVwiMTQ0XCInLFxuICAnc2VjLWNoLXVhLW1vYmlsZSc6ICc/MCcsXG4gICdzZWMtY2gtdWEtcGxhdGZvcm0nOiAnXCJXaW5kb3dzXCInLFxuICAnc2VjLWZldGNoLWRlc3QnOiAnZW1wdHknLFxuICAnc2VjLWZldGNoLW1vZGUnOiAnY29ycycsXG4gICdzZWMtZmV0Y2gtc2l0ZSc6ICdzYW1lLW9yaWdpbicsXG59O1xuXG4vKipcbiAqIOWIm+W7uuS4tOaXtumCrueusVxuICogQVBJOiBQT1NUIC9hcGkvbWFpbC9jcmVhdGVcbiAqIOi/lOWbnjogeyBjb2RlOiAwLCBkYXRhOiB7IG1haWxJZCwgYWRkcmVzcywgdHlwZSwgc3RhcnRBdCwgZW5kQXQgfSB9XG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZW5lcmF0ZUVtYWlsKCk6IFByb21pc2U8SW50ZXJuYWxFbWFpbEluZm8+IHtcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChgJHtCQVNFX1VSTH0vbWFpbC9jcmVhdGVgLCB7XG4gICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgaGVhZGVyczogREVGQVVMVF9IRUFERVJTLFxuICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgdHVybnN0aWxlOiAnJyB9KSxcbiAgfSk7XG5cbiAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGdlbmVyYXRlIGVtYWlsOiAke3Jlc3BvbnNlLnN0YXR1c31gKTtcbiAgfVxuXG4gIGNvbnN0IGRhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7XG5cbiAgaWYgKGRhdGEuY29kZSAhPT0gMCB8fCAhZGF0YS5kYXRhKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdGYWlsZWQgdG8gZ2VuZXJhdGUgZW1haWwnKTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgY2hhbm5lbDogQ0hBTk5FTCxcbiAgICBlbWFpbDogZGF0YS5kYXRhLmFkZHJlc3MsXG4gICAgZXhwaXJlc0F0OiBkYXRhLmRhdGEuZW5kQXQsXG4gICAgY3JlYXRlZEF0OiBkYXRhLmRhdGEuc3RhcnRBdCxcbiAgfTtcbn1cblxuLyoqXG4gKiDojrflj5bpgq7ku7bliJfooahcbiAqIEFQSTogUE9TVCAvYXBpL21haWwvYm94XG4gKiDor7fmsYI6IHsgYWRkcmVzcywgY3Vyc29yIH1cbiAqIOi/lOWbnjogeyBjb2RlOiAwLCBkYXRhOiB7IHJvd3M6IFsuLi5dLCBjdXJzb3IsIGhhc01vcmUgfSB9XG4gKiDmr4/lsIHpgq7ku7Y6IHsgbWFpbGJveElkLCBtZXNzYWdlRnJvbSwgc3ViamVjdCwgY3JlYXRlZEF0LCBodG1sLCBhdHRhY2htZW50cywgcmVhZCB9XG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRFbWFpbHMoZW1haWw6IHN0cmluZyk6IFByb21pc2U8RW1haWxbXT4ge1xuICBjb25zdCBhbGxFbWFpbHM6IGFueVtdID0gW107XG4gIGxldCBjdXJzb3I6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICBsZXQgaGFzTW9yZSA9IHRydWU7XG5cbiAgLy8g5pSv5oyB5YiG6aG177yM5b6q546v6I635Y+W5omA5pyJ6YKu5Lu2XG4gIHdoaWxlIChoYXNNb3JlKSB7XG4gICAgY29uc3QgcmVzcG9uc2U6IFJlc3BvbnNlID0gYXdhaXQgZmV0Y2goYCR7QkFTRV9VUkx9L21haWwvYm94YCwge1xuICAgICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgICBoZWFkZXJzOiBERUZBVUxUX0hFQURFUlMsXG4gICAgICBib2R5OiBKU09OLnN0cmluZ2lmeSh7IGFkZHJlc3M6IGVtYWlsLCBjdXJzb3IgfSksXG4gICAgfSk7XG5cbiAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgZW1haWxzOiAke3Jlc3BvbnNlLnN0YXR1c31gKTtcbiAgICB9XG5cbiAgICBjb25zdCBkYXRhOiBhbnkgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7XG5cbiAgICBpZiAoZGF0YS5jb2RlICE9PSAwIHx8ICFkYXRhLmRhdGEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignRmFpbGVkIHRvIGdldCBlbWFpbHMnKTtcbiAgICB9XG5cbiAgICBjb25zdCByb3dzID0gZGF0YS5kYXRhLnJvd3MgfHwgW107XG4gICAgYWxsRW1haWxzLnB1c2goLi4ucm93cyk7XG5cbiAgICBpZiAoZGF0YS5kYXRhLmhhc01vcmUgJiYgZGF0YS5kYXRhLmN1cnNvcikge1xuICAgICAgY3Vyc29yID0gZGF0YS5kYXRhLmN1cnNvcjtcbiAgICB9IGVsc2Uge1xuICAgICAgaGFzTW9yZSA9IGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBhbGxFbWFpbHMubWFwKChyYXc6IGFueSkgPT4gbm9ybWFsaXplRW1haWwocmF3LCBlbWFpbCkpO1xufVxuIl19
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { InternalEmailInfo, Email, Channel } from '../types';
|
|
2
|
-
import { normalizeEmail } from '../normalize';
|
|
3
|
-
|
|
4
|
-
const CHANNEL: Channel = 'tempmail-la';
|
|
5
|
-
const BASE_URL = 'https://tempmail.la/api';
|
|
6
|
-
|
|
7
|
-
const DEFAULT_HEADERS: Record<string, string> = {
|
|
8
|
-
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0',
|
|
9
|
-
'Accept': 'application/json, text/plain, */*',
|
|
10
|
-
'Content-Type': 'application/json',
|
|
11
|
-
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
|
|
12
|
-
'cache-control': 'no-cache',
|
|
13
|
-
'dnt': '1',
|
|
14
|
-
'locale': 'zh-CN',
|
|
15
|
-
'origin': 'https://tempmail.la',
|
|
16
|
-
'platform': 'PC',
|
|
17
|
-
'pragma': 'no-cache',
|
|
18
|
-
'product': 'TEMP_MAIL',
|
|
19
|
-
'referer': 'https://tempmail.la/zh-CN/tempmail',
|
|
20
|
-
'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Microsoft Edge";v="144"',
|
|
21
|
-
'sec-ch-ua-mobile': '?0',
|
|
22
|
-
'sec-ch-ua-platform': '"Windows"',
|
|
23
|
-
'sec-fetch-dest': 'empty',
|
|
24
|
-
'sec-fetch-mode': 'cors',
|
|
25
|
-
'sec-fetch-site': 'same-origin',
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* 创建临时邮箱
|
|
30
|
-
* API: POST /api/mail/create
|
|
31
|
-
* 返回: { code: 0, data: { mailId, address, type, startAt, endAt } }
|
|
32
|
-
*/
|
|
33
|
-
export async function generateEmail(): Promise<InternalEmailInfo> {
|
|
34
|
-
const response = await fetch(`${BASE_URL}/mail/create`, {
|
|
35
|
-
method: 'POST',
|
|
36
|
-
headers: DEFAULT_HEADERS,
|
|
37
|
-
body: JSON.stringify({ turnstile: '' }),
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
if (!response.ok) {
|
|
41
|
-
throw new Error(`Failed to generate email: ${response.status}`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const data = await response.json();
|
|
45
|
-
|
|
46
|
-
if (data.code !== 0 || !data.data) {
|
|
47
|
-
throw new Error('Failed to generate email');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
channel: CHANNEL,
|
|
52
|
-
email: data.data.address,
|
|
53
|
-
expiresAt: data.data.endAt,
|
|
54
|
-
createdAt: data.data.startAt,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* 获取邮件列表
|
|
60
|
-
* API: POST /api/mail/box
|
|
61
|
-
* 请求: { address, cursor }
|
|
62
|
-
* 返回: { code: 0, data: { rows: [...], cursor, hasMore } }
|
|
63
|
-
* 每封邮件: { mailboxId, messageFrom, subject, createdAt, html, attachments, read }
|
|
64
|
-
*/
|
|
65
|
-
export async function getEmails(email: string): Promise<Email[]> {
|
|
66
|
-
const allEmails: any[] = [];
|
|
67
|
-
let cursor: string | null = null;
|
|
68
|
-
let hasMore = true;
|
|
69
|
-
|
|
70
|
-
// 支持分页,循环获取所有邮件
|
|
71
|
-
while (hasMore) {
|
|
72
|
-
const response: Response = await fetch(`${BASE_URL}/mail/box`, {
|
|
73
|
-
method: 'POST',
|
|
74
|
-
headers: DEFAULT_HEADERS,
|
|
75
|
-
body: JSON.stringify({ address: email, cursor }),
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
if (!response.ok) {
|
|
79
|
-
throw new Error(`Failed to get emails: ${response.status}`);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const data: any = await response.json();
|
|
83
|
-
|
|
84
|
-
if (data.code !== 0 || !data.data) {
|
|
85
|
-
throw new Error('Failed to get emails');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const rows = data.data.rows || [];
|
|
89
|
-
allEmails.push(...rows);
|
|
90
|
-
|
|
91
|
-
if (data.data.hasMore && data.data.cursor) {
|
|
92
|
-
cursor = data.data.cursor;
|
|
93
|
-
} else {
|
|
94
|
-
hasMore = false;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return allEmails.map((raw: any) => normalizeEmail(raw, email));
|
|
99
|
-
}
|