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.
Files changed (47) hide show
  1. package/README.md +43 -38
  2. package/demo/poll-emails.ts +290 -28
  3. package/dist/config.d.ts +16 -0
  4. package/dist/config.js +4 -1
  5. package/dist/index.d.ts +4 -1
  6. package/dist/index.js +33 -16
  7. package/dist/providers/awamail.js +4 -3
  8. package/dist/providers/chatgpt-org-uk.d.ts +1 -1
  9. package/dist/providers/chatgpt-org-uk.js +188 -20
  10. package/dist/providers/dropmail.js +135 -3
  11. package/dist/providers/guerrillamail.js +4 -3
  12. package/dist/providers/linshi-email.d.ts +1 -1
  13. package/dist/providers/linshi-email.js +19 -7
  14. package/dist/providers/linshi-token.d.ts +25 -0
  15. package/dist/providers/linshi-token.js +69 -0
  16. package/dist/providers/mail-tm.js +43 -25
  17. package/dist/providers/maildrop.js +3 -2
  18. package/dist/providers/smail-pw.d.ts +9 -0
  19. package/dist/providers/smail-pw.js +356 -0
  20. package/dist/providers/temp-mail-io.js +5 -4
  21. package/dist/providers/tempmail-lol.js +4 -3
  22. package/dist/providers/tempmail.js +4 -3
  23. package/dist/retry.d.ts +2 -10
  24. package/dist/retry.js +41 -10
  25. package/dist/types.d.ts +6 -1
  26. package/dist/types.js +1 -1
  27. package/package.json +1 -1
  28. package/src/config.ts +16 -0
  29. package/src/index.ts +31 -14
  30. package/src/providers/awamail.ts +3 -2
  31. package/src/providers/chatgpt-org-uk.ts +213 -22
  32. package/src/providers/dropmail.ts +162 -2
  33. package/src/providers/guerrillamail.ts +3 -2
  34. package/src/providers/linshi-email.ts +24 -7
  35. package/src/providers/linshi-token.ts +86 -0
  36. package/src/providers/mail-tm.ts +43 -24
  37. package/src/providers/maildrop.ts +2 -1
  38. package/src/providers/smail-pw.ts +382 -0
  39. package/src/providers/temp-mail-io.ts +4 -3
  40. package/src/providers/tempmail-lol.ts +3 -2
  41. package/src/providers/tempmail.ts +3 -2
  42. package/src/retry.ts +42 -9
  43. package/src/types.ts +6 -1
  44. package/test/example.ts +183 -4
  45. package/dist/providers/tempmail-la.d.ts +0 -15
  46. package/dist/providers/tempmail-la.js +0 -89
  47. 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
- * 网络错误、超时、HTTP 4xx/5xx 错误均可重试
36
- * 仅参数校验类错误(由 SDK 内部抛出)不重试
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
- * - 不可恢复的错误(4xx 参数错误等)直接抛出不重试
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 config = getConfig();
136
- const effectiveTimeout = timeoutMs ?? config.timeout ?? DEFAULT_RETRY_OPTIONS.timeout;
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
- /* 使用自定义 fetch 或原生 fetch */
141
- const fetchFn = config.customFetch || fetch;
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' | 'tempmail-la' | 'temp-mail-io' | 'awamail' | 'mail-tm' | 'dropmail' | 'guerrillamail' | 'maildrop';
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[] = ['tempmail', 'linshi-email', 'tempmail-lol', 'chatgpt-org-uk', 'tempmail-la', 'temp-mail-io', 'awamail', 'mail-tm', 'dropmail'];
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
- }