icloud-utils.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. (function icloudUtilsModule(root, factory) {
  2. if (typeof module !== 'undefined' && module.exports) {
  3. module.exports = factory();
  4. return;
  5. }
  6. root.IcloudUtils = factory();
  7. })(typeof self !== 'undefined' ? self : globalThis, function createIcloudUtils() {
  8. function normalizeIcloudHost(rawHost) {
  9. const host = String(rawHost || '').trim().toLowerCase();
  10. if (!host) return '';
  11. if (host === 'icloud.com' || host === 'www.icloud.com' || host === 'setup.icloud.com') return 'icloud.com';
  12. if (host === 'icloud.com.cn' || host === 'www.icloud.com.cn' || host === 'setup.icloud.com.cn') return 'icloud.com.cn';
  13. return '';
  14. }
  15. function getConfiguredIcloudHostPreference(stateOrValue = '') {
  16. const preference = typeof stateOrValue === 'object'
  17. ? String(stateOrValue?.icloudHostPreference || '').trim().toLowerCase()
  18. : String(stateOrValue || '').trim().toLowerCase();
  19. if (!preference || preference === 'auto') return '';
  20. return normalizeIcloudHost(preference);
  21. }
  22. function getIcloudLoginUrlForHost(host) {
  23. const normalizedHost = normalizeIcloudHost(host);
  24. if (normalizedHost === 'icloud.com') return 'https://www.icloud.com/';
  25. if (normalizedHost === 'icloud.com.cn') return 'https://www.icloud.com.cn/';
  26. return '';
  27. }
  28. function getIcloudMailUrlForHost(host) {
  29. const normalizedHost = normalizeIcloudHost(host);
  30. if (normalizedHost === 'icloud.com') return 'https://www.icloud.com/mail/';
  31. if (normalizedHost === 'icloud.com.cn') return 'https://www.icloud.com.cn/mail/';
  32. return '';
  33. }
  34. function getIcloudSetupUrlForHost(host) {
  35. const normalizedHost = normalizeIcloudHost(host);
  36. if (normalizedHost === 'icloud.com') return 'https://setup.icloud.com/setup/ws/1';
  37. if (normalizedHost === 'icloud.com.cn') return 'https://setup.icloud.com.cn/setup/ws/1';
  38. return '';
  39. }
  40. function getIcloudHostHintFromMessage(message) {
  41. const lower = String(message || '').toLowerCase();
  42. if (lower.includes('setup.icloud.com.cn') || lower.includes('www.icloud.com.cn') || lower.includes('icloud.com.cn')) {
  43. return 'icloud.com.cn';
  44. }
  45. if (lower.includes('setup.icloud.com') || lower.includes('www.icloud.com') || lower.includes('icloud.com')) {
  46. return 'icloud.com';
  47. }
  48. return '';
  49. }
  50. function normalizeBooleanMap(rawValue = {}) {
  51. if (!rawValue || typeof rawValue !== 'object' || Array.isArray(rawValue)) {
  52. return {};
  53. }
  54. return Object.entries(rawValue).reduce((result, [key, value]) => {
  55. const normalizedKey = String(key || '').trim().toLowerCase();
  56. if (!normalizedKey) {
  57. return result;
  58. }
  59. result[normalizedKey] = Boolean(value);
  60. return result;
  61. }, {});
  62. }
  63. function toNormalizedEmailSet(values = []) {
  64. if (values instanceof Set) {
  65. return new Set(Array.from(values, (item) => String(item || '').trim().toLowerCase()).filter(Boolean));
  66. }
  67. if (Array.isArray(values)) {
  68. return new Set(values.map((item) => String(item || '').trim().toLowerCase()).filter(Boolean));
  69. }
  70. if (values && typeof values === 'object') {
  71. const normalizedMap = normalizeBooleanMap(values);
  72. return new Set(Object.entries(normalizedMap)
  73. .filter(([, value]) => value)
  74. .map(([email]) => email));
  75. }
  76. return new Set();
  77. }
  78. function findIcloudAliasArray(node, depth = 0) {
  79. if (!node || depth > 4) return null;
  80. if (Array.isArray(node)) {
  81. return node.some((item) => item && typeof item === 'object') ? node : null;
  82. }
  83. if (typeof node !== 'object') return null;
  84. const priorityKeys = ['hmeEmails', 'hmeEmailList', 'hmeList', 'hmes', 'aliases', 'items'];
  85. for (const key of priorityKeys) {
  86. if (Array.isArray(node[key])) return node[key];
  87. }
  88. for (const value of Object.values(node)) {
  89. const nested = findIcloudAliasArray(value, depth + 1);
  90. if (nested) return nested;
  91. }
  92. return null;
  93. }
  94. function normalizeIcloudAliasRecord(raw, options = {}) {
  95. const usedEmails = toNormalizedEmailSet(options.usedEmails);
  96. const preservedEmails = toNormalizedEmailSet(options.preservedEmails);
  97. const anonymousId = String(raw?.anonymousId || raw?.id || '').trim();
  98. const email = String(
  99. raw?.hme
  100. || raw?.email
  101. || raw?.alias
  102. || raw?.address
  103. || raw?.metaData?.hme
  104. || ''
  105. ).trim().toLowerCase();
  106. if (!email || !email.includes('@')) return null;
  107. const label = String(raw?.label || raw?.metaData?.label || '').trim();
  108. const note = String(raw?.note || raw?.metaData?.note || '').trim();
  109. const state = String(raw?.state || raw?.status || '').trim().toLowerCase();
  110. const createdAt = raw?.createTimestamp
  111. || raw?.createTime
  112. || raw?.createdAt
  113. || raw?.createdDate
  114. || null;
  115. return {
  116. anonymousId,
  117. email,
  118. label,
  119. note,
  120. state,
  121. active: raw?.active !== false && raw?.isActive !== false && state !== 'inactive' && state !== 'deleted',
  122. used: usedEmails.has(email),
  123. preserved: preservedEmails.has(email),
  124. createdAt,
  125. };
  126. }
  127. function normalizeIcloudAliasList(response, options = {}) {
  128. const aliases = findIcloudAliasArray(response);
  129. if (!aliases) return [];
  130. return aliases
  131. .map((alias) => normalizeIcloudAliasRecord(alias, options))
  132. .filter(Boolean)
  133. .sort((left, right) => {
  134. if (left.active !== right.active) return left.active ? -1 : 1;
  135. if (left.used !== right.used) return left.used ? 1 : -1;
  136. return String(left.email).localeCompare(String(right.email));
  137. });
  138. }
  139. function pickReusableIcloudAlias(aliases = []) {
  140. return (Array.isArray(aliases) ? aliases : []).find((alias) => alias?.active && !alias?.used) || null;
  141. }
  142. function findIcloudAliasByEmail(aliases = [], email = '') {
  143. const normalizedEmail = String(email || '').trim().toLowerCase();
  144. if (!normalizedEmail) return null;
  145. return (Array.isArray(aliases) ? aliases : [])
  146. .find((alias) => String(alias?.email || '').trim().toLowerCase() === normalizedEmail) || null;
  147. }
  148. return {
  149. findIcloudAliasArray,
  150. findIcloudAliasByEmail,
  151. getConfiguredIcloudHostPreference,
  152. getIcloudHostHintFromMessage,
  153. getIcloudLoginUrlForHost,
  154. getIcloudMailUrlForHost,
  155. getIcloudSetupUrlForHost,
  156. normalizeBooleanMap,
  157. normalizeIcloudAliasList,
  158. normalizeIcloudAliasRecord,
  159. normalizeIcloudHost,
  160. pickReusableIcloudAlias,
  161. toNormalizedEmailSet,
  162. };
  163. });