mirror of
https://github.com/halfwaystudent/douyin-sparkflow.git
synced 2026-09-06 16:07:22 +08:00
Import sanitized project structure and GitHub docs
This commit is contained in:
@@ -0,0 +1,389 @@
|
||||
(function attachCloudflareTempEmailHelpers(globalScope) {
|
||||
const PRIMARY_LOCAL_PART_WORD_BANKS = [
|
||||
[
|
||||
'anew', 'brisk', 'candid', 'fervent', 'gentle',
|
||||
'humble', 'jovial', 'kindly', 'lucid', 'mellow',
|
||||
'nimble', 'open', 'polished', 'quick', 'rosy',
|
||||
'steady', 'tidy', 'upbeat', 'vantage', 'thunderous',
|
||||
],
|
||||
[
|
||||
'angled', 'bordered', 'crisp', 'eager', 'frozen',
|
||||
'golden', 'dotted', 'hushed', 'jagged', 'layered',
|
||||
'mellowed', 'narrow', 'opal', 'primal', 'quiet',
|
||||
'rippled', 'sunlit', 'trimmed', 'velvet', 'thumping',
|
||||
],
|
||||
[
|
||||
'anchor', 'beacon', 'cinder', 'drifter', 'ember',
|
||||
'meadow', 'nickel', 'orbit', 'prairie', 'quartz',
|
||||
'rivet', 'latch', 'signal', 'thicket', 'uplift',
|
||||
'voyager', 'willow', 'yonder', 'zephyr', 'wilderness',
|
||||
],
|
||||
];
|
||||
const EXTENDED_LOCAL_PART_WORD_BANKS = [
|
||||
['aurora', 'breezy', 'copper', 'drizzle', 'whimsy', 'glimmer', 'sapphire', 'harbor', 'inkwell', 'juniper'],
|
||||
['almond', 'bronzed', 'cobbled', 'dappled', 'marbled', 'northern', 'moonlit', 'orchard', 'plaited', 'radiant'],
|
||||
['acorn', 'bramble', 'citadel', 'daybreak', 'solstice', 'harvest', 'treeline', 'updraft', 'wildfire', 'yearling'],
|
||||
];
|
||||
const MID_EXTENDED_LOCAL_PART_WORD_BANKS = [
|
||||
[
|
||||
'citrine', 'dapper', 'elmwood', 'feather', 'gossamer',
|
||||
'halcyon', 'ivory', 'kestrel', 'lively', 'mistral',
|
||||
],
|
||||
[
|
||||
'lantern', 'mosaic', 'notched', 'oaken', 'pearled',
|
||||
'quilted', 'rusted', 'silken', 'tapered', 'umber',
|
||||
],
|
||||
[
|
||||
'meridian', 'northstar', 'overlook', 'peninsula', 'quickstep',
|
||||
'ridgeline', 'starling', 'turnpike', 'undertow', 'vale',
|
||||
],
|
||||
];
|
||||
const TOP_EXTENDED_LOCAL_PART_WORD_BANKS = [
|
||||
['whimsy', 'afterglow', 'birdsong', 'clearwater', 'dreamscape', 'everbright', 'firecrest', 'hinterland', 'isleward', 'keystone'],
|
||||
['marbled', 'auric', 'blossomed', 'celestial', 'dawnlit', 'embered', 'frosted', 'gilded', 'heartland', 'ironbound'],
|
||||
['solstice', 'airstream', 'brightside', 'crestfall', 'dovetail', 'elmshade', 'fieldstone', 'goldleaf', 'highwater', 'ivytrail'],
|
||||
];
|
||||
const HIGH_EXTENDED_LOCAL_PART_WORD_BANKS = [
|
||||
[
|
||||
'adrift', 'bellwether', 'cedar', 'daystar', 'emberglow',
|
||||
'fjord', 'glasswing', 'horizon', 'islander', 'jetstream',
|
||||
'kingsley', 'longview', 'moonrise', 'northbound', 'oakleaf',
|
||||
'pinelight', 'quasar', 'runestone', 'seaborne', 'trailhead',
|
||||
],
|
||||
[
|
||||
'bronzed', 'cobbled', 'drifted', 'etched', 'fernlike',
|
||||
'granulated', 'honeyed', 'indigo', 'jadeite', 'kindled',
|
||||
'lacquered', 'measured', 'navy', 'opaline', 'painted',
|
||||
'quenched', 'reeded', 'sanded', 'tempered', 'uplifted',
|
||||
],
|
||||
[
|
||||
'cosmos', 'drumbeat', 'everglade', 'fjordline', 'grove',
|
||||
'headland', 'icefield', 'journey', 'knoll', 'lagoon',
|
||||
'moorland', 'narrows', 'outpost', 'passage', 'quarry',
|
||||
'riverbend', 'shoal', 'tideline', 'upland', 'vista',
|
||||
],
|
||||
];
|
||||
const APEX_EXTENDED_LOCAL_PART_WORD_BANKS = [
|
||||
[
|
||||
'atlas', 'bluebird', 'crestline', 'dewdrop', 'eastwind',
|
||||
'flare', 'glen', 'harvestmoon', 'iris', 'joyride',
|
||||
'kindred', 'larkspur', 'midway', 'nightfall', 'overture',
|
||||
'prairiesky', 'quill', 'rosewood', 'sunflare', 'turnstone',
|
||||
'uplight', 'violet', 'wildwood', 'xylia', 'yearbright',
|
||||
'zenway', 'amberline', 'brightshore', 'cloudrest', 'dawnsong',
|
||||
],
|
||||
[
|
||||
'bronze', 'coppered', 'dawnwashed', 'everspun', 'firelit',
|
||||
'glazed', 'harbored', 'ivied', 'jade', 'keelmarked',
|
||||
'leafed', 'misted', 'nacre', 'oakmoss', 'pearlstone',
|
||||
'quartzite', 'rainsoft', 'sunwashed', 'timbered', 'umbered',
|
||||
'velour', 'windcut', 'xanthic', 'yellowed', 'zestful',
|
||||
'ashen', 'brightened', 'coasted', 'deepwater', 'emberlit',
|
||||
],
|
||||
[
|
||||
'cosmos', 'daybreak', 'evercrest', 'fieldpath', 'groveside',
|
||||
'hilltop', 'inlet', 'junction', 'keyway', 'lakeside',
|
||||
'moonpath', 'nest', 'oakridge', 'portside', 'quayside',
|
||||
'riverside', 'stonepath', 'trailway', 'uplook', 'valecrest',
|
||||
'woodline', 'xylogrove', 'yardarm', 'zenithal', 'aircrest',
|
||||
'bayshore', 'crossing', 'driftway', 'elmtrail', 'foreside',
|
||||
],
|
||||
];
|
||||
const BASE_EXTENDED_WORD_RANGE_START = 0.91;
|
||||
const MID_EXTENDED_WORD_RANGE_START = 0.95;
|
||||
const TOP_EXTENDED_WORD_RANGE_START = 0.97;
|
||||
const HIGH_EXTENDED_WORD_RANGE_START = 0.985;
|
||||
const APEX_EXTENDED_WORD_RANGE_START = 0.993;
|
||||
|
||||
function normalizeWhitespace(value) {
|
||||
return String(value || '').replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
|
||||
function combineDistinctTextParts(parts = []) {
|
||||
const seen = new Set();
|
||||
const normalizedParts = [];
|
||||
|
||||
for (const part of parts) {
|
||||
const value = normalizeWhitespace(part);
|
||||
if (!value || seen.has(value)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(value);
|
||||
normalizedParts.push(value);
|
||||
}
|
||||
|
||||
return normalizedParts.join(' ');
|
||||
}
|
||||
|
||||
function normalizeText(value) {
|
||||
return normalizeWhitespace(value).toLowerCase();
|
||||
}
|
||||
|
||||
function normalizeEmail(value) {
|
||||
return normalizeText(value);
|
||||
}
|
||||
|
||||
function normalizeDomainSuffix(value) {
|
||||
const match = normalizeText(value).match(/@?([a-z0-9.-]+\.[a-z]{2,})/i);
|
||||
return match ? match[1].toLowerCase() : '';
|
||||
}
|
||||
|
||||
function toFiniteNumber(value) {
|
||||
const numeric = typeof value === 'number' ? value : Number(value);
|
||||
return Number.isFinite(numeric) ? numeric : null;
|
||||
}
|
||||
|
||||
function parseAdminTimestamp(value) {
|
||||
const match = normalizeWhitespace(value).match(
|
||||
/^(\d{4})\/(\d{1,2})\/(\d{1,2})\s+(\d{1,2}):(\d{2})(?::(\d{2}))?$/
|
||||
);
|
||||
if (!match) return null;
|
||||
|
||||
const [, year, month, day, hour, minute, second = '0'] = match;
|
||||
const timestamp = new Date(
|
||||
Number(year),
|
||||
Number(month) - 1,
|
||||
Number(day),
|
||||
Number(hour),
|
||||
Number(minute),
|
||||
Number(second)
|
||||
).getTime();
|
||||
|
||||
return Number.isFinite(timestamp) ? timestamp : null;
|
||||
}
|
||||
|
||||
function extractVerificationCode(text) {
|
||||
const content = String(text || '');
|
||||
|
||||
const matchCn = content.match(/(?:代码为|验证码[^0-9]*?)[\s::]*(\d{6})/);
|
||||
if (matchCn) return matchCn[1];
|
||||
|
||||
const matchEn = content.match(/code[:\s]+is[:\s]+(\d{6})|code[:\s]+(\d{6})/i);
|
||||
if (matchEn) return matchEn[1] || matchEn[2];
|
||||
|
||||
const match6 = content.match(/\b(\d{6})\b/);
|
||||
if (match6) return match6[1];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function decodeBase64Url(segment) {
|
||||
const base64 = String(segment || '')
|
||||
.replace(/-/g, '+')
|
||||
.replace(/_/g, '/');
|
||||
const padding = base64.length % 4 === 0 ? '' : '='.repeat(4 - (base64.length % 4));
|
||||
const padded = base64 + padding;
|
||||
|
||||
if (typeof Buffer !== 'undefined') {
|
||||
return Buffer.from(padded, 'base64').toString('utf8');
|
||||
}
|
||||
|
||||
if (typeof atob === 'function') {
|
||||
return atob(padded);
|
||||
}
|
||||
|
||||
throw new Error('No base64 decoder available.');
|
||||
}
|
||||
|
||||
function decodeJwtPayload(token) {
|
||||
const parts = String(token || '').split('.');
|
||||
if (parts.length < 2) return null;
|
||||
|
||||
try {
|
||||
return JSON.parse(decodeBase64Url(parts[1]));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function parseCloudflareMailboxCredential(token) {
|
||||
const jwtMatch = String(token || '').match(/[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/);
|
||||
if (!jwtMatch) return null;
|
||||
|
||||
const payload = decodeJwtPayload(jwtMatch[0]);
|
||||
const email = normalizeEmail(payload?.address || payload?.email || '');
|
||||
if (!email || !email.includes('@')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [localPart, ...domainParts] = email.split('@');
|
||||
const domain = domainParts.join('@');
|
||||
|
||||
return {
|
||||
addressId: toFiniteNumber(payload?.address_id),
|
||||
domain,
|
||||
email,
|
||||
localPart,
|
||||
provenance: 'created',
|
||||
};
|
||||
}
|
||||
|
||||
function pickWord(words, randomFn) {
|
||||
const randomValue = Math.max(0, Math.min(0.999999999999, Number(randomFn())));
|
||||
return words[Math.floor(randomValue * words.length)] || words[0];
|
||||
}
|
||||
|
||||
function pickReadableWord(bankIndex, randomFn) {
|
||||
const randomValue = Math.max(0, Math.min(0.999999999999, Number(randomFn())));
|
||||
const primaryWords = PRIMARY_LOCAL_PART_WORD_BANKS[bankIndex] || [];
|
||||
const extendedWords = EXTENDED_LOCAL_PART_WORD_BANKS[bankIndex] || [];
|
||||
const midExtendedWords = MID_EXTENDED_LOCAL_PART_WORD_BANKS[bankIndex] || [];
|
||||
const topExtendedWords = TOP_EXTENDED_LOCAL_PART_WORD_BANKS[bankIndex] || [];
|
||||
const highExtendedWords = HIGH_EXTENDED_LOCAL_PART_WORD_BANKS[bankIndex] || [];
|
||||
const apexExtendedWords = APEX_EXTENDED_LOCAL_PART_WORD_BANKS[bankIndex] || [];
|
||||
|
||||
if (apexExtendedWords.length > 0 && randomValue >= APEX_EXTENDED_WORD_RANGE_START) {
|
||||
const apexExtendedSpan = 1 - APEX_EXTENDED_WORD_RANGE_START;
|
||||
const apexExtendedValue = Math.min(0.999999999999, (randomValue - APEX_EXTENDED_WORD_RANGE_START) / apexExtendedSpan);
|
||||
return pickWord(apexExtendedWords, () => apexExtendedValue);
|
||||
}
|
||||
|
||||
if (highExtendedWords.length > 0 && randomValue >= HIGH_EXTENDED_WORD_RANGE_START) {
|
||||
const highExtendedSpan = APEX_EXTENDED_WORD_RANGE_START - HIGH_EXTENDED_WORD_RANGE_START;
|
||||
const highExtendedValue = Math.min(0.999999999999, (randomValue - HIGH_EXTENDED_WORD_RANGE_START) / highExtendedSpan);
|
||||
return pickWord(highExtendedWords, () => highExtendedValue);
|
||||
}
|
||||
|
||||
if (topExtendedWords.length > 0 && randomValue >= TOP_EXTENDED_WORD_RANGE_START) {
|
||||
const topExtendedSpan = HIGH_EXTENDED_WORD_RANGE_START - TOP_EXTENDED_WORD_RANGE_START;
|
||||
const topExtendedValue = Math.min(0.999999999999, (randomValue - TOP_EXTENDED_WORD_RANGE_START) / topExtendedSpan);
|
||||
return pickWord(topExtendedWords, () => topExtendedValue);
|
||||
}
|
||||
|
||||
if (midExtendedWords.length > 0 && randomValue >= MID_EXTENDED_WORD_RANGE_START) {
|
||||
const midExtendedSpan = TOP_EXTENDED_WORD_RANGE_START - MID_EXTENDED_WORD_RANGE_START;
|
||||
const midExtendedValue = Math.min(0.999999999999, (randomValue - MID_EXTENDED_WORD_RANGE_START) / midExtendedSpan);
|
||||
return pickWord(midExtendedWords, () => midExtendedValue);
|
||||
}
|
||||
|
||||
if (extendedWords.length > 0 && randomValue >= BASE_EXTENDED_WORD_RANGE_START) {
|
||||
const extendedSpan = MID_EXTENDED_WORD_RANGE_START - BASE_EXTENDED_WORD_RANGE_START;
|
||||
const extendedValue = Math.min(0.999999999999, (randomValue - BASE_EXTENDED_WORD_RANGE_START) / extendedSpan);
|
||||
return pickWord(extendedWords, () => extendedValue);
|
||||
}
|
||||
|
||||
return pickWord(primaryWords, () => randomValue);
|
||||
}
|
||||
|
||||
function generateReadableLocalPart(randomFn = Math.random, maxLength = 24) {
|
||||
for (let attempt = 1; attempt <= 20; attempt++) {
|
||||
const value = PRIMARY_LOCAL_PART_WORD_BANKS
|
||||
.map((_, bankIndex) => pickReadableWord(bankIndex, randomFn))
|
||||
.join('-');
|
||||
|
||||
if (value.length <= maxLength) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return 'anew-dotted-latch';
|
||||
}
|
||||
|
||||
function pickRandomSuffix(options = [], randomFn = Math.random) {
|
||||
const seen = new Set();
|
||||
const suffixes = [];
|
||||
|
||||
for (const option of options) {
|
||||
const suffix = normalizeDomainSuffix(option);
|
||||
if (!suffix || seen.has(suffix)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(suffix);
|
||||
suffixes.push(suffix);
|
||||
}
|
||||
|
||||
if (suffixes.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return pickWord(suffixes, randomFn);
|
||||
}
|
||||
|
||||
function compareMessageIds(left, right) {
|
||||
const leftNumber = toFiniteNumber(left);
|
||||
const rightNumber = toFiniteNumber(right);
|
||||
|
||||
if (leftNumber !== null && rightNumber !== null) {
|
||||
return rightNumber - leftNumber;
|
||||
}
|
||||
|
||||
return String(right || '').localeCompare(String(left || ''));
|
||||
}
|
||||
|
||||
function selectVerificationMessage(messages = [], options = {}) {
|
||||
const targetEmail = normalizeEmail(options.targetEmail || '');
|
||||
const senderFilters = (options.senderFilters || []).map(normalizeText);
|
||||
const subjectFilters = (options.subjectFilters || []).map(normalizeText);
|
||||
const filterAfterTimestamp = toFiniteNumber(options.filterAfterTimestamp) || 0;
|
||||
const candidates = [];
|
||||
|
||||
for (const message of messages) {
|
||||
const matchedEmail = normalizeEmail(message?.matchedEmail || message?.toEmail || '');
|
||||
if (targetEmail && matchedEmail && matchedEmail !== targetEmail) {
|
||||
continue;
|
||||
}
|
||||
if (targetEmail && !matchedEmail) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const subject = normalizeWhitespace(message?.subject || '');
|
||||
const combinedText = normalizeWhitespace(message?.combinedText || '');
|
||||
const sender = normalizeText(message?.sender || '');
|
||||
const searchText = normalizeText(`${subject} ${combinedText}`);
|
||||
const code = extractVerificationCode(`${subject} ${combinedText}`);
|
||||
if (!code) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const senderMatch = senderFilters.length === 0
|
||||
|| senderFilters.some((filter) => sender.includes(filter) || searchText.includes(filter));
|
||||
const subjectMatch = subjectFilters.length === 0
|
||||
|| subjectFilters.some((filter) => normalizeText(subject).includes(filter) || searchText.includes(filter));
|
||||
|
||||
if (!senderMatch && !subjectMatch) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const emailTimestamp = toFiniteNumber(message?.emailTimestamp)
|
||||
|| parseAdminTimestamp(message?.timestampText || '');
|
||||
|
||||
if (filterAfterTimestamp > 0 && (!emailTimestamp || emailTimestamp <= filterAfterTimestamp)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
candidates.push({
|
||||
code,
|
||||
emailTimestamp: emailTimestamp || 0,
|
||||
matchedEmail,
|
||||
messageId: message?.messageId ?? null,
|
||||
subject: subject || null,
|
||||
});
|
||||
}
|
||||
|
||||
candidates.sort((left, right) => {
|
||||
if (left.emailTimestamp !== right.emailTimestamp) {
|
||||
return right.emailTimestamp - left.emailTimestamp;
|
||||
}
|
||||
return compareMessageIds(left.messageId, right.messageId);
|
||||
});
|
||||
|
||||
return candidates[0] || null;
|
||||
}
|
||||
|
||||
const api = {
|
||||
combineDistinctTextParts,
|
||||
extractVerificationCode,
|
||||
generateReadableLocalPart,
|
||||
normalizeDomainSuffix,
|
||||
parseAdminTimestamp,
|
||||
parseCloudflareMailboxCredential,
|
||||
pickRandomSuffix,
|
||||
selectVerificationMessage,
|
||||
};
|
||||
|
||||
globalScope.MultiPageCloudflareTempEmail = api;
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = api;
|
||||
}
|
||||
})(typeof globalThis !== 'undefined' ? globalThis : this);
|
||||
@@ -0,0 +1,99 @@
|
||||
(function attachEmailProviderHelpers(globalScope) {
|
||||
const DEFAULT_CLOUDFLARE_TEMP_EMAIL_ADMIN_URL = 'https://mail.cloudflare.com/admin';
|
||||
const EMAIL_PROVIDER_DUCK = 'duckduckgo';
|
||||
const EMAIL_PROVIDER_RELAY_FIREFOX = 'relay_firefox';
|
||||
const EMAIL_PROVIDER_CLOUDFLARE_TEMP_EMAIL = 'cloudflare_temp_email';
|
||||
|
||||
function normalizeEmailProvider(value) {
|
||||
if (value === EMAIL_PROVIDER_RELAY_FIREFOX) {
|
||||
return EMAIL_PROVIDER_RELAY_FIREFOX;
|
||||
}
|
||||
if (value === EMAIL_PROVIDER_CLOUDFLARE_TEMP_EMAIL) {
|
||||
return EMAIL_PROVIDER_CLOUDFLARE_TEMP_EMAIL;
|
||||
}
|
||||
return EMAIL_PROVIDER_DUCK;
|
||||
}
|
||||
|
||||
function isRelayFirefoxProvider(value) {
|
||||
return normalizeEmailProvider(value) === EMAIL_PROVIDER_RELAY_FIREFOX;
|
||||
}
|
||||
|
||||
function isCloudflareTempEmailProvider(value) {
|
||||
return normalizeEmailProvider(value) === EMAIL_PROVIDER_CLOUDFLARE_TEMP_EMAIL;
|
||||
}
|
||||
|
||||
function getEmailProviderDisplayName(value) {
|
||||
if (isRelayFirefoxProvider(value)) {
|
||||
return 'Firefox Relay';
|
||||
}
|
||||
if (isCloudflareTempEmailProvider(value)) {
|
||||
return 'Cloudflare Temp Email';
|
||||
}
|
||||
return 'DuckDuckGo';
|
||||
}
|
||||
|
||||
function shouldUseEmailSourceForVerification(value) {
|
||||
return isCloudflareTempEmailProvider(value);
|
||||
}
|
||||
|
||||
function shouldSkipStep9Cleanup(value) {
|
||||
return !isRelayFirefoxProvider(value);
|
||||
}
|
||||
|
||||
function normalizeCloudflareTempEmailAdminUrl(value) {
|
||||
const raw = String(value || '').trim();
|
||||
if (!raw) {
|
||||
return DEFAULT_CLOUDFLARE_TEMP_EMAIL_ADMIN_URL;
|
||||
}
|
||||
|
||||
const candidate = /^[a-z][a-z0-9+.-]*:\/\//i.test(raw) ? raw : `https://${raw}`;
|
||||
|
||||
try {
|
||||
const parsed = new URL(candidate);
|
||||
parsed.pathname = parsed.pathname.replace(/\/+$/, '') || '/';
|
||||
return parsed.toString();
|
||||
} catch {
|
||||
return DEFAULT_CLOUDFLARE_TEMP_EMAIL_ADMIN_URL;
|
||||
}
|
||||
}
|
||||
|
||||
function getNextRelayMaskLabel(labels = []) {
|
||||
const used = new Set();
|
||||
|
||||
for (const rawLabel of labels) {
|
||||
const match = String(rawLabel || '').trim().match(/^t(\d+)$/i);
|
||||
if (!match) continue;
|
||||
const nextValue = Number(match[1]);
|
||||
if (Number.isInteger(nextValue) && nextValue > 0) {
|
||||
used.add(nextValue);
|
||||
}
|
||||
}
|
||||
|
||||
let candidate = 1;
|
||||
while (used.has(candidate)) {
|
||||
candidate += 1;
|
||||
}
|
||||
return `t${candidate}`;
|
||||
}
|
||||
|
||||
const api = {
|
||||
DEFAULT_CLOUDFLARE_TEMP_EMAIL_ADMIN_URL,
|
||||
EMAIL_PROVIDER_CLOUDFLARE_TEMP_EMAIL,
|
||||
EMAIL_PROVIDER_DUCK,
|
||||
EMAIL_PROVIDER_RELAY_FIREFOX,
|
||||
getEmailProviderDisplayName,
|
||||
getNextRelayMaskLabel,
|
||||
isCloudflareTempEmailProvider,
|
||||
isRelayFirefoxProvider,
|
||||
normalizeCloudflareTempEmailAdminUrl,
|
||||
normalizeEmailProvider,
|
||||
shouldUseEmailSourceForVerification,
|
||||
shouldSkipStep9Cleanup,
|
||||
};
|
||||
|
||||
globalScope.MultiPageEmailProvider = api;
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = api;
|
||||
}
|
||||
})(typeof globalThis !== 'undefined' ? globalThis : this);
|
||||
@@ -0,0 +1,85 @@
|
||||
(function attachOAuthFlowHelpers(globalScope) {
|
||||
const EXACT_CONSENT_PATH = '/sign-in-with-chatgpt/codex/consent';
|
||||
const SIGN_IN_WITH_CHATGPT_PATH_SEGMENT = '/sign-in-with-chatgpt/';
|
||||
|
||||
function parseUrl(input) {
|
||||
if (!input || typeof input !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return new URL(input);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function isConsentUrl(url) {
|
||||
const parsed = parseUrl(url);
|
||||
if (!parsed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parsed.pathname === EXACT_CONSENT_PATH;
|
||||
}
|
||||
|
||||
function isConsentPageState(state = {}) {
|
||||
const { hasVisibleContinueButton = false, url = '' } = state;
|
||||
if (isConsentUrl(url)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const parsed = parseUrl(url);
|
||||
if (!parsed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parsed.pathname.includes(SIGN_IN_WITH_CHATGPT_PATH_SEGMENT) && Boolean(hasVisibleContinueButton);
|
||||
}
|
||||
|
||||
function hasAnyConsentPageState(states = []) {
|
||||
return states.some((state) => isConsentPageState(state));
|
||||
}
|
||||
|
||||
function isLoopbackCallbackUrl(url) {
|
||||
const parsed = parseUrl(url);
|
||||
if (!parsed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parsed.hostname === 'localhost'
|
||||
|| parsed.hostname === '127.0.0.1'
|
||||
|| parsed.hostname === '::1'
|
||||
|| parsed.hostname === '[::1]';
|
||||
}
|
||||
|
||||
function findLoopbackCallbackUrl(candidates = []) {
|
||||
for (const candidate of candidates) {
|
||||
if (isLoopbackCallbackUrl(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const api = {
|
||||
EXACT_CONSENT_PATH,
|
||||
SIGN_IN_WITH_CHATGPT_PATH_SEGMENT,
|
||||
findLoopbackCallbackUrl,
|
||||
hasAnyConsentPageState,
|
||||
isConsentPageState,
|
||||
isConsentUrl,
|
||||
isLoopbackCallbackUrl,
|
||||
};
|
||||
|
||||
globalScope.MultiPageOAuthFlow = api;
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = api;
|
||||
}
|
||||
})(typeof globalThis !== 'undefined' ? globalThis : this);
|
||||
@@ -0,0 +1,67 @@
|
||||
(function attachQQMailHelpers(globalScope) {
|
||||
function normalizeText(value) {
|
||||
return (value || '').toLowerCase();
|
||||
}
|
||||
|
||||
function extractVerificationCode(text) {
|
||||
const matchCn = text.match(/(?:代码为|验证码[^0-9]*?)[\s::]*(\d{6})/);
|
||||
if (matchCn) return matchCn[1];
|
||||
|
||||
const matchEn = text.match(/code[:\s]+is[:\s]+(\d{6})|code[:\s]+(\d{6})/i);
|
||||
if (matchEn) return matchEn[1] || matchEn[2];
|
||||
|
||||
const match6 = text.match(/\b(\d{6})\b/);
|
||||
if (match6) return match6[1];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function findNewQQVerificationCode(messages = [], options = {}) {
|
||||
const existingMailIds = new Set(options.existingMailIds || []);
|
||||
const senderFilters = options.senderFilters || [];
|
||||
const subjectFilters = options.subjectFilters || [];
|
||||
|
||||
for (const message of messages) {
|
||||
const mailId = message.mailId || '';
|
||||
if (!mailId || existingMailIds.has(mailId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const sender = normalizeText(message.sender);
|
||||
const subject = normalizeText(message.subject);
|
||||
const digest = message.digest || '';
|
||||
|
||||
const senderMatch = senderFilters.some((filter) => sender.includes(normalizeText(filter)));
|
||||
const subjectMatch = subjectFilters.some((filter) => subject.includes(normalizeText(filter)));
|
||||
|
||||
if (!senderMatch && !subjectMatch) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const code = extractVerificationCode(`${message.subject || ''} ${digest}`);
|
||||
if (!code) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return {
|
||||
code,
|
||||
mailId,
|
||||
source: 'new',
|
||||
subject: message.subject || '',
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const api = {
|
||||
extractVerificationCode,
|
||||
findNewQQVerificationCode,
|
||||
};
|
||||
|
||||
globalScope.MultiPageQQMail = api;
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = api;
|
||||
}
|
||||
})(typeof globalThis !== 'undefined' ? globalThis : this);
|
||||
Reference in New Issue
Block a user