generated-email-helpers.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. (function attachGeneratedEmailHelpers(root, factory) {
  2. root.MultiPageGeneratedEmailHelpers = factory();
  3. })(typeof self !== 'undefined' ? self : globalThis, function createGeneratedEmailHelpersModule() {
  4. function createGeneratedEmailHelpers(deps = {}) {
  5. const {
  6. addLog,
  7. buildGeneratedAliasEmail,
  8. buildCloudflareTempEmailHeaders,
  9. CLOUDFLARE_TEMP_EMAIL_GENERATOR,
  10. DUCK_AUTOFILL_URL,
  11. fetch,
  12. fetchIcloudHideMyEmail,
  13. getCloudflareTempEmailAddressFromResponse,
  14. getCloudflareTempEmailConfig,
  15. getState,
  16. joinCloudflareTempEmailUrl,
  17. normalizeCloudflareDomain,
  18. normalizeCloudflareTempEmailAddress,
  19. normalizeEmailGenerator,
  20. isGeneratedAliasProvider,
  21. reuseOrCreateTab,
  22. sendToContentScript,
  23. setEmailState,
  24. throwIfStopped,
  25. } = deps;
  26. function generateCloudflareAliasLocalPart() {
  27. const letters = 'abcdefghijklmnopqrstuvwxyz';
  28. const digits = '0123456789';
  29. const chars = [];
  30. for (let i = 0; i < 6; i++) {
  31. chars.push(letters[Math.floor(Math.random() * letters.length)]);
  32. }
  33. for (let i = 0; i < 4; i++) {
  34. chars.push(digits[Math.floor(Math.random() * digits.length)]);
  35. }
  36. for (let i = chars.length - 1; i > 0; i--) {
  37. const j = Math.floor(Math.random() * (i + 1));
  38. [chars[i], chars[j]] = [chars[j], chars[i]];
  39. }
  40. return chars.join('');
  41. }
  42. async function fetchCloudflareEmail(state, options = {}) {
  43. throwIfStopped();
  44. const latestState = state || await getState();
  45. const domain = normalizeCloudflareDomain(latestState.cloudflareDomain);
  46. if (!domain) {
  47. throw new Error('Cloudflare 域名为空或格式无效。');
  48. }
  49. const localPart = String(options.localPart || '').trim().toLowerCase() || generateCloudflareAliasLocalPart();
  50. const aliasEmail = `${localPart}@${domain}`;
  51. await setEmailState(aliasEmail);
  52. await addLog(`Cloudflare 邮箱:已生成 ${aliasEmail}`, 'ok');
  53. return aliasEmail;
  54. }
  55. function ensureCloudflareTempEmailConfig(state, options = {}) {
  56. const {
  57. requireAdminAuth = false,
  58. requireDomain = false,
  59. } = options;
  60. const config = getCloudflareTempEmailConfig(state);
  61. if (!config.baseUrl) {
  62. throw new Error('Cloudflare Temp Email 服务地址为空或格式无效。');
  63. }
  64. if (requireAdminAuth && !config.adminAuth) {
  65. throw new Error('Cloudflare Temp Email 缺少 Admin Auth。');
  66. }
  67. if (requireDomain && !config.domain) {
  68. throw new Error('Cloudflare Temp Email 域名为空或格式无效。');
  69. }
  70. return config;
  71. }
  72. async function requestCloudflareTempEmailJson(config, path, options = {}) {
  73. const {
  74. method = 'GET',
  75. payload,
  76. searchParams,
  77. timeoutMs = 20000,
  78. } = options;
  79. const url = new URL(joinCloudflareTempEmailUrl(config.baseUrl, path));
  80. if (searchParams && typeof searchParams === 'object') {
  81. for (const [key, value] of Object.entries(searchParams)) {
  82. if (value === undefined || value === null || value === '') continue;
  83. url.searchParams.set(key, String(value));
  84. }
  85. }
  86. const controller = new AbortController();
  87. const timeoutId = setTimeout(() => controller.abort(new Error('timeout')), timeoutMs);
  88. let response;
  89. try {
  90. response = await fetch(url.toString(), {
  91. method,
  92. headers: buildCloudflareTempEmailHeaders(config, {
  93. json: payload !== undefined,
  94. }),
  95. body: payload !== undefined ? JSON.stringify(payload) : undefined,
  96. signal: controller.signal,
  97. });
  98. } catch (err) {
  99. const errorMessage = err?.name === 'AbortError'
  100. ? `Cloudflare Temp Email 请求超时(>${Math.round(timeoutMs / 1000)} 秒)`
  101. : `Cloudflare Temp Email 请求失败:${err.message}`;
  102. throw new Error(errorMessage);
  103. } finally {
  104. clearTimeout(timeoutId);
  105. }
  106. const text = await response.text();
  107. let parsed;
  108. try {
  109. parsed = text ? JSON.parse(text) : {};
  110. } catch {
  111. parsed = text;
  112. }
  113. if (!response.ok) {
  114. const payloadError = typeof parsed === 'object' && parsed
  115. ? (parsed.message || parsed.error || parsed.msg)
  116. : '';
  117. throw new Error(`Cloudflare Temp Email 请求失败:${payloadError || text || `HTTP ${response.status}`}`);
  118. }
  119. return parsed;
  120. }
  121. async function fetchCloudflareTempEmailAddress(state, options = {}) {
  122. throwIfStopped();
  123. const latestState = state || await getState();
  124. const config = ensureCloudflareTempEmailConfig(latestState, {
  125. requireAdminAuth: true,
  126. requireDomain: true,
  127. });
  128. const requestedName = String(options.localPart || options.name || '').trim().toLowerCase() || generateCloudflareAliasLocalPart();
  129. const payload = {
  130. enablePrefix: true,
  131. name: requestedName,
  132. domain: config.domain,
  133. };
  134. const result = await requestCloudflareTempEmailJson(config, '/admin/new_address', {
  135. method: 'POST',
  136. payload,
  137. });
  138. const address = normalizeCloudflareTempEmailAddress(getCloudflareTempEmailAddressFromResponse(result));
  139. if (!address) {
  140. throw new Error('Cloudflare Temp Email 未返回可用邮箱地址。');
  141. }
  142. await setEmailState(address);
  143. await addLog(`Cloudflare Temp Email:已生成 ${address}`, 'ok');
  144. return address;
  145. }
  146. async function fetchDuckEmail(options = {}) {
  147. throwIfStopped();
  148. const { generateNew = true } = options;
  149. await addLog(`Duck 邮箱:正在打开自动填充设置(${generateNew ? '生成新地址' : '复用当前地址'})...`);
  150. await reuseOrCreateTab('duck-mail', DUCK_AUTOFILL_URL);
  151. const result = await sendToContentScript('duck-mail', {
  152. type: 'FETCH_DUCK_EMAIL',
  153. source: 'background',
  154. payload: { generateNew },
  155. });
  156. if (result?.error) {
  157. throw new Error(result.error);
  158. }
  159. if (!result?.email) {
  160. throw new Error('未返回 Duck 邮箱地址。');
  161. }
  162. await setEmailState(result.email);
  163. await addLog(`Duck 邮箱:${result.generated ? '已生成' : '已读取'} ${result.email}`, 'ok');
  164. return result.email;
  165. }
  166. async function fetchManagedAliasEmail(state, options = {}) {
  167. throwIfStopped();
  168. const provider = String(options.mailProvider || state?.mailProvider || '').trim().toLowerCase();
  169. const mergedState = {
  170. ...(state || {}),
  171. mailProvider: provider,
  172. };
  173. if (options.gmailBaseEmail !== undefined) {
  174. mergedState.gmailBaseEmail = String(options.gmailBaseEmail || '').trim();
  175. }
  176. if (options.mail2925BaseEmail !== undefined) {
  177. mergedState.mail2925BaseEmail = String(options.mail2925BaseEmail || '').trim();
  178. }
  179. const email = buildGeneratedAliasEmail(mergedState);
  180. await setEmailState(email);
  181. await addLog(`${provider === 'gmail' ? 'Gmail +tag' : '2925'}:已生成 ${email}`, 'ok');
  182. return email;
  183. }
  184. async function fetchGeneratedEmail(state, options = {}) {
  185. const currentState = state || await getState();
  186. const provider = String(options.mailProvider || currentState.mailProvider || '').trim().toLowerCase();
  187. if (isGeneratedAliasProvider?.(provider)) {
  188. return fetchManagedAliasEmail(currentState, options);
  189. }
  190. const generator = normalizeEmailGenerator(options.generator ?? currentState.emailGenerator);
  191. if (generator === 'custom') {
  192. throw new Error('当前邮箱生成方式为自定义邮箱,请直接填写注册邮箱。');
  193. }
  194. if (generator === 'icloud') {
  195. return fetchIcloudHideMyEmail();
  196. }
  197. if (generator === 'cloudflare') {
  198. return fetchCloudflareEmail(currentState, options);
  199. }
  200. if (generator === CLOUDFLARE_TEMP_EMAIL_GENERATOR) {
  201. return fetchCloudflareTempEmailAddress(currentState, options);
  202. }
  203. return fetchDuckEmail(options);
  204. }
  205. return {
  206. ensureCloudflareTempEmailConfig,
  207. fetchCloudflareEmail,
  208. fetchCloudflareTempEmailAddress,
  209. fetchDuckEmail,
  210. fetchGeneratedEmail,
  211. generateCloudflareAliasLocalPart,
  212. requestCloudflareTempEmailJson,
  213. };
  214. }
  215. return {
  216. createGeneratedEmailHelpers,
  217. };
  218. });