// sidepanel/sidepanel.js — Side Panel logic const STATUS_ICONS = { pending: '', running: '', completed: '\u2713', // ✓ failed: '\u2717', // ✗ stopped: '\u25A0', // ■ manual_completed: '跳', skipped: '跳', disabled: '禁', }; const logArea = document.getElementById('log-area'); const btnOpenAccountRecords = document.getElementById('btn-open-account-records'); const accountRecordsOverlay = document.getElementById('account-records-overlay'); const accountRecordsMeta = document.getElementById('account-records-meta'); const accountRecordsStats = document.getElementById('account-records-stats'); const accountRecordsList = document.getElementById('account-records-list'); const accountRecordsPageLabel = document.getElementById('account-records-page-label'); const btnAccountRecordsPrev = document.getElementById('btn-account-records-prev'); const btnAccountRecordsNext = document.getElementById('btn-account-records-next'); const btnCloseAccountRecords = document.getElementById('btn-close-account-records'); const btnClearAccountRecords = document.getElementById('btn-clear-account-records'); const btnToggleAccountRecordsSelection = document.getElementById('btn-toggle-account-records-selection'); const btnDeleteSelectedAccountRecords = document.getElementById('btn-delete-selected-account-records'); const updateSection = document.getElementById('update-section'); const linkRepoHome = document.getElementById('link-repo-home'); const extensionUpdateStatus = document.getElementById('extension-update-status'); const extensionVersionMeta = document.getElementById('extension-version-meta'); const btnReleaseLog = document.getElementById('btn-release-log'); const updateCardVersion = document.getElementById('update-card-version'); const updateCardSummary = document.getElementById('update-card-summary'); const updateReleaseList = document.getElementById('update-release-list'); const btnIgnoreRelease = document.getElementById('btn-ignore-release'); const btnOpenRelease = document.getElementById('btn-open-release'); const settingsCard = document.getElementById('settings-card'); const selectFlow = document.getElementById('select-flow'); const accountContributionPanel = document.getElementById('contribution-mode-panel'); const accountContributionBadge = document.getElementById('contribution-mode-badge'); const accountContributionText = document.getElementById('contribution-mode-text'); const inputContributionNickname = document.getElementById('input-contribution-nickname'); const inputContributionQq = document.getElementById('input-contribution-qq'); const contributionPrimaryStatusLabel = document.getElementById('contribution-primary-status-label'); const contributionSecondaryStatusLabel = document.getElementById('contribution-secondary-status-label'); const contributionOauthStatus = document.getElementById('contribution-oauth-status'); const contributionCallbackStatus = document.getElementById('contribution-callback-status'); const accountContributionSummary = document.getElementById('contribution-mode-summary'); const btnStartContribution = document.getElementById('btn-start-contribution'); const btnOpenContributionUpload = document.getElementById('btn-open-contribution-upload'); const btnExitContributionMode = document.getElementById('btn-exit-contribution-mode'); const displayOauthUrl = document.getElementById('display-oauth-url'); const displayLocalhostUrl = document.getElementById('display-localhost-url'); const displayStatus = document.getElementById('display-status'); const statusBar = document.getElementById('status-bar'); const inputEmail = document.getElementById('input-email'); const inputSignupPhone = document.getElementById('input-signup-phone'); const inputPassword = document.getElementById('input-password'); const btnToggleVpsUrl = document.getElementById('btn-toggle-vps-url'); const btnToggleVpsPassword = document.getElementById('btn-toggle-vps-password'); const btnFetchEmail = document.getElementById('btn-fetch-email'); const btnTogglePassword = document.getElementById('btn-toggle-password'); const btnStop = document.getElementById('btn-stop'); const btnReset = document.getElementById('btn-reset'); const btnContributionMode = document.getElementById('btn-contribution-mode'); const contributionUpdateLayer = document.getElementById('contribution-update-layer'); const contributionUpdateHint = document.getElementById('contribution-update-hint'); const contributionUpdateHintText = document.getElementById('contribution-update-hint-text'); const btnDismissContributionUpdateHint = document.getElementById('btn-dismiss-contribution-update-hint'); const autoRunAdBar = document.getElementById('auto-run-ad-bar'); const autoRunAdViewport = document.getElementById('auto-run-ad-viewport'); const autoRunAdTrack = document.getElementById('auto-run-ad-track'); const autoRunAdText = document.getElementById('auto-run-ad-text'); const autoRunAdTextClone = document.getElementById('auto-run-ad-text-clone'); const stepsProgress = document.getElementById('steps-progress'); const btnAutoRun = document.getElementById('btn-auto-run'); const btnAutoContinue = document.getElementById('btn-auto-continue'); const autoContinueBar = document.getElementById('auto-continue-bar'); const autoCountdownBar = document.getElementById('auto-countdown-bar'); const autoCountdownTitle = document.getElementById('auto-countdown-title'); const autoCountdownMeta = document.getElementById('auto-countdown-meta'); const btnAutoRunNow = document.getElementById('btn-auto-run-now'); const btnClearLog = document.getElementById('btn-clear-log'); const configMenuShell = document.getElementById('config-menu-shell'); const btnConfigMenu = document.getElementById('btn-config-menu'); const configMenu = document.getElementById('config-menu'); const btnExportSettings = document.getElementById('btn-export-settings'); const btnImportSettings = document.getElementById('btn-import-settings'); const inputImportSettingsFile = document.getElementById('input-import-settings-file'); const labelSourceSelector = document.getElementById('label-source-selector'); const selectPanelMode = document.getElementById('select-panel-mode'); const btnOpenTargetRepository = document.getElementById('btn-open-target-repository'); const rowVpsUrl = document.getElementById('row-vps-url'); const inputVpsUrl = document.getElementById('input-vps-url'); const rowVpsPassword = document.getElementById('row-vps-password'); const inputVpsPassword = document.getElementById('input-vps-password'); const rowLocalCpaStep9Mode = document.getElementById('row-local-cpa-step9-mode'); const localCpaStep9ModeButtons = Array.from(document.querySelectorAll('[data-local-cpa-step9-mode]')); const rowSub2ApiUrl = document.getElementById('row-sub2api-url'); const inputSub2ApiUrl = document.getElementById('input-sub2api-url'); const rowSub2ApiEmail = document.getElementById('row-sub2api-email'); const inputSub2ApiEmail = document.getElementById('input-sub2api-email'); const rowSub2ApiPassword = document.getElementById('row-sub2api-password'); const inputSub2ApiPassword = document.getElementById('input-sub2api-password'); const rowSub2ApiGroup = document.getElementById('row-sub2api-group'); const inputSub2ApiGroup = document.getElementById('input-sub2api-group'); const sub2ApiGroupPickerRoot = document.getElementById('sub2api-group-picker'); const btnSub2ApiGroupMenu = document.getElementById('btn-sub2api-group-menu'); const sub2ApiGroupCurrent = document.getElementById('sub2api-group-current'); const sub2ApiGroupMenu = document.getElementById('sub2api-group-menu'); const btnAddSub2ApiGroup = document.getElementById('btn-add-sub2api-group'); const rowSub2ApiAccountPriority = document.getElementById('row-sub2api-account-priority'); const inputSub2ApiAccountPriority = document.getElementById('input-sub2api-account-priority'); const rowSub2ApiDefaultProxy = document.getElementById('row-sub2api-default-proxy'); const inputSub2ApiDefaultProxy = document.getElementById('input-sub2api-default-proxy'); const rowIpProxyEnabled = document.getElementById('row-ip-proxy-enabled'); const inputIpProxyEnabled = document.getElementById('input-ip-proxy-enabled'); const btnToggleIpProxySection = document.getElementById('btn-toggle-ip-proxy-section'); const ipProxyEnabledStatus = document.getElementById('ip-proxy-enabled-status'); const ipProxyEnabledStatusDot = document.getElementById('ip-proxy-enabled-status-dot'); const ipProxyEnabledStatusText = document.getElementById('ip-proxy-enabled-status-text'); const ipProxyEnabledButtons = Array.from(document.querySelectorAll('[data-ip-proxy-enabled]')); const rowIpProxyFold = document.getElementById('row-ip-proxy-fold'); const rowIpProxyService = document.getElementById('row-ip-proxy-service'); const selectIpProxyService = document.getElementById('select-ip-proxy-service'); const btnIpProxyServiceLogin = document.getElementById('btn-ip-proxy-service-login'); const rowIpProxyMode = document.getElementById('row-ip-proxy-mode'); const ipProxyModeButtons = Array.from(document.querySelectorAll('[data-ip-proxy-mode]')); const rowIpProxyLayout = document.getElementById('row-ip-proxy-layout'); const ipProxyLayout = document.getElementById('ip-proxy-layout'); const ipProxyApiPanel = document.getElementById('ip-proxy-api-panel'); const rowIpProxyApiUrl = document.getElementById('row-ip-proxy-api-url'); const inputIpProxyApiUrl = document.getElementById('input-ip-proxy-api-url'); const btnToggleIpProxyApiUrl = document.getElementById('btn-toggle-ip-proxy-api-url'); const rowIpProxyAccountList = document.getElementById('row-ip-proxy-account-list'); const inputIpProxyAccountList = document.getElementById('input-ip-proxy-account-list'); const rowIpProxyAccountSessionPrefix = document.getElementById('row-ip-proxy-account-session-prefix'); const inputIpProxyAccountSessionPrefix = document.getElementById('input-ip-proxy-account-session-prefix'); const rowIpProxyAccountLifeMinutes = document.getElementById('row-ip-proxy-account-life-minutes'); const inputIpProxyAccountLifeMinutes = document.getElementById('input-ip-proxy-account-life-minutes'); const rowIpProxyPoolTargetCount = document.getElementById('row-ip-proxy-pool-target-count'); const inputIpProxyPoolTargetCount = document.getElementById('input-ip-proxy-pool-target-count'); const rowIpProxyAutoSyncEnabled = document.getElementById('row-ip-proxy-auto-sync-enabled'); const inputIpProxyAutoSyncEnabled = document.getElementById('input-ip-proxy-auto-sync-enabled'); const rowIpProxyAutoSyncInterval = document.getElementById('row-ip-proxy-auto-sync-interval'); const inputIpProxyAutoSyncIntervalMinutes = document.getElementById('input-ip-proxy-auto-sync-interval-minutes'); const rowIpProxyHost = document.getElementById('row-ip-proxy-host'); const inputIpProxyHost = document.getElementById('input-ip-proxy-host'); const rowIpProxyPort = document.getElementById('row-ip-proxy-port'); const inputIpProxyPort = document.getElementById('input-ip-proxy-port'); const rowIpProxyProtocol = document.getElementById('row-ip-proxy-protocol'); const selectIpProxyProtocol = document.getElementById('select-ip-proxy-protocol'); const rowIpProxyUsername = document.getElementById('row-ip-proxy-username'); const inputIpProxyUsername = document.getElementById('input-ip-proxy-username'); const btnToggleIpProxyUsername = document.getElementById('btn-toggle-ip-proxy-username'); const rowIpProxyPassword = document.getElementById('row-ip-proxy-password'); const inputIpProxyPassword = document.getElementById('input-ip-proxy-password'); const btnToggleIpProxyPassword = document.getElementById('btn-toggle-ip-proxy-password'); const rowIpProxyRegion = document.getElementById('row-ip-proxy-region'); const inputIpProxyRegion = document.getElementById('input-ip-proxy-region'); const rowIpProxyActions = document.getElementById('row-ip-proxy-actions'); const ipProxyActionButtons = document.getElementById('ip-proxy-action-buttons'); const ipProxyActionHint = document.getElementById('ip-proxy-action-hint'); const btnIpProxyRefresh = document.getElementById('btn-ip-proxy-refresh'); const btnIpProxyNext = document.getElementById('btn-ip-proxy-next'); const btnIpProxyChange = document.getElementById('btn-ip-proxy-change'); const btnIpProxyProbe = document.getElementById('btn-ip-proxy-probe'); const btnIpProxyCheckIp = document.getElementById('btn-ip-proxy-check-ip'); const ipProxyCurrent = document.getElementById('ip-proxy-current'); const rowIpProxyRuntimeStatus = document.getElementById('row-ip-proxy-runtime-status'); const ipProxyRuntimeStatus = document.getElementById('ip-proxy-runtime-status'); const ipProxyRuntimeDot = document.getElementById('ip-proxy-runtime-dot'); const ipProxyRuntimeText = document.getElementById('ip-proxy-runtime-text'); const ipProxyRuntimeDetails = document.getElementById('ip-proxy-runtime-details'); const ipProxyRuntimeDetailsText = document.getElementById('ip-proxy-runtime-details-text'); const rowCodex2ApiUrl = document.getElementById('row-codex2api-url'); const inputCodex2ApiUrl = document.getElementById('input-codex2api-url'); const rowCodex2ApiAdminKey = document.getElementById('row-codex2api-admin-key'); const inputCodex2ApiAdminKey = document.getElementById('input-codex2api-admin-key'); const rowKiroRsUrl = document.getElementById('row-kiro-rs-url'); const inputKiroRsUrl = document.getElementById('input-kiro-rs-url'); const rowKiroRsKey = document.getElementById('row-kiro-rs-key'); const inputKiroRsKey = document.getElementById('input-kiro-rs-key'); const btnTestKiroRs = document.getElementById('btn-test-kiro-rs'); const rowKiroRsTestStatus = document.getElementById('row-kiro-rs-test-status'); const displayKiroRsTestStatus = document.getElementById('display-kiro-rs-test-status'); const rowGrokWebchat2ApiUrl = document.getElementById('row-grok-webchat2api-url'); const inputGrokWebchat2ApiUrl = document.getElementById('input-grok-webchat2api-url'); const rowGrokWebchat2ApiKey = document.getElementById('row-grok-webchat2api-key'); const inputGrokWebchat2ApiKey = document.getElementById('input-grok-webchat2api-key'); const rowKiroWebStatus = document.getElementById('row-kiro-web-status'); const displayKiroWebStatus = document.getElementById('display-kiro-web-status'); const rowKiroLoginUrl = document.getElementById('row-kiro-login-url'); const displayKiroLoginUrl = document.getElementById('display-kiro-login-url'); const rowKiroUploadStatus = document.getElementById('row-kiro-upload-status'); const displayKiroUploadStatus = document.getElementById('display-kiro-upload-status'); const rowGrokRegisterStatus = document.getElementById('row-grok-register-status'); const displayGrokRegisterStatus = document.getElementById('display-grok-register-status'); const rowGrokSsoStatus = document.getElementById('row-grok-sso-status'); const displayGrokSsoStatus = document.getElementById('display-grok-sso-status'); const rowGrokWebchat2ApiUploadStatus = document.getElementById('row-grok-webchat2api-upload-status'); const displayGrokWebchat2ApiUploadStatus = document.getElementById('display-grok-webchat2api-upload-status'); const rowGrokSsoSettings = document.getElementById('row-grok-sso-settings'); const displayGrokSsoCookie = document.getElementById('display-grok-sso-cookie'); const btnCopyGrokSso = document.getElementById('btn-copy-grok-sso'); const btnClearGrokSso = document.getElementById('btn-clear-grok-sso'); const rowCustomPassword = document.getElementById('row-custom-password'); const rowPlusMode = document.getElementById('row-plus-mode'); const inputPlusModeEnabled = document.getElementById('input-plus-mode-enabled'); const rowPlusPaymentMethod = document.getElementById('row-plus-payment-method'); const selectPlusPaymentMethod = document.getElementById('select-plus-payment-method'); const btnGpcCardKeyPurchase = document.getElementById('btn-gpc-card-key-purchase'); const plusPaymentMethodCaption = document.getElementById('plus-payment-method-caption'); const rowPlusAccountAccessStrategy = document.getElementById('row-plus-account-access-strategy'); const selectPlusAccountAccessStrategy = document.getElementById('select-plus-account-access-strategy'); const plusAccountAccessStrategyCaption = document.getElementById('plus-account-access-strategy-caption'); const rowPayPalAccount = document.getElementById('row-paypal-account'); const selectPayPalAccount = document.getElementById('select-paypal-account'); const payPalAccountPickerRoot = document.getElementById('paypal-account-picker'); const btnPayPalAccountMenu = document.getElementById('btn-paypal-account-menu'); const payPalAccountCurrent = document.getElementById('paypal-account-current'); const payPalAccountMenu = document.getElementById('paypal-account-menu'); const btnAddPayPalAccount = document.getElementById('btn-add-paypal-account'); const rowHostedCheckoutVerificationUrl = document.getElementById('row-hosted-checkout-verification-url'); const inputHostedCheckoutVerificationUrl = document.getElementById('input-hosted-checkout-verification-url'); const rowHostedCheckoutPhone = document.getElementById('row-hosted-checkout-phone'); const inputHostedCheckoutPhone = document.getElementById('input-hosted-checkout-phone'); const rowPlusHostedCheckoutOauthDelay = document.getElementById('row-plus-hosted-checkout-oauth-delay'); const inputPlusHostedCheckoutOauthDelaySeconds = document.getElementById('input-plus-hosted-checkout-oauth-delay-seconds'); const rowGpcCardKey = document.getElementById('row-gpc-card-key'); const inputGpcCardKey = document.getElementById('input-gpc-card-key'); const displayGpcCardKeyStatus = document.getElementById('display-gpc-card-key-status'); const btnGpcCardKeyQuery = document.getElementById('btn-gpc-card-key-query'); const rowGoPayCountryCode = document.getElementById('row-gopay-country-code'); const selectGoPayCountryCode = document.getElementById('select-gopay-country-code'); const rowGoPayPhone = document.getElementById('row-gopay-phone'); const inputGoPayPhone = document.getElementById('input-gopay-phone'); const rowGoPayOtp = document.getElementById('row-gopay-otp'); const inputGoPayOtp = document.getElementById('input-gopay-otp'); const rowGoPayPin = document.getElementById('row-gopay-pin'); const inputGoPayPin = document.getElementById('input-gopay-pin'); const selectMailProvider = document.getElementById('select-mail-provider'); const btnMailLogin = document.getElementById('btn-mail-login'); const rowCustomMailReceiveMode = document.getElementById('row-custom-mail-receive-mode'); const selectCustomMailReceiveMode = document.getElementById('select-custom-mail-receive-mode'); const rowCustomMailHelperBaseUrl = document.getElementById('row-custom-mail-helper-base-url'); const inputCustomMailHelperBaseUrl = document.getElementById('input-custom-mail-helper-base-url'); const rowCustomMailProviderPool = document.getElementById('row-custom-mail-provider-pool'); const inputCustomMailProviderPool = document.getElementById('input-custom-mail-provider-pool'); const rowMail2925Mode = document.getElementById('row-mail-2925-mode'); const rowMail2925PoolSettings = document.getElementById('row-mail2925-pool-settings'); const mail2925ModeButtons = Array.from(document.querySelectorAll('[data-mail2925-mode]')); const rowEmailGenerator = document.getElementById('row-email-generator'); const selectEmailGenerator = document.getElementById('select-email-generator'); const rowCustomEmailPool = document.getElementById('row-custom-email-pool'); const inputCustomEmailPool = document.getElementById('input-custom-email-pool'); const btnCustomEmailPoolRefresh = document.getElementById('btn-custom-email-pool-refresh'); const btnCustomEmailPoolClearUsed = document.getElementById('btn-custom-email-pool-clear-used'); const btnCustomEmailPoolDeleteAll = document.getElementById('btn-custom-email-pool-delete-all'); const inputCustomEmailPoolImport = document.getElementById('input-custom-email-pool-import'); const btnCustomEmailPoolImport = document.getElementById('btn-custom-email-pool-import'); const customEmailPoolSummary = document.getElementById('custom-email-pool-summary'); const inputCustomEmailPoolSearch = document.getElementById('input-custom-email-pool-search'); const selectCustomEmailPoolFilter = document.getElementById('select-custom-email-pool-filter'); const checkboxCustomEmailPoolSelectAll = document.getElementById('checkbox-custom-email-pool-select-all'); const customEmailPoolSelectionSummary = document.getElementById('custom-email-pool-selection-summary'); const btnCustomEmailPoolBulkUsed = document.getElementById('btn-custom-email-pool-bulk-used'); const btnCustomEmailPoolBulkUnused = document.getElementById('btn-custom-email-pool-bulk-unused'); const btnCustomEmailPoolBulkEnable = document.getElementById('btn-custom-email-pool-bulk-enable'); const btnCustomEmailPoolBulkDisable = document.getElementById('btn-custom-email-pool-bulk-disable'); const btnCustomEmailPoolBulkDelete = document.getElementById('btn-custom-email-pool-bulk-delete'); const customEmailPoolList = document.getElementById('custom-email-pool-list'); const rowTempEmailBaseUrl = document.getElementById('row-temp-email-base-url'); const inputTempEmailBaseUrl = document.getElementById('input-temp-email-base-url'); const rowTempEmailAdminAuth = document.getElementById('row-temp-email-admin-auth'); const inputTempEmailAdminAuth = document.getElementById('input-temp-email-admin-auth'); const rowTempEmailCustomAuth = document.getElementById('row-temp-email-custom-auth'); const inputTempEmailCustomAuth = document.getElementById('input-temp-email-custom-auth'); const rowTempEmailLookupMode = document.getElementById('row-temp-email-lookup-mode'); const tempEmailLookupModeButtons = Array.from(document.querySelectorAll('[data-temp-email-lookup-mode]')); const rowTempEmailReceiveMailbox = document.getElementById('row-temp-email-receive-mailbox'); const inputTempEmailReceiveMailbox = document.getElementById('input-temp-email-receive-mailbox'); const rowTempEmailSubdomainMode = document.getElementById('row-temp-email-subdomain-mode'); const tempEmailSubdomainModeButtons = Array.from(document.querySelectorAll('[data-temp-email-subdomain-mode]')); const tempEmailSubdomainModeCaption = document.getElementById('temp-email-subdomain-mode-caption'); const inputTempEmailUseRandomSubdomain = document.getElementById('input-temp-email-use-random-subdomain'); const inputTempEmailUseFixedSubdomain = document.getElementById('input-temp-email-use-fixed-subdomain'); const rowTempEmailFixedSubdomainPrefix = document.getElementById('row-temp-email-fixed-subdomain-prefix'); const inputTempEmailSubdomainPrefix = document.getElementById('input-temp-email-subdomain-prefix'); const tempEmailSubdomainPrefixFeedback = document.getElementById('temp-email-subdomain-prefix-feedback'); const tempEmailEffectiveDomainPreview = document.getElementById('temp-email-effective-domain-preview'); const rowTempEmailDomain = document.getElementById('row-temp-email-domain'); const labelTempEmailDomain = document.getElementById('label-temp-email-domain'); const selectTempEmailDomain = document.getElementById('select-temp-email-domain'); const tempEmailDomainPickerRoot = document.getElementById('temp-email-domain-picker'); const btnTempEmailDomainMenu = document.getElementById('btn-temp-email-domain-menu'); const tempEmailDomainCurrent = document.getElementById('temp-email-domain-current'); const tempEmailDomainMenu = document.getElementById('temp-email-domain-menu'); const inputTempEmailDomain = document.getElementById('input-temp-email-domain'); const btnTempEmailDomainMode = document.getElementById('btn-temp-email-domain-mode'); const cloudflareTempEmailSection = document.getElementById('cloudflare-temp-email-section'); const btnCloudflareTempEmailUsageGuide = document.getElementById('btn-cloudflare-temp-email-usage-guide'); const btnCloudflareTempEmailGithub = document.getElementById('btn-cloudflare-temp-email-github'); const cloudMailSection = document.getElementById('cloud-mail-section'); const rowCloudMailBaseUrl = document.getElementById('row-cloud-mail-base-url'); const rowCloudMailAdminEmail = document.getElementById('row-cloud-mail-admin-email'); const rowCloudMailAdminPassword = document.getElementById('row-cloud-mail-admin-password'); const rowCloudMailReceiveMailbox = document.getElementById('row-cloud-mail-receive-mailbox'); const rowCloudMailDomain = document.getElementById('row-cloud-mail-domain'); const inputCloudMailBaseUrl = document.getElementById('input-cloud-mail-base-url'); const inputCloudMailAdminEmail = document.getElementById('input-cloud-mail-admin-email'); const inputCloudMailAdminPassword = document.getElementById('input-cloud-mail-admin-password'); const inputCloudMailReceiveMailbox = document.getElementById('input-cloud-mail-receive-mailbox'); const inputCloudMailDomain = document.getElementById('input-cloud-mail-domain'); const yydsMailSection = document.getElementById('yyds-mail-section'); const inputYydsMailApiKey = document.getElementById('input-yyds-mail-api-key'); const inputYydsMailBaseUrl = document.getElementById('input-yyds-mail-base-url'); const hotmailSection = document.getElementById('hotmail-section'); const mail2925Section = document.getElementById('mail2925-section'); const luckmailSection = document.getElementById('luckmail-section'); const icloudSection = document.getElementById('icloud-section'); const icloudSummary = document.getElementById('icloud-summary'); const icloudList = document.getElementById('icloud-list'); const icloudLoginHelp = document.getElementById('icloud-login-help'); const icloudLoginHelpTitle = document.getElementById('icloud-login-help-title'); const icloudLoginHelpText = document.getElementById('icloud-login-help-text'); const btnIcloudLoginDone = document.getElementById('btn-icloud-login-done'); const btnIcloudRefresh = document.getElementById('btn-icloud-refresh'); const btnIcloudDeleteUsed = document.getElementById('btn-icloud-delete-used'); const selectIcloudHostPreference = document.getElementById('select-icloud-host-preference'); const rowIcloudTargetMailboxType = document.getElementById('row-icloud-target-mailbox-type'); const selectIcloudTargetMailboxType = document.getElementById('select-icloud-target-mailbox-type'); const rowIcloudForwardMailProvider = document.getElementById('row-icloud-forward-mail-provider'); const selectIcloudForwardMailProvider = document.getElementById('select-icloud-forward-mail-provider'); const selectIcloudFetchMode = document.getElementById('select-icloud-fetch-mode'); const checkboxAutoDeleteIcloud = document.getElementById('checkbox-auto-delete-icloud'); const inputIcloudSearch = document.getElementById('input-icloud-search'); const selectIcloudFilter = document.getElementById('select-icloud-filter'); const checkboxIcloudSelectAll = document.getElementById('checkbox-icloud-select-all'); const icloudSelectionSummary = document.getElementById('icloud-selection-summary'); const btnIcloudBulkUsed = document.getElementById('btn-icloud-bulk-used'); const btnIcloudBulkUnused = document.getElementById('btn-icloud-bulk-unused'); const btnIcloudBulkPreserve = document.getElementById('btn-icloud-bulk-preserve'); const btnIcloudBulkUnpreserve = document.getElementById('btn-icloud-bulk-unpreserve'); const btnIcloudBulkDelete = document.getElementById('btn-icloud-bulk-delete'); const rowHotmailServiceMode = document.getElementById('row-hotmail-service-mode'); const hotmailServiceModeButtons = Array.from(document.querySelectorAll('[data-hotmail-service-mode]')); const rowHotmailRemoteBaseUrl = document.getElementById('row-hotmail-remote-base-url'); const inputHotmailRemoteBaseUrl = document.getElementById('input-hotmail-remote-base-url'); const rowHotmailLocalBaseUrl = document.getElementById('row-hotmail-local-base-url'); const inputHotmailLocalBaseUrl = document.getElementById('input-hotmail-local-base-url'); const inputHotmailEmail = document.getElementById('input-hotmail-email'); const inputHotmailClientId = document.getElementById('input-hotmail-client-id'); const inputHotmailPassword = document.getElementById('input-hotmail-password'); const inputHotmailRefreshToken = document.getElementById('input-hotmail-refresh-token'); const inputHotmailImport = document.getElementById('input-hotmail-import'); const inputHotmailSearch = document.getElementById('input-hotmail-search'); const selectHotmailFilter = document.getElementById('select-hotmail-filter'); const btnAddHotmailAccount = document.getElementById('btn-add-hotmail-account'); const btnImportHotmailAccounts = document.getElementById('btn-import-hotmail-accounts'); const btnToggleHotmailForm = document.getElementById('btn-toggle-hotmail-form'); const btnHotmailUsageGuide = document.getElementById('btn-hotmail-usage-guide'); const btnClearUsedHotmailAccounts = document.getElementById('btn-clear-used-hotmail-accounts'); const btnDeleteAllHotmailAccounts = document.getElementById('btn-delete-all-hotmail-accounts'); const btnToggleHotmailList = document.getElementById('btn-toggle-hotmail-list'); const hotmailFormShell = document.getElementById('hotmail-form-shell'); const hotmailListShell = document.getElementById('hotmail-list-shell'); const hotmailAccountsList = document.getElementById('hotmail-accounts-list'); const inputMail2925Email = document.getElementById('input-mail2925-email'); const inputMail2925Password = document.getElementById('input-mail2925-password'); const inputMail2925Import = document.getElementById('input-mail2925-import'); const inputMail2925Search = document.getElementById('input-mail2925-search'); const selectMail2925Filter = document.getElementById('select-mail2925-filter'); const btnAddMail2925Account = document.getElementById('btn-add-mail2925-account'); const btnToggleMail2925Form = document.getElementById('btn-toggle-mail2925-form'); const btnImportMail2925Accounts = document.getElementById('btn-import-mail2925-accounts'); const btnDeleteAllMail2925Accounts = document.getElementById('btn-delete-all-mail2925-accounts'); const btnToggleMail2925List = document.getElementById('btn-toggle-mail2925-list'); const mail2925FormShell = document.getElementById('mail2925-form-shell'); const mail2925ListShell = document.getElementById('mail2925-list-shell'); const mail2925AccountsList = document.getElementById('mail2925-accounts-list'); const inputLuckmailApiKey = document.getElementById('input-luckmail-api-key'); const inputLuckmailBaseUrl = document.getElementById('input-luckmail-base-url'); const selectLuckmailEmailType = document.getElementById('select-luckmail-email-type'); const inputLuckmailDomain = document.getElementById('input-luckmail-domain'); const btnLuckmailRefresh = document.getElementById('btn-luckmail-refresh'); const btnLuckmailDisableUsed = document.getElementById('btn-luckmail-disable-used'); const luckmailSummary = document.getElementById('luckmail-summary'); const inputLuckmailSearch = document.getElementById('input-luckmail-search'); const selectLuckmailFilter = document.getElementById('select-luckmail-filter'); const checkboxLuckmailSelectAll = document.getElementById('checkbox-luckmail-select-all'); const luckmailSelectionSummary = document.getElementById('luckmail-selection-summary'); const btnLuckmailBulkUsed = document.getElementById('btn-luckmail-bulk-used'); const btnLuckmailBulkUnused = document.getElementById('btn-luckmail-bulk-unused'); const btnLuckmailBulkPreserve = document.getElementById('btn-luckmail-bulk-preserve'); const btnLuckmailBulkUnpreserve = document.getElementById('btn-luckmail-bulk-unpreserve'); const btnLuckmailBulkDisable = document.getElementById('btn-luckmail-bulk-disable'); const btnLuckmailBulkEnable = document.getElementById('btn-luckmail-bulk-enable'); const luckmailList = document.getElementById('luckmail-list'); const rowEmailPrefix = document.getElementById('row-email-prefix'); const labelEmailPrefix = document.getElementById('label-email-prefix'); const inputEmailPrefix = document.getElementById('input-email-prefix'); const selectMail2925PoolAccount = document.getElementById('select-mail2925-pool-account'); const inputMail2925UseAccountPool = document.getElementById('input-mail2925-use-account-pool'); const labelMail2925UseAccountPool = document.getElementById('label-mail2925-use-account-pool'); const rowInbucketHost = document.getElementById('row-inbucket-host'); const inputInbucketHost = document.getElementById('input-inbucket-host'); const rowInbucketMailbox = document.getElementById('row-inbucket-mailbox'); const inputInbucketMailbox = document.getElementById('input-inbucket-mailbox'); const rowCfDomain = document.getElementById('row-cf-domain'); const selectCfDomain = document.getElementById('select-cf-domain'); const cfDomainPickerRoot = document.getElementById('cf-domain-picker'); const btnCfDomainMenu = document.getElementById('btn-cf-domain-menu'); const cfDomainCurrent = document.getElementById('cf-domain-current'); const cfDomainMenu = document.getElementById('cf-domain-menu'); const inputCfDomain = document.getElementById('input-cf-domain'); const btnCfDomainMode = document.getElementById('btn-cf-domain-mode'); const inputRunCount = document.getElementById('input-run-count'); const inputAutoSkipFailures = document.getElementById('input-auto-skip-failures'); const inputAutoSkipFailuresThreadIntervalMinutes = document.getElementById('input-auto-skip-failures-thread-interval-minutes'); const inputStep6CookieCleanupEnabled = document.getElementById('input-step6-cookie-cleanup-enabled'); const inputAutoStepDelaySeconds = document.getElementById('input-auto-step-delay-seconds'); const rowStepExecutionRange = document.getElementById('row-step-execution-range'); const inputStepExecutionRangeEnabled = document.getElementById('input-step-execution-range-enabled'); const inputStepExecutionRangeFrom = document.getElementById('input-step-execution-range-from'); const inputStepExecutionRangeTo = document.getElementById('input-step-execution-range-to'); const inputVerificationResendCount = document.getElementById('input-verification-resend-count'); const rowPhoneVerificationEnabled = document.getElementById('row-phone-verification-enabled'); const btnTogglePhoneVerificationSection = document.getElementById('btn-toggle-phone-verification-section'); const rowPhoneVerificationFold = document.getElementById('row-phone-verification-fold'); const inputPhoneVerificationEnabled = document.getElementById('input-phone-verification-enabled'); const rowSignupMethod = document.getElementById('row-signup-method'); const rowPhoneSignupReloginAfterBindEmail = document.getElementById('row-phone-signup-relogin-after-bind-email'); const inputPhoneSignupReloginAfterBindEmail = document.getElementById('input-phone-signup-relogin-after-bind-email'); const rowSignupPhone = document.getElementById('row-signup-phone'); const signupMethodButtons = Array.from(document.querySelectorAll('[data-signup-method]')); const selectPhoneSmsProvider = document.getElementById('select-phone-sms-provider'); const rowHeroSmsPlatform = document.getElementById('row-hero-sms-platform'); const rowHeroSmsCountry = document.getElementById('row-hero-sms-country'); const rowHeroSmsCountryFallback = document.getElementById('row-hero-sms-country-fallback'); const rowHeroSmsAcquirePriority = document.getElementById('row-hero-sms-acquire-priority'); const rowHeroSmsOperator = document.getElementById('row-hero-sms-operator'); const rowHeroSmsApiKey = document.getElementById('row-hero-sms-api-key'); const rowHeroSmsMaxPrice = document.getElementById('row-hero-sms-max-price'); const rowPhoneSmsProvider = document.getElementById('row-phone-sms-provider'); const rowPhoneSmsProviderOrder = document.getElementById('row-phone-sms-provider-order'); const rowPhoneSmsProviderOrderActions = document.getElementById('row-phone-sms-provider-order-actions'); const rowFiveSimApiKey = document.getElementById('row-five-sim-api-key'); const rowFiveSimCountry = document.getElementById('row-five-sim-country'); const rowFiveSimCountryFallback = document.getElementById('row-five-sim-country-fallback'); const rowFiveSimOperator = document.getElementById('row-five-sim-operator'); const rowFiveSimProduct = document.getElementById('row-five-sim-product'); const rowNexSmsApiKey = document.getElementById('row-nex-sms-api-key'); const rowNexSmsCountry = document.getElementById('row-nex-sms-country'); const rowNexSmsCountryFallback = document.getElementById('row-nex-sms-country-fallback'); const rowNexSmsServiceCode = document.getElementById('row-nex-sms-service-code'); const rowSmsBowerApiKey = document.getElementById('row-sms-bower-api-key'); const rowSmsBowerCountry = document.getElementById('row-sms-bower-country'); const rowSmsBowerCountryFallback = document.getElementById('row-sms-bower-country-fallback'); const rowMaDaoBaseUrl = document.getElementById('row-madao-base-url'); const rowMaDaoHttpSecret = document.getElementById('row-madao-http-secret'); const rowMaDaoMode = document.getElementById('row-madao-mode'); const rowMaDaoRoutingPlanId = document.getElementById('row-madao-routing-plan-id'); const rowMaDaoProviderId = document.getElementById('row-madao-provider-id'); const rowMaDaoCountry = document.getElementById('row-madao-country'); const rowMaDaoOperator = document.getElementById('row-madao-operator'); const rowMaDaoAutoPickCountry = document.getElementById('row-madao-auto-pick-country'); const rowMaDaoReusePhone = document.getElementById('row-madao-reuse-phone'); const rowMaDaoPriceRange = document.getElementById('row-madao-price-range'); const rowHeroSmsRuntimePair = document.getElementById('row-hero-sms-runtime-pair'); const rowHeroSmsCurrentNumber = document.getElementById('row-hero-sms-current-number'); const rowHeroSmsCurrentCountdown = document.getElementById('row-hero-sms-current-countdown'); const rowHeroSmsPriceTiers = document.getElementById('row-hero-sms-price-tiers'); const rowHeroSmsCurrentCode = document.getElementById('row-hero-sms-current-code'); const rowHeroSmsPreferredActivation = document.getElementById('row-hero-sms-preferred-activation'); const rowPhoneCodeSettingsGroup = document.getElementById('row-phone-code-settings-group'); const rowPhoneVerificationResendCount = document.getElementById('row-phone-verification-resend-count'); const rowPhoneReplacementLimit = document.getElementById('row-phone-replacement-limit'); const rowPhoneCodeWaitSeconds = document.getElementById('row-phone-code-wait-seconds'); const rowPhoneCodeTimeoutWindows = document.getElementById('row-phone-code-timeout-windows'); const rowPhoneCodePollIntervalSeconds = document.getElementById('row-phone-code-poll-interval-seconds'); const rowPhoneCodePollMaxRounds = document.getElementById('row-phone-code-poll-max-rounds'); const rowFreePhoneReuseEnabled = document.getElementById('row-free-phone-reuse-enabled'); const rowFreePhoneReuseAutoEnabled = document.getElementById('row-free-phone-reuse-auto-enabled'); const rowFreeReusablePhone = document.getElementById('row-free-reusable-phone'); const rowPhoneSmsPreferredPriceControl = document.getElementById('row-phone-sms-preferred-price-control'); const rowPhoneSmsReuseControl = document.getElementById('row-phone-sms-reuse-control'); const inputHeroSmsApiKey = document.getElementById('input-hero-sms-api-key'); const btnToggleHeroSmsApiKey = document.getElementById('btn-toggle-hero-sms-api-key'); const inputFiveSimApiKey = document.getElementById('input-five-sim-api-key'); const btnToggleFiveSimApiKey = document.getElementById('btn-toggle-five-sim-api-key'); const inputFiveSimOperator = document.getElementById('input-five-sim-operator'); const inputFiveSimProduct = document.getElementById('input-five-sim-product'); const inputNexSmsApiKey = document.getElementById('input-nex-sms-api-key'); const btnToggleNexSmsApiKey = document.getElementById('btn-toggle-nex-sms-api-key'); const inputNexSmsServiceCode = document.getElementById('input-nex-sms-service-code'); const inputSmsBowerApiKey = document.getElementById('input-sms-bower-api-key'); const btnToggleSmsBowerApiKey = document.getElementById('btn-toggle-sms-bower-api-key'); const inputMaDaoBaseUrl = document.getElementById('input-madao-base-url'); const inputMaDaoHttpSecret = document.getElementById('input-madao-http-secret'); const btnToggleMaDaoHttpSecret = document.getElementById('btn-toggle-madao-http-secret'); const selectMaDaoMode = document.getElementById('select-madao-mode'); const selectMaDaoRoutingPlanId = document.getElementById('select-madao-routing-plan-id'); const btnMaDaoRefreshRoutingPlans = document.getElementById('btn-madao-refresh-routing-plans'); const selectMaDaoProviderId = document.getElementById('select-madao-provider-id'); const btnMaDaoRefreshProviders = document.getElementById('btn-madao-refresh-providers'); const selectMaDaoCountry = document.getElementById('select-madao-country'); const btnMaDaoRefreshCountries = document.getElementById('btn-madao-refresh-countries'); const selectMaDaoOperator = document.getElementById('select-madao-operator'); const btnMaDaoRefreshOperators = document.getElementById('btn-madao-refresh-operators'); const inputMaDaoAutoPickCountry = document.getElementById('input-madao-auto-pick-country'); const inputMaDaoReusePhone = document.getElementById('input-madao-reuse-phone'); const inputMaDaoMinPrice = document.getElementById('input-madao-min-price'); const inputMaDaoMaxPrice = document.getElementById('input-madao-max-price'); const inputHeroSmsMinPrice = document.getElementById('input-hero-sms-min-price'); const inputHeroSmsMaxPrice = document.getElementById('input-hero-sms-max-price'); const inputHeroSmsPreferredPrice = document.getElementById('input-hero-sms-preferred-price'); const inputPhoneReplacementLimit = document.getElementById('input-phone-replacement-limit'); const inputPhoneCodeWaitSeconds = document.getElementById('input-phone-code-wait-seconds'); const inputPhoneCodeTimeoutWindows = document.getElementById('input-phone-code-timeout-windows'); const inputPhoneCodePollIntervalSeconds = document.getElementById('input-phone-code-poll-interval-seconds'); const inputPhoneCodePollMaxRounds = document.getElementById('input-phone-code-poll-max-rounds'); const inputHeroSmsReuseEnabled = document.getElementById('input-hero-sms-reuse-enabled'); const inputFreePhoneReuseEnabled = document.getElementById('input-free-phone-reuse-enabled'); const inputFreePhoneReuseAutoEnabled = document.getElementById('input-free-phone-reuse-auto-enabled'); const inputFreeReusablePhone = document.getElementById('input-free-reusable-phone'); const selectHeroSmsCountry = document.getElementById('select-hero-sms-country'); const selectHeroSmsCountryFallback = document.getElementById('select-hero-sms-country-fallback'); const selectHeroSmsAcquirePriority = document.getElementById('select-hero-sms-acquire-priority'); const selectHeroSmsOperator = document.getElementById('select-hero-sms-operator'); const selectHeroSmsPreferredActivation = document.getElementById('select-hero-sms-preferred-activation'); const selectFiveSimCountry = document.getElementById('select-five-sim-country'); const heroSmsCountryMenuShell = document.getElementById('hero-sms-country-menu-shell'); const btnHeroSmsCountryMenu = document.getElementById('btn-hero-sms-country-menu'); const heroSmsCountryMenu = document.getElementById('hero-sms-country-menu'); const btnHeroSmsCountryClear = document.getElementById('btn-hero-sms-country-clear'); const fiveSimCountryMenuShell = document.getElementById('five-sim-country-menu-shell'); const btnFiveSimCountryMenu = document.getElementById('btn-five-sim-country-menu'); const fiveSimCountryMenu = document.getElementById('five-sim-country-menu'); const btnFiveSimCountryClear = document.getElementById('btn-five-sim-country-clear'); const selectNexSmsCountry = document.getElementById('select-nex-sms-country'); const nexSmsCountryMenuShell = document.getElementById('nex-sms-country-menu-shell'); const btnNexSmsCountryMenu = document.getElementById('btn-nex-sms-country-menu'); const nexSmsCountryMenu = document.getElementById('nex-sms-country-menu'); const btnNexSmsCountryClear = document.getElementById('btn-nex-sms-country-clear'); const selectSmsBowerCountry = document.getElementById('select-sms-bower-country'); const displaySmsBowerCountryFallbackOrder = document.getElementById('display-sms-bower-country-fallback-order'); const btnSmsBowerCountryClear = document.getElementById('btn-sms-bower-country-clear'); const selectPhoneSmsProviderOrder = document.getElementById('select-phone-sms-provider-order'); const phoneSmsProviderOrderMenuShell = document.getElementById('phone-sms-provider-order-menu-shell'); const btnPhoneSmsProviderOrderMenu = document.getElementById('btn-phone-sms-provider-order-menu'); const phoneSmsProviderOrderMenu = document.getElementById('phone-sms-provider-order-menu'); const btnPhoneSmsProviderOrderReset = document.getElementById('btn-phone-sms-provider-order-reset'); const btnHeroSmsPricePreview = document.getElementById('btn-hero-sms-price-preview'); const btnPhoneSmsBalance = document.getElementById('btn-phone-sms-balance'); const displayHeroSmsPlatform = document.getElementById('display-hero-sms-platform'); const displayHeroSmsCurrentNumber = document.getElementById('display-hero-sms-current-number'); const displayHeroSmsCurrentCountdown = document.getElementById('display-hero-sms-current-countdown'); const displayHeroSmsPriceTiers = document.getElementById('display-hero-sms-price-tiers'); const displayPhoneSmsBalance = document.getElementById('display-phone-sms-balance'); const displayHeroSmsCurrentCode = document.getElementById('display-hero-sms-current-code'); const displayFreeReusablePhoneCountry = document.getElementById('display-free-reusable-phone-country'); const displayFreeReusablePhone = document.getElementById('display-free-reusable-phone'); const displayHeroSmsCountryFallbackOrder = document.getElementById('display-hero-sms-country-fallback-order'); const displayFiveSimCountryFallbackOrder = document.getElementById('display-five-sim-country-fallback-order'); const displayNexSmsCountryFallbackOrder = document.getElementById('display-nex-sms-country-fallback-order'); const displayPhoneSmsProviderOrder = document.getElementById('display-phone-sms-provider-order'); const btnSaveFreeReusablePhone = document.getElementById('btn-save-free-reusable-phone'); const btnClearFreeReusablePhone = document.getElementById('btn-clear-free-reusable-phone'); const rowAccountRunHistoryHelperBaseUrl = document.getElementById('row-account-run-history-helper-base-url'); const inputAccountRunHistoryHelperBaseUrl = document.getElementById('input-account-run-history-helper-base-url'); const autoStartModal = document.getElementById('auto-start-modal'); const sharedFormModal = document.getElementById('shared-form-modal'); const sharedFormModalTitle = document.getElementById('shared-form-modal-title'); const btnSharedFormModalClose = document.getElementById('btn-shared-form-modal-close'); const sharedFormModalMessage = document.getElementById('shared-form-modal-message'); const sharedFormModalAlert = document.getElementById('shared-form-modal-alert'); const sharedFormModalFields = document.getElementById('shared-form-modal-fields'); const btnSharedFormModalCancel = document.getElementById('btn-shared-form-modal-cancel'); const btnSharedFormModalConfirm = document.getElementById('btn-shared-form-modal-confirm'); const autoStartTitle = autoStartModal?.querySelector('.modal-title'); const autoStartMessage = document.getElementById('auto-start-message'); const autoStartAlert = document.getElementById('auto-start-alert'); const modalOptionRow = document.getElementById('modal-option-row'); const modalOptionInput = document.getElementById('modal-option-input'); const modalOptionText = document.getElementById('modal-option-text'); const btnAutoStartClose = document.getElementById('btn-auto-start-close'); const btnAutoStartCancel = document.getElementById('btn-auto-start-cancel'); const btnAutoStartRestart = document.getElementById('btn-auto-start-restart'); const btnAutoStartContinue = document.getElementById('btn-auto-start-continue'); const autoHintText = document.querySelector('.auto-hint'); const stepsList = document.querySelector('.steps-list'); const PLUS_PAYMENT_METHOD_PAYPAL = 'paypal'; const PLUS_PAYMENT_METHOD_PAYPAL_HOSTED = 'paypal-hosted'; const PLUS_PAYMENT_METHOD_NONE = 'none'; const PLUS_PAYMENT_METHOD_GOPAY = 'gopay'; const PLUS_PAYMENT_METHOD_GPC_HELPER = 'gpc-helper'; const DEFAULT_GPC_BASE_URL = 'https://gpc.qlhazycoder.top'; const DEFAULT_PLUS_HOSTED_CHECKOUT_OAUTH_DELAY_SECONDS = 3; const DEFAULT_PLUS_PAYMENT_METHOD = PLUS_PAYMENT_METHOD_PAYPAL_HOSTED; const PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH = 'oauth'; const PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION = 'sub2api_codex_session'; const PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION = 'cpa_codex_session'; const PLUS_ACCOUNT_ACCESS_STRATEGY_CODEX_SESSION_UI = 'codex_session'; const DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY = PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; const SIGNUP_METHOD_EMAIL = 'email'; const SIGNUP_METHOD_PHONE = 'phone'; const DEFAULT_SIGNUP_METHOD = SIGNUP_METHOD_EMAIL; const DEFAULT_ACTIVE_FLOW_ID = 'openai'; const DEFAULT_PHONE_SIGNUP_RELOGIN_AFTER_BIND_EMAIL_ENABLED = false; const PHONE_SIGNUP_REUSE_LOCK_TITLE = '手机号注册流程不使用号码复用,切回邮箱注册后会恢复原设置'; let latestState = null; let currentPlusModeEnabled = false; let currentPlusPaymentMethod = DEFAULT_PLUS_PAYMENT_METHOD; let currentPlusAccountAccessStrategy = DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY; let currentSignupMethod = DEFAULT_SIGNUP_METHOD; let currentPhoneVerificationEnabled = false; let currentPhoneSignupReloginAfterBindEmailEnabled = DEFAULT_PHONE_SIGNUP_RELOGIN_AFTER_BIND_EMAIL_ENABLED; let currentStepDefinitionFlowId = DEFAULT_ACTIVE_FLOW_ID; let phoneSignupReuseUiWasLocked = false; let kiroRsConnectionTestStatusText = '未测试'; let lastPhoneSmsProviderBeforeChange = null; let heroSmsCountrySelectionOrder = []; let heroSmsOperatorsByCountryId = new Map(); let heroSmsOperatorsLoadedAt = 0; let isRenderingHeroSmsOperatorOptions = false; let phoneSmsProviderOrderSelection = []; let heroSmsCountryMenuSearchKeyword = ''; const heroSmsCountrySearchTextById = new Map(); const phonePreferredActivationOptionMap = new Map(); let phoneRuntimeCountdownTimer = null; let phoneRuntimeCountdownEndsAt = 0; let phoneRuntimeCountdownWindowIndex = 0; let phoneRuntimeCountdownWindowTotal = 0; let fiveSimCountrySelectionOrder = []; let fiveSimCountryMenuSearchKeyword = ''; const fiveSimCountrySearchTextByCode = new Map(); let nexSmsCountrySelectionOrder = []; let nexSmsCountryMenuSearchKeyword = ''; const nexSmsCountrySearchTextById = new Map(); let stepDefinitions = getStepDefinitionsForMode(false, { plusPaymentMethod: currentPlusPaymentMethod, plusAccountAccessStrategy: currentPlusAccountAccessStrategy, signupMethod: currentSignupMethod, phoneVerificationEnabled: currentPhoneVerificationEnabled, phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled, }); let workflowNodes = getWorkflowNodesForMode(false, { plusPaymentMethod: currentPlusPaymentMethod, plusAccountAccessStrategy: currentPlusAccountAccessStrategy, signupMethod: currentSignupMethod, phoneVerificationEnabled: currentPhoneVerificationEnabled, phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled, }); let STEP_IDS = stepDefinitions.map((step) => Number(step.id)).filter(Number.isFinite); let STEP_DEFAULT_STATUSES = Object.fromEntries(STEP_IDS.map((stepId) => [stepId, 'pending'])); let SKIPPABLE_STEPS = new Set(STEP_IDS); let NODE_IDS = workflowNodes.map((node) => String(node.nodeId || '').trim()).filter(Boolean); let NODE_DEFAULT_STATUSES = Object.fromEntries(NODE_IDS.map((nodeId) => [nodeId, 'pending'])); let SKIPPABLE_NODES = new Set(NODE_IDS); const AUTO_FALLBACK_THREAD_INTERVAL_MIN_MINUTES = 0; const AUTO_FALLBACK_THREAD_INTERVAL_MAX_MINUTES = 1440; const AUTO_FALLBACK_THREAD_INTERVAL_DEFAULT_MINUTES = 0; const AUTO_RUN_MAX_RETRIES_PER_ROUND = 3; const AUTO_STEP_DELAY_MIN_SECONDS = 0; const AUTO_STEP_DELAY_MAX_SECONDS = 600; const VERIFICATION_RESEND_COUNT_MIN = 0; const VERIFICATION_RESEND_COUNT_MAX = 20; const DEFAULT_VERIFICATION_RESEND_COUNT = 4; const PHONE_REPLACEMENT_LIMIT_MIN = 1; const PHONE_REPLACEMENT_LIMIT_MAX = 20; const DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT = 3; const PHONE_CODE_WAIT_SECONDS_MIN = 15; const PHONE_CODE_WAIT_SECONDS_MAX = 300; const DEFAULT_PHONE_CODE_WAIT_SECONDS = 60; const PHONE_CODE_TIMEOUT_WINDOWS_MIN = 1; const PHONE_CODE_TIMEOUT_WINDOWS_MAX = 10; const DEFAULT_PHONE_CODE_TIMEOUT_WINDOWS = 2; const PHONE_CODE_POLL_INTERVAL_SECONDS_MIN = 1; const PHONE_CODE_POLL_INTERVAL_SECONDS_MAX = 30; const DEFAULT_PHONE_CODE_POLL_INTERVAL_SECONDS = 5; const PHONE_CODE_POLL_MAX_ROUNDS_MIN = 1; const PHONE_CODE_POLL_MAX_ROUNDS_MAX = 120; const DEFAULT_PHONE_CODE_POLL_MAX_ROUNDS = 4; const PHONE_SMS_PROVIDER_HERO = 'hero-sms'; const PHONE_SMS_PROVIDER_FIVE_SIM = '5sim'; const PHONE_SMS_PROVIDER_HERO_SMS = PHONE_SMS_PROVIDER_HERO; const PHONE_SMS_PROVIDER_NEXSMS = 'nexsms'; const PHONE_SMS_PROVIDER_MADAO = 'madao'; const PHONE_SMS_PROVIDER_SMS_BOWER = 'sms-bower'; const DEFAULT_PHONE_SMS_PROVIDER = PHONE_SMS_PROVIDER_HERO; const DEFAULT_PHONE_SMS_PROVIDER_ORDER = Object.freeze([ PHONE_SMS_PROVIDER_HERO, PHONE_SMS_PROVIDER_FIVE_SIM, PHONE_SMS_PROVIDER_NEXSMS, PHONE_SMS_PROVIDER_MADAO, PHONE_SMS_PROVIDER_SMS_BOWER, ]); const DEFAULT_FIVE_SIM_COUNTRY_ORDER = Object.freeze(['thailand']); const DEFAULT_FIVE_SIM_OPERATOR = 'any'; const DEFAULT_FIVE_SIM_PRODUCT = 'openai'; const DEFAULT_NEX_SMS_COUNTRY_ORDER = Object.freeze([1]); const DEFAULT_NEX_SMS_SERVICE_CODE = 'ot'; const DEFAULT_MADAO_BASE_URL = 'http://127.0.0.1:7822'; const MADAO_MODE_ROUTING_PLAN = 'routing_plan'; const MADAO_MODE_DIRECT = 'direct'; const DEFAULT_MADAO_MODE = MADAO_MODE_ROUTING_PLAN; let maDaoRoutingPlanOptions = []; let maDaoProviderOptions = []; let maDaoCountryOptions = []; let maDaoOperatorOptions = []; const PHONE_SMS_PROVIDER_UI_DESCRIPTORS = Object.freeze({ [PHONE_SMS_PROVIDER_HERO]: Object.freeze({ rowKeys: Object.freeze([ 'rowHeroSmsCountry', 'rowHeroSmsCountryFallback', 'rowHeroSmsAcquirePriority', 'rowHeroSmsOperator', 'rowHeroSmsApiKey', 'rowHeroSmsMaxPrice', ]), priceControlKeys: Object.freeze([ 'rowPhoneSmsPreferredPriceControl', 'rowPhoneSmsReuseControl', ]), }), [PHONE_SMS_PROVIDER_FIVE_SIM]: Object.freeze({ rowKeys: Object.freeze([ 'rowFiveSimApiKey', 'rowFiveSimCountry', 'rowFiveSimCountryFallback', 'rowFiveSimOperator', 'rowFiveSimProduct', 'rowHeroSmsMaxPrice', ]), }), [PHONE_SMS_PROVIDER_NEXSMS]: Object.freeze({ rowKeys: Object.freeze([ 'rowNexSmsApiKey', 'rowNexSmsCountry', 'rowNexSmsCountryFallback', 'rowNexSmsServiceCode', ]), }), [PHONE_SMS_PROVIDER_SMS_BOWER]: Object.freeze({ rowKeys: Object.freeze([ 'rowSmsBowerApiKey', 'rowSmsBowerCountry', 'rowSmsBowerCountryFallback', 'rowHeroSmsMaxPrice', ]), }), [PHONE_SMS_PROVIDER_MADAO]: Object.freeze({ rowKeys: Object.freeze([ 'rowMaDaoBaseUrl', 'rowMaDaoHttpSecret', 'rowMaDaoMode', ]), routingRowKeys: Object.freeze([ 'rowMaDaoRoutingPlanId', ]), directRowKeys: Object.freeze([ 'rowMaDaoProviderId', 'rowMaDaoCountry', 'rowMaDaoOperator', 'rowMaDaoPriceRange', ]), }), }); const HERO_SMS_COUNTRY_SELECTION_MAX = 3; function getPhoneSmsProviderUiRowMap() { return { rowHeroSmsCountry, rowHeroSmsCountryFallback, rowHeroSmsAcquirePriority, rowHeroSmsOperator, rowHeroSmsApiKey, rowHeroSmsMaxPrice, rowFiveSimApiKey, rowFiveSimCountry, rowFiveSimCountryFallback, rowFiveSimOperator, rowFiveSimProduct, rowNexSmsApiKey, rowNexSmsCountry, rowNexSmsCountryFallback, rowNexSmsServiceCode, rowSmsBowerApiKey, rowSmsBowerCountry, rowSmsBowerCountryFallback, rowMaDaoBaseUrl, rowMaDaoHttpSecret, rowMaDaoMode, rowMaDaoRoutingPlanId, rowMaDaoProviderId, rowMaDaoCountry, rowMaDaoOperator, rowMaDaoAutoPickCountry, rowMaDaoReusePhone, rowMaDaoPriceRange, rowPhoneSmsPreferredPriceControl, rowPhoneSmsReuseControl, }; } function getProviderUiRows(provider = DEFAULT_PHONE_SMS_PROVIDER) { const rowMap = getPhoneSmsProviderUiRowMap(); const normalizedProvider = normalizePhoneSmsProviderValue(provider); const descriptor = PHONE_SMS_PROVIDER_UI_DESCRIPTORS[normalizedProvider] || PHONE_SMS_PROVIDER_UI_DESCRIPTORS[PHONE_SMS_PROVIDER_HERO]; const rowKeys = [...(descriptor.rowKeys || [])]; if (normalizedProvider === PHONE_SMS_PROVIDER_MADAO) { const mode = typeof selectMaDaoMode !== 'undefined' && selectMaDaoMode ? normalizeMaDaoModeValue(selectMaDaoMode.value) : normalizeMaDaoModeValue(latestState?.madaoMode); rowKeys.push(...(mode === MADAO_MODE_DIRECT ? descriptor.directRowKeys : descriptor.routingRowKeys)); } return rowKeys.map((key) => rowMap[key]).filter(Boolean); } function getAllProviderUiRows() { const rowMap = getPhoneSmsProviderUiRowMap(); const rowSet = new Set(); Object.values(PHONE_SMS_PROVIDER_UI_DESCRIPTORS).forEach((descriptor) => { [ ...(descriptor.rowKeys || []), ...(descriptor.routingRowKeys || []), ...(descriptor.directRowKeys || []), ...(descriptor.priceControlKeys || []), ].forEach((key) => { if (rowMap[key]) { rowSet.add(rowMap[key]); } }); }); return Array.from(rowSet); } function updateProviderPriceControls(provider = DEFAULT_PHONE_SMS_PROVIDER, showSettings = false) { const normalizedProvider = normalizePhoneSmsProviderValue(provider); const showHeroOnlyControls = showSettings && normalizedProvider === PHONE_SMS_PROVIDER_HERO_SMS; [rowPhoneSmsPreferredPriceControl, rowPhoneSmsReuseControl].forEach((row) => { if (row) { row.style.display = showHeroOnlyControls ? '' : 'none'; } }); } const DEFAULT_HERO_SMS_OPERATOR = 'any'; const HERO_SMS_OPERATORS_URL = 'https://hero-sms.com/stubs/handler_api.php?action=getOperators'; const DEFAULT_HERO_SMS_REUSE_ENABLED = true; const HERO_SMS_ACQUIRE_PRIORITY_COUNTRY = 'country'; const HERO_SMS_ACQUIRE_PRIORITY_PRICE = 'price'; const HERO_SMS_ACQUIRE_PRIORITY_PRICE_HIGH = 'price_high'; const DEFAULT_HERO_SMS_ACQUIRE_PRIORITY = HERO_SMS_ACQUIRE_PRIORITY_COUNTRY; const HERO_SMS_SUPPORTED_COUNTRY_ITEMS = Object.freeze([ { id: 6, chn: '印度尼西亚', eng: 'Indonesia' }, { id: 52, chn: '泰国', eng: 'Thailand' }, { id: 187, chn: '美国(物理)', eng: 'USA' }, { id: 16, chn: '英国', eng: 'United Kingdom' }, { id: 151, chn: '日本', eng: 'Japan' }, { id: 43, chn: '德国', eng: 'Germany' }, { id: 73, chn: '法国', eng: 'France' }, { id: 10, chn: '越南', eng: 'Vietnam' }, ]); const HERO_SMS_SUPPORTED_COUNTRY_ID_SET = new Set(HERO_SMS_SUPPORTED_COUNTRY_ITEMS.map((item) => String(item.id))); const HERO_SMS_FALLBACK_COUNTRY_ITEMS = HERO_SMS_SUPPORTED_COUNTRY_ITEMS; const FIVE_SIM_COUNTRY_CN_BY_ID = Object.freeze({ afghanistan: '阿富汗', albania: '阿尔巴尼亚', algeria: '阿尔及利亚', angola: '安哥拉', argentina: '阿根廷', armenia: '亚美尼亚', australia: '澳大利亚', austria: '奥地利', azerbaijan: '阿塞拜疆', bahamas: '巴哈马', bahrain: '巴林', bangladesh: '孟加拉国', belarus: '白俄罗斯', belgium: '比利时', bolivia: '玻利维亚', bosnia: '波黑', brazil: '巴西', bulgaria: '保加利亚', cambodia: '柬埔寨', cameroon: '喀麦隆', canada: '加拿大', chile: '智利', china: '中国', colombia: '哥伦比亚', croatia: '克罗地亚', cyprus: '塞浦路斯', czech: '捷克', denmark: '丹麦', egypt: '埃及', england: '英国', estonia: '爱沙尼亚', ethiopia: '埃塞俄比亚', finland: '芬兰', france: '法国', georgia: '格鲁吉亚', germany: '德国', ghana: '加纳', greece: '希腊', hongkong: '中国香港', hungary: '匈牙利', india: '印度', indonesia: '印度尼西亚', ireland: '爱尔兰', israel: '以色列', italy: '意大利', japan: '日本', jordan: '约旦', kazakhstan: '哈萨克斯坦', kenya: '肯尼亚', kyrgyzstan: '吉尔吉斯斯坦', laos: '老挝', latvia: '拉脱维亚', lithuania: '立陶宛', malaysia: '马来西亚', mexico: '墨西哥', moldova: '摩尔多瓦', morocco: '摩洛哥', myanmar: '缅甸', nepal: '尼泊尔', netherlands: '荷兰', newzealand: '新西兰', nigeria: '尼日利亚', norway: '挪威', pakistan: '巴基斯坦', paraguay: '巴拉圭', peru: '秘鲁', philippines: '菲律宾', poland: '波兰', portugal: '葡萄牙', romania: '罗马尼亚', russia: '俄罗斯', saudiarabia: '沙特阿拉伯', serbia: '塞尔维亚', singapore: '新加坡', slovakia: '斯洛伐克', slovenia: '斯洛文尼亚', southafrica: '南非', spain: '西班牙', srilanka: '斯里兰卡', sweden: '瑞典', switzerland: '瑞士', taiwan: '中国台湾', tajikistan: '塔吉克斯坦', tanzania: '坦桑尼亚', thailand: '泰国', turkey: '土耳其', ukraine: '乌克兰', uruguay: '乌拉圭', usa: '美国', uzbekistan: '乌兹别克斯坦', venezuela: '委内瑞拉', vietnam: '越南', }); const HERO_SMS_COUNTRY_CODE_ALIAS_OVERRIDES = Object.freeze({ 'bahamas': ['BS'], 'bolivia': ['BO'], 'czech republic': ['CZ'], 'democratic republic of the congo': ['CD'], 'laos': ['LA'], 'moldova': ['MD'], 'north korea': ['KP'], 'south korea': ['KR'], 'russia': ['RU'], 'russian federation': ['RU'], 'syria': ['SY'], 'taiwan': ['TW'], 'tanzania': ['TZ'], 'united kingdom': ['GB', 'UK'], 'united states': ['US', 'USA'], 'venezuela': ['VE'], 'vietnam': ['VN'], }); const HERO_SMS_COUNTRY_ISO_CODE_BY_NAME = (() => { const lookup = new Map(); if (typeof Intl === 'undefined' || typeof Intl.DisplayNames !== 'function') { return lookup; } const displayNames = new Intl.DisplayNames(['en'], { type: 'region' }); for (let first = 65; first <= 90; first += 1) { for (let second = 65; second <= 90; second += 1) { const code = String.fromCharCode(first) + String.fromCharCode(second); const name = displayNames.of(code); const key = normalizeHeroSmsCountryAliasKey(name); if (!key || lookup.has(key)) { continue; } lookup.set(key, code); } } return lookup; })(); const DEFAULT_LOCAL_CPA_STEP9_MODE = 'submit'; const DEFAULT_CPA_CALLBACK_MODE = 'step8'; const MAIL_2925_MODE_PROVIDE = 'provide'; const MAIL_2925_MODE_RECEIVE = 'receive'; const DEFAULT_MAIL_2925_MODE = MAIL_2925_MODE_PROVIDE; const CUSTOM_MAIL_RECEIVE_MODE_MANUAL = 'manual'; const CUSTOM_MAIL_RECEIVE_MODE_HELPER = 'helper'; const DEFAULT_CUSTOM_MAIL_RECEIVE_MODE = CUSTOM_MAIL_RECEIVE_MODE_MANUAL; const DEFAULT_CUSTOM_MAIL_HELPER_BASE_URL = 'http://127.0.0.1:17374'; const CLOUDFLARE_TEMP_EMAIL_LOOKUP_MODE_RECEIVE_MAILBOX = 'receive-mailbox'; const CLOUDFLARE_TEMP_EMAIL_LOOKUP_MODE_REGISTRATION_EMAIL = 'registration-email'; const DEFAULT_CLOUDFLARE_TEMP_EMAIL_LOOKUP_MODE = CLOUDFLARE_TEMP_EMAIL_LOOKUP_MODE_RECEIVE_MAILBOX; const CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_NONE = 'none'; const CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_RANDOM = 'random'; const CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_FIXED = 'fixed'; const DEFAULT_CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE = CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_NONE; const NEW_USER_GUIDE_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-new-user-guide-prompt-dismissed'; const AUTO_SKIP_FAILURES_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-auto-skip-failures-prompt-dismissed'; const AUTO_RUN_FALLBACK_RISK_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-auto-run-fallback-risk-prompt-dismissed'; const CPA_PHONE_SIGNUP_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-cpa-phone-signup-prompt-dismissed'; const CLOUDFLARE_TEMP_EMAIL_REGISTRATION_LOOKUP_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-cloudflare-temp-email-registration-lookup-prompt-dismissed'; const CPA_PHONE_SIGNUP_WARNING_MESSAGE = '请确保打开手机接码设置中的“绑定后重登”开关,不然可能无法使用(有些版本无需开启)'; const PHONE_VERIFICATION_SECTION_EXPANDED_STORAGE_KEY = 'multipage-phone-verification-section-expanded'; let phoneVerificationSectionExpanded = false; function readPhoneVerificationSectionExpanded() { try { return window.localStorage?.getItem(PHONE_VERIFICATION_SECTION_EXPANDED_STORAGE_KEY) === '1'; } catch (err) { return false; } } function persistPhoneVerificationSectionExpanded(expanded) { try { if (expanded) { window.localStorage?.setItem(PHONE_VERIFICATION_SECTION_EXPANDED_STORAGE_KEY, '1'); } else { window.localStorage?.removeItem(PHONE_VERIFICATION_SECTION_EXPANDED_STORAGE_KEY); } } catch (err) { // Ignore storage errors; the current in-memory state is enough for this session. } } function setPhoneVerificationSectionExpanded(expanded) { phoneVerificationSectionExpanded = Boolean(expanded); persistPhoneVerificationSectionExpanded(phoneVerificationSectionExpanded); if (typeof updatePhoneVerificationSettingsUI === 'function') { updatePhoneVerificationSettingsUI(); } } function togglePhoneVerificationSectionExpanded() { setPhoneVerificationSectionExpanded(!phoneVerificationSectionExpanded); } function initPhoneVerificationSectionExpandedState() { phoneVerificationSectionExpanded = readPhoneVerificationSectionExpanded(); if (typeof updatePhoneVerificationSettingsUI === 'function') { updatePhoneVerificationSettingsUI(); } } function getStepDefinitionsForMode(plusModeEnabled = false, options = {}) { const defaultFlowId = typeof DEFAULT_ACTIVE_FLOW_ID !== 'undefined' ? DEFAULT_ACTIVE_FLOW_ID : 'openai'; const defaultMethod = typeof DEFAULT_PLUS_PAYMENT_METHOD !== 'undefined' ? DEFAULT_PLUS_PAYMENT_METHOD : 'paypal'; const defaultStrategy = typeof DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY !== 'undefined' ? DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY : 'oauth'; const rawPaymentMethod = typeof options === 'string' ? options : (options.plusPaymentMethod || currentPlusPaymentMethod || defaultMethod); const rawPlusAccountAccessStrategy = typeof options === 'string' ? currentPlusAccountAccessStrategy : (options.plusAccountAccessStrategy || currentPlusAccountAccessStrategy || defaultStrategy); const rawSignupMethod = typeof options === 'string' ? currentSignupMethod : (options.signupMethod || currentSignupMethod || DEFAULT_SIGNUP_METHOD); const phoneVerificationEnabled = typeof options === 'string' ? (typeof inputPhoneVerificationEnabled !== 'undefined' && inputPhoneVerificationEnabled ? Boolean(inputPhoneVerificationEnabled.checked) : Boolean(typeof latestState !== 'undefined' ? latestState?.phoneVerificationEnabled : false)) : Boolean(options.phoneVerificationEnabled ?? (typeof inputPhoneVerificationEnabled !== 'undefined' && inputPhoneVerificationEnabled ? inputPhoneVerificationEnabled.checked : (typeof latestState !== 'undefined' ? latestState?.phoneVerificationEnabled : false))); const phoneSignupReloginAfterBindEmailEnabled = typeof options === 'string' ? currentPhoneSignupReloginAfterBindEmailEnabled : Boolean(options.phoneSignupReloginAfterBindEmailEnabled ?? currentPhoneSignupReloginAfterBindEmailEnabled); const accountContributionEnabled = typeof options === 'string' ? Boolean(typeof latestState !== 'undefined' ? latestState?.accountContributionEnabled : false) : Boolean(options.accountContributionEnabled ?? (typeof latestState !== 'undefined' ? latestState?.accountContributionEnabled : false)); const activeFlowId = typeof options === 'string' ? ((typeof latestState !== 'undefined' ? latestState?.activeFlowId : '') || defaultFlowId) : (options.activeFlowId || (typeof latestState !== 'undefined' ? latestState?.activeFlowId : '') || defaultFlowId); return (window.MultiPageStepDefinitions?.getSteps?.({ activeFlowId: String(activeFlowId || '').trim().toLowerCase() || defaultFlowId, plusModeEnabled, plusPaymentMethod: normalizePlusPaymentMethod(rawPaymentMethod), plusAccountAccessStrategy: normalizePlusAccountAccessStrategy(rawPlusAccountAccessStrategy), signupMethod: normalizeSignupMethod(rawSignupMethod), phoneVerificationEnabled, phoneSignupReloginAfterBindEmailEnabled, accountContributionEnabled, }) || []) .sort((left, right) => { const leftOrder = Number.isFinite(left.order) ? left.order : left.id; const rightOrder = Number.isFinite(right.order) ? right.order : right.id; if (leftOrder !== rightOrder) return leftOrder - rightOrder; return left.id - right.id; }); } function getWorkflowNodesForMode(plusModeEnabled = false, options = {}) { const defaultFlowId = typeof DEFAULT_ACTIVE_FLOW_ID !== 'undefined' ? DEFAULT_ACTIVE_FLOW_ID : 'openai'; const defaultMethod = typeof DEFAULT_PLUS_PAYMENT_METHOD !== 'undefined' ? DEFAULT_PLUS_PAYMENT_METHOD : 'paypal'; const defaultStrategy = typeof DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY !== 'undefined' ? DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY : 'oauth'; const rawPaymentMethod = typeof options === 'string' ? options : (options.plusPaymentMethod || currentPlusPaymentMethod || defaultMethod); const rawPlusAccountAccessStrategy = typeof options === 'string' ? currentPlusAccountAccessStrategy : (options.plusAccountAccessStrategy || currentPlusAccountAccessStrategy || defaultStrategy); const rawSignupMethod = typeof options === 'string' ? currentSignupMethod : (options.signupMethod || currentSignupMethod || DEFAULT_SIGNUP_METHOD); const phoneVerificationEnabled = typeof options === 'string' ? (typeof inputPhoneVerificationEnabled !== 'undefined' && inputPhoneVerificationEnabled ? Boolean(inputPhoneVerificationEnabled.checked) : Boolean(typeof latestState !== 'undefined' ? latestState?.phoneVerificationEnabled : false)) : Boolean(options.phoneVerificationEnabled ?? (typeof inputPhoneVerificationEnabled !== 'undefined' && inputPhoneVerificationEnabled ? inputPhoneVerificationEnabled.checked : (typeof latestState !== 'undefined' ? latestState?.phoneVerificationEnabled : false))); const phoneSignupReloginAfterBindEmailEnabled = typeof options === 'string' ? currentPhoneSignupReloginAfterBindEmailEnabled : Boolean(options.phoneSignupReloginAfterBindEmailEnabled ?? currentPhoneSignupReloginAfterBindEmailEnabled); const accountContributionEnabled = typeof options === 'string' ? Boolean(typeof latestState !== 'undefined' ? latestState?.accountContributionEnabled : false) : Boolean(options.accountContributionEnabled ?? (typeof latestState !== 'undefined' ? latestState?.accountContributionEnabled : false)); const activeFlowId = typeof options === 'string' ? ((typeof latestState !== 'undefined' ? latestState?.activeFlowId : '') || defaultFlowId) : (options.activeFlowId || (typeof latestState !== 'undefined' ? latestState?.activeFlowId : '') || defaultFlowId); const nodes = window.MultiPageStepDefinitions?.getNodes?.({ activeFlowId: String(activeFlowId || '').trim().toLowerCase() || defaultFlowId, plusModeEnabled, plusPaymentMethod: normalizePlusPaymentMethod(rawPaymentMethod), plusAccountAccessStrategy: normalizePlusAccountAccessStrategy(rawPlusAccountAccessStrategy), signupMethod: normalizeSignupMethod(rawSignupMethod), phoneVerificationEnabled, phoneSignupReloginAfterBindEmailEnabled, accountContributionEnabled, }); if (Array.isArray(nodes) && nodes.length) { return nodes.slice().sort((left, right) => { const leftOrder = Number.isFinite(Number(left.displayOrder)) ? Number(left.displayOrder) : 0; const rightOrder = Number.isFinite(Number(right.displayOrder)) ? Number(right.displayOrder) : 0; if (leftOrder !== rightOrder) return leftOrder - rightOrder; return String(left.nodeId || '').localeCompare(String(right.nodeId || '')); }); } return getStepDefinitionsForMode(plusModeEnabled, options).map((step) => ({ nodeId: String(step.key || '').trim(), title: step.title, displayOrder: Number.isFinite(Number(step.id)) ? Number(step.id) : Number(step.order), executeKey: String(step.key || '').trim(), })).filter((node) => node.nodeId); } function getStepIdByKeyForCurrentMode(stepKey = '') { const normalizedKey = String(stepKey || '').trim(); if (!normalizedKey) { return 0; } const match = (stepDefinitions || []).find((step) => String(step?.key || '') === normalizedKey); return Number(match?.id) || 0; } function getNodeIdByStepForCurrentMode(step) { const numericStep = Number(step); const node = (workflowNodes || []).find((candidate) => Number(candidate?.displayOrder) === numericStep); if (node?.nodeId) { return String(node.nodeId).trim(); } const definition = (stepDefinitions || []).find((candidate) => Number(candidate?.id) === numericStep); return String(definition?.key || '').trim(); } function getStepIdByNodeIdForCurrentMode(nodeId = '') { const normalizedNodeId = String(nodeId || '').trim(); if (!normalizedNodeId) { return 0; } const node = (workflowNodes || []).find((candidate) => String(candidate?.nodeId || '').trim() === normalizedNodeId); const displayOrder = Number(node?.displayOrder); if (Number.isInteger(displayOrder) && displayOrder > 0) { return displayOrder; } return getStepIdByKeyForCurrentMode(normalizedNodeId); } function rebuildStepDefinitionState(plusModeEnabled = false, options = {}) { currentPlusModeEnabled = Boolean(plusModeEnabled); const defaultFlowId = typeof DEFAULT_ACTIVE_FLOW_ID !== 'undefined' ? DEFAULT_ACTIVE_FLOW_ID : 'openai'; const defaultMethod = typeof DEFAULT_PLUS_PAYMENT_METHOD !== 'undefined' ? DEFAULT_PLUS_PAYMENT_METHOD : 'paypal'; const defaultStrategy = typeof DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY !== 'undefined' ? DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY : 'oauth'; const rawPaymentMethod = typeof options === 'string' ? options : (options.plusPaymentMethod || currentPlusPaymentMethod || defaultMethod); const rawPlusAccountAccessStrategy = typeof options === 'string' ? currentPlusAccountAccessStrategy : (options.plusAccountAccessStrategy || currentPlusAccountAccessStrategy || defaultStrategy); const rawSignupMethod = typeof options === 'string' ? currentSignupMethod : (options.signupMethod || currentSignupMethod || DEFAULT_SIGNUP_METHOD); const phoneVerificationEnabled = typeof options === 'string' ? (typeof inputPhoneVerificationEnabled !== 'undefined' && inputPhoneVerificationEnabled ? Boolean(inputPhoneVerificationEnabled.checked) : Boolean(typeof latestState !== 'undefined' ? latestState?.phoneVerificationEnabled : false)) : Boolean(options.phoneVerificationEnabled ?? (typeof inputPhoneVerificationEnabled !== 'undefined' && inputPhoneVerificationEnabled ? inputPhoneVerificationEnabled.checked : (typeof latestState !== 'undefined' ? latestState?.phoneVerificationEnabled : false))); const phoneSignupReloginAfterBindEmailEnabled = typeof options === 'string' ? currentPhoneSignupReloginAfterBindEmailEnabled : Boolean(options.phoneSignupReloginAfterBindEmailEnabled ?? currentPhoneSignupReloginAfterBindEmailEnabled); const accountContributionEnabled = Boolean( options.accountContributionEnabled ?? (typeof latestState !== 'undefined' ? latestState?.accountContributionEnabled : false) ); currentPlusPaymentMethod = normalizePlusPaymentMethod(rawPaymentMethod); currentPlusAccountAccessStrategy = normalizePlusAccountAccessStrategy(rawPlusAccountAccessStrategy); currentSignupMethod = normalizeSignupMethod(rawSignupMethod); currentPhoneVerificationEnabled = Boolean(phoneVerificationEnabled); currentPhoneSignupReloginAfterBindEmailEnabled = phoneSignupReloginAfterBindEmailEnabled; const nextActiveFlowId = String( options?.activeFlowId || (typeof latestState !== 'undefined' ? latestState?.activeFlowId : '') || defaultFlowId ).trim().toLowerCase() || defaultFlowId; if (typeof currentStepDefinitionFlowId !== 'undefined') { currentStepDefinitionFlowId = nextActiveFlowId; } stepDefinitions = getStepDefinitionsForMode(currentPlusModeEnabled, { activeFlowId: nextActiveFlowId, plusPaymentMethod: currentPlusPaymentMethod, plusAccountAccessStrategy: currentPlusAccountAccessStrategy, signupMethod: currentSignupMethod, phoneVerificationEnabled, phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled, accountContributionEnabled, }); const nextWorkflowNodes = typeof getWorkflowNodesForMode === 'function' ? getWorkflowNodesForMode(currentPlusModeEnabled, { activeFlowId: nextActiveFlowId, plusPaymentMethod: currentPlusPaymentMethod, plusAccountAccessStrategy: currentPlusAccountAccessStrategy, signupMethod: currentSignupMethod, phoneVerificationEnabled, phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled, accountContributionEnabled, }) : stepDefinitions.map((step) => ({ nodeId: String(step.key || step.id || '').trim(), title: step.title, displayOrder: Number.isFinite(Number(step.id)) ? Number(step.id) : Number(step.order), })); if (typeof workflowNodes !== 'undefined') { workflowNodes = nextWorkflowNodes; } STEP_IDS = stepDefinitions.map((step) => Number(step.id)).filter(Number.isFinite); STEP_DEFAULT_STATUSES = Object.fromEntries(STEP_IDS.map((stepId) => [stepId, 'pending'])); SKIPPABLE_STEPS = new Set(STEP_IDS); if (typeof NODE_IDS !== 'undefined') { NODE_IDS = nextWorkflowNodes.map((node) => String(node.nodeId || '').trim()).filter(Boolean); } if (typeof NODE_DEFAULT_STATUSES !== 'undefined') { NODE_DEFAULT_STATUSES = Object.fromEntries((typeof NODE_IDS !== 'undefined' ? NODE_IDS : []).map((nodeId) => [nodeId, 'pending'])); } if (typeof SKIPPABLE_NODES !== 'undefined') { SKIPPABLE_NODES = new Set(typeof NODE_IDS !== 'undefined' ? NODE_IDS : []); } } const CONTRIBUTION_CONTENT_PROMPT_DISMISSED_VERSION_STORAGE_KEY = 'multipage-contribution-content-prompt-dismissed-version'; const AUTO_RUN_FALLBACK_RISK_WARNING_MIN_RUNS = 3; const HOTMAIL_SERVICE_MODE_REMOTE = 'remote'; const HOTMAIL_SERVICE_MODE_LOCAL = 'local'; const ICLOUD_PROVIDER = 'icloud'; const GMAIL_PROVIDER = 'gmail'; const GMAIL_ALIAS_GENERATOR = 'gmail-alias'; const LUCKMAIL_PROVIDER = 'luckmail-api'; const YYDS_MAIL_PROVIDER = 'yyds-mail'; const CUSTOM_EMAIL_POOL_GENERATOR = 'custom-pool'; const DEFAULT_LUCKMAIL_BASE_URL = 'https://mails.luckyous.com'; const DEFAULT_LUCKMAIL_EMAIL_TYPE = 'ms_graph'; const DEFAULT_YYDS_MAIL_BASE_URL = window.YydsMailUtils?.DEFAULT_YYDS_MAIL_BASE_URL || 'https://maliapi.215.im/v1'; const DISPLAY_TIMEZONE = 'Asia/Shanghai'; const DEFAULT_ACCOUNT_RUN_HISTORY_HELPER_BASE_URL = 'http://127.0.0.1:17373'; const CONTRIBUTION_UPLOAD_URL = 'https://flowpilot.qlhazycoder.top/'; const DEFAULT_PHONE_VERIFICATION_ENABLED = false; const DEFAULT_HERO_SMS_COUNTRY_ID = 52; const DEFAULT_HERO_SMS_COUNTRY_LABEL = 'Thailand'; const DEFAULT_FIVE_SIM_COUNTRY_ID = 'vietnam'; const DEFAULT_FIVE_SIM_COUNTRY_LABEL = '越南 (Vietnam)'; const FIVE_SIM_SUPPORTED_COUNTRY_ITEMS = Object.freeze([ { id: 'indonesia', chn: '印度尼西亚', eng: 'Indonesia', searchText: 'indonesia 印度尼西亚 印尼 Indonesia ID +62' }, { id: 'thailand', chn: '泰国', eng: 'Thailand', searchText: 'thailand 泰国 Thailand TH +66' }, { id: 'england', chn: '英国', eng: 'England', searchText: 'england 英国 England UK GB United Kingdom +44' }, { id: 'usa', chn: '美国', eng: 'United States', searchText: 'usa 美国 United States US +1' }, { id: 'japan', chn: '日本', eng: 'Japan', searchText: 'japan 日本 Japan JP +81' }, { id: 'germany', chn: '德国', eng: 'Germany', searchText: 'germany 德国 Germany DE +49' }, { id: 'vietnam', chn: '越南', eng: 'Vietnam', searchText: 'vietnam 越南 Vietnam VN +84' }, ]); const FIVE_SIM_SUPPORTED_COUNTRY_ID_SET = new Set(FIVE_SIM_SUPPORTED_COUNTRY_ITEMS.map((item) => item.id)); const NEX_SMS_FALLBACK_COUNTRY_ITEMS = Object.freeze([ { id: 1, label: 'Ukraine (#1)', searchText: 'Ukraine 1 UA' }, { id: 6, label: 'Indonesia (#6)', searchText: 'Indonesia 6 ID' }, { id: 7, label: 'Malaysia (#7)', searchText: 'Malaysia 7 MY' }, ]); const DEFAULT_IP_PROXY_SERVICE = '711proxy'; const SUPPORTED_IP_PROXY_SERVICES = ['711proxy', 'lumiproxy', 'iproyal', 'omegaproxy']; const IP_PROXY_ENABLED_SERVICES = ['711proxy']; const DEFAULT_IP_PROXY_MODE = 'account'; const SUPPORTED_IP_PROXY_MODES = ['api', 'account']; const DEFAULT_IP_PROXY_PROTOCOL = 'http'; const SUPPORTED_IP_PROXY_PROTOCOLS = ['http', 'https', 'socks4', 'socks5']; const IP_PROXY_API_MODE_ENABLED = false; const IP_PROXY_ACCOUNT_LIST_ENABLED = false; function getManagedAliasUtils() { return window.MultiPageManagedAliasUtils || null; } function isManagedAliasProvider(provider = selectMailProvider.value, mail2925Mode = getSelectedMail2925Mode()) { const utils = getManagedAliasUtils(); if (utils?.usesManagedAliasGeneration) { return utils.usesManagedAliasGeneration(provider, { mail2925Mode }); } if (utils?.isManagedAliasProvider) { const normalizedProvider = String(provider || '').trim().toLowerCase(); if (normalizedProvider === '2925') { return utils.isManagedAliasProvider(provider) && normalizeMail2925Mode(mail2925Mode) === MAIL_2925_MODE_PROVIDE; } return utils.isManagedAliasProvider(provider); } const normalizedProvider = String(provider || '').trim().toLowerCase(); if (normalizedProvider === '2925') { return normalizeMail2925Mode(mail2925Mode) === MAIL_2925_MODE_PROVIDE; } return normalizedProvider === GMAIL_PROVIDER; } function parseManagedAliasBaseEmail(rawValue, provider = selectMailProvider.value) { const utils = getManagedAliasUtils(); if (utils?.parseManagedAliasBaseEmail) { return utils.parseManagedAliasBaseEmail(rawValue, provider); } return null; } function isManagedAliasEmail(value, baseEmail = '', provider = selectMailProvider.value) { const utils = getManagedAliasUtils(); if (utils?.isManagedAliasEmail) { return utils.isManagedAliasEmail(value, provider, baseEmail); } return false; } function getManagedAliasProviderUiCopy(provider = selectMailProvider.value, mail2925Mode = getSelectedMail2925Mode()) { if (!isManagedAliasProvider(provider, mail2925Mode)) { return null; } const utils = getManagedAliasUtils(); if (utils?.getManagedAliasProviderUiCopy) { return utils.getManagedAliasProviderUiCopy(provider); } if (String(provider || '').trim().toLowerCase() === GMAIL_PROVIDER) { return { baseLabel: '基邮箱', basePlaceholder: '例如 yourname@gmail.com', buttonLabel: '生成', successVerb: '生成', label: 'Gmail +tag 邮箱', placeholder: '点击生成 Gmail +tag 邮箱,或手动填写完整邮箱', hint: '先填写基邮箱后点“生成”,也可以直接手动填写完整的 Gmail 邮箱。', }; } if (String(provider || '').trim().toLowerCase() === '2925') { return { baseLabel: '基邮箱', basePlaceholder: '例如 yourname@2925.com', buttonLabel: '生成', successVerb: '生成', label: '2925 邮箱', placeholder: '点击生成 2925 邮箱,或手动填写完整邮箱', hint: '先填写基邮箱后点“生成”,也可以直接手动填写完整的 2925 邮箱。', }; } return null; } function getManagedAliasBaseEmailKey(provider = selectMailProvider.value) { const normalizedProvider = String(provider || '').trim().toLowerCase(); if (normalizedProvider === GMAIL_PROVIDER) { return 'gmailBaseEmail'; } if (normalizedProvider === '2925') { return 'mail2925BaseEmail'; } return ''; } function isMail2925AccountPoolEnabled(state = latestState) { return Boolean(state?.mail2925UseAccountPool); } function getPreferredMail2925PoolAccountId(state = latestState) { const currentId = String(state?.currentMail2925AccountId || '').trim(); if (currentId && getMail2925Accounts(state).some((account) => account.id === currentId)) { return currentId; } return ''; } function syncMail2925PoolAccountOptions(state = latestState) { if (!selectMail2925PoolAccount) { return; } const accounts = getMail2925Accounts(state); const selectedId = getPreferredMail2925PoolAccountId(state); const options = [''].concat( accounts.map((account) => ``) ); selectMail2925PoolAccount.innerHTML = options.join(''); selectMail2925PoolAccount.value = selectedId; } async function syncSelectedMail2925PoolAccount(options = {}) { const { silent = false } = options; if (!selectMail2925PoolAccount || !isMail2925AccountPoolEnabled(latestState)) { return null; } const accountId = String(selectMail2925PoolAccount.value || '').trim(); if (!accountId) { syncLatestState({ currentMail2925AccountId: null }); setManagedAliasBaseEmailInputForProvider('2925', latestState); return null; } const response = await chrome.runtime.sendMessage({ type: 'SELECT_MAIL2925_ACCOUNT', source: 'sidepanel', payload: { accountId }, }); if (response?.error) { throw new Error(response.error); } syncLatestState({ currentMail2925AccountId: response.account?.id || accountId, ...(response.account?.email ? { mail2925BaseEmail: String(response.account.email).trim() } : {}), }); setManagedAliasBaseEmailInputForProvider('2925', latestState); if (!silent) { showToast(`已切换当前 2925 号池邮箱为 ${response.account?.email || accountId}`, 'success', 1800); } return response.account || null; } function getManagedAliasBaseEmailForProvider(provider = selectMailProvider.value, state = latestState) { if (String(provider || '').trim().toLowerCase() === '2925' && isMail2925AccountPoolEnabled(state)) { const currentMail2925Email = getCurrentMail2925Email(state); if (currentMail2925Email) { return currentMail2925Email; } } const key = getManagedAliasBaseEmailKey(provider); if (!key) { return ''; } const providerValue = String(state?.[key] || '').trim(); if (providerValue) { return providerValue; } const legacyEmailPrefix = String(state?.emailPrefix || '').trim(); return parseManagedAliasBaseEmail(legacyEmailPrefix, provider) ? legacyEmailPrefix : ''; } function buildManagedAliasBaseEmailPayload(state = latestState) { const payload = { gmailBaseEmail: String(state?.gmailBaseEmail || '').trim(), mail2925BaseEmail: String(state?.mail2925BaseEmail || '').trim(), mail2925UseAccountPool: Boolean(state?.mail2925UseAccountPool), emailPrefix: '', }; const key = getManagedAliasBaseEmailKey(); if (key) { if (key === 'mail2925BaseEmail' && isMail2925AccountPoolEnabled(state)) { payload[key] = String(state?.mail2925BaseEmail || '').trim(); } else { payload[key] = inputEmailPrefix.value.trim(); } } return payload; } function syncManagedAliasBaseEmailDraftFromInput(provider = selectMailProvider.value) { const key = getManagedAliasBaseEmailKey(provider); if (!key) { return; } if (key === 'mail2925BaseEmail' && isMail2925AccountPoolEnabled(latestState)) { return; } syncLatestState({ [key]: inputEmailPrefix.value.trim() }); } function setManagedAliasBaseEmailInputForProvider(provider = selectMailProvider.value, state = latestState) { syncMail2925PoolAccountOptions(state); inputEmailPrefix.value = getManagedAliasBaseEmailForProvider(provider, state); } function getCurrentRegistrationEmailUiCopy() { if (isCustomMailProvider()) { return getCustomMailProviderUiCopy(); } const useYydsMail = typeof isYydsMailProvider === 'function' ? isYydsMailProvider() : String(selectMailProvider.value || '').trim().toLowerCase() === 'yyds-mail'; if (useYydsMail) { return { buttonLabel: '获取', placeholder: '点击获取 YYDS Mail 邮箱,或手动粘贴邮箱', successVerb: '获取', label: 'YYDS Mail', }; } if (usesGeneratedAliasMailProvider()) { return getManagedAliasProviderUiCopy(); } return getEmailGeneratorUiCopy(); } function isCurrentRegistrationEmailCompatible(email = inputEmail.value.trim(), provider = selectMailProvider.value, state = latestState) { if (!usesGeneratedAliasMailProvider(provider, getSelectedMail2925Mode()) || !email) { return true; } const baseEmail = getManagedAliasBaseEmailForProvider(provider, state); return isManagedAliasEmail(email, baseEmail, provider); } function validateCurrentRegistrationEmail(email = inputEmail.value.trim(), options = {}) { const { showToastOnFailure = false } = options; if (isCurrentRegistrationEmailCompatible(email)) { return true; } if (showToastOnFailure) { const uiCopy = getManagedAliasProviderUiCopy(); const baseEmail = getManagedAliasBaseEmailForProvider(); showToast( baseEmail ? `当前邮箱服务为“${uiCopy?.label || '别名邮箱'}”,注册邮箱需与 ${uiCopy?.baseLabel || '基邮箱'} 对应。` : `当前邮箱服务为“${uiCopy?.label || '别名邮箱'}”,请直接填写完整邮箱,或先填写基邮箱后点击“生成”。`, 'warn' ); } return false; } let currentAutoRun = { autoRunning: false, phase: 'idle', currentRun: 0, totalRuns: 1, attemptRun: 0, countdownAt: null, countdownTitle: '', countdownNote: '', }; let pendingAutoRunStartTotalRuns = 0; let pendingAutoRunStartExpiresAt = 0; let settingsDirty = false; let settingsSaveInFlight = false; let settingsAutoSaveTimer = null; let settingsSaveRevision = 0; let signupPhoneInputDirty = false; let signupPhoneInputFocused = false; let signupPhoneInputPersistPromise = null; let cloudflareDomainEditMode = false; let cloudflareTempEmailDomainEditMode = false; let modalChoiceResolver = null; let currentModalActions = []; let modalResultBuilder = null; let activePlusManualConfirmationRequestId = ''; let plusManualConfirmationDialogInFlight = false; let autoRunCountdownTimer = null; let configMenuOpen = false; let configActionInFlight = false; let currentReleaseSnapshot = null; let currentContributionContentSnapshot = null; let contributionContentSnapshotRequestInFlight = null; let autoRunAdScrollSyncFrame = 0; function normalizeAutomationWindowId(value) { if (value === null || value === undefined || value === '') { return null; } const numeric = Number(value); return Number.isInteger(numeric) && numeric >= 0 ? numeric : null; } async function getCurrentSidepanelWindowId() { if (chrome?.windows?.getCurrent) { try { const currentWindow = await chrome.windows.getCurrent(); const windowId = normalizeAutomationWindowId(currentWindow?.id); if (windowId !== null) { return windowId; } } catch (error) { console.warn('Failed to get current sidepanel window:', error?.message || error); } } return normalizeAutomationWindowId(latestState?.automationWindowId); } function shouldAttachAutomationWindow(message = {}) { const source = String(message?.source || '').trim(); if (source && source !== 'sidepanel') { return false; } return [ 'EXECUTE_NODE', 'AUTO_RUN', 'RESUME_AUTO_RUN', 'SKIP_AUTO_RUN_COUNTDOWN', 'PROBE_IP_PROXY_EXIT', ].includes(String(message?.type || '').trim()); } async function sendSidepanelMessage(message = {}) { const payload = { ...(message || {}), source: message?.source || 'sidepanel', }; if (shouldAttachAutomationWindow(payload)) { const windowId = await getCurrentSidepanelWindowId(); if (windowId !== null) { payload.payload = { ...(payload.payload || {}), automationWindowId: windowId, }; syncLatestState({ automationWindowId: windowId }); } } return chrome.runtime.sendMessage(payload); } window.sendSidepanelMessage = sendSidepanelMessage; const DEFAULT_SUB2API_GROUP_OPTIONS = ['codex', 'openai-plus']; const editableListPickerModule = window.SidepanelEditableListPicker || {}; const normalizeEditableListValues = editableListPickerModule.normalizeEditableListValues || ((...sources) => { const values = []; const seen = new Set(); const append = (value) => { const items = Array.isArray(value) ? value : String(value || '').split(/[\r\n,,、]+/); items.forEach((item) => { const normalized = String(item || '').trim(); const key = normalized.toLowerCase(); if (key && !seen.has(key)) { seen.add(key); values.push(normalized); } }); }; sources.forEach(append); return values; }); const createEditableListPicker = editableListPickerModule.createEditableListPicker || (() => ({ close() { }, render() { }, setOpen() { }, setSelection() { }, setVisible() { }, })); const closeEditableListPickers = editableListPickerModule.closeEditableListPickers || (() => { }); const isClickInsideEditableListPicker = editableListPickerModule.isClickInsideEditableListPicker || (() => false); function normalizeSub2ApiGroupOptions(...sources) { return normalizeEditableListValues(...sources); } function normalizeSub2ApiAccountPriorityValue(value) { const rawValue = String(value ?? '').trim(); const numeric = Number(rawValue); if (!rawValue || !Number.isSafeInteger(numeric) || numeric < 1) { return 1; } return numeric; } function getSelectedSub2ApiGroupName() { return String(inputSub2ApiGroup?.value || '').trim() || DEFAULT_SUB2API_GROUP_OPTIONS[0]; } function getSub2ApiGroupOptionsState(state = latestState) { const options = normalizeSub2ApiGroupOptions( state?.sub2apiGroupNames, state?.sub2apiGroupName ); return options.length ? options : [...DEFAULT_SUB2API_GROUP_OPTIONS]; } const sub2ApiGroupPicker = createEditableListPicker({ root: sub2ApiGroupPickerRoot, input: inputSub2ApiGroup, trigger: btnSub2ApiGroupMenu, current: sub2ApiGroupCurrent, menu: sub2ApiGroupMenu, fallbackItems: DEFAULT_SUB2API_GROUP_OPTIONS, minItems: 1, itemLabel: '分组', onDelete: handleDeleteSub2ApiGroup, onDeleteError: (error) => showToast(error?.message || '删除 SUB2API 分组失败。', 'error'), }); const cfDomainPicker = createEditableListPicker({ root: cfDomainPickerRoot, input: selectCfDomain, trigger: btnCfDomainMenu, current: cfDomainCurrent, menu: cfDomainMenu, emptyLabel: '请先添加域名', itemLabel: '域名', normalizeItems: normalizeCloudflareDomains, normalizeValue: normalizeCloudflareDomainValue, onDelete: handleDeleteCloudflareDomain, onDeleteError: (error) => showToast(error?.message || '删除 Cloudflare 域名失败。', 'error'), }); const tempEmailDomainPicker = createEditableListPicker({ root: tempEmailDomainPickerRoot, input: selectTempEmailDomain, trigger: btnTempEmailDomainMenu, current: tempEmailDomainCurrent, menu: tempEmailDomainMenu, emptyLabel: '请先更新域名', itemLabel: '域名', normalizeItems: normalizeCloudflareTempEmailDomains, normalizeValue: normalizeCloudflareTempEmailDomainValue, onDelete: handleDeleteCloudflareTempEmailDomain, onDeleteError: (error) => showToast(error?.message || '删除 Cloudflare Temp Email 域名失败。', 'error'), }); function renderSub2ApiGroupOptions(state = latestState, selectedValue = '') { if (!inputSub2ApiGroup) { return; } const selected = String(selectedValue || state?.sub2apiGroupName || '').trim(); const options = getSub2ApiGroupOptionsState({ ...(state || {}), sub2apiGroupName: selected || state?.sub2apiGroupName, }); if (selected && !options.some((name) => name.toLowerCase() === selected.toLowerCase())) { options.unshift(selected); } sub2ApiGroupPicker.render(options, selected || options[0] || DEFAULT_SUB2API_GROUP_OPTIONS[0]); } let customEmailPoolEntriesState = []; const EYE_OPEN_ICON = ''; const EYE_CLOSED_ICON = ''; const TARGET_REPOSITORY_URLS = Object.freeze({ openai: Object.freeze({ cpa: 'https://github.com/router-for-me/CLIProxyAPI', sub2api: 'https://github.com/Wei-Shaw/sub2api', }), kiro: Object.freeze({ 'kiro-rs': 'https://github.com/QLHazyCoder/kiro.rs', }), grok: Object.freeze({ webchat2api: 'https://github.com/zqbxdev/webchat2api', }), }); const PRIVACY_MASKED_INPUT_IDS = Object.freeze([ 'input-contribution-qq', 'input-sub2api-url', 'input-sub2api-email', 'input-sub2api-default-proxy', 'input-codex2api-url', 'input-kiro-rs-url', 'input-grok-webchat2api-url', 'input-gopay-phone', 'input-gopay-otp', 'input-email-prefix', 'input-inbucket-host', 'input-inbucket-mailbox', 'input-email', 'input-temp-email-base-url', 'input-temp-email-receive-mailbox', 'input-cloud-mail-base-url', 'input-cloud-mail-admin-email', 'input-cloud-mail-receive-mailbox', 'input-cloud-mail-domain', 'input-yyds-mail-base-url', 'input-hotmail-remote-base-url', 'input-hotmail-local-base-url', 'input-hotmail-email', 'input-hotmail-client-id', 'input-mail2925-email', 'input-luckmail-base-url', 'input-luckmail-domain', 'input-ip-proxy-account-session-prefix', 'input-ip-proxy-host', 'input-ip-proxy-region', 'input-account-run-history-helper-base-url', 'input-signup-phone', ]); const PRIVACY_MASKED_TEXTAREA_IDS = Object.freeze([ 'input-custom-mail-provider-pool', 'input-custom-email-pool-import', 'input-hotmail-import', 'input-mail2925-import', 'input-ip-proxy-account-list', ]); const COPY_ICON = ''; const parseHotmailImportText = window.HotmailUtils?.parseHotmailImportText; const normalizeHotmailServiceModeFromUtils = window.HotmailUtils?.normalizeHotmailServiceMode; const shouldClearHotmailCurrentSelection = window.HotmailUtils?.shouldClearHotmailCurrentSelection; const upsertHotmailAccountInList = window.HotmailUtils?.upsertHotmailAccountInList; const filterHotmailAccountsByUsage = window.HotmailUtils?.filterHotmailAccountsByUsage; const getHotmailBulkActionLabel = window.HotmailUtils?.getHotmailBulkActionLabel; const getHotmailListToggleLabel = window.HotmailUtils?.getHotmailListToggleLabel; const upsertPayPalAccountInList = window.PayPalUtils?.upsertPayPalAccountInList; const normalizeLuckmailTimestampValue = window.LuckMailUtils?.normalizeTimestamp || ((value) => { const timestamp = Date.parse(String(value || '')); return Number.isFinite(timestamp) ? timestamp : 0; }); const sidepanelUpdateService = window.SidepanelUpdateService; const contributionContentService = window.SidepanelContributionContentService; const sharedFormDialog = window.SidepanelFormDialog?.createFormDialog?.({ overlay: sharedFormModal, titleNode: sharedFormModalTitle, closeButton: btnSharedFormModalClose, messageNode: sharedFormModalMessage, alertNode: sharedFormModalAlert, fieldsContainer: sharedFormModalFields, cancelButton: btnSharedFormModalCancel, confirmButton: btnSharedFormModalConfirm, }); const DEFAULT_LUCKMAIL_PRESERVE_TAG_NAME = window.LuckMailUtils?.DEFAULT_LUCKMAIL_PRESERVE_TAG_NAME || '保留'; const normalizeIcloudHost = window.IcloudUtils?.normalizeIcloudHost || ((value) => { const normalized = String(value || '').trim().toLowerCase(); return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : ''; }); const normalizeIcloudFetchMode = (value) => { const normalized = String(value || '').trim().toLowerCase(); return normalized === 'always_new' ? 'always_new' : 'reuse_existing'; }; const normalizeIcloudTargetMailboxType = window.MailProviderUtils?.normalizeIcloudTargetMailboxType || ((value) => String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'); const getIcloudForwardMailProviderOptions = window.MailProviderUtils?.getIcloudForwardMailProviderOptions || (() => Array.from(selectIcloudForwardMailProvider?.options || []) .map((option) => ({ value: String(option?.value || '').trim().toLowerCase(), label: String(option?.textContent || option?.label || option?.value || '').trim(), })) .filter((option) => option.value)); const normalizeIcloudForwardMailProvider = window.MailProviderUtils?.normalizeIcloudForwardMailProvider || ((value) => { const normalized = String(value || '').trim().toLowerCase(); const options = getIcloudForwardMailProviderOptions(); return options.some((option) => option.value === normalized) ? normalized : (options[0]?.value || 'qq'); }); const ICLOUD_FORWARD_MAIL_PROVIDER_LABELS = Object.fromEntries( getIcloudForwardMailProviderOptions().map((option) => [option.value, option.label]) ); const getIcloudLoginUrlForHost = window.IcloudUtils?.getIcloudLoginUrlForHost || ((host) => host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : (host === 'icloud.com' ? 'https://www.icloud.com/' : '')); const MAIL_PROVIDER_LOGIN_CONFIGS = { [ICLOUD_PROVIDER]: { label: 'iCloud 邮箱', buttonLabel: '登录', }, [GMAIL_PROVIDER]: { label: 'Gmail 邮箱', url: 'https://mail.google.com/mail/u/0/#inbox', buttonLabel: '登录', }, '163': { label: '163 邮箱', url: 'https://mail.163.com/', buttonLabel: '登录', }, '163-vip': { label: '163 VIP 邮箱', url: 'https://webmail.vip.163.com/', buttonLabel: '登录', }, '126': { label: '126 邮箱', url: 'https://mail.126.com/', buttonLabel: '登录', }, qq: { label: 'QQ 邮箱', url: 'https://wx.mail.qq.com/', buttonLabel: '登录', }, 'cloudflare-temp-email': { label: 'Cloudflare Temp Email 部署', url: 'https://github.com/QLHazyCoder/cloudflare_temp_email', buttonLabel: '部署', }, [YYDS_MAIL_PROVIDER]: { label: 'YYDS Mail', url: 'https://vip.215.im/docs', buttonLabel: '文档', }, '2925': { label: '2925 邮箱', url: 'https://2925.com/#/mailList', }, }; const IP_PROXY_SERVICE_LOGIN_CONFIGS = { '711proxy': { label: '711Proxy', url: 'https://www.711proxy.com/signup?code=AD2497', buttonLabel: '注册', }, }; // ============================================================ // Toast Notifications // ============================================================ const toastContainer = document.getElementById('toast-container'); const TOAST_ICONS = { error: '', warn: '', success: '', info: '', }; const LOG_LEVEL_LABELS = { info: '信息', ok: '成功', warn: '警告', error: '错误', }; const CLOUDFLARE_TEMP_EMAIL_REPOSITORY_URL = 'https://github.com/QLHazyCoder/cloudflare_temp_email'; function usesGeneratedAliasMailProvider( provider, mail2925Mode = getSelectedMail2925Mode(), generator = undefined ) { const customEmailPoolGenerator = typeof CUSTOM_EMAIL_POOL_GENERATOR === 'string' ? CUSTOM_EMAIL_POOL_GENERATOR : 'custom-pool'; const resolvedGenerator = generator !== undefined ? generator : (typeof getSelectedEmailGenerator === 'function' ? getSelectedEmailGenerator() : ''); return resolvedGenerator !== customEmailPoolGenerator && isManagedAliasProvider(provider, mail2925Mode); } function parseGmailBaseEmail(rawValue = '') { const value = String(rawValue || '').trim().toLowerCase(); const match = value.match(/^([^@\s+]+)@((?:gmail|googlemail)\.com)$/i); if (!match) return null; return { localPart: match[1], domain: match[2].toLowerCase(), }; } function isManagedGmailAlias(value, baseEmail) { const parsedBase = parseGmailBaseEmail(baseEmail); if (!parsedBase) return false; const match = String(value || '').trim().toLowerCase().match(/^([^@\s+]+)(?:\+[^@\s]+)?@((?:gmail|googlemail)\.com)$/i); if (!match) return false; return match[1] === parsedBase.localPart && match[2] === parsedBase.domain; } function showToast(message, type = 'error', duration = 4000) { const toast = document.createElement('div'); toast.className = `toast toast-${type}`; toast.innerHTML = `${TOAST_ICONS[type] || ''}${escapeHtml(message)}`; toast.querySelector('.toast-close').addEventListener('click', () => dismissToast(toast)); toastContainer.appendChild(toast); if (duration > 0) { setTimeout(() => dismissToast(toast), duration); } } function dismissToast(toast) { if (!toast.parentNode) return; toast.classList.add('toast-exit'); toast.addEventListener('animationend', () => toast.remove()); } function resetActionModalOption() { if (!modalOptionRow || !modalOptionInput || !modalOptionText) { return; } modalOptionRow.hidden = true; modalOptionInput.checked = false; modalOptionInput.disabled = false; modalOptionText.textContent = '不再提示'; } function resetActionModalAlert() { if (!autoStartAlert) { return; } autoStartAlert.hidden = true; autoStartAlert.textContent = ''; autoStartAlert.className = 'modal-alert'; } function setActionModalMessageContent({ text = '', html = '' } = {}) { if (!autoStartMessage) { return; } if (html) { autoStartMessage.innerHTML = html; return; } autoStartMessage.textContent = text; } function resetActionModalButtons() { const buttons = [btnAutoStartCancel, btnAutoStartRestart, btnAutoStartContinue]; buttons.forEach((button) => { if (!button) return; button.hidden = true; button.disabled = false; button.onclick = null; }); currentModalActions = []; } function configureActionModalButton(button, action) { if (!button) return; if (!action) { button.hidden = true; button.onclick = null; return; } button.hidden = false; button.disabled = false; button.textContent = action.label; button.className = `btn ${action.variant || 'btn-outline'} btn-sm`; button.onclick = () => resolveModalChoice(action.id); } function configureActionModalOption(option) { if (!modalOptionRow || !modalOptionInput || !modalOptionText) { return; } if (!option) { resetActionModalOption(); return; } modalOptionRow.hidden = false; modalOptionInput.checked = Boolean(option.checked); modalOptionInput.disabled = Boolean(option.disabled); modalOptionText.textContent = option.label || '不再提示'; } function configureActionModalAlert(alert) { if (!autoStartAlert) { return; } if (!alert?.text) { resetActionModalAlert(); return; } autoStartAlert.hidden = false; autoStartAlert.textContent = alert.text; autoStartAlert.className = `modal-alert${alert.tone === 'danger' ? ' is-danger' : ''}`; } function resolveModalChoice(choice) { const optionChecked = Boolean(modalOptionInput?.checked); const result = typeof modalResultBuilder === 'function' ? modalResultBuilder(choice, { optionChecked }) : choice; if (modalChoiceResolver) { modalChoiceResolver(result); modalChoiceResolver = null; } modalResultBuilder = null; resetActionModalButtons(); resetActionModalAlert(); resetActionModalOption(); if (autoStartModal) { autoStartModal.hidden = true; } } function openActionModal({ title, message, messageHtml, actions, option, alert, buildResult }) { if (!autoStartModal) { return Promise.resolve(null); } if (modalChoiceResolver) { resolveModalChoice(null); } resetActionModalButtons(); autoStartTitle.textContent = title; setActionModalMessageContent({ text: message, html: messageHtml }); currentModalActions = actions || []; modalResultBuilder = typeof buildResult === 'function' ? buildResult : null; const buttonSlots = currentModalActions.length <= 2 ? [btnAutoStartCancel, btnAutoStartContinue] : [btnAutoStartCancel, btnAutoStartRestart, btnAutoStartContinue]; buttonSlots.forEach((button, index) => { configureActionModalButton(button, currentModalActions[index]); }); configureActionModalAlert(alert); configureActionModalOption(option); autoStartModal.hidden = false; return new Promise((resolve) => { modalChoiceResolver = resolve; }); } function openAutoStartChoiceDialog(startStep, options = {}) { const runningStep = Number.isInteger(options.runningStep) ? options.runningStep : null; const continueMessage = runningStep ? `继续当前会先等待步骤 ${runningStep} 完成,再按最新进度自动执行。` : `继续当前会从步骤 ${startStep} 开始自动执行。`; return openActionModal({ title: '启动自动', message: `检测到当前已有流程进度。${continueMessage}重新开始会清空当前流程进度并从步骤 1 新开一轮。`, actions: [ { id: null, label: '取消', variant: 'btn-ghost' }, { id: 'restart', label: '重新开始', variant: 'btn-outline' }, { id: 'continue', label: '继续当前', variant: 'btn-primary' }, ], }); } async function openConfirmModal({ title, message, confirmLabel = '确认', confirmVariant = 'btn-primary', alert = null }) { const choice = await openActionModal({ title, message, alert, actions: [ { id: null, label: '取消', variant: 'btn-ghost' }, { id: 'confirm', label: confirmLabel, variant: confirmVariant }, ], }); return choice === 'confirm'; } async function openConfirmModalWithOption({ title, message, messageHtml = '', confirmLabel = '确认', confirmVariant = 'btn-primary', alert = null, optionLabel = '不再提示', optionChecked = false, optionDisabled = false, }) { const result = await openActionModal({ title, message, messageHtml, alert, actions: [ { id: null, label: '取消', variant: 'btn-ghost' }, { id: 'confirm', label: confirmLabel, variant: confirmVariant }, ], option: { label: optionLabel, checked: optionChecked, disabled: optionDisabled, }, buildResult: (choice, meta) => ({ choice, optionChecked: Boolean(meta?.optionChecked), }), }); return { confirmed: result?.choice === 'confirm', optionChecked: Boolean(result?.optionChecked), }; } async function openPlusManualConfirmationDialog(options = {}) { const method = String(options.method || '').trim().toLowerCase(); const activeFlowId = String(latestState?.activeFlowId || latestState?.flowId || 'openai').trim().toLowerCase(); const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function' ? normalizeTargetIdForFlow : ((flowId, targetId = '', fallback = '') => { const normalizedFlowId = String(flowId || '').trim().toLowerCase() || 'openai'; if (normalizedFlowId === 'openai') { const normalizedTargetId = String(targetId || fallback || '').trim().toLowerCase(); return normalizedTargetId === 'sub2api' || normalizedTargetId === 'codex2api' ? normalizedTargetId : 'cpa'; } const normalizedTargetId = String(targetId || '').trim().toLowerCase(); return normalizedTargetId || String(fallback || '').trim().toLowerCase() || 'kiro-rs'; }); const getDefaultTargetIdForFlowSafe = typeof getDefaultTargetIdForFlow === 'function' ? getDefaultTargetIdForFlow : ((flowId = 'openai') => (String(flowId || '').trim().toLowerCase() === 'openai' ? 'cpa' : 'kiro-rs')); const normalizePlusStrategyTargetIdSafe = typeof normalizePlusStrategyTargetId === 'function' ? normalizePlusStrategyTargetId : ((value = '') => { const normalized = String(value || '').trim().toLowerCase(); if (normalized === 'sub2api' || normalized === 'codex2api') { return normalized; } return 'cpa'; }); const targetId = normalizePlusStrategyTargetIdSafe( typeof getSelectedTargetIdForState === 'function' ? getSelectedTargetIdForState(latestState, activeFlowId) : normalizeTargetIdForFlowSafe( activeFlowId, latestState?.targetId || '', getDefaultTargetIdForFlowSafe(activeFlowId) ) ); const signupMethod = String(latestState?.resolvedSignupMethod || latestState?.signupMethod || 'email').trim().toLowerCase(); const plusModeEnabled = latestState?.plusModeEnabled === undefined ? true : Boolean(latestState.plusModeEnabled); const plusAccountAccessStrategy = String(latestState?.plusAccountAccessStrategy || 'oauth').trim().toLowerCase(); const useSub2ApiSessionImport = plusModeEnabled && activeFlowId === 'openai' && targetId === 'sub2api' && signupMethod === 'email' && plusAccountAccessStrategy === 'sub2api_codex_session'; const continuationActionLabel = useSub2ApiSessionImport ? '导入当前 ChatGPT 会话到 SUB2API' : 'OAuth 登录'; const title = String(options.title || '').trim() || (method === 'gopay' ? 'GoPay 订阅确认' : '手动确认'); const message = String(options.message || '').trim() || (method === 'gopay' ? '请在当前订阅页中手动完成 GoPay 订阅,完成后点击“我已完成订阅”继续。' : '请先在页面中完成当前手动操作,完成后点击确认继续。'); return openActionModal({ title, message, actions: [ { id: 'cancel', label: '取消等待', variant: 'btn-ghost' }, { id: 'confirm', label: '我已完成订阅', variant: 'btn-primary' }, ], alert: method === 'gopay' ? { text: `确认后流程会直接继续到 Plus 模式后续的${continuationActionLabel}。`, tone: 'info' } : null, }); } async function syncPlusManualConfirmationDialog() { const requestId = String(latestState?.plusManualConfirmationRequestId || '').trim(); const pending = Boolean(latestState?.plusManualConfirmationPending); if (!pending || !requestId || plusManualConfirmationDialogInFlight || activePlusManualConfirmationRequestId === requestId) { return; } const step = Number(latestState?.plusManualConfirmationStep) || 0; const method = String(latestState?.plusManualConfirmationMethod || '').trim().toLowerCase(); const activeFlowId = String(latestState?.activeFlowId || latestState?.flowId || 'openai').trim().toLowerCase(); const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function' ? normalizeTargetIdForFlow : ((flowId, targetId = '', fallback = '') => { const normalizedFlowId = String(flowId || '').trim().toLowerCase() || 'openai'; if (normalizedFlowId === 'openai') { const normalizedTargetId = String(targetId || fallback || '').trim().toLowerCase(); return normalizedTargetId === 'sub2api' || normalizedTargetId === 'codex2api' ? normalizedTargetId : 'cpa'; } const normalizedTargetId = String(targetId || '').trim().toLowerCase(); return normalizedTargetId || String(fallback || '').trim().toLowerCase() || 'kiro-rs'; }); const getDefaultTargetIdForFlowSafe = typeof getDefaultTargetIdForFlow === 'function' ? getDefaultTargetIdForFlow : ((flowId = 'openai') => (String(flowId || '').trim().toLowerCase() === 'openai' ? 'cpa' : 'kiro-rs')); const normalizePlusStrategyTargetIdSafe = typeof normalizePlusStrategyTargetId === 'function' ? normalizePlusStrategyTargetId : ((value = '') => { const normalized = String(value || '').trim().toLowerCase(); if (normalized === 'sub2api' || normalized === 'codex2api') { return normalized; } return 'cpa'; }); const targetId = normalizePlusStrategyTargetIdSafe( typeof getSelectedTargetIdForState === 'function' ? getSelectedTargetIdForState(latestState, activeFlowId) : normalizeTargetIdForFlowSafe( activeFlowId, latestState?.targetId || '', getDefaultTargetIdForFlowSafe(activeFlowId) ) ); const signupMethod = String(latestState?.resolvedSignupMethod || latestState?.signupMethod || 'email').trim().toLowerCase(); const plusModeEnabled = latestState?.plusModeEnabled === undefined ? true : Boolean(latestState.plusModeEnabled); const plusAccountAccessStrategy = String(latestState?.plusAccountAccessStrategy || 'oauth').trim().toLowerCase(); const useSub2ApiSessionImport = plusModeEnabled && activeFlowId === 'openai' && targetId === 'sub2api' && signupMethod === 'email' && plusAccountAccessStrategy === 'sub2api_codex_session'; const continuationActionLabel = useSub2ApiSessionImport ? '导入当前 ChatGPT 会话到 SUB2API' : 'OAuth 登录'; const title = latestState?.plusManualConfirmationTitle; const message = latestState?.plusManualConfirmationMessage; activePlusManualConfirmationRequestId = requestId; plusManualConfirmationDialogInFlight = true; let shouldReopenDialog = false; try { const choice = await openPlusManualConfirmationDialog({ method, title, message, }); const currentRequestId = String(latestState?.plusManualConfirmationRequestId || '').trim(); const stillPending = Boolean(latestState?.plusManualConfirmationPending); if (!stillPending || currentRequestId !== requestId) { return; } if (choice == null) { shouldReopenDialog = true; showToast('当前订阅确认仍在等待中,将重新弹出确认窗口。', 'info', 1800); return; } const confirmed = choice === 'confirm'; const response = await chrome.runtime.sendMessage({ type: 'RESOLVE_PLUS_MANUAL_CONFIRMATION', source: 'sidepanel', payload: { step, requestId, confirmed, }, }); if (response?.error) { throw new Error(response.error); } if (confirmed) { showToast(method === 'gopay' ? `GoPay 订阅已确认,正在继续${continuationActionLabel}...` : '已确认,流程继续执行中...', 'info', 2200); } else { showToast(method === 'gopay' ? '已取消 GoPay 订阅等待。' : '已取消当前手动确认。', 'warn', 2200); } } catch (error) { showToast(error?.message || String(error || '未知错误'), 'error'); } finally { if (activePlusManualConfirmationRequestId === requestId) { activePlusManualConfirmationRequestId = ''; } plusManualConfirmationDialogInFlight = false; if ( shouldReopenDialog && latestState?.plusManualConfirmationPending && String(latestState?.plusManualConfirmationRequestId || '').trim() === requestId ) { setTimeout(() => { void syncPlusManualConfirmationDialog(); }, 0); } } } function isPromptDismissed(storageKey) { return localStorage.getItem(storageKey) === '1'; } function setPromptDismissed(storageKey, dismissed) { if (dismissed) { localStorage.setItem(storageKey, '1'); } else { localStorage.removeItem(storageKey); } } function isNewUserGuidePromptDismissed() { return isPromptDismissed(NEW_USER_GUIDE_PROMPT_DISMISSED_STORAGE_KEY); } function setNewUserGuidePromptDismissed(dismissed) { setPromptDismissed(NEW_USER_GUIDE_PROMPT_DISMISSED_STORAGE_KEY, dismissed); } function shouldPromptNewUserGuide() { if (isNewUserGuidePromptDismissed()) { return false; } if (!btnContributionMode || btnContributionMode.disabled) { return false; } if (typeof isContributionModeActiveForFlow === 'function' ? isContributionModeActiveForFlow(latestState) : Boolean(latestState?.accountContributionEnabled)) { return false; } return true; } function getContributionPortalUrl() { return String(contributionContentService?.portalUrl || 'https://flowpilot.qlhazycoder.top').trim(); } function getContributionContentFlowId(state = latestState) { return String(state?.activeFlowId || state?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID; } function getContributionContentTargetId(state = latestState) { const flowId = getContributionContentFlowId(state); if (typeof getSelectedTargetIdForState === 'function') { return getSelectedTargetIdForState(state, flowId); } return normalizeTargetIdForFlow(flowId, state?.targetId || '', getDefaultTargetIdForFlow(flowId)); } function openNewUserGuidePrompt() { return openActionModal({ title: '新手引导', message: '如果你是第一次使用,可以先查看贡献页里的公告和使用教程。点击“查看引导”会自动打开贡献页面。', alert: { text: '本提示仅出现一次。', }, actions: [ { id: null, label: '取消', variant: 'btn-ghost' }, { id: 'confirm', label: '查看引导', variant: 'btn-primary' }, ], }); } async function maybeShowNewUserGuidePrompt() { if (!shouldPromptNewUserGuide()) { return false; } setNewUserGuidePromptDismissed(true); const choice = await openNewUserGuidePrompt(); if (choice === 'confirm') { openExternalUrl(getContributionPortalUrl()); return true; } return false; } function getContributionContentPromptScope(snapshot = currentContributionContentSnapshot) { return { flowId: String(snapshot?.flowId || getContributionContentFlowId()).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID, targetId: String(snapshot?.targetId || getContributionContentTargetId()).trim().toLowerCase() || 'cpa', }; } function getContributionContentPromptDismissedStorageKey(snapshot = currentContributionContentSnapshot) { const scope = getContributionContentPromptScope(snapshot); return `${CONTRIBUTION_CONTENT_PROMPT_DISMISSED_VERSION_STORAGE_KEY}:${scope.flowId}:${scope.targetId}`; } function getDismissedContributionContentPromptVersion(snapshot = currentContributionContentSnapshot) { return String(localStorage.getItem(getContributionContentPromptDismissedStorageKey(snapshot)) || '').trim(); } function setDismissedContributionContentPromptVersion(version, snapshot = currentContributionContentSnapshot) { const normalized = String(version || '').trim(); const storageKey = getContributionContentPromptDismissedStorageKey(snapshot); if (normalized) { localStorage.setItem(storageKey, normalized); } else { localStorage.removeItem(storageKey); } } function isAutoSkipFailuresPromptDismissed() { return isPromptDismissed(AUTO_SKIP_FAILURES_PROMPT_DISMISSED_STORAGE_KEY); } function setAutoSkipFailuresPromptDismissed(dismissed) { setPromptDismissed(AUTO_SKIP_FAILURES_PROMPT_DISMISSED_STORAGE_KEY, dismissed); } function isAutoRunFallbackRiskPromptDismissed() { return isPromptDismissed(AUTO_RUN_FALLBACK_RISK_PROMPT_DISMISSED_STORAGE_KEY); } function setAutoRunFallbackRiskPromptDismissed(dismissed) { setPromptDismissed(AUTO_RUN_FALLBACK_RISK_PROMPT_DISMISSED_STORAGE_KEY, dismissed); } function isCpaPhoneSignupPromptDismissed() { return isPromptDismissed(CPA_PHONE_SIGNUP_PROMPT_DISMISSED_STORAGE_KEY); } function setCpaPhoneSignupPromptDismissed(dismissed) { setPromptDismissed(CPA_PHONE_SIGNUP_PROMPT_DISMISSED_STORAGE_KEY, dismissed); } function isCloudflareTempEmailRegistrationLookupPromptDismissed() { return isPromptDismissed(CLOUDFLARE_TEMP_EMAIL_REGISTRATION_LOOKUP_PROMPT_DISMISSED_STORAGE_KEY); } function setCloudflareTempEmailRegistrationLookupPromptDismissed(dismissed) { setPromptDismissed(CLOUDFLARE_TEMP_EMAIL_REGISTRATION_LOOKUP_PROMPT_DISMISSED_STORAGE_KEY, dismissed); } function shouldWarnAutoRunFallbackRisk(totalRuns, autoRunSkipFailures) { return totalRuns >= AUTO_RUN_FALLBACK_RISK_WARNING_MIN_RUNS; } function shouldWarnCpaPhoneSignup(signupMethod = null, targetId = null) { const defaultFlowId = typeof DEFAULT_ACTIVE_FLOW_ID === 'string' ? DEFAULT_ACTIVE_FLOW_ID : 'openai'; const resolvedSignupMethod = normalizeSignupMethod( signupMethod ?? ( typeof getSelectedSignupMethod === 'function' ? getSelectedSignupMethod() : DEFAULT_SIGNUP_METHOD ) ); const activeFlowId = typeof getSelectedFlowId === 'function' ? getSelectedFlowId(latestState) : ( typeof normalizeFlowId === 'function' ? normalizeFlowId(latestState?.activeFlowId || latestState?.flowId || defaultFlowId) : (String(latestState?.activeFlowId || latestState?.flowId || defaultFlowId).trim().toLowerCase() || defaultFlowId) ); const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function' ? normalizeTargetIdForFlow : ((flowId, value = '', fallback = '') => { const normalizedFlowId = String(flowId || '').trim().toLowerCase() || defaultFlowId; if (normalizedFlowId === defaultFlowId) { return normalizePanelMode(value || fallback); } const normalizedValue = String(value || '').trim().toLowerCase(); return normalizedValue || String(fallback || '').trim().toLowerCase() || 'kiro-rs'; }); const resolvedTargetId = normalizeTargetIdForFlowSafe( activeFlowId, targetId ?? ( typeof getSelectedTargetId === 'function' ? getSelectedTargetId(activeFlowId) : getSelectedTargetIdForState(latestState, activeFlowId) ), typeof getDefaultTargetIdForFlow === 'function' ? getDefaultTargetIdForFlow(activeFlowId) : (activeFlowId === defaultFlowId ? 'cpa' : 'kiro-rs') ); const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function' ? resolveCurrentSidepanelCapabilities({ activeFlowId, targetId: resolvedTargetId, signupMethod: resolvedSignupMethod, state: { ...(typeof latestState !== 'undefined' ? latestState : {}), activeFlowId, flowId: activeFlowId, targetId: resolvedTargetId, signupMethod: resolvedSignupMethod, }, }) : (() => { const rootScope = typeof window !== 'undefined' ? window : globalThis; const registry = rootScope.MultiPageFlowCapabilities?.createFlowCapabilityRegistry?.({ defaultFlowId: typeof DEFAULT_ACTIVE_FLOW_ID === 'string' ? DEFAULT_ACTIVE_FLOW_ID : 'openai', }) || null; return registry?.resolveSidepanelCapabilities ? registry.resolveSidepanelCapabilities({ activeFlowId, targetId: resolvedTargetId, signupMethod: resolvedSignupMethod, state: { ...(typeof latestState !== 'undefined' ? latestState : {}), activeFlowId, flowId: activeFlowId, targetId: resolvedTargetId, signupMethod: resolvedSignupMethod, }, }) : null; })(); if (capabilityState && typeof capabilityState.shouldWarnCpaPhoneSignup === 'boolean') { return capabilityState.shouldWarnCpaPhoneSignup && !isCpaPhoneSignupPromptDismissed(); } return resolvedSignupMethod === SIGNUP_METHOD_PHONE && resolvedTargetId === 'cpa' && !isCpaPhoneSignupPromptDismissed(); } async function openCpaPhoneSignupWarningModal() { const result = await openConfirmModalWithOption({ title: 'CPA 手机号注册提醒', message: CPA_PHONE_SIGNUP_WARNING_MESSAGE, confirmLabel: '继续', optionLabel: '不再提醒', }); return { confirmed: result.confirmed, dismissPrompt: result.optionChecked, }; } async function confirmCpaPhoneSignupIfNeeded(options = {}) { const signupMethod = Object.prototype.hasOwnProperty.call(options, 'signupMethod') ? options.signupMethod : null; const targetId = Object.prototype.hasOwnProperty.call(options, 'targetId') ? options.targetId : null; if (!shouldWarnCpaPhoneSignup(signupMethod, targetId)) { return true; } const result = await openCpaPhoneSignupWarningModal(); if (result.dismissPrompt) { setCpaPhoneSignupPromptDismissed(true); } return result.confirmed; } function buildCloudflareTempEmailRegistrationLookupPromptHtml() { const repositoryUrl = escapeHtml(CLOUDFLARE_TEMP_EMAIL_REPOSITORY_URL); return `需要部署本扩展作者修改后的 Cloudflare Temp Email;部署后可支持多线程收码。`; } async function confirmCloudflareTempEmailRegistrationLookupIfNeeded() { if (isCloudflareTempEmailRegistrationLookupPromptDismissed()) { return true; } const result = await openConfirmModalWithOption({ title: '注册邮箱查信', messageHtml: buildCloudflareTempEmailRegistrationLookupPromptHtml(), confirmLabel: '我已知晓', optionLabel: '不再提醒', }); if (result.confirmed && result.optionChecked) { setCloudflareTempEmailRegistrationLookupPromptDismissed(true); } return result.confirmed; } async function openAutoSkipFailuresConfirmModal() { const result = await openConfirmModalWithOption({ title: '自动重试说明', message: `开启后,自动模式在某一轮失败时,会先在当前轮自动重试;单轮最多重试 ${AUTO_RUN_MAX_RETRIES_PER_ROUND} 次,仍失败则放弃当前轮并继续下一轮。线程间隔只在开启自动重试且总轮数大于 1 时生效。`, confirmLabel: '确认开启', }); return { confirmed: result.confirmed, dismissPrompt: result.optionChecked, }; } async function openAutoRunFallbackRiskConfirmModal(totalRuns) { const result = await openConfirmModalWithOption({ title: '自动运行风险提醒', message: `当前轮数已经不适合单节点情况,请确保已经配置并打开节点轮询功能(若没有配置,请点击贡献/使用按钮,根据网页中使用教程进行配置),避免连续使用一个节点注册,导致出现手机号验证。`, confirmLabel: '继续', }); return { confirmed: result.confirmed, dismissPrompt: result.optionChecked, }; } function updateConfigMenuControls() { const disabled = configActionInFlight || settingsSaveInFlight; const accountContributionEnabled = typeof isContributionModeActiveForFlow === 'function' ? isContributionModeActiveForFlow(latestState) : Boolean(latestState?.accountContributionEnabled); if (accountContributionEnabled && configMenuOpen) { configMenuOpen = false; } const importLocked = disabled || accountContributionEnabled || currentAutoRun.autoRunning || Object.values(getStepStatuses()).some((status) => status === 'running'); if (btnConfigMenu) { btnConfigMenu.disabled = disabled || accountContributionEnabled; btnConfigMenu.setAttribute('aria-expanded', String(configMenuOpen)); } if (configMenu) { configMenu.hidden = accountContributionEnabled || !configMenuOpen; } if (btnExportSettings) { btnExportSettings.disabled = disabled || accountContributionEnabled; } if (btnImportSettings) { btnImportSettings.disabled = importLocked; } } function closeConfigMenu() { configMenuOpen = false; updateConfigMenuControls(); } function openConfigMenu() { configMenuOpen = true; updateConfigMenuControls(); } function toggleConfigMenu() { configMenuOpen ? closeConfigMenu() : openConfigMenu(); } async function waitForSettingsSaveIdle() { while (settingsSaveInFlight) { await new Promise((resolve) => setTimeout(resolve, 50)); } } async function flushPendingSettingsBeforeExport() { clearTimeout(settingsAutoSaveTimer); await waitForSettingsSaveIdle(); if (settingsDirty) { await saveSettings({ silent: true }); } } async function settlePendingSettingsBeforeImport() { clearTimeout(settingsAutoSaveTimer); await waitForSettingsSaveIdle(); } function downloadTextFile(content, fileName, mimeType = 'application/json;charset=utf-8') { const blob = new Blob([content], { type: mimeType }); const objectUrl = URL.createObjectURL(blob); const anchor = document.createElement('a'); anchor.href = objectUrl; anchor.download = fileName; document.body.appendChild(anchor); anchor.click(); anchor.remove(); setTimeout(() => URL.revokeObjectURL(objectUrl), 0); } function isDoneStatus(status) { return status === 'completed' || status === 'manual_completed' || status === 'skipped'; } function isPlainObjectValue(value) { return Boolean(value) && typeof value === 'object' && !Array.isArray(value); } function normalizeStepExecutionRangeFlowId(value = '', fallback = DEFAULT_ACTIVE_FLOW_ID) { const normalized = String(value || '').trim().toLowerCase(); if (normalized === 'codex') { return DEFAULT_ACTIVE_FLOW_ID; } const fallbackValue = String(fallback || '').trim().toLowerCase(); return normalized || fallbackValue || DEFAULT_ACTIVE_FLOW_ID; } function getCurrentStepExecutionRangeFlowId(state = latestState) { const selectedFlow = String(selectFlow?.value || '').trim().toLowerCase(); if (selectedFlow) { return normalizeStepExecutionRangeFlowId(selectedFlow); } return normalizeStepExecutionRangeFlowId(state?.activeFlowId || state?.flowId || DEFAULT_ACTIVE_FLOW_ID); } function hasStepExecutionRangeShape(value) { return isPlainObjectValue(value) && ( Object.prototype.hasOwnProperty.call(value, 'enabled') || Object.prototype.hasOwnProperty.call(value, 'fromStep') || Object.prototype.hasOwnProperty.call(value, 'toStep') || Object.prototype.hasOwnProperty.call(value, 'from') || Object.prototype.hasOwnProperty.call(value, 'to') ); } function normalizePositiveStepNumber(value, fallback = 0) { const numeric = Math.floor(Number(value)); if (Number.isInteger(numeric) && numeric > 0) { return numeric; } const fallbackNumber = Math.floor(Number(fallback)); return Number.isInteger(fallbackNumber) && fallbackNumber > 0 ? fallbackNumber : 0; } function normalizeStepExecutionRangeEntry(value = {}) { const source = isPlainObjectValue(value) ? value : {}; const rawFrom = Object.prototype.hasOwnProperty.call(source, 'fromStep') ? source.fromStep : source.from; const rawTo = Object.prototype.hasOwnProperty.call(source, 'toStep') ? source.toStep : source.to; let fromStep = normalizePositiveStepNumber(rawFrom, 1); let toStep = normalizePositiveStepNumber(rawTo, fromStep || 1); if (fromStep > 0 && toStep > 0 && fromStep > toStep) { [fromStep, toStep] = [toStep, fromStep]; } const hasBounds = fromStep > 0 && toStep > 0; const enabled = Object.prototype.hasOwnProperty.call(source, 'enabled') ? Boolean(source.enabled) : hasBounds; return { enabled: Boolean(enabled && hasBounds), fromStep: fromStep || 1, toStep: toStep || fromStep || 1, }; } function getKiroUploadStatusLabel(value = '') { const rawValue = String(value || '').trim(); if (!rawValue) { return '未开始'; } const normalizedValue = rawValue.toLowerCase(); switch (normalizedValue) { case 'waiting_login': return '等待登录授权'; case 'ready_to_upload': return '等待上传'; case 'uploading': return '上传中'; case 'uploaded': case 'credential uploaded.': return '上传成功'; case 'error': return '上传失败'; case 'waiting_user': return '等待用户确认'; case 'authorized': return '已授权'; case 'expired': return '已过期'; default: return rawValue; } } function getKiroRuntimeState(state = {}) { const runtimeState = state?.runtimeState?.flowState?.kiro; if (runtimeState && typeof runtimeState === 'object' && !Array.isArray(runtimeState)) { return runtimeState; } const flowState = state?.flowState?.kiro; if (flowState && typeof flowState === 'object' && !Array.isArray(flowState)) { return flowState; } return {}; } function getGrokRuntimeState(state = {}) { const runtimeState = state?.runtimeState?.flowState?.grok; if (runtimeState && typeof runtimeState === 'object' && !Array.isArray(runtimeState)) { return runtimeState; } const flowState = state?.flowState?.grok; if (flowState && typeof flowState === 'object' && !Array.isArray(flowState)) { return flowState; } return {}; } function normalizeGrokSsoCookies(state = latestState) { const runtimeState = getGrokRuntimeState(state); const rawCookies = Array.isArray(runtimeState?.sso?.cookies) ? runtimeState.sso.cookies : (Array.isArray(state?.grokSsoCookies) ? state.grokSsoCookies : []); const currentCookie = String(runtimeState?.sso?.currentCookie || state?.grokSsoCookie || '').trim(); return Array.from(new Set([ currentCookie, ...rawCookies, ].map((entry) => String(entry || '').trim()).filter(Boolean))); } function getGrokRegisterStatusLabel(value = '') { const normalized = String(value || '').trim().toLowerCase(); switch (normalized) { case 'signup_page_opened': return '注册页已打开'; case 'email_submitting': return '正在提交邮箱'; case 'verification_requested': return '等待验证码'; case 'verified': return '验证码已提交'; case 'profile_submitting': return '正在提交资料'; case 'profile_submitted': return '资料已提交'; case 'completed': return '已完成'; case 'error': return '失败'; default: return String(value || '').trim() || '未开始'; } } function getGrokWebchat2ApiUploadStatusLabel(value = '') { const normalized = String(value || '').trim().toLowerCase(); switch (normalized) { case 'uploading': return '正在上传'; case 'uploaded': return '已上传'; case 'error': return '上传失败'; default: return String(value || '').trim() || '未开始'; } } function renderGrokRuntimeState(state = latestState) { const runtimeState = getGrokRuntimeState(state); const registerStatus = String( runtimeState?.register?.status || state?.grokRegisterStatus || '' ).trim(); const cookies = normalizeGrokSsoCookies(state); const currentCookie = String( runtimeState?.sso?.currentCookie || state?.grokSsoCookie || cookies[0] || '' ).trim(); const extractedAt = Number( runtimeState?.sso?.extractedAt || state?.grokSsoExtractedAt || 0 ) || 0; const uploadState = runtimeState?.upload || {}; const uploadStatus = String( uploadState.status || state?.grokWebchat2ApiUploadStatus || '' ).trim(); const uploadedAt = Number( uploadState.uploadedAt || state?.grokWebchat2ApiUploadedAt || 0 ) || 0; const uploadMessage = String( uploadState.message || state?.grokWebchat2ApiUploadMessage || '' ).trim(); const uploadTargetUrl = String( uploadState.targetUrl || state?.grokWebchat2ApiTargetUrl || '' ).trim(); if (displayGrokRegisterStatus) { displayGrokRegisterStatus.textContent = getGrokRegisterStatusLabel(registerStatus); } if (displayGrokSsoStatus) { displayGrokSsoStatus.textContent = currentCookie ? `已提取 ${cookies.length || 1} 条${extractedAt ? `,${new Date(extractedAt).toLocaleString()}` : ''}` : '未提取'; } if (displayGrokSsoCookie) { displayGrokSsoCookie.textContent = currentCookie ? `${currentCookie.slice(0, 8)}...${currentCookie.slice(-6)}` : '未提取'; displayGrokSsoCookie.title = currentCookie ? '已隐藏完整 SSO Cookie,可使用复制' : ''; } if (displayGrokWebchat2ApiUploadStatus) { const label = getGrokWebchat2ApiUploadStatusLabel(uploadStatus); const suffix = uploadedAt ? `,${new Date(uploadedAt).toLocaleString()}` : ''; displayGrokWebchat2ApiUploadStatus.textContent = `${label}${uploadMessage ? `:${uploadMessage}` : ''}${suffix}`; displayGrokWebchat2ApiUploadStatus.title = uploadTargetUrl || ''; } [btnCopyGrokSso, btnClearGrokSso].forEach((button) => { if (button) { button.disabled = cookies.length === 0; } }); } function setKiroRsConnectionTestStatus(message = '') { const nextText = String(message || '').trim() || '未测试'; kiroRsConnectionTestStatusText = nextText; if (typeof displayKiroRsTestStatus !== 'undefined' && displayKiroRsTestStatus) { displayKiroRsTestStatus.textContent = nextText; } } function normalizeStepExecutionRangeByFlow(value = {}) { const source = isPlainObjectValue(value) ? value : {}; const next = {}; if (hasStepExecutionRangeShape(source)) { next[DEFAULT_ACTIVE_FLOW_ID] = normalizeStepExecutionRangeEntry(source); return next; } for (const [rawFlowId, rawEntry] of Object.entries(source)) { if (!hasStepExecutionRangeShape(rawEntry)) { continue; } const flowId = normalizeStepExecutionRangeFlowId(rawFlowId, ''); if (!flowId) { continue; } next[flowId] = normalizeStepExecutionRangeEntry(rawEntry); } return next; } function getStepExecutionRangeForCurrentFlow(state = latestState) { const config = normalizeStepExecutionRangeByFlow(state?.stepExecutionRangeByFlow || {}); const flowId = getCurrentStepExecutionRangeFlowId(state); return config[flowId] || { enabled: false, fromStep: 1, toStep: getLastCurrentStepId() || 1 }; } function getLastCurrentStepId() { return STEP_IDS.length ? Math.max(...STEP_IDS) : 1; } function getStepExecutionRangeNodes() { return Array.isArray(workflowNodes) ? workflowNodes.filter((node) => String(node?.nodeId || '').trim()) : []; } function getStepExecutionRangeNodeLabel(node = {}) { const nodeId = String(node?.nodeId || '').trim(); const displayOrder = Number(node?.displayOrder); const title = String(node?.title || nodeId).trim(); const orderLabel = Number.isInteger(displayOrder) && displayOrder > 0 ? `步骤 ${displayOrder}` : nodeId; return title ? `${orderLabel} · ${title}` : orderLabel; } function getStepExecutionRangeBoundaryNodeId(stepNumber, boundary = 'start') { const nodes = getStepExecutionRangeNodes(); const fallbackNode = boundary === 'end' ? nodes[nodes.length - 1] : nodes[0]; const resolvedNodeId = getNodeIdByStepForCurrentMode(stepNumber); return String(resolvedNodeId || fallbackNode?.nodeId || '').trim(); } function getStepExecutionRangeStepOptionLabel(node = {}) { const nodeId = String(node?.nodeId || '').trim(); const step = getStepIdByNodeIdForCurrentMode(nodeId); const displayOrder = Number(node?.displayOrder); if (Number.isInteger(step) && step > 0) { return String(step); } if (Number.isInteger(displayOrder) && displayOrder > 0) { return String(displayOrder); } return nodeId; } function syncStepExecutionRangeSelectOptions(selectedFromNodeId = '', selectedToNodeId = '') { const nodes = getStepExecutionRangeNodes(); const fromSelect = inputStepExecutionRangeFrom; const toSelect = inputStepExecutionRangeTo; if (!fromSelect || !toSelect) { return; } const buildOptions = (selectedValue) => nodes.map((node) => { const nodeId = String(node?.nodeId || '').trim(); const label = getStepExecutionRangeStepOptionLabel(node); return ``; }).join(''); fromSelect.innerHTML = buildOptions(String(selectedFromNodeId || nodes[0]?.nodeId || '').trim()); toSelect.innerHTML = buildOptions(String(selectedToNodeId || nodes[nodes.length - 1]?.nodeId || '').trim()); } function isStepExecutionRangeUiAvailable(state = latestState) { return getCurrentStepExecutionRangeFlowId(state) === DEFAULT_ACTIVE_FLOW_ID; } function clampStepExecutionRangeInputs() { const nodes = getStepExecutionRangeNodes(); const firstNodeId = String(nodes[0]?.nodeId || '').trim(); const lastNodeId = String(nodes[nodes.length - 1]?.nodeId || '').trim(); const selectedFromNodeId = String(inputStepExecutionRangeFrom?.value || firstNodeId).trim() || firstNodeId; const selectedToNodeId = String(inputStepExecutionRangeTo?.value || lastNodeId).trim() || lastNodeId; const fromStep = Math.max(1, getStepIdByNodeIdForCurrentMode(selectedFromNodeId) || 1); const toStep = Math.max(1, getStepIdByNodeIdForCurrentMode(selectedToNodeId) || fromStep); const normalizedFromStep = Math.min(fromStep, toStep); const normalizedToStep = Math.max(fromStep, toStep); const normalizedFromNodeId = getStepExecutionRangeBoundaryNodeId(normalizedFromStep, 'start'); const normalizedToNodeId = getStepExecutionRangeBoundaryNodeId(normalizedToStep, 'end'); syncStepExecutionRangeSelectOptions(normalizedFromNodeId, normalizedToNodeId); if (inputStepExecutionRangeFrom) { inputStepExecutionRangeFrom.value = normalizedFromNodeId; } if (inputStepExecutionRangeTo) { inputStepExecutionRangeTo.value = normalizedToNodeId; } return { fromNodeId: normalizedFromNodeId, toNodeId: normalizedToNodeId, fromStep: normalizedFromStep, toStep: normalizedToStep, }; } function buildStepExecutionRangeByFlowPayload(existingConfig = latestState?.stepExecutionRangeByFlow || {}) { const config = normalizeStepExecutionRangeByFlow(existingConfig); if (!isStepExecutionRangeUiAvailable(latestState)) { return config; } const normalizedRange = clampStepExecutionRangeInputs(); const flowId = getCurrentStepExecutionRangeFlowId(latestState); config[flowId] = normalizeStepExecutionRangeEntry({ enabled: Boolean(inputStepExecutionRangeEnabled?.checked), fromStep: normalizedRange?.fromStep, toStep: normalizedRange?.toStep, }); return config; } function isNodeDisabledByStepExecutionRange(nodeId, state = latestState) { const range = getStepExecutionRangeForCurrentFlow(state); if (!range.enabled) { return false; } const step = getStepIdByNodeIdForCurrentMode(nodeId); if (!Number.isInteger(step) || step <= 0) { return false; } return step < range.fromStep || step > range.toStep; } function getEnabledNodeIdsForStepExecutionRange(state = latestState) { return NODE_IDS.filter((nodeId) => !isNodeDisabledByStepExecutionRange(nodeId, state)); } function applyStepExecutionRangeState(state = latestState) { if (!rowStepExecutionRange) { return; } const available = isStepExecutionRangeUiAvailable(state); rowStepExecutionRange.style.display = available ? '' : 'none'; const range = getStepExecutionRangeForCurrentFlow(state); const fromNodeId = getStepExecutionRangeBoundaryNodeId(range.fromStep, 'start'); const toNodeId = getStepExecutionRangeBoundaryNodeId(range.toStep, 'end'); syncStepExecutionRangeSelectOptions(fromNodeId, toNodeId); if (inputStepExecutionRangeFrom) { inputStepExecutionRangeFrom.value = fromNodeId; } if (inputStepExecutionRangeTo) { inputStepExecutionRangeTo.value = toNodeId; } if (inputStepExecutionRangeEnabled) { inputStepExecutionRangeEnabled.checked = Boolean(range.enabled); } const controlsDisabled = !available || isAutoRunLockedPhase(); if (inputStepExecutionRangeEnabled) inputStepExecutionRangeEnabled.disabled = controlsDisabled; if (inputStepExecutionRangeFrom) inputStepExecutionRangeFrom.disabled = controlsDisabled || !inputStepExecutionRangeEnabled?.checked; if (inputStepExecutionRangeTo) inputStepExecutionRangeTo.disabled = controlsDisabled || !inputStepExecutionRangeEnabled?.checked; } function getDisplayNodeStatus(nodeId, status, state = latestState) { return isNodeDisabledByStepExecutionRange(nodeId, state) ? 'disabled' : (status || 'pending'); } function escapeCssValue(value = '') { const raw = String(value || ''); if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') { return CSS.escape(raw); } return raw.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); } function normalizeStoredNodeStatus(status = '') { const normalized = String(status || '').trim() || 'pending'; return normalized === 'disabled' ? 'pending' : normalized; } function getStoredNodeStatuses(state = latestState) { const merged = { ...NODE_DEFAULT_STATUSES, ...(state?.nodeStatuses || {}) }; return Object.fromEntries(NODE_IDS.map((nodeId) => [ nodeId, normalizeStoredNodeStatus(merged[nodeId]), ])); } function getNodeStatuses(state = latestState) { const merged = typeof getStoredNodeStatuses === 'function' ? getStoredNodeStatuses(state) : Object.fromEntries(NODE_IDS.map((nodeId) => { const source = { ...NODE_DEFAULT_STATUSES, ...(state?.nodeStatuses || {}) }; const status = String(source[nodeId] || '').trim() || 'pending'; return [nodeId, status === 'disabled' ? 'pending' : status]; })); return Object.fromEntries(NODE_IDS.map((nodeId) => [ nodeId, typeof getDisplayNodeStatus === 'function' ? getDisplayNodeStatus(nodeId, merged[nodeId] || 'pending', state) : (merged[nodeId] || 'pending'), ])); } function getStepStatuses(state = latestState) { const merged = { ...STEP_DEFAULT_STATUSES }; if (typeof getNodeStatuses === 'function') { const nodeStatuses = getNodeStatuses(state); for (const [nodeId, status] of Object.entries(nodeStatuses)) { const step = getStepIdByNodeIdForCurrentMode(nodeId); if (step) { merged[step] = status || 'pending'; } } } return Object.fromEntries(STEP_IDS.map((stepId) => [stepId, merged[stepId] || 'pending'])); } function getFirstUnfinishedNode(state = latestState) { const statuses = getNodeStatuses(state); for (const nodeId of NODE_IDS) { if (statuses[nodeId] === 'disabled') { continue; } if (!isDoneStatus(statuses[nodeId])) { return nodeId; } } return ''; } function getFirstUnfinishedStep(state = latestState) { const nodeId = getFirstUnfinishedNode(state); return nodeId ? getStepIdByNodeIdForCurrentMode(nodeId) : null; } function getRunningNodes(state = latestState) { const statuses = getNodeStatuses(state); return Object.entries(statuses) .filter(([, status]) => status === 'running') .map(([nodeId]) => nodeId); } function getRunningSteps(state = latestState) { return getRunningNodes(state) .map((nodeId) => getStepIdByNodeIdForCurrentMode(nodeId)) .filter((step) => Number.isInteger(step) && step > 0) .sort((a, b) => a - b); } function hasSavedProgress(state = latestState) { const statuses = getNodeStatuses(state); return Object.values(statuses).some((status) => status !== 'pending' && status !== 'disabled'); } function isContributionModeSwitchBlocked(state = latestState) { const statuses = getStepStatuses(state); const anyRunning = Object.values(statuses).some((status) => status === 'running'); return anyRunning || isAutoRunLockedPhase() || isAutoRunPausedPhase(); } function shouldOfferAutoModeChoice(state = latestState) { return hasSavedProgress(state) && getFirstUnfinishedStep(state) !== null; } function syncLatestState(nextState) { const normalizedNextState = { ...(nextState || {}), }; if ( Object.prototype.hasOwnProperty.call(normalizedNextState, 'activeFlowId') || Object.prototype.hasOwnProperty.call(normalizedNextState, 'flowId') ) { const fallbackFlowId = latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID; const rawFlowId = Object.prototype.hasOwnProperty.call(normalizedNextState, 'activeFlowId') ? normalizedNextState.activeFlowId : normalizedNextState.flowId; const normalizedFlowId = typeof normalizeFlowId === 'function' ? normalizeFlowId(rawFlowId, fallbackFlowId) : (String(rawFlowId || fallbackFlowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID); normalizedNextState.activeFlowId = normalizedFlowId; normalizedNextState.flowId = normalizedFlowId; } const mergedNodeStatuses = normalizedNextState?.nodeStatuses ? getStoredNodeStatuses({ nodeStatuses: { ...NODE_DEFAULT_STATUSES, ...(latestState?.nodeStatuses || {}), ...normalizedNextState.nodeStatuses }, }) : getStoredNodeStatuses(latestState); latestState = { ...(latestState || {}), ...normalizedNextState, nodeStatuses: mergedNodeStatuses, }; const activeFlowId = typeof normalizeFlowId === 'function' ? normalizeFlowId(latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID, DEFAULT_ACTIVE_FLOW_ID) : (String(latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID); const schema = typeof getSettingsSchema === 'function' ? getSettingsSchema() : null; const selectedTargetId = schema?.getSelectedTargetId ? schema.getSelectedTargetId(latestState, activeFlowId) : (String(latestState?.targetId || '').trim().toLowerCase() || (activeFlowId === 'kiro' ? 'kiro-rs' : 'cpa')); latestState.targetId = typeof normalizeTargetIdForFlow === 'function' ? normalizeTargetIdForFlow(activeFlowId, selectedTargetId) : selectedTargetId; renderAccountRecords(latestState); } function isContributionModeActiveForFlow(state = latestState, flowId = undefined) { const rawFlowId = flowId !== undefined ? flowId : (state?.activeFlowId || state?.flowId || DEFAULT_ACTIVE_FLOW_ID); const normalizedFlowId = typeof normalizeFlowId === 'function' ? normalizeFlowId(rawFlowId, DEFAULT_ACTIVE_FLOW_ID) : (String(rawFlowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID); const stateFlowId = typeof normalizeFlowId === 'function' ? normalizeFlowId(state?.activeFlowId || state?.flowId || DEFAULT_ACTIVE_FLOW_ID, DEFAULT_ACTIVE_FLOW_ID) : (String(state?.activeFlowId || state?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID); return normalizedFlowId === stateFlowId && Boolean(state?.accountContributionEnabled); } let accountRunHistoryRefreshTimer = null; function scheduleAccountRunHistoryRefresh(delayMs = 150) { if (accountRunHistoryRefreshTimer) { clearTimeout(accountRunHistoryRefreshTimer); } accountRunHistoryRefreshTimer = setTimeout(() => { accountRunHistoryRefreshTimer = null; chrome.runtime.sendMessage({ type: 'GET_STATE', source: 'sidepanel' }).then(state => { syncLatestState(state); syncAutoRunState(state); updateStatusDisplay(latestState); updateButtonStates(); }).catch(() => { }); }, Math.max(0, Number(delayMs) || 0)); } function normalizeOperationDelayEnabled(value) { return true; } function appendOperationDelayLog(enabled, level = 'info', message = '') { appendLog({ timestamp: Date.now(), level, message: message || (enabled ? '操作间延迟已固定开启。' : '操作间延迟保持固定开启。'), }); } function applyOperationDelayState(state = latestState, options = {}) { const enabled = options.restoreFailed ? true : normalizeOperationDelayEnabled(state?.operationDelayEnabled); if (typeof syncLatestState === 'function') { syncLatestState({ operationDelayEnabled: enabled }); } if (options.restoreFailed) { appendOperationDelayLog(true, 'warn', '操作间延迟设置读取失败,已回退为默认开启。'); } } async function persistOperationDelayToggle() { applyOperationDelayState({ operationDelayEnabled: true }); return true; const nextEnabled = normalizeOperationDelayEnabled(inputOperationDelayEnabled?.checked); try { const response = await chrome.runtime.sendMessage({ type: 'SAVE_SETTING', source: 'sidepanel', payload: { operationDelayEnabled: nextEnabled }, }); if (response?.error) throw new Error(response.error); const confirmed = normalizeOperationDelayEnabled(response?.state?.operationDelayEnabled ?? nextEnabled); lastConfirmedOperationDelayEnabled = confirmed; if (inputOperationDelayEnabled) inputOperationDelayEnabled.checked = confirmed; syncLatestState({ operationDelayEnabled: confirmed }); appendOperationDelayLog(confirmed); } catch (error) { if (inputOperationDelayEnabled) inputOperationDelayEnabled.checked = lastConfirmedOperationDelayEnabled; appendOperationDelayLog(lastConfirmedOperationDelayEnabled, 'error', `操作间延迟设置保存失败,已恢复为上一次确认的状态:${error.message}`); throw error; } } function normalizePlusPaymentMethod(value = '') { const rootScope = typeof window !== 'undefined' ? window : globalThis; if (rootScope.GoPayUtils?.normalizePlusPaymentMethod) { return rootScope.GoPayUtils.normalizePlusPaymentMethod(value); } const gopayValue = typeof PLUS_PAYMENT_METHOD_GOPAY !== 'undefined' ? PLUS_PAYMENT_METHOD_GOPAY : 'gopay'; const gpcValue = typeof PLUS_PAYMENT_METHOD_GPC_HELPER !== 'undefined' ? PLUS_PAYMENT_METHOD_GPC_HELPER : 'gpc-helper'; const paypalValue = typeof PLUS_PAYMENT_METHOD_PAYPAL !== 'undefined' ? PLUS_PAYMENT_METHOD_PAYPAL : 'paypal'; const paypalHostedValue = typeof PLUS_PAYMENT_METHOD_PAYPAL_HOSTED !== 'undefined' ? PLUS_PAYMENT_METHOD_PAYPAL_HOSTED : 'paypal-hosted'; const noneValue = typeof PLUS_PAYMENT_METHOD_NONE !== 'undefined' ? PLUS_PAYMENT_METHOD_NONE : 'none'; const normalized = String(value || '').trim().toLowerCase(); if (normalized === noneValue || normalized === 'no-payment' || normalized === 'skip-payment') { return noneValue; } if (normalized === paypalHostedValue || normalized === 'paypal_direct' || normalized === 'paypal-direct') { return paypalHostedValue; } if (normalized === gpcValue) { return gpcValue; } return normalized === gopayValue ? gopayValue : paypalValue; } function normalizePlusHostedCheckoutOauthDelaySeconds(value) { const numeric = Number(String(value ?? '').trim()); if (!Number.isFinite(numeric)) { return DEFAULT_PLUS_HOSTED_CHECKOUT_OAUTH_DELAY_SECONDS; } return Math.min(120, Math.max(0, Math.floor(numeric))); } function normalizePlusAccountAccessStrategy(value = '') { const normalized = String(value || '').trim().toLowerCase(); if (normalized === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION) { return PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION; } if (normalized === PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION) { return PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION; } return PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; } function normalizePlusAccountAccessStrategyUiValue(value = '') { const normalized = String(value || '').trim().toLowerCase(); if ( normalized === PLUS_ACCOUNT_ACCESS_STRATEGY_CODEX_SESSION_UI || normalized === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION || normalized === PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION ) { return PLUS_ACCOUNT_ACCESS_STRATEGY_CODEX_SESSION_UI; } return PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; } function getPlusAccountSessionStrategyForTarget(targetId = '') { const normalizedTargetId = normalizePlusStrategyTargetId(targetId); if (normalizedTargetId === 'sub2api') { return PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION; } if (normalizedTargetId === 'cpa') { return PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION; } return PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; } function resolvePlusAccountAccessStrategyForTarget(value = '', targetId = '') { const normalizedUiValue = normalizePlusAccountAccessStrategyUiValue(value); if (normalizedUiValue !== PLUS_ACCOUNT_ACCESS_STRATEGY_CODEX_SESSION_UI) { return PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; } return getPlusAccountSessionStrategyForTarget(targetId); } function getSelectedPlusPaymentMethod(state = latestState) { const defaultMethod = typeof DEFAULT_PLUS_PAYMENT_METHOD !== 'undefined' ? DEFAULT_PLUS_PAYMENT_METHOD : 'paypal'; if (typeof selectPlusPaymentMethod !== 'undefined' && selectPlusPaymentMethod?.value) { return normalizePlusPaymentMethod(selectPlusPaymentMethod.value); } return normalizePlusPaymentMethod(state?.plusPaymentMethod || currentPlusPaymentMethod || defaultMethod); } function getRequestedPlusAccountAccessStrategy(state = latestState) { const defaultStrategy = typeof DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY !== 'undefined' ? DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY : 'oauth'; const oauthStrategyValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH !== 'undefined' ? PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH : 'oauth'; const sub2apiSessionStrategyValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION !== 'undefined' ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : 'sub2api_codex_session'; const cpaSessionStrategyValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION !== 'undefined' ? PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION : 'cpa_codex_session'; const sessionUiValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_CODEX_SESSION_UI !== 'undefined' ? PLUS_ACCOUNT_ACCESS_STRATEGY_CODEX_SESSION_UI : 'codex_session'; const resolveTargetId = () => { if (typeof getSelectedPanelMode === 'function') { return getSelectedPanelMode(); } if (typeof selectPanelMode !== 'undefined' && selectPanelMode?.value) { return selectPanelMode.value; } return getSelectedTargetIdForState({ ...(latestState || {}), ...(state || {}), activeFlowId: DEFAULT_ACTIVE_FLOW_ID, flowId: DEFAULT_ACTIVE_FLOW_ID, }, DEFAULT_ACTIVE_FLOW_ID); }; const resolveStrategyForTarget = typeof resolvePlusAccountAccessStrategyForTarget === 'function' ? resolvePlusAccountAccessStrategyForTarget : ((value = '', targetId = '') => { const normalizedValue = String(value || '').trim().toLowerCase(); const isSessionImport = normalizedValue === sessionUiValue || normalizedValue === sub2apiSessionStrategyValue || normalizedValue === cpaSessionStrategyValue; if (!isSessionImport) { return oauthStrategyValue; } const normalizedTargetId = typeof normalizePlusStrategyTargetId === 'function' ? normalizePlusStrategyTargetId(targetId) : String(targetId || '').trim().toLowerCase(); if (normalizedTargetId === 'sub2api') { return sub2apiSessionStrategyValue; } if (normalizedTargetId === 'cpa') { return cpaSessionStrategyValue; } return oauthStrategyValue; }); const fallbackStrategy = normalizePlusAccountAccessStrategy( (typeof selectPlusAccountAccessStrategy !== 'undefined' && selectPlusAccountAccessStrategy?.dataset?.requestedValue) || state?.plusAccountAccessStrategy || currentPlusAccountAccessStrategy || defaultStrategy ); if ( typeof selectPlusAccountAccessStrategy !== 'undefined' && selectPlusAccountAccessStrategy && !selectPlusAccountAccessStrategy.disabled ) { return resolveStrategyForTarget(selectPlusAccountAccessStrategy.value || fallbackStrategy, resolveTargetId()); } return fallbackStrategy; } function normalizePlusStrategyTargetId(value = '') { const normalized = String(value || '').trim().toLowerCase(); if (normalized === 'sub2api') { return 'sub2api'; } if (normalized === 'codex2api') { return 'codex2api'; } return 'cpa'; } function getPlusAccountAccessStrategyContinuationLabel(strategy = '', targetId = '') { const normalizedStrategy = normalizePlusAccountAccessStrategy(strategy); if (normalizedStrategy === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION) { return '导入当前 ChatGPT 会话到 SUB2API'; } if (normalizedStrategy === PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION) { return '导入当前 ChatGPT 会话到 CPA'; } return 'OAuth 登录'; } function getPlusAccountAccessStrategyDescription(strategy = '', targetId = '') { const normalizedStrategy = normalizePlusAccountAccessStrategy(strategy); const normalizedTargetId = normalizePlusStrategyTargetId(targetId); if (normalizedStrategy === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION) { return '复用当前 Plus 已登录会话,直接导入到 SUB2API'; } if (normalizedStrategy === PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION) { return '复用当前 Plus 已登录会话,直接导入到 CPA'; } if (normalizedTargetId === 'sub2api') { return '通过 OAuth 回调创建 SUB2API 账号'; } if (normalizedTargetId === 'codex2api') { return '通过 OAuth 回调创建 Codex2API 账号'; } return '通过 OAuth 回调创建 CPA 账号'; } function resolvePlusManualContinuationActionLabelFromState(state = latestState) { const activeFlowId = String(state?.activeFlowId || state?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase(); const signupMethod = normalizeSignupMethod(state?.resolvedSignupMethod || state?.signupMethod || DEFAULT_SIGNUP_METHOD); const plusModeEnabled = state?.plusModeEnabled === undefined ? true : Boolean(state?.plusModeEnabled); const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function' ? normalizeTargetIdForFlow : ((flowId, targetId = '', fallback = '') => { const normalizedFlowId = String(flowId || '').trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID; if (normalizedFlowId === DEFAULT_ACTIVE_FLOW_ID) { const normalizedTargetId = String(targetId || fallback || '').trim().toLowerCase(); return normalizedTargetId === 'sub2api' || normalizedTargetId === 'codex2api' ? normalizedTargetId : 'cpa'; } const normalizedTargetId = String(targetId || '').trim().toLowerCase(); return normalizedTargetId || String(fallback || '').trim().toLowerCase() || 'kiro-rs'; }); const getDefaultTargetIdForFlowSafe = typeof getDefaultTargetIdForFlow === 'function' ? getDefaultTargetIdForFlow : ((flowId = DEFAULT_ACTIVE_FLOW_ID) => (String(flowId || '').trim().toLowerCase() === DEFAULT_ACTIVE_FLOW_ID ? 'cpa' : 'kiro-rs')); const normalizePlusStrategyTargetIdSafe = typeof normalizePlusStrategyTargetId === 'function' ? normalizePlusStrategyTargetId : ((value = '') => { const normalized = String(value || '').trim().toLowerCase(); if (normalized === 'sub2api' || normalized === 'codex2api') { return normalized; } return 'cpa'; }); const targetId = normalizePlusStrategyTargetIdSafe( typeof getSelectedTargetIdForState === 'function' ? getSelectedTargetIdForState(state, activeFlowId) : normalizeTargetIdForFlowSafe( activeFlowId, state?.targetId || '', getDefaultTargetIdForFlowSafe(activeFlowId) ) ); const strategy = normalizePlusAccountAccessStrategy(state?.plusAccountAccessStrategy || DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY); const effectiveStrategy = plusModeEnabled && activeFlowId === DEFAULT_ACTIVE_FLOW_ID && signupMethod === SIGNUP_METHOD_EMAIL ? strategy : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; return getPlusAccountAccessStrategyContinuationLabel(effectiveStrategy, targetId); } function hasOwnStateValue(source, key) { return Object.prototype.hasOwnProperty.call(source, key); } function readAutoRunStateValue(source, keys, fallback) { for (const key of keys) { if (hasOwnStateValue(source, key)) { return source[key]; } } return fallback; } function normalizePendingAutoRunStartRunCount(value) { const numeric = Math.floor(Number(value) || 0); return numeric > 0 ? numeric : 0; } function registerPendingAutoRunStartRunCount(totalRuns) { pendingAutoRunStartTotalRuns = normalizePendingAutoRunStartRunCount(totalRuns); pendingAutoRunStartExpiresAt = pendingAutoRunStartTotalRuns > 0 ? Date.now() + 30000 : 0; } function clearPendingAutoRunStartRunCount() { pendingAutoRunStartTotalRuns = 0; pendingAutoRunStartExpiresAt = 0; } function getPendingAutoRunStartRunCount() { if (pendingAutoRunStartTotalRuns > 0 && pendingAutoRunStartExpiresAt > 0 && Date.now() > pendingAutoRunStartExpiresAt) { clearPendingAutoRunStartRunCount(); } return pendingAutoRunStartTotalRuns; } function getAutoRunSourceTotalRuns(source = {}) { return normalizePendingAutoRunStartRunCount(readAutoRunStateValue(source, ['autoRunTotalRuns', 'totalRuns'], 0)); } function syncAutoRunState(source = {}) { const phase = source.autoRunPhase ?? source.phase ?? currentAutoRun.phase; const autoRunning = source.autoRunning !== undefined ? Boolean(source.autoRunning) : (source.autoRunPhase !== undefined || source.phase !== undefined ? ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase) : currentAutoRun.autoRunning); currentAutoRun = { autoRunning, phase, currentRun: readAutoRunStateValue(source, ['autoRunCurrentRun', 'currentRun'], currentAutoRun.currentRun), totalRuns: readAutoRunStateValue(source, ['autoRunTotalRuns', 'totalRuns'], currentAutoRun.totalRuns), attemptRun: readAutoRunStateValue(source, ['autoRunAttemptRun', 'attemptRun'], currentAutoRun.attemptRun), countdownAt: readAutoRunStateValue(source, ['autoRunCountdownAt', 'countdownAt'], currentAutoRun.countdownAt), countdownTitle: readAutoRunStateValue(source, ['autoRunCountdownTitle', 'countdownTitle'], currentAutoRun.countdownTitle), countdownNote: readAutoRunStateValue(source, ['autoRunCountdownNote', 'countdownNote'], currentAutoRun.countdownNote), }; } function isContributionButtonLocked() { const autoActive = currentAutoRun.autoRunning || isAutoRunLockedPhase() || isAutoRunPausedPhase(); if (autoActive) { return false; } const statuses = getStepStatuses(); const anyRunning = Object.values(statuses).some((status) => status === 'running'); return anyRunning; } function isAutoRunLockedPhase() { return currentAutoRun.phase === 'running' || currentAutoRun.phase === 'waiting_step' || currentAutoRun.phase === 'retrying' || currentAutoRun.phase === 'waiting_interval'; } function isAutoRunPausedPhase() { return currentAutoRun.phase === 'waiting_email'; } function isAutoRunWaitingStepPhase() { return currentAutoRun.phase === 'waiting_step'; } function isAutoRunSourceSyncPhase(phase) { return ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase); } function shouldSyncRunCountFromAutoRunSource(source = {}) { const phase = source.autoRunPhase ?? source.phase ?? currentAutoRun.phase; const autoRunning = source.autoRunning !== undefined ? Boolean(source.autoRunning) : isAutoRunSourceSyncPhase(phase); const shouldSync = autoRunning || isAutoRunSourceSyncPhase(phase); if (!shouldSync) { return false; } const pendingTotalRuns = getPendingAutoRunStartRunCount(); if (pendingTotalRuns > 0) { const sourceTotalRuns = getAutoRunSourceTotalRuns(source); if (sourceTotalRuns > 0 && sourceTotalRuns !== pendingTotalRuns) { return false; } if (sourceTotalRuns === pendingTotalRuns) { clearPendingAutoRunStartRunCount(); } } return true; } function getAutoRunLabel(payload = currentAutoRun) { if ((payload.phase ?? currentAutoRun.phase) === 'scheduled') { return (payload.totalRuns || 1) > 1 ? ` (${payload.totalRuns}轮)` : ''; } const attemptLabel = payload.attemptRun ? ` · 尝试${payload.attemptRun}` : ''; if ((payload.totalRuns || 1) > 1) { return ` (${payload.currentRun}/${payload.totalRuns}${attemptLabel})`; } return attemptLabel ? ` (${attemptLabel.slice(3)})` : ''; } function normalizeAutoRunThreadIntervalMinutes(value) { const rawValue = String(value ?? '').trim(); if (!rawValue) { return AUTO_FALLBACK_THREAD_INTERVAL_DEFAULT_MINUTES; } const numeric = Number(rawValue); if (!Number.isFinite(numeric)) { return AUTO_FALLBACK_THREAD_INTERVAL_DEFAULT_MINUTES; } return Math.min( AUTO_FALLBACK_THREAD_INTERVAL_MAX_MINUTES, Math.max(AUTO_FALLBACK_THREAD_INTERVAL_MIN_MINUTES, Math.floor(numeric)) ); } function normalizeAutoStepDelaySeconds(value) { const rawValue = String(value ?? '').trim(); if (!rawValue) { return null; } const numeric = Number(rawValue); if (!Number.isFinite(numeric)) { return null; } return Math.min(AUTO_STEP_DELAY_MAX_SECONDS, Math.max(AUTO_STEP_DELAY_MIN_SECONDS, Math.floor(numeric))); } function normalizeVerificationResendCount(value, fallback) { const rawValue = String(value ?? '').trim(); if (!rawValue) { return fallback; } const numeric = Number(rawValue); if (!Number.isFinite(numeric)) { return fallback; } return Math.min( VERIFICATION_RESEND_COUNT_MAX, Math.max(VERIFICATION_RESEND_COUNT_MIN, Math.floor(numeric)) ); } function formatAutoStepDelayInputValue(value) { const normalized = normalizeAutoStepDelaySeconds(value); return normalized === null ? '' : String(normalized); } function normalizeCustomEmailPoolEntries(value = '') { const source = Array.isArray(value) ? value : String(value || '').split(/[\r\n,,;;]+/); return source .map((item) => String(item || '').trim().toLowerCase()) .filter((item) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(item)); } function normalizeCustomEmailPoolEntryEmail(value = '') { return String(value || '').trim().toLowerCase(); } function createCustomEmailPoolEntryId() { if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { return crypto.randomUUID(); } return `custom-pool-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`; } function normalizeCustomEmailPoolEntryObjects(value = []) { const source = Array.isArray(value) ? value : []; const seenEmails = new Set(); const entries = []; for (const rawEntry of source) { const asObject = rawEntry && typeof rawEntry === 'object' ? rawEntry : { email: rawEntry }; const email = normalizeCustomEmailPoolEntryEmail(asObject.email || ''); if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { continue; } if (seenEmails.has(email)) { continue; } seenEmails.add(email); entries.push({ id: String(asObject.id || createCustomEmailPoolEntryId()), email, enabled: asObject.enabled !== undefined ? Boolean(asObject.enabled) : true, used: Boolean(asObject.used), note: String(asObject.note || '').trim(), lastUsedAt: Number.isFinite(Number(asObject.lastUsedAt)) ? Number(asObject.lastUsedAt) : 0, }); } return entries; } function getNormalizedCustomEmailPoolEntriesState() { const entries = (typeof customEmailPoolEntriesState !== 'undefined' && Array.isArray(customEmailPoolEntriesState)) ? customEmailPoolEntriesState : []; return normalizeCustomEmailPoolEntryObjects(entries); } function getActiveCustomEmailPoolEmails(entries = getNormalizedCustomEmailPoolEntriesState()) { return normalizeCustomEmailPoolEntryObjects(entries) .filter((entry) => entry.enabled && !entry.used) .map((entry) => entry.email); } function setCustomEmailPoolEntriesState(entries = [], options = {}) { const { syncInput = true } = options; customEmailPoolEntriesState = normalizeCustomEmailPoolEntryObjects(entries); if (syncInput && inputCustomEmailPool) { inputCustomEmailPool.value = getActiveCustomEmailPoolEmails(customEmailPoolEntriesState).join('\n'); } } function restoreCustomEmailPoolEntriesFromState(state = {}) { const rawEntries = Array.isArray(state?.customEmailPoolEntries) ? state.customEmailPoolEntries : []; if (rawEntries.length > 0) { return normalizeCustomEmailPoolEntryObjects(rawEntries); } return normalizeCustomEmailPoolEntries(state?.customEmailPool).map((email) => ({ id: createCustomEmailPoolEntryId(), email, enabled: true, used: false, note: '', lastUsedAt: 0, })); } function usesCustomEmailPoolGenerator(provider = selectMailProvider.value) { const providerUsesYydsMail = typeof isYydsMailProvider === 'function' ? isYydsMailProvider(provider) : String(provider || '').trim().toLowerCase() === 'yyds-mail'; return !isCustomMailProvider(provider) && !isLuckmailProvider(provider) && !providerUsesYydsMail && getSelectedEmailGenerator() === CUSTOM_EMAIL_POOL_GENERATOR; } function getCustomMailProviderPoolSize() { return normalizeCustomEmailPoolEntries(inputCustomMailProviderPool?.value).length; } function usesCustomMailProviderPool(provider = selectMailProvider.value) { return isCustomMailProvider(provider) && getCustomMailProviderPoolSize() > 0; } function getCustomEmailPoolSize() { if (typeof customEmailPoolEntriesState !== 'undefined' && Array.isArray(customEmailPoolEntriesState)) { const activeEntries = getActiveCustomEmailPoolEmails(customEmailPoolEntriesState); if (activeEntries.length > 0 || customEmailPoolEntriesState.length > 0) { return activeEntries.length; } } return normalizeCustomEmailPoolEntries(inputCustomEmailPool?.value).length; } function getLockedRunCountFromEmailPool(provider = selectMailProvider.value) { if (usesCustomMailProviderPool(provider)) { return getCustomMailProviderPoolSize(); } if (usesCustomEmailPoolGenerator(provider)) { return getCustomEmailPoolSize(); } return 0; } function shouldLockRunCountToEmailPool(provider = (typeof selectMailProvider !== 'undefined' ? selectMailProvider?.value : undefined)) { return getLockedRunCountFromEmailPool(provider) > 0; } function syncRunCountFromCustomEmailPool() { if (!usesCustomEmailPoolGenerator()) { return; } inputRunCount.value = String(getCustomEmailPoolSize()); } function syncRunCountFromCustomMailProviderPool() { if (!usesCustomMailProviderPool()) { return; } inputRunCount.value = String(getCustomMailProviderPoolSize()); } function syncRunCountFromConfiguredEmailPool(provider = selectMailProvider.value) { const poolSize = getLockedRunCountFromEmailPool(provider); if (poolSize > 0) { inputRunCount.value = String(poolSize); } } function getRunCountValue() { const lockedRunCount = typeof getLockedRunCountFromEmailPool === 'function' ? getLockedRunCountFromEmailPool() : 0; if (lockedRunCount > 0) { return lockedRunCount; } return Math.max(1, parseInt(inputRunCount.value, 10) || 1); } function updateFallbackThreadIntervalInputState() { if (!inputAutoSkipFailuresThreadIntervalMinutes) { return; } inputAutoSkipFailuresThreadIntervalMinutes.disabled = Boolean(inputAutoSkipFailures.disabled); } function formatCountdown(remainingMs) { const totalSeconds = Math.max(0, Math.ceil(remainingMs / 1000)); const hours = Math.floor(totalSeconds / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; } function formatScheduleTime(timestamp) { return new Date(timestamp).toLocaleString('zh-CN', { hour12: false, timeZone: DISPLAY_TIMEZONE, month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', }); } function stopAutoRunCountdownTicker() { clearInterval(autoRunCountdownTimer); autoRunCountdownTimer = null; } function getActiveAutoRunCountdown() { if (currentAutoRun.phase !== 'waiting_interval') { return null; } if (!Number.isFinite(currentAutoRun.countdownAt)) { return null; } return { at: currentAutoRun.countdownAt, title: currentAutoRun.countdownTitle || '等待中', note: currentAutoRun.countdownNote || '', tone: 'running', }; } function renderAutoRunCountdownInfo() { if (!autoCountdownBar) { return; } const countdown = getActiveAutoRunCountdown(); if (!countdown) { autoCountdownBar.style.display = 'none'; return; } const remainingMs = countdown.at - Date.now(); autoCountdownBar.style.display = 'flex'; if (btnAutoRunNow) { btnAutoRunNow.hidden = false; btnAutoRunNow.textContent = '立即继续'; } autoCountdownTitle.textContent = countdown.title; autoCountdownMeta.textContent = remainingMs > 0 ? `${countdown.note ? `${countdown.note},` : ''}剩余 ${formatCountdown(remainingMs)}` : '倒计时即将结束,正在准备继续...'; return; } function syncAutoRunCountdownTicker() { renderAutoRunCountdownInfo(); if (getActiveAutoRunCountdown()) { if (autoRunCountdownTimer) { return; } autoRunCountdownTimer = setInterval(() => { renderAutoRunCountdownInfo(); updateStatusDisplay(latestState); }, 1000); return; } stopAutoRunCountdownTicker(); return; } function setDefaultAutoRunButton() { btnAutoRun.disabled = false; inputRunCount.disabled = shouldLockRunCountToEmailPool(); btnAutoRun.textContent = '自动'; } function normalizeCloudflareDomainValue(value = '') { let normalized = String(value || '').trim().toLowerCase(); if (!normalized) return ''; normalized = normalized.replace(/^@+/, ''); normalized = normalized.replace(/^https?:\/\//, ''); normalized = normalized.replace(/\/.*$/, ''); if (!/^[a-z0-9.-]+\.[a-z]{2,}$/.test(normalized)) { return ''; } return normalized; } function normalizeCloudflareDomains(values = []) { const seen = new Set(); const domains = []; for (const value of Array.isArray(values) ? values : []) { const normalized = normalizeCloudflareDomainValue(value); if (!normalized || seen.has(normalized)) continue; seen.add(normalized); domains.push(normalized); } return domains; } function normalizeCloudflareTempEmailBaseUrlValue(value = '') { const raw = String(value || '').trim(); if (!raw) return ''; const candidate = /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(raw) ? raw : `https://${raw}`; try { const parsed = new URL(candidate); parsed.hash = ''; parsed.search = ''; const pathname = parsed.pathname === '/' ? '' : parsed.pathname.replace(/\/+$/, ''); return `${parsed.origin}${pathname}`; } catch { return ''; } } function normalizeCloudflareTempEmailReceiveMailboxValue(value = '') { const normalized = String(value || '').trim().toLowerCase(); if (!normalized) return ''; return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalized) ? normalized : ''; } function normalizeCloudflareTempEmailDomainValue(value = '') { return normalizeCloudflareDomainValue(value); } function normalizeCloudflareTempEmailSubdomainPrefixValue(value = '') { let normalized = String(value || '').trim().toLowerCase(); if (!normalized) return ''; normalized = normalized.replace(/^@+/, ''); normalized = normalized.replace(/^https?:\/\//, ''); normalized = normalized.replace(/\/.*$/, ''); normalized = normalized.replace(/^\.+|\.+$/g, ''); if (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(normalized)) { return ''; } return normalized; } function buildCloudflareTempEmailEffectiveDomainValue(domain = '', subdomainPrefix = '', options = {}) { const normalizedDomain = normalizeCloudflareTempEmailDomainValue(domain); if (!normalizedDomain) return ''; const normalizedPrefix = normalizeCloudflareTempEmailSubdomainPrefixValue(subdomainPrefix); if (options.requirePrefix && !normalizedPrefix) return ''; return normalizedPrefix ? `${normalizedPrefix}.${normalizedDomain}` : normalizedDomain; } function getCloudflareTempEmailSubdomainPrefixValidation(value = '') { const raw = String(value || '').trim(); if (!raw) { return { raw, normalized: '', valid: false, message: '请填写子域前缀', }; } const normalized = normalizeCloudflareTempEmailSubdomainPrefixValue(raw); if (!normalized) { return { raw, normalized: '', valid: false, message: '仅支持 1-63 位字母、数字或中划线,且不能以中划线开头或结尾', }; } return { raw, normalized, valid: true, message: '', }; } function normalizeCloudflareTempEmailDomains(values = []) { const seen = new Set(); const domains = []; for (const value of Array.isArray(values) ? values : []) { const normalized = normalizeCloudflareTempEmailDomainValue(value); if (!normalized || seen.has(normalized)) continue; seen.add(normalized); domains.push(normalized); } return domains; } function normalizeCloudMailBaseUrlValue(value = '') { return normalizeCloudflareTempEmailBaseUrlValue(value); } function normalizeCloudMailReceiveMailboxValue(value = '') { return normalizeCloudflareTempEmailReceiveMailboxValue(value); } function normalizeCloudMailDomainValue(value = '') { return normalizeCloudflareDomainValue(value); } function getCloudflareDomainsFromState() { const domains = normalizeCloudflareDomains(latestState?.cloudflareDomains || []); const activeDomain = normalizeCloudflareDomainValue(latestState?.cloudflareDomain || ''); if (activeDomain && !domains.includes(activeDomain)) { domains.unshift(activeDomain); } return { domains, activeDomain: activeDomain || domains[0] || '' }; } function getCloudflareTempEmailDomainsFromState() { const domains = normalizeCloudflareTempEmailDomains(latestState?.cloudflareTempEmailDomains || []); const activeDomain = normalizeCloudflareTempEmailDomainValue(latestState?.cloudflareTempEmailDomain || ''); if (activeDomain && !domains.includes(activeDomain)) { domains.unshift(activeDomain); } return { domains, activeDomain: activeDomain || domains[0] || '' }; } function renderCloudflareDomainOptions(preferredDomain = '') { const preferred = normalizeCloudflareDomainValue(preferredDomain); const { domains, activeDomain } = getCloudflareDomainsFromState(); const selected = preferred || activeDomain; cfDomainPicker.render(domains, domains.includes(selected) ? selected : domains[0] || ''); } function renderCloudflareTempEmailDomainOptions(preferredDomain = '') { const preferred = normalizeCloudflareTempEmailDomainValue(preferredDomain); const { domains, activeDomain } = getCloudflareTempEmailDomainsFromState(); const selected = preferred || activeDomain; tempEmailDomainPicker.render(domains, domains.includes(selected) ? selected : domains[0] || ''); } function setCloudflareDomainEditMode(editing, options = {}) { const { clearInput = false } = options; cloudflareDomainEditMode = Boolean(editing); cfDomainPicker.setVisible(!cloudflareDomainEditMode); inputCfDomain.style.display = cloudflareDomainEditMode ? '' : 'none'; btnCfDomainMode.textContent = cloudflareDomainEditMode ? '保存' : '添加'; if (cloudflareDomainEditMode) { if (clearInput) { inputCfDomain.value = ''; } inputCfDomain.focus(); } else if (clearInput) { inputCfDomain.value = ''; } } function setCloudflareTempEmailDomainEditMode(editing, options = {}) { const { clearInput = false } = options; cloudflareTempEmailDomainEditMode = false; tempEmailDomainPicker.setVisible(true); inputTempEmailDomain.style.display = 'none'; btnTempEmailDomainMode.textContent = '更新'; if (clearInput) { inputTempEmailDomain.value = ''; } } function applyCloudflareTempEmailSettingsState(state = {}) { inputTempEmailBaseUrl.value = state?.cloudflareTempEmailBaseUrl || ''; inputTempEmailAdminAuth.value = state?.cloudflareTempEmailAdminAuth || ''; inputTempEmailCustomAuth.value = state?.cloudflareTempEmailCustomAuth || ''; inputTempEmailReceiveMailbox.value = state?.cloudflareTempEmailReceiveMailbox || ''; setCloudflareTempEmailLookupMode(state?.cloudflareTempEmailLookupMode); setCloudflareTempEmailSubdomainMode( state?.cloudflareTempEmailUseFixedSubdomain ? CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_FIXED : (state?.cloudflareTempEmailUseRandomSubdomain ? CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_RANDOM : CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_NONE) ); if (inputTempEmailSubdomainPrefix) { inputTempEmailSubdomainPrefix.value = normalizeCloudflareTempEmailSubdomainPrefixValue( state?.cloudflareTempEmailSubdomainPrefix || '' ); } renderCloudflareTempEmailDomainOptions(state?.cloudflareTempEmailDomain || ''); setCloudflareTempEmailDomainEditMode(false, { clearInput: true }); } function applyCloudMailSettingsState(state = {}) { if (inputCloudMailBaseUrl) { inputCloudMailBaseUrl.value = state?.cloudMailBaseUrl || ''; } if (inputCloudMailAdminEmail) { inputCloudMailAdminEmail.value = state?.cloudMailAdminEmail || ''; } if (inputCloudMailAdminPassword) { inputCloudMailAdminPassword.value = state?.cloudMailAdminPassword || ''; } if (inputCloudMailReceiveMailbox) { inputCloudMailReceiveMailbox.value = state?.cloudMailReceiveMailbox || ''; } if (inputCloudMailDomain) { inputCloudMailDomain.value = state?.cloudMailDomain || ''; } } function applyYydsMailSettingsState(state = {}) { const normalizeYydsBaseUrlValue = typeof normalizeYydsMailBaseUrl === 'function' ? normalizeYydsMailBaseUrl : ((value) => String(value || '').trim() || 'https://maliapi.215.im/v1'); if (inputYydsMailApiKey) { inputYydsMailApiKey.value = state?.yydsMailApiKey || ''; } if (inputYydsMailBaseUrl) { inputYydsMailBaseUrl.value = normalizeYydsBaseUrlValue(state?.yydsMailBaseUrl); } } function collectSettingsPayload() { const defaultGpcBaseUrl = typeof DEFAULT_GPC_BASE_URL !== 'undefined' ? DEFAULT_GPC_BASE_URL : 'https://gpc.qlhazycoder.top'; const normalizeYydsBaseUrlValue = typeof normalizeYydsMailBaseUrl === 'function' ? normalizeYydsMailBaseUrl : ((value) => String(value || '').trim() || 'https://maliapi.215.im/v1'); const { domains, activeDomain } = getCloudflareDomainsFromState(); const selectedCloudflareDomain = normalizeCloudflareDomainValue( !cloudflareDomainEditMode ? selectCfDomain.value : activeDomain ) || activeDomain; const { domains: tempEmailDomains, activeDomain: tempEmailActiveDomain } = getCloudflareTempEmailDomainsFromState(); const selectedCloudflareTempEmailDomain = normalizeCloudflareTempEmailDomainValue( !cloudflareTempEmailDomainEditMode ? selectTempEmailDomain.value : tempEmailActiveDomain ) || tempEmailActiveDomain; const cloudflareTempEmailSubdomainMode = getSelectedCloudflareTempEmailSubdomainMode(); const normalizeCloudMailBaseUrlInput = typeof normalizeCloudMailBaseUrlValue === 'function' ? normalizeCloudMailBaseUrlValue : normalizeCloudflareTempEmailBaseUrlValue; const normalizeCloudMailReceiveMailboxInput = typeof normalizeCloudMailReceiveMailboxValue === 'function' ? normalizeCloudMailReceiveMailboxValue : normalizeCloudflareTempEmailReceiveMailboxValue; const normalizeCloudMailDomainInput = typeof normalizeCloudMailDomainValue === 'function' ? normalizeCloudMailDomainValue : normalizeCloudflareTempEmailDomainValue; const defaultFlowId = typeof DEFAULT_ACTIVE_FLOW_ID !== 'undefined' ? DEFAULT_ACTIVE_FLOW_ID : 'openai'; const activeFlowId = typeof getSelectedFlowId === 'function' ? getSelectedFlowId(latestState) : (() => { const normalized = String( latestState?.activeFlowId || latestState?.flowId || defaultFlowId ).trim().toLowerCase(); if (normalized === 'codex') { return defaultFlowId; } return normalized || defaultFlowId; })(); const accountContributionEnabled = typeof isContributionModeActiveForFlow === 'function' ? isContributionModeActiveForFlow(latestState, activeFlowId) : (activeFlowId === defaultFlowId && Boolean(latestState?.accountContributionEnabled)); const icloudFetchModeRawValue = typeof selectIcloudFetchMode !== 'undefined' ? String(selectIcloudFetchMode?.value || '') : ''; const icloudTargetMailboxTypeValue = typeof selectIcloudTargetMailboxType !== 'undefined' ? selectIcloudTargetMailboxType?.value : ''; const icloudForwardMailProviderValue = typeof selectIcloudForwardMailProvider !== 'undefined' ? selectIcloudForwardMailProvider?.value : ''; const normalizedIcloudTargetMailboxType = normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue); const normalizedIcloudForwardMailProvider = normalizeIcloudForwardMailProvider(icloudForwardMailProviderValue); const normalizeIpProxyServiceSafe = typeof normalizeIpProxyService === 'function' ? normalizeIpProxyService : ((value = '') => { const normalized = String(value || '').trim().toLowerCase(); return ['711proxy'].includes(normalized) ? normalized : '711proxy'; }); const normalizeIpProxyModeSafe = typeof normalizeIpProxyMode === 'function' ? normalizeIpProxyMode : ((value = '') => { const normalized = String(value || '').trim().toLowerCase(); return ['api', 'account'].includes(normalized) ? normalized : 'account'; }); const normalizeIpProxyProtocolSafe = typeof normalizeIpProxyProtocol === 'function' ? normalizeIpProxyProtocol : ((value = '') => { const normalized = String(value || '').trim().toLowerCase(); return ['http', 'https', 'socks4', 'socks5'].includes(normalized) ? normalized : 'http'; }); const normalizeIpProxyPortSafe = typeof normalizeIpProxyPort === 'function' ? normalizeIpProxyPort : ((value = '') => { const numeric = Number.parseInt(String(value || '').trim(), 10); if (!Number.isInteger(numeric) || numeric <= 0 || numeric > 65535) { return 0; } return numeric; }); const normalizeIpProxyPoolTargetCountSafe = typeof normalizeIpProxyPoolTargetCount === 'function' ? normalizeIpProxyPoolTargetCount : ((value = '', fallback = 20) => { const rawValue = String(value ?? '').trim(); if (!rawValue) { return String(Math.max(1, Math.min(500, Number(fallback) || 20))); } const numeric = Number.parseInt(rawValue, 10); if (!Number.isInteger(numeric)) { return String(Math.max(1, Math.min(500, Number(fallback) || 20))); } return String(Math.max(1, Math.min(500, numeric))); }); const normalizeIpProxyAutoSyncIntervalMinutesSafe = (value = '', fallback = 15) => { const rawValue = String(value ?? '').trim(); if (!rawValue) { return Math.max(1, Math.min(1440, Number(fallback) || 15)); } const numeric = Number.parseInt(rawValue, 10); if (!Number.isFinite(numeric)) { return Math.max(1, Math.min(1440, Number(fallback) || 15)); } return Math.max(1, Math.min(1440, numeric)); }; const normalizeIpProxyAccountLifeMinutesSafe = typeof normalizeIpProxyAccountLifeMinutes === 'function' ? normalizeIpProxyAccountLifeMinutes : ((value = '', fallback = '') => { const rawValue = String(value ?? '').trim(); if (!rawValue) { return String(fallback || '').trim(); } const numeric = Number.parseInt(rawValue, 10); if (!Number.isInteger(numeric)) { return String(fallback || '').trim(); } return String(Math.max(1, Math.min(1440, numeric))); }); const normalizeIpProxyAccountSessionPrefixSafe = typeof normalizeIpProxyAccountSessionPrefix === 'function' ? normalizeIpProxyAccountSessionPrefix : ((value = '') => String(value || '').trim().replace(/[^A-Za-z0-9_-]/g, '').slice(0, 32)); const normalizeIpProxyAccountListSafe = typeof normalizeIpProxyAccountList === 'function' ? normalizeIpProxyAccountList : ((value = '') => String(value || '') .replace(/\r/g, '') .split('\n') .map((line) => line.trim()) .filter(Boolean) .join('\n')); const normalizeMaDaoBaseUrlSafe = typeof normalizeMaDaoBaseUrlValue === 'function' ? normalizeMaDaoBaseUrlValue : ((value = '') => { const fallback = 'http://127.0.0.1:7822'; const rawValue = String(value || fallback).trim(); try { const parsed = new URL(rawValue); if (!['http:', 'https:'].includes(parsed.protocol)) { return fallback; } parsed.pathname = parsed.pathname.replace(/\/api\/(?:acquire|poll|release|routing\/replace)$/i, ''); parsed.search = ''; parsed.hash = ''; return parsed.toString().replace(/\/$/, ''); } catch { return fallback; } }); const normalizeMaDaoModeSafe = typeof normalizeMaDaoModeValue === 'function' ? normalizeMaDaoModeValue : ((value = '') => (String(value || '').trim().toLowerCase() === 'direct' ? 'direct' : 'routing_plan')); const normalizeMaDaoIdentifierSafe = typeof normalizeMaDaoIdentifierValue === 'function' ? normalizeMaDaoIdentifierValue : ((value = '') => String(value || '').trim()); const normalizeMaDaoProviderIdSafe = typeof normalizeMaDaoProviderIdValue === 'function' ? normalizeMaDaoProviderIdValue : ((value = '') => String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '')); const normalizeMaDaoOperatorSafe = typeof normalizeMaDaoOperatorValue === 'function' ? normalizeMaDaoOperatorValue : ((value = '') => String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '')); const normalizeMaDaoCountrySafe = typeof normalizeMaDaoCountry === 'function' ? normalizeMaDaoCountry : ((value = '') => { const trimmed = String(value || '').trim(); if (!trimmed) return ''; const lowered = trimmed.toLowerCase(); if (lowered === 'any' || lowered === 'local') return lowered; if (/^[a-z]{2}$/i.test(trimmed)) return trimmed.toUpperCase(); return lowered.replace(/[^a-z0-9_-]+/g, ''); }); const normalizeMaDaoPriceSafe = typeof normalizeMaDaoPriceValue === 'function' ? normalizeMaDaoPriceValue : ((value = '') => { const numeric = Number(String(value ?? '').trim()); return Number.isFinite(numeric) && numeric > 0 ? String(Math.round(numeric * 10000) / 10000) : ''; }); const getSelectedIpProxyEnabledSafe = typeof getSelectedIpProxyEnabled === 'function' ? getSelectedIpProxyEnabled : (() => false); const getSelectedIpProxyModeSafe = typeof getSelectedIpProxyMode === 'function' ? getSelectedIpProxyMode : (() => 'account'); const isIpProxyApiModeEnabledSafe = typeof isIpProxyApiModeAvailable === 'function' ? Boolean(isIpProxyApiModeAvailable()) : (typeof IP_PROXY_API_MODE_ENABLED !== 'undefined' ? Boolean(IP_PROXY_API_MODE_ENABLED) : false); const normalizeIpProxyServiceProfilesSafe = typeof normalizeIpProxyServiceProfiles === 'function' ? normalizeIpProxyServiceProfiles : ((rawValue = {}, fallbackState = {}) => { const raw = (rawValue && typeof rawValue === 'object' && !Array.isArray(rawValue)) ? rawValue : {}; const services = ['711proxy']; const fallbackProfile = { mode: normalizeIpProxyModeSafe(fallbackState?.ipProxyMode || 'account'), apiUrl: String(fallbackState?.ipProxyApiUrl || '').trim(), accountList: normalizeIpProxyAccountListSafe(fallbackState?.ipProxyAccountList || ''), accountSessionPrefix: normalizeIpProxyAccountSessionPrefixSafe(fallbackState?.ipProxyAccountSessionPrefix || ''), accountLifeMinutes: normalizeIpProxyAccountLifeMinutesSafe(fallbackState?.ipProxyAccountLifeMinutes || ''), poolTargetCount: normalizeIpProxyPoolTargetCountSafe(fallbackState?.ipProxyPoolTargetCount || '', 20), host: String(fallbackState?.ipProxyHost || '').trim(), port: String(normalizeIpProxyPortSafe(fallbackState?.ipProxyPort || '') || ''), protocol: normalizeIpProxyProtocolSafe(fallbackState?.ipProxyProtocol || ''), username: String(fallbackState?.ipProxyUsername || '').trim(), password: String(fallbackState?.ipProxyPassword || ''), region: String(fallbackState?.ipProxyRegion || '').trim(), }; const result = {}; services.forEach((service) => { const candidate = raw?.[service]; const source = (candidate && typeof candidate === 'object' && !Array.isArray(candidate)) ? candidate : fallbackProfile; result[service] = { mode: normalizeIpProxyModeSafe(source.mode || fallbackProfile.mode), apiUrl: String(source.apiUrl || fallbackProfile.apiUrl || '').trim(), accountList: normalizeIpProxyAccountListSafe(source.accountList || fallbackProfile.accountList), accountSessionPrefix: normalizeIpProxyAccountSessionPrefixSafe(source.accountSessionPrefix || fallbackProfile.accountSessionPrefix), accountLifeMinutes: normalizeIpProxyAccountLifeMinutesSafe(source.accountLifeMinutes || fallbackProfile.accountLifeMinutes), poolTargetCount: normalizeIpProxyPoolTargetCountSafe(source.poolTargetCount || fallbackProfile.poolTargetCount, 20), host: String(source.host || fallbackProfile.host || '').trim(), port: String(normalizeIpProxyPortSafe(source.port || fallbackProfile.port || '') || ''), protocol: normalizeIpProxyProtocolSafe(source.protocol || fallbackProfile.protocol), username: String(source.username || fallbackProfile.username || '').trim(), password: String(source.password || fallbackProfile.password || ''), region: String(source.region || fallbackProfile.region || '').trim(), }; }); return result; }); const ipProxyServiceRawValue = typeof selectIpProxyService !== 'undefined' ? selectIpProxyService?.value : ''; const ipProxyApiUrlRawValue = typeof inputIpProxyApiUrl !== 'undefined' ? inputIpProxyApiUrl?.value : ''; const ipProxyAccountListRawValue = typeof inputIpProxyAccountList !== 'undefined' ? inputIpProxyAccountList?.value : ''; const ipProxyAccountSessionPrefixRawValue = typeof inputIpProxyAccountSessionPrefix !== 'undefined' ? inputIpProxyAccountSessionPrefix?.value : ''; const ipProxyAccountLifeMinutesRawValue = typeof inputIpProxyAccountLifeMinutes !== 'undefined' ? inputIpProxyAccountLifeMinutes?.value : ''; const ipProxyPoolTargetCountRawValue = typeof inputIpProxyPoolTargetCount !== 'undefined' ? inputIpProxyPoolTargetCount?.value : ''; const ipProxyAutoSyncEnabledRawValue = typeof inputIpProxyAutoSyncEnabled !== 'undefined' ? Boolean(inputIpProxyAutoSyncEnabled?.checked) : Boolean(latestState?.ipProxyAutoSyncEnabled); const ipProxyAutoSyncIntervalMinutesRawValue = typeof inputIpProxyAutoSyncIntervalMinutes !== 'undefined' ? inputIpProxyAutoSyncIntervalMinutes?.value : ''; const ipProxyHostRawValue = typeof inputIpProxyHost !== 'undefined' ? inputIpProxyHost?.value : ''; const ipProxyPortRawValue = typeof inputIpProxyPort !== 'undefined' ? inputIpProxyPort?.value : ''; const ipProxyProtocolRawValue = typeof selectIpProxyProtocol !== 'undefined' ? selectIpProxyProtocol?.value : ''; const ipProxyUsernameRawValue = typeof inputIpProxyUsername !== 'undefined' ? inputIpProxyUsername?.value : ''; const ipProxyPasswordRawValue = typeof inputIpProxyPassword !== 'undefined' ? inputIpProxyPassword?.value : ''; const ipProxyRegionRawValue = typeof inputIpProxyRegion !== 'undefined' ? inputIpProxyRegion?.value : ''; const selectedIpProxyService = normalizeIpProxyServiceSafe( ipProxyServiceRawValue || latestState?.ipProxyService || '711proxy' ); const selectedIpProxyModeRaw = normalizeIpProxyModeSafe(getSelectedIpProxyModeSafe()); const selectedIpProxyMode = (!isIpProxyApiModeEnabledSafe && selectedIpProxyModeRaw === 'api') ? 'account' : selectedIpProxyModeRaw; const currentIpProxyServiceProfile = { mode: selectedIpProxyMode, apiUrl: String(ipProxyApiUrlRawValue || '').trim(), accountList: normalizeIpProxyAccountListSafe(ipProxyAccountListRawValue || ''), accountSessionPrefix: normalizeIpProxyAccountSessionPrefixSafe(ipProxyAccountSessionPrefixRawValue || ''), accountLifeMinutes: normalizeIpProxyAccountLifeMinutesSafe(ipProxyAccountLifeMinutesRawValue || ''), poolTargetCount: normalizeIpProxyPoolTargetCountSafe(ipProxyPoolTargetCountRawValue || '', 20), host: String(ipProxyHostRawValue || '').trim(), port: String(normalizeIpProxyPortSafe(ipProxyPortRawValue || '') || ''), protocol: normalizeIpProxyProtocolSafe(ipProxyProtocolRawValue), username: String(ipProxyUsernameRawValue || '').trim(), password: String(ipProxyPasswordRawValue || ''), region: String(ipProxyRegionRawValue || '').trim(), }; const ipProxyServiceProfiles = normalizeIpProxyServiceProfilesSafe({ ...(latestState?.ipProxyServiceProfiles || {}), [selectedIpProxyService]: currentIpProxyServiceProfile, }, { ...(latestState || {}), ipProxyService: selectedIpProxyService, ipProxyMode: currentIpProxyServiceProfile.mode, ipProxyApiUrl: currentIpProxyServiceProfile.apiUrl, ipProxyAccountList: currentIpProxyServiceProfile.accountList, ipProxyAccountSessionPrefix: currentIpProxyServiceProfile.accountSessionPrefix, ipProxyAccountLifeMinutes: currentIpProxyServiceProfile.accountLifeMinutes, ipProxyPoolTargetCount: currentIpProxyServiceProfile.poolTargetCount, ipProxyHost: currentIpProxyServiceProfile.host, ipProxyPort: currentIpProxyServiceProfile.port, ipProxyProtocol: currentIpProxyServiceProfile.protocol, ipProxyUsername: currentIpProxyServiceProfile.username, ipProxyPassword: currentIpProxyServiceProfile.password, ipProxyRegion: currentIpProxyServiceProfile.region, }); const mail2925UseAccountPool = typeof inputMail2925UseAccountPool !== 'undefined' ? Boolean(inputMail2925UseAccountPool?.checked) : Boolean(latestState?.mail2925UseAccountPool); const phoneSmsProviderValue = typeof selectPhoneSmsProvider !== 'undefined' && selectPhoneSmsProvider ? normalizePhoneSmsProvider(selectPhoneSmsProvider.value) : normalizePhoneSmsProvider(latestState?.phoneSmsProvider); const phoneSmsProviderOrderValue = typeof getSelectedPhoneSmsProviderOrder === 'function' ? getSelectedPhoneSmsProviderOrder() : (typeof normalizePhoneSmsProviderOrderValue === 'function' ? normalizePhoneSmsProviderOrderValue(latestState?.phoneSmsProviderOrder || [], []) : []); const currentPhoneSmsApiKeyValue = typeof inputHeroSmsApiKey !== 'undefined' && inputHeroSmsApiKey ? (inputHeroSmsApiKey.value || '') : ''; const heroSmsApiKeyValue = phoneSmsProviderValue === PHONE_SMS_PROVIDER_HERO_SMS ? currentPhoneSmsApiKeyValue : String(latestState?.heroSmsApiKey || ''); const fiveSimApiKeyValue = typeof inputFiveSimApiKey !== 'undefined' && inputFiveSimApiKey ? String(inputFiveSimApiKey.value || '') : String(latestState?.fiveSimApiKey || ''); const nexSmsApiKeyValue = typeof inputNexSmsApiKey !== 'undefined' && inputNexSmsApiKey ? String(inputNexSmsApiKey.value || '') : String(latestState?.nexSmsApiKey || ''); const smsBowerApiKeyValue = typeof inputSmsBowerApiKey !== 'undefined' && inputSmsBowerApiKey ? String(inputSmsBowerApiKey.value || '') : String(latestState?.smsBowerApiKey || ''); const maDaoBaseUrlValue = typeof inputMaDaoBaseUrl !== 'undefined' && inputMaDaoBaseUrl ? normalizeMaDaoBaseUrlSafe(inputMaDaoBaseUrl.value || latestState?.madaoBaseUrl) : normalizeMaDaoBaseUrlSafe(latestState?.madaoBaseUrl); const maDaoHttpSecretValue = typeof inputMaDaoHttpSecret !== 'undefined' && inputMaDaoHttpSecret ? String(inputMaDaoHttpSecret.value || '') : String(latestState?.madaoHttpSecret || ''); const maDaoModeValue = typeof selectMaDaoMode !== 'undefined' && selectMaDaoMode ? normalizeMaDaoModeSafe(selectMaDaoMode.value || latestState?.madaoMode) : normalizeMaDaoModeSafe(latestState?.madaoMode); const maDaoRoutingPlanIdValue = typeof selectMaDaoRoutingPlanId !== 'undefined' && selectMaDaoRoutingPlanId ? normalizeMaDaoIdentifierSafe(selectMaDaoRoutingPlanId.value || '') : normalizeMaDaoIdentifierSafe(latestState?.madaoRoutingPlanId || ''); const maDaoDirectModeValue = typeof MADAO_MODE_DIRECT !== 'undefined' ? MADAO_MODE_DIRECT : 'direct'; const shouldReadMaDaoDirectControls = maDaoModeValue === maDaoDirectModeValue; const maDaoProviderIdValue = shouldReadMaDaoDirectControls && typeof selectMaDaoProviderId !== 'undefined' && selectMaDaoProviderId ? normalizeMaDaoProviderIdSafe(selectMaDaoProviderId.value || '') : normalizeMaDaoProviderIdSafe(latestState?.madaoProviderId || ''); const maDaoCountryValue = shouldReadMaDaoDirectControls && typeof selectMaDaoCountry !== 'undefined' && selectMaDaoCountry ? normalizeMaDaoCountrySafe(selectMaDaoCountry.value || '') : normalizeMaDaoCountrySafe(latestState?.madaoCountry || ''); const maDaoOperatorValue = shouldReadMaDaoDirectControls && typeof selectMaDaoOperator !== 'undefined' && selectMaDaoOperator ? normalizeMaDaoOperatorSafe(selectMaDaoOperator.value || '') : normalizeMaDaoOperatorSafe(latestState?.madaoOperator || ''); const maDaoAutoPickCountryValue = typeof inputMaDaoAutoPickCountry !== 'undefined' && inputMaDaoAutoPickCountry ? Boolean(inputMaDaoAutoPickCountry.checked) : (latestState?.madaoAutoPickCountry !== undefined ? Boolean(latestState.madaoAutoPickCountry) : true); const maDaoReusePhoneValue = typeof inputMaDaoReusePhone !== 'undefined' && inputMaDaoReusePhone ? Boolean(inputMaDaoReusePhone.checked) : (latestState?.madaoReusePhone !== undefined ? Boolean(latestState.madaoReusePhone) : true); const maDaoMinPriceValue = typeof inputMaDaoMinPrice !== 'undefined' && inputMaDaoMinPrice ? normalizeMaDaoPriceSafe(inputMaDaoMinPrice.value) : normalizeMaDaoPriceSafe(latestState?.madaoMinPrice || ''); const maDaoMaxPriceValue = typeof inputMaDaoMaxPrice !== 'undefined' && inputMaDaoMaxPrice ? normalizeMaDaoPriceSafe(inputMaDaoMaxPrice.value) : normalizeMaDaoPriceSafe(latestState?.madaoMaxPrice || ''); const defaultHeroSmsReuseEnabled = typeof DEFAULT_HERO_SMS_REUSE_ENABLED !== 'undefined' ? DEFAULT_HERO_SMS_REUSE_ENABLED : true; const defaultPhoneCodeWaitSeconds = typeof DEFAULT_PHONE_CODE_WAIT_SECONDS !== 'undefined' ? DEFAULT_PHONE_CODE_WAIT_SECONDS : 60; const defaultPhoneCodeTimeoutWindows = typeof DEFAULT_PHONE_CODE_TIMEOUT_WINDOWS !== 'undefined' ? DEFAULT_PHONE_CODE_TIMEOUT_WINDOWS : 2; const defaultPhoneCodePollIntervalSeconds = typeof DEFAULT_PHONE_CODE_POLL_INTERVAL_SECONDS !== 'undefined' ? DEFAULT_PHONE_CODE_POLL_INTERVAL_SECONDS : 5; const defaultPhoneCodePollMaxRounds = typeof DEFAULT_PHONE_CODE_POLL_MAX_ROUNDS !== 'undefined' ? DEFAULT_PHONE_CODE_POLL_MAX_ROUNDS : 12; const selectedSignupMethod = typeof getSelectedSignupMethod === 'function' ? getSelectedSignupMethod() : ( (typeof normalizeSignupMethod === 'function' ? normalizeSignupMethod(latestState?.signupMethod) : (String(latestState?.signupMethod || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email')) ); const phoneSignupReuseLocked = typeof isPhoneSignupReuseLocked === 'function' ? isPhoneSignupReuseLocked(latestState, { signupMethod: selectedSignupMethod }) : selectedSignupMethod === 'phone'; const phoneSmsReuseEnabledValue = phoneSignupReuseLocked ? getStoredPhoneSmsReuseEnabled(latestState) : typeof inputHeroSmsReuseEnabled !== 'undefined' && inputHeroSmsReuseEnabled ? normalizeHeroSmsReuseEnabledValue(inputHeroSmsReuseEnabled.checked) : normalizeHeroSmsReuseEnabledValue( latestState?.phoneSmsReuseEnabled, latestState?.heroSmsReuseEnabled ); const heroSmsReuseEnabledValue = phoneSmsReuseEnabledValue; const freePhoneReuseEnabledValue = phoneSignupReuseLocked ? getStoredFreePhoneReuseEnabled(latestState) : typeof inputFreePhoneReuseEnabled !== 'undefined' && inputFreePhoneReuseEnabled ? Boolean(inputFreePhoneReuseEnabled.checked) : Boolean(latestState?.freePhoneReuseEnabled); const freePhoneReuseAutoEnabledValue = phoneSignupReuseLocked ? getStoredFreePhoneReuseAutoEnabled(latestState) : typeof inputFreePhoneReuseAutoEnabled !== 'undefined' && inputFreePhoneReuseAutoEnabled ? Boolean(inputFreePhoneReuseAutoEnabled.checked) : Boolean(latestState?.freePhoneReuseAutoEnabled); const defaultHeroSmsAcquirePriority = typeof DEFAULT_HERO_SMS_ACQUIRE_PRIORITY !== 'undefined' ? DEFAULT_HERO_SMS_ACQUIRE_PRIORITY : (typeof HERO_SMS_ACQUIRE_PRIORITY_COUNTRY !== 'undefined' ? HERO_SMS_ACQUIRE_PRIORITY_COUNTRY : 'country'); const normalizeHeroSmsOperatorSafe = typeof normalizeHeroSmsOperatorValue === 'function' ? normalizeHeroSmsOperatorValue : ((value = '') => String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || 'any'); const heroSmsAcquirePriorityValue = typeof selectHeroSmsAcquirePriority !== 'undefined' && selectHeroSmsAcquirePriority ? normalizeHeroSmsAcquirePriority(selectHeroSmsAcquirePriority.value) : normalizeHeroSmsAcquirePriority( typeof DEFAULT_HERO_SMS_ACQUIRE_PRIORITY !== 'undefined' ? DEFAULT_HERO_SMS_ACQUIRE_PRIORITY : 'country' ); const heroSmsOperatorValue = typeof selectHeroSmsOperator !== 'undefined' && selectHeroSmsOperator ? normalizeHeroSmsOperatorSafe(selectHeroSmsOperator.value || latestState?.heroSmsOperator) : normalizeHeroSmsOperatorSafe(latestState?.heroSmsOperator); const currentPhoneSmsMaxPriceValue = typeof inputHeroSmsMaxPrice !== 'undefined' && inputHeroSmsMaxPrice ? normalizePhoneSmsMaxPriceValue(inputHeroSmsMaxPrice.value, phoneSmsProviderValue) : ''; const normalizePhoneSmsMinPriceValueSafe = typeof normalizePhoneSmsMinPriceValue === 'function' ? normalizePhoneSmsMinPriceValue : ((value = '', provider = phoneSmsProviderValue) => { const normalizedProvider = normalizePhoneSmsProvider(provider); if (normalizedProvider === PHONE_SMS_PROVIDER_FIVE_SIM && typeof normalizeFiveSimMaxPriceValue === 'function') { return normalizeFiveSimMaxPriceValue(value); } if (typeof normalizeHeroSmsMaxPriceValue === 'function') { return normalizeHeroSmsMaxPriceValue(value); } return String(value || '').trim(); }); const currentPhoneSmsMinPriceValue = typeof inputHeroSmsMinPrice !== 'undefined' && inputHeroSmsMinPrice ? normalizePhoneSmsMinPriceValueSafe(inputHeroSmsMinPrice.value, phoneSmsProviderValue) : ''; const heroSmsMaxPriceValue = phoneSmsProviderValue === PHONE_SMS_PROVIDER_HERO_SMS ? currentPhoneSmsMaxPriceValue : normalizeHeroSmsMaxPriceValue(latestState?.heroSmsMaxPrice || ''); const fiveSimMaxPriceValue = phoneSmsProviderValue === PHONE_SMS_PROVIDER_FIVE_SIM ? currentPhoneSmsMaxPriceValue : normalizeFiveSimMaxPriceValue(latestState?.fiveSimMaxPrice || ''); const heroSmsMinPriceValue = phoneSmsProviderValue === PHONE_SMS_PROVIDER_FIVE_SIM ? normalizePhoneSmsMinPriceValueSafe(latestState?.heroSmsMinPrice || '', PHONE_SMS_PROVIDER_HERO_SMS) : currentPhoneSmsMinPriceValue; const fiveSimMinPriceValue = phoneSmsProviderValue === PHONE_SMS_PROVIDER_FIVE_SIM ? currentPhoneSmsMinPriceValue : normalizePhoneSmsMinPriceValueSafe(latestState?.fiveSimMinPrice || '', PHONE_SMS_PROVIDER_FIVE_SIM); const smsBowerProviderConstant = typeof PHONE_SMS_PROVIDER_SMS_BOWER !== 'undefined' ? PHONE_SMS_PROVIDER_SMS_BOWER : 'sms-bower'; const smsBowerMaxPriceValue = phoneSmsProviderValue === smsBowerProviderConstant ? currentPhoneSmsMaxPriceValue : normalizeHeroSmsMaxPriceValue(latestState?.smsBowerMaxPrice || ''); const smsBowerMinPriceValue = phoneSmsProviderValue === smsBowerProviderConstant ? currentPhoneSmsMinPriceValue : normalizePhoneSmsMinPriceValueSafe(latestState?.smsBowerMinPrice || '', smsBowerProviderConstant); const defaultFiveSimProduct = typeof DEFAULT_FIVE_SIM_PRODUCT !== 'undefined' ? DEFAULT_FIVE_SIM_PRODUCT : 'openai'; const defaultNexSmsServiceCode = typeof DEFAULT_NEX_SMS_SERVICE_CODE !== 'undefined' ? DEFAULT_NEX_SMS_SERVICE_CODE : 'ot'; const normalizeFiveSimProductForPayload = typeof normalizeFiveSimProductValue === 'function' ? normalizeFiveSimProductValue : ((value = '') => String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || defaultFiveSimProduct); const normalizeNexSmsServiceCodeForPayload = typeof normalizeNexSmsServiceCodeValue === 'function' ? normalizeNexSmsServiceCodeValue : ((value = '') => String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || defaultNexSmsServiceCode); const normalizeFiveSimCountryOrderForPayload = typeof normalizeFiveSimCountryOrderValue === 'function' ? normalizeFiveSimCountryOrderValue : ((value = []) => (Array.isArray(value) ? value : []) .map((entry) => normalizeFiveSimCountryId(entry, '')) .filter(Boolean)); const normalizeNexSmsCountryIdForPayload = typeof normalizeNexSmsCountryIdValue === 'function' ? normalizeNexSmsCountryIdValue : ((value, fallback = -1) => { const normalized = Math.floor(Number(value)); return Number.isFinite(normalized) && normalized >= 0 ? normalized : fallback; }); const normalizeNexSmsCountryOrderForPayload = typeof normalizeNexSmsCountryOrderValue === 'function' ? normalizeNexSmsCountryOrderValue : ((value = []) => (Array.isArray(value) ? value : []) .map((entry) => normalizeNexSmsCountryIdForPayload(entry, -1)) .filter((entry) => entry >= 0)); const phoneSmsProviderNexsms = typeof PHONE_SMS_PROVIDER_NEXSMS !== 'undefined' ? PHONE_SMS_PROVIDER_NEXSMS : 'nexsms'; const defaultNexSmsCountryOrder = typeof DEFAULT_NEX_SMS_COUNTRY_ORDER !== 'undefined' ? DEFAULT_NEX_SMS_COUNTRY_ORDER : [1]; const fiveSimOperatorValue = typeof inputFiveSimOperator !== 'undefined' && inputFiveSimOperator ? normalizeFiveSimOperator(inputFiveSimOperator.value || latestState?.fiveSimOperator) : normalizeFiveSimOperator(latestState?.fiveSimOperator); const fiveSimProductValue = typeof inputFiveSimProduct !== 'undefined' && inputFiveSimProduct ? normalizeFiveSimProductForPayload(inputFiveSimProduct.value || latestState?.fiveSimProduct) : normalizeFiveSimProductForPayload(latestState?.fiveSimProduct || defaultFiveSimProduct); const nexSmsServiceCodeValue = typeof inputNexSmsServiceCode !== 'undefined' && inputNexSmsServiceCode ? normalizeNexSmsServiceCodeForPayload(inputNexSmsServiceCode.value || latestState?.nexSmsServiceCode) : normalizeNexSmsServiceCodeForPayload(latestState?.nexSmsServiceCode || defaultNexSmsServiceCode); const heroSmsPreferredPriceValue = typeof inputHeroSmsPreferredPrice !== 'undefined' && inputHeroSmsPreferredPrice ? normalizeHeroSmsMaxPriceValue(inputHeroSmsPreferredPrice.value) : normalizeHeroSmsMaxPriceValue(latestState?.heroSmsPreferredPrice || ''); const phonePreferredActivationValue = phoneSignupReuseLocked ? (latestState?.phonePreferredActivation ? { ...latestState.phonePreferredActivation } : null) : typeof getSelectedPhonePreferredActivation === 'function' ? getSelectedPhonePreferredActivation() : null; const phoneVerificationReplacementLimitValue = typeof inputPhoneReplacementLimit !== 'undefined' && inputPhoneReplacementLimit ? normalizePhoneVerificationReplacementLimit( inputPhoneReplacementLimit.value, latestState?.phoneVerificationReplacementLimit ) : DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT; const phoneCodeWaitSecondsValue = typeof inputPhoneCodeWaitSeconds !== 'undefined' && inputPhoneCodeWaitSeconds ? normalizePhoneCodeWaitSecondsValue( inputPhoneCodeWaitSeconds.value, latestState?.phoneCodeWaitSeconds ) : defaultPhoneCodeWaitSeconds; const phoneCodeTimeoutWindowsValue = typeof inputPhoneCodeTimeoutWindows !== 'undefined' && inputPhoneCodeTimeoutWindows ? normalizePhoneCodeTimeoutWindowsValue( inputPhoneCodeTimeoutWindows.value, latestState?.phoneCodeTimeoutWindows ) : defaultPhoneCodeTimeoutWindows; const phoneCodePollIntervalSecondsValue = typeof inputPhoneCodePollIntervalSeconds !== 'undefined' && inputPhoneCodePollIntervalSeconds ? normalizePhoneCodePollIntervalSecondsValue( inputPhoneCodePollIntervalSeconds.value, latestState?.phoneCodePollIntervalSeconds ) : defaultPhoneCodePollIntervalSeconds; const phoneCodePollMaxRoundsValue = typeof inputPhoneCodePollMaxRounds !== 'undefined' && inputPhoneCodePollMaxRounds ? normalizePhoneCodePollMaxRoundsValue( inputPhoneCodePollMaxRounds.value, latestState?.phoneCodePollMaxRounds ) : defaultPhoneCodePollMaxRounds; const selectedPhoneSmsCountry = phoneSmsProviderValue === PHONE_SMS_PROVIDER_FIVE_SIM ? ((typeof getSelectedFiveSimCountries === 'function' ? getSelectedFiveSimCountries()[0] : null) || { id: DEFAULT_FIVE_SIM_COUNTRY_ID, code: DEFAULT_FIVE_SIM_COUNTRY_ID, label: DEFAULT_FIVE_SIM_COUNTRY_LABEL }) : (phoneSmsProviderValue === phoneSmsProviderNexsms ? ((typeof getSelectedNexSmsCountries === 'function' ? getSelectedNexSmsCountries()[0] : null) || { id: defaultNexSmsCountryOrder[0], label: `Country #${defaultNexSmsCountryOrder[0]}` }) : (typeof getSelectedHeroSmsCountryOption === 'function' ? getSelectedHeroSmsCountryOption() : { id: typeof DEFAULT_HERO_SMS_COUNTRY_ID !== 'undefined' ? DEFAULT_HERO_SMS_COUNTRY_ID : 52, label: typeof DEFAULT_HERO_SMS_COUNTRY_LABEL !== 'undefined' ? DEFAULT_HERO_SMS_COUNTRY_LABEL : 'Thailand', })); const heroSmsCountry = phoneSmsProviderValue === PHONE_SMS_PROVIDER_HERO_SMS ? selectedPhoneSmsCountry : { id: normalizeHeroSmsCountryId(latestState?.heroSmsCountryId), label: normalizeHeroSmsCountryLabel(latestState?.heroSmsCountryLabel), }; const fiveSimCountry = phoneSmsProviderValue === PHONE_SMS_PROVIDER_FIVE_SIM ? selectedPhoneSmsCountry : { id: normalizeFiveSimCountryId(latestState?.fiveSimCountryId), label: normalizeFiveSimCountryLabel(latestState?.fiveSimCountryLabel), }; const normalizedCustomEmailPool = typeof getActiveCustomEmailPoolEmails === 'function' ? getActiveCustomEmailPoolEmails() : (typeof normalizeCustomEmailPoolEntries === 'function' ? normalizeCustomEmailPoolEntries(inputCustomEmailPool?.value) : []); const normalizedCustomEmailPoolEntries = typeof getNormalizedCustomEmailPoolEntriesState === 'function' ? getNormalizedCustomEmailPoolEntriesState() : []; const selectedPhoneSmsCountryFallback = typeof syncHeroSmsFallbackSelectionOrderFromSelect === 'function' ? syncHeroSmsFallbackSelectionOrderFromSelect() .filter((country) => String(country.id) !== String(selectedPhoneSmsCountry.id)) : []; const fiveSimCountryOrderValue = typeof getSelectedFiveSimCountries === 'function' ? getSelectedFiveSimCountries() .map((country) => normalizeFiveSimCountryCode(country.code || country.id, '')) .filter(Boolean) : normalizeFiveSimCountryOrderForPayload(latestState?.fiveSimCountryOrder || []); const nexSmsCountryOrderValue = typeof getSelectedNexSmsCountries === 'function' ? getSelectedNexSmsCountries() .map((country) => normalizeNexSmsCountryIdForPayload(country.id, -1)) .filter((countryId) => countryId >= 0) : normalizeNexSmsCountryOrderForPayload(latestState?.nexSmsCountryOrder || []); const normalizeSmsBowerCountryIdForPayload = typeof normalizeSmsBowerCountryIdValue === 'function' ? normalizeSmsBowerCountryIdValue : ((value, fallback = 6) => { const parsed = Math.floor(Number(value)); return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback; }); const normalizeSmsBowerCountryLabelForPayload = typeof normalizeSmsBowerCountryLabelValue === 'function' ? normalizeSmsBowerCountryLabelValue : ((value = '', fallback = 'Indonesia') => String(value || '').trim() || fallback); const normalizeSmsBowerCountryOrderForPayload = typeof normalizeSmsBowerCountryOrderValue === 'function' ? normalizeSmsBowerCountryOrderValue : ((value = []) => { const values = Array.isArray(value) ? value : [value]; const mapped = values .map((entry) => normalizeSmsBowerCountryIdForPayload(entry && typeof entry === 'object' ? entry.id : entry, -1)) .filter((entry) => entry >= 0); return mapped.length > 0 ? mapped.map((id) => ({ id, label: id === 6 ? 'Indonesia' : `Country #${id}` })) : [{ id: 6, label: 'Indonesia' }]; }); const smsBowerSelectedCountries = phoneSmsProviderValue === smsBowerProviderConstant && typeof getSelectedSmsBowerCountries === 'function' ? getSelectedSmsBowerCountries() : normalizeSmsBowerCountryOrderForPayload(latestState?.smsBowerCountryOrder || []); const smsBowerCountry = smsBowerSelectedCountries[0] || { id: normalizeSmsBowerCountryIdForPayload(latestState?.smsBowerCountryId), label: normalizeSmsBowerCountryLabelForPayload(latestState?.smsBowerCountryLabel), }; const smsBowerCountryOrderValue = smsBowerSelectedCountries.map((country) => country.id); const heroSmsCountryFallback = phoneSmsProviderValue === PHONE_SMS_PROVIDER_HERO_SMS ? selectedPhoneSmsCountryFallback : normalizeHeroSmsCountryFallbackList(latestState?.heroSmsCountryFallback || []); const fiveSimCountryFallback = phoneSmsProviderValue === PHONE_SMS_PROVIDER_FIVE_SIM ? selectedPhoneSmsCountryFallback : normalizeFiveSimCountryFallbackList(latestState?.fiveSimCountryFallback || []); const payPalAccounts = typeof getPayPalAccounts === 'function' ? getPayPalAccounts(latestState) : (Array.isArray(latestState?.paypalAccounts) ? latestState.paypalAccounts : []); const currentPayPalAccount = typeof getCurrentPayPalAccount === 'function' ? getCurrentPayPalAccount(latestState) : payPalAccounts.find((account) => account?.id === String(latestState?.currentPayPalAccountId || '').trim()) || null; const normalizePanelModeSafe = typeof normalizePanelMode === 'function' ? normalizePanelMode : ((value = '') => { const normalized = String(value || '').trim().toLowerCase(); return normalized === 'sub2api' || normalized === 'codex2api' ? normalized : 'cpa'; }); const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function' ? normalizeTargetIdForFlow : ((flowId, targetId = '', fallback = '') => { const normalizedFlowId = String(flowId || '').trim().toLowerCase() || defaultFlowId; if (normalizedFlowId === defaultFlowId) { return normalizePanelModeSafe(targetId || fallback); } const normalizedTargetId = String(targetId || '').trim().toLowerCase(); return normalizedTargetId || String(fallback || '').trim().toLowerCase() || 'kiro-rs'; }); const resolvePlusAccountAccessStrategyForTargetSafe = typeof resolvePlusAccountAccessStrategyForTarget === 'function' ? resolvePlusAccountAccessStrategyForTarget : ((value = '', targetId = '') => { const oauthStrategyValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH !== 'undefined' ? PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH : 'oauth'; const sub2apiSessionStrategyValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION !== 'undefined' ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : 'sub2api_codex_session'; const cpaSessionStrategyValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION !== 'undefined' ? PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION : 'cpa_codex_session'; const sessionUiValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_CODEX_SESSION_UI !== 'undefined' ? PLUS_ACCOUNT_ACCESS_STRATEGY_CODEX_SESSION_UI : 'codex_session'; const normalizedValue = String(value || '').trim().toLowerCase(); const isSessionImport = normalizedValue === sessionUiValue || normalizedValue === sub2apiSessionStrategyValue || normalizedValue === cpaSessionStrategyValue; if (!isSessionImport) { return oauthStrategyValue; } const normalizedTargetId = typeof normalizePlusStrategyTargetId === 'function' ? normalizePlusStrategyTargetId(targetId) : normalizePanelModeSafe(targetId || ''); if (normalizedTargetId === 'sub2api') { return sub2apiSessionStrategyValue; } if (normalizedTargetId === 'cpa') { return cpaSessionStrategyValue; } return oauthStrategyValue; }); const selectedTargetId = typeof getSelectedTargetId === 'function' ? getSelectedTargetId(activeFlowId) : normalizeTargetIdForFlowSafe( activeFlowId, selectPanelMode?.value || latestState?.targetId || '', typeof getDefaultTargetIdForFlow === 'function' ? getDefaultTargetIdForFlow(activeFlowId) : (activeFlowId === defaultFlowId ? 'cpa' : 'kiro-rs') ); const openAiTargetId = normalizePanelModeSafe( activeFlowId === defaultFlowId ? selectedTargetId : getSelectedPanelMode(latestState) ); const rawPlusModeEnabled = typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled ? Boolean(inputPlusModeEnabled.checked) : Boolean(latestState?.plusModeEnabled); const requestedPlusAccountAccessStrategy = typeof getRequestedPlusAccountAccessStrategy === 'function' ? getRequestedPlusAccountAccessStrategy(latestState) : normalizePlusAccountAccessStrategy(latestState?.plusAccountAccessStrategy || DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY); const rawPhoneVerificationEnabled = Boolean(inputPhoneVerificationEnabled?.checked); const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function' ? resolveCurrentSidepanelCapabilities({ activeFlowId, targetId: selectedTargetId, signupMethod: selectedSignupMethod, state: { ...(latestState || {}), activeFlowId, flowId: activeFlowId, targetId: selectedTargetId, plusModeEnabled: rawPlusModeEnabled, plusAccountAccessStrategy: requestedPlusAccountAccessStrategy, phoneVerificationEnabled: rawPhoneVerificationEnabled, signupMethod: selectedSignupMethod, }, }) : (() => { const rootScope = typeof window !== 'undefined' ? window : globalThis; const registry = rootScope.MultiPageFlowCapabilities?.createFlowCapabilityRegistry?.({ defaultFlowId: typeof DEFAULT_ACTIVE_FLOW_ID === 'string' ? DEFAULT_ACTIVE_FLOW_ID : 'openai', }) || null; return registry?.resolveSidepanelCapabilities ? registry.resolveSidepanelCapabilities({ activeFlowId, targetId: selectedTargetId, signupMethod: selectedSignupMethod, state: { ...(latestState || {}), activeFlowId, flowId: activeFlowId, targetId: selectedTargetId, plusModeEnabled: rawPlusModeEnabled, plusAccountAccessStrategy: requestedPlusAccountAccessStrategy, phoneVerificationEnabled: rawPhoneVerificationEnabled, signupMethod: selectedSignupMethod, }, }) : null; })(); const effectiveTargetId = capabilityState?.effectiveTargetId || selectedTargetId; const effectiveOpenAiTargetId = normalizePanelModeSafe( activeFlowId === defaultFlowId ? effectiveTargetId : openAiTargetId ); const effectivePlusModeEnabled = capabilityState ? Boolean(capabilityState.runtimeLocks?.plusModeEnabled) : rawPlusModeEnabled; const effectivePhoneVerificationEnabled = capabilityState ? Boolean(capabilityState.runtimeLocks?.phoneVerificationEnabled) : rawPhoneVerificationEnabled; const effectiveSignupMethod = capabilityState?.effectiveSignupMethod || selectedSignupMethod; const plusPaymentMethod = getSelectedPlusPaymentMethod(); const selectedSub2ApiGroupName = String(inputSub2ApiGroup.value || '').trim(); const sub2apiGroupNames = []; const seenSub2ApiGroupNames = new Set(); const appendSub2ApiGroupNames = (value) => { if (Array.isArray(value)) { value.forEach(appendSub2ApiGroupNames); return; } String(value || '') .split(/[\r\n,,、]+/) .map((name) => name.trim()) .filter(Boolean) .forEach((name) => { const key = name.toLowerCase(); if (!key || seenSub2ApiGroupNames.has(key)) { return; } seenSub2ApiGroupNames.add(key); sub2apiGroupNames.push(name); }); }; [ latestState?.sub2apiGroupNames, latestState?.sub2apiGroupName, selectedSub2ApiGroupName, ].forEach(appendSub2ApiGroupNames); if (sub2apiGroupNames.length === 0) { appendSub2ApiGroupNames(['codex', 'openai-plus']); } const sub2apiAccountPriorityNormalizer = typeof normalizeSub2ApiAccountPriorityValue === 'function' ? normalizeSub2ApiAccountPriorityValue : ((value) => { const numeric = Number(String(value ?? '').trim()); return Number.isSafeInteger(numeric) && numeric >= 1 ? numeric : 1; }); const flowRegistryApi = typeof getFlowRegistry === 'function' ? getFlowRegistry() : null; const defaultKiroRsUrl = String(flowRegistryApi?.DEFAULT_KIRO_RS_URL || '').trim(); const normalizeKiroTargetIdSafe = typeof normalizeTargetIdForFlow === 'function' ? normalizeTargetIdForFlow : ((_flowId, targetId = '', fallback = 'kiro-rs') => { const normalized = String(targetId || '').trim().toLowerCase(); return normalized || String(fallback || '').trim().toLowerCase() || 'kiro-rs'; }); const currentKiroRsUrlValue = typeof inputKiroRsUrl !== 'undefined' && inputKiroRsUrl ? String(inputKiroRsUrl.value ?? '').trim() : null; const currentKiroRsKeyValue = typeof inputKiroRsKey !== 'undefined' && inputKiroRsKey ? String(inputKiroRsKey.value ?? '').trim() : null; const currentGrokWebchat2ApiUrlValue = typeof inputGrokWebchat2ApiUrl !== 'undefined' && inputGrokWebchat2ApiUrl ? String(inputGrokWebchat2ApiUrl.value ?? '').trim() : null; const currentGrokWebchat2ApiKeyValue = typeof inputGrokWebchat2ApiKey !== 'undefined' && inputGrokWebchat2ApiKey ? String(inputGrokWebchat2ApiKey.value ?? '').trim() : null; const normalizeHostedCheckoutDelaySecondsSafe = typeof normalizePlusHostedCheckoutOauthDelaySeconds === 'function' ? normalizePlusHostedCheckoutOauthDelaySeconds : ((value) => { const numeric = Number(String(value ?? '').trim()); if (!Number.isFinite(numeric)) { return 3; } return Math.min(120, Math.max(0, Math.floor(numeric))); }); return { activeFlowId, targetId: effectiveTargetId, kiroRsUrl: currentKiroRsUrlValue !== null ? (currentKiroRsUrlValue || defaultKiroRsUrl) : (String(latestState?.kiroRsUrl || defaultKiroRsUrl).trim() || defaultKiroRsUrl), kiroRsKey: currentKiroRsKeyValue !== null ? currentKiroRsKeyValue : String(latestState?.kiroRsKey || '').trim(), grokWebchat2ApiUrl: currentGrokWebchat2ApiUrlValue !== null ? currentGrokWebchat2ApiUrlValue : String(latestState?.grokWebchat2ApiUrl || '').trim(), grokWebchat2ApiAdminKey: currentGrokWebchat2ApiKeyValue !== null ? currentGrokWebchat2ApiKeyValue : String(latestState?.grokWebchat2ApiAdminKey || '').trim(), vpsUrl: inputVpsUrl.value.trim(), vpsPassword: inputVpsPassword.value, localCpaStep9Mode: getSelectedLocalCpaStep9Mode(), sub2apiUrl: inputSub2ApiUrl.value.trim(), sub2apiEmail: inputSub2ApiEmail.value.trim(), sub2apiPassword: inputSub2ApiPassword.value, sub2apiGroupName: selectedSub2ApiGroupName, sub2apiGroupNames, sub2apiAccountPriority: sub2apiAccountPriorityNormalizer( typeof inputSub2ApiAccountPriority !== 'undefined' && inputSub2ApiAccountPriority ? inputSub2ApiAccountPriority.value : latestState?.sub2apiAccountPriority ), sub2apiDefaultProxyName: inputSub2ApiDefaultProxy.value.trim(), ipProxyEnabled: getSelectedIpProxyEnabledSafe(), ipProxyService: selectedIpProxyService, ipProxyMode: currentIpProxyServiceProfile.mode, ipProxyApiUrl: currentIpProxyServiceProfile.apiUrl, ipProxyServiceProfiles, ipProxyAccountList: currentIpProxyServiceProfile.accountList, ipProxyAccountSessionPrefix: currentIpProxyServiceProfile.accountSessionPrefix, ipProxyAccountLifeMinutes: currentIpProxyServiceProfile.accountLifeMinutes, ipProxyPoolTargetCount: currentIpProxyServiceProfile.poolTargetCount, ipProxyAutoSyncEnabled: Boolean(ipProxyAutoSyncEnabledRawValue), ipProxyAutoSyncIntervalMinutes: normalizeIpProxyAutoSyncIntervalMinutesSafe( ipProxyAutoSyncIntervalMinutesRawValue, latestState?.ipProxyAutoSyncIntervalMinutes ), ipProxyHost: currentIpProxyServiceProfile.host, ipProxyPort: normalizeIpProxyPortSafe(currentIpProxyServiceProfile.port), ipProxyProtocol: currentIpProxyServiceProfile.protocol, ipProxyUsername: currentIpProxyServiceProfile.username, ipProxyPassword: currentIpProxyServiceProfile.password, ipProxyRegion: currentIpProxyServiceProfile.region, codex2apiUrl: inputCodex2ApiUrl.value.trim(), codex2apiAdminKey: inputCodex2ApiAdminKey.value.trim(), plusModeEnabled: effectivePlusModeEnabled, plusPaymentMethod, plusAccountAccessStrategy: activeFlowId === defaultFlowId ? resolvePlusAccountAccessStrategyForTargetSafe(requestedPlusAccountAccessStrategy, effectiveOpenAiTargetId) : requestedPlusAccountAccessStrategy, hostedCheckoutVerificationUrl: typeof inputHostedCheckoutVerificationUrl !== 'undefined' && inputHostedCheckoutVerificationUrl ? String(inputHostedCheckoutVerificationUrl.value || '').trim() : String(latestState?.hostedCheckoutVerificationUrl || '').trim(), hostedCheckoutPhoneNumber: typeof inputHostedCheckoutPhone !== 'undefined' && inputHostedCheckoutPhone ? String(inputHostedCheckoutPhone.value || '').trim() : String(latestState?.hostedCheckoutPhoneNumber || '').trim(), plusHostedCheckoutOauthDelaySeconds: normalizeHostedCheckoutDelaySecondsSafe( typeof inputPlusHostedCheckoutOauthDelaySeconds !== 'undefined' && inputPlusHostedCheckoutOauthDelaySeconds ? inputPlusHostedCheckoutOauthDelaySeconds.value : latestState?.plusHostedCheckoutOauthDelaySeconds ), paypalEmail: String(currentPayPalAccount?.email || latestState?.paypalEmail || '').trim(), paypalPassword: String(currentPayPalAccount?.password || latestState?.paypalPassword || ''), currentPayPalAccountId: String(latestState?.currentPayPalAccountId || '').trim(), paypalAccounts: payPalAccounts, gopayCountryCode: window.GoPayUtils?.normalizeGoPayCountryCode ? window.GoPayUtils.normalizeGoPayCountryCode(typeof selectGoPayCountryCode !== 'undefined' && selectGoPayCountryCode ? selectGoPayCountryCode.value : latestState?.gopayCountryCode) : (typeof selectGoPayCountryCode !== 'undefined' && selectGoPayCountryCode ? String(selectGoPayCountryCode.value || '+86').trim() : String(latestState?.gopayCountryCode || '+86').trim()), gopayPhone: window.GoPayUtils?.normalizeGoPayPhone ? window.GoPayUtils.normalizeGoPayPhone(typeof inputGoPayPhone !== 'undefined' && inputGoPayPhone ? inputGoPayPhone.value : latestState?.gopayPhone) : (typeof inputGoPayPhone !== 'undefined' && inputGoPayPhone ? String(inputGoPayPhone.value || '').trim() : String(latestState?.gopayPhone || '').trim()), gopayOtp: window.GoPayUtils?.normalizeGoPayOtp ? window.GoPayUtils.normalizeGoPayOtp(typeof inputGoPayOtp !== 'undefined' && inputGoPayOtp ? inputGoPayOtp.value : latestState?.gopayOtp) : (typeof inputGoPayOtp !== 'undefined' && inputGoPayOtp ? String(inputGoPayOtp.value || '').trim().replace(/[^\d]/g, '') : String(latestState?.gopayOtp || '').trim().replace(/[^\d]/g, '')), gopayPin: window.GoPayUtils?.normalizeGoPayPin ? window.GoPayUtils.normalizeGoPayPin(typeof inputGoPayPin !== 'undefined' && inputGoPayPin ? inputGoPayPin.value : latestState?.gopayPin) : (typeof inputGoPayPin !== 'undefined' && inputGoPayPin ? String(inputGoPayPin.value || '') : String(latestState?.gopayPin || '')), gpcBaseUrl: window.GoPayUtils?.normalizeGpcBaseUrl ? window.GoPayUtils.normalizeGpcBaseUrl(defaultGpcBaseUrl) : String(defaultGpcBaseUrl).trim().replace(/\/+$/g, ''), gpcCardKey: typeof inputGpcCardKey !== 'undefined' && inputGpcCardKey ? normalizeGpcCardKeyInput(inputGpcCardKey.value || '') : normalizeGpcCardKeyInput(latestState?.gpcCardKey || ''), ...(accountContributionEnabled ? {} : { customPassword: inputPassword.value, }), mailProvider: selectMailProvider.value, mail2925Mode: getSelectedMail2925Mode(), mail2925UseAccountPool, customMailReceiveMode: typeof getSelectedCustomMailReceiveMode === 'function' ? getSelectedCustomMailReceiveMode() : 'manual', customMailHelperBaseUrl: typeof normalizeCustomMailHelperBaseUrl === 'function' ? normalizeCustomMailHelperBaseUrl(typeof inputCustomMailHelperBaseUrl !== 'undefined' && inputCustomMailHelperBaseUrl ? inputCustomMailHelperBaseUrl.value : '') : 'http://127.0.0.1:17374', currentMail2925AccountId: String(latestState?.currentMail2925AccountId || '').trim(), emailGenerator: selectEmailGenerator.value, customMailProviderPool: typeof normalizeCustomEmailPoolEntries === 'function' ? normalizeCustomEmailPoolEntries(inputCustomMailProviderPool?.value) : [], customEmailPool: normalizedCustomEmailPool, customEmailPoolEntries: normalizedCustomEmailPoolEntries, autoDeleteUsedIcloudAlias: checkboxAutoDeleteIcloud?.checked, icloudHostPreference: selectIcloudHostPreference?.value || 'auto', icloudTargetMailboxType: normalizedIcloudTargetMailboxType, icloudForwardMailProvider: normalizedIcloudForwardMailProvider, icloudFetchMode: (icloudFetchModeRawValue.trim().toLowerCase() === 'always_new' ? 'always_new' : 'reuse_existing'), ...(accountContributionEnabled ? {} : { accountRunHistoryTextEnabled: true, accountRunHistoryHelperBaseUrl: normalizeAccountRunHistoryHelperBaseUrlValue(inputAccountRunHistoryHelperBaseUrl?.value), }), ...buildManagedAliasBaseEmailPayload(), inbucketHost: inputInbucketHost.value.trim(), inbucketMailbox: inputInbucketMailbox.value.trim(), hotmailServiceMode: getSelectedHotmailServiceMode(), hotmailRemoteBaseUrl: inputHotmailRemoteBaseUrl.value.trim(), hotmailLocalBaseUrl: inputHotmailLocalBaseUrl.value.trim(), luckmailApiKey: inputLuckmailApiKey.value, luckmailBaseUrl: normalizeLuckmailBaseUrl(inputLuckmailBaseUrl.value), luckmailEmailType: normalizeLuckmailEmailType(selectLuckmailEmailType.value), luckmailDomain: inputLuckmailDomain.value.trim(), cloudflareDomain: selectedCloudflareDomain, cloudflareDomains: domains, cloudflareTempEmailBaseUrl: normalizeCloudflareTempEmailBaseUrlValue(inputTempEmailBaseUrl.value), cloudflareTempEmailAdminAuth: inputTempEmailAdminAuth.value, cloudflareTempEmailCustomAuth: inputTempEmailCustomAuth.value, cloudflareTempEmailLookupMode: typeof getSelectedCloudflareTempEmailLookupMode === 'function' ? getSelectedCloudflareTempEmailLookupMode() : 'receive-mailbox', cloudflareTempEmailReceiveMailbox: normalizeCloudflareTempEmailReceiveMailboxValue(inputTempEmailReceiveMailbox.value), cloudflareTempEmailUseRandomSubdomain: cloudflareTempEmailSubdomainMode === CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_RANDOM, cloudflareTempEmailUseFixedSubdomain: cloudflareTempEmailSubdomainMode === CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_FIXED, cloudflareTempEmailSubdomainPrefix: normalizeCloudflareTempEmailSubdomainPrefixValue( inputTempEmailSubdomainPrefix?.value || '' ), cloudflareTempEmailDomain: selectedCloudflareTempEmailDomain, cloudflareTempEmailDomains: tempEmailDomains, cloudMailBaseUrl: normalizeCloudMailBaseUrlInput((typeof inputCloudMailBaseUrl !== 'undefined' && inputCloudMailBaseUrl) ? inputCloudMailBaseUrl.value : ''), cloudMailAdminEmail: ((typeof inputCloudMailAdminEmail !== 'undefined' && inputCloudMailAdminEmail) ? inputCloudMailAdminEmail.value : '').trim(), cloudMailAdminPassword: (typeof inputCloudMailAdminPassword !== 'undefined' && inputCloudMailAdminPassword) ? inputCloudMailAdminPassword.value : '', cloudMailReceiveMailbox: normalizeCloudMailReceiveMailboxInput((typeof inputCloudMailReceiveMailbox !== 'undefined' && inputCloudMailReceiveMailbox) ? inputCloudMailReceiveMailbox.value : ''), cloudMailDomain: normalizeCloudMailDomainInput((typeof inputCloudMailDomain !== 'undefined' && inputCloudMailDomain) ? inputCloudMailDomain.value : ''), yydsMailApiKey: (typeof inputYydsMailApiKey !== 'undefined' && inputYydsMailApiKey) ? inputYydsMailApiKey.value.trim() : '', yydsMailBaseUrl: normalizeYydsBaseUrlValue((typeof inputYydsMailBaseUrl !== 'undefined' && inputYydsMailBaseUrl) ? inputYydsMailBaseUrl.value : ''), autoRunSkipFailures: inputAutoSkipFailures.checked, autoRunFallbackThreadIntervalMinutes: normalizeAutoRunThreadIntervalMinutes(inputAutoSkipFailuresThreadIntervalMinutes.value), step6CookieCleanupEnabled: typeof inputStep6CookieCleanupEnabled !== 'undefined' && inputStep6CookieCleanupEnabled ? Boolean(inputStep6CookieCleanupEnabled.checked) : false, stepExecutionRangeByFlow: typeof buildStepExecutionRangeByFlowPayload === 'function' ? buildStepExecutionRangeByFlowPayload(latestState?.stepExecutionRangeByFlow) : (latestState?.stepExecutionRangeByFlow || {}), autoStepDelaySeconds: normalizeAutoStepDelaySeconds(inputAutoStepDelaySeconds.value), phoneVerificationEnabled: effectivePhoneVerificationEnabled, signupMethod: effectiveSignupMethod, phoneSignupReloginAfterBindEmailEnabled: typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail ? Boolean(inputPhoneSignupReloginAfterBindEmail.checked) : false, phoneSmsProvider: phoneSmsProviderValue, phoneSmsProviderOrder: phoneSmsProviderOrderValue, verificationResendCount: normalizeVerificationResendCount( inputVerificationResendCount?.value, DEFAULT_VERIFICATION_RESEND_COUNT ), heroSmsApiKey: heroSmsApiKeyValue, fiveSimApiKey: fiveSimApiKeyValue, fiveSimCountryOrder: fiveSimCountryOrderValue, fiveSimOperator: fiveSimOperatorValue, fiveSimProduct: fiveSimProductValue, nexSmsApiKey: nexSmsApiKeyValue, nexSmsCountryOrder: nexSmsCountryOrderValue, nexSmsServiceCode: nexSmsServiceCodeValue, smsBowerApiKey: smsBowerApiKeyValue, smsBowerCountryId: smsBowerCountry.id, smsBowerCountryLabel: smsBowerCountry.label, smsBowerCountryOrder: smsBowerCountryOrderValue, smsBowerMinPrice: smsBowerMinPriceValue, smsBowerMaxPrice: smsBowerMaxPriceValue, madaoBaseUrl: maDaoBaseUrlValue, madaoHttpSecret: maDaoHttpSecretValue, madaoMode: maDaoModeValue, madaoRoutingPlanId: maDaoRoutingPlanIdValue, madaoProviderId: maDaoProviderIdValue, madaoCountry: maDaoCountryValue, madaoOperator: maDaoOperatorValue, madaoAutoPickCountry: maDaoAutoPickCountryValue, madaoReusePhone: maDaoReusePhoneValue, madaoMinPrice: maDaoMinPriceValue, madaoMaxPrice: maDaoMaxPriceValue, phoneSmsReuseEnabled: phoneSmsReuseEnabledValue, heroSmsReuseEnabled: heroSmsReuseEnabledValue, freePhoneReuseEnabled: freePhoneReuseEnabledValue, freePhoneReuseAutoEnabled: freePhoneReuseAutoEnabledValue, heroSmsAcquirePriority: heroSmsAcquirePriorityValue, heroSmsOperator: heroSmsOperatorValue, heroSmsMinPrice: heroSmsMinPriceValue, heroSmsMaxPrice: heroSmsMaxPriceValue, heroSmsPreferredPrice: heroSmsPreferredPriceValue, phonePreferredActivation: phonePreferredActivationValue, phoneVerificationReplacementLimit: phoneVerificationReplacementLimitValue, phoneCodeWaitSeconds: phoneCodeWaitSecondsValue, phoneCodeTimeoutWindows: phoneCodeTimeoutWindowsValue, phoneCodePollIntervalSeconds: phoneCodePollIntervalSecondsValue, phoneCodePollMaxRounds: phoneCodePollMaxRoundsValue, heroSmsCountryId: heroSmsCountry.id, heroSmsCountryLabel: heroSmsCountry.label, heroSmsCountryFallback, fiveSimCountryId: fiveSimCountry.id, fiveSimCountryLabel: fiveSimCountry.label, fiveSimCountryFallback, fiveSimMaxPrice: fiveSimMaxPriceValue, fiveSimMinPrice: fiveSimMinPriceValue, }; } function normalizeLocalCpaStep9Mode(value = '') { return String(value || '').trim().toLowerCase() === 'bypass' ? 'bypass' : DEFAULT_LOCAL_CPA_STEP9_MODE; } function normalizeMail2925Mode(value = '') { return String(value || '').trim().toLowerCase() === MAIL_2925_MODE_RECEIVE ? MAIL_2925_MODE_RECEIVE : DEFAULT_MAIL_2925_MODE; } function normalizeCustomMailReceiveMode(value = '') { return String(value || '').trim().toLowerCase() === CUSTOM_MAIL_RECEIVE_MODE_HELPER ? CUSTOM_MAIL_RECEIVE_MODE_HELPER : DEFAULT_CUSTOM_MAIL_RECEIVE_MODE; } function getSelectedCustomMailReceiveMode() { return normalizeCustomMailReceiveMode(selectCustomMailReceiveMode?.value); } function normalizeCustomMailHelperBaseUrl(value = '') { const trimmed = String(value || '').trim(); const candidate = trimmed || DEFAULT_CUSTOM_MAIL_HELPER_BASE_URL; try { const parsed = new URL(candidate); if (!['http:', 'https:'].includes(parsed.protocol)) { return DEFAULT_CUSTOM_MAIL_HELPER_BASE_URL; } parsed.hash = ''; parsed.search = ''; parsed.pathname = parsed.pathname.replace(/\/+$/, ''); const path = parsed.pathname === '/' ? '' : parsed.pathname; return `${parsed.origin}${path}` || DEFAULT_CUSTOM_MAIL_HELPER_BASE_URL; } catch { return DEFAULT_CUSTOM_MAIL_HELPER_BASE_URL; } } function normalizeCloudflareTempEmailLookupMode(value = '') { return String(value || '').trim().toLowerCase() === CLOUDFLARE_TEMP_EMAIL_LOOKUP_MODE_REGISTRATION_EMAIL ? CLOUDFLARE_TEMP_EMAIL_LOOKUP_MODE_REGISTRATION_EMAIL : DEFAULT_CLOUDFLARE_TEMP_EMAIL_LOOKUP_MODE; } function normalizeCloudflareTempEmailSubdomainMode(value = '') { const normalized = String(value || '').trim().toLowerCase(); if (normalized === CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_RANDOM) { return CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_RANDOM; } if (normalized === CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_FIXED) { return CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_FIXED; } return DEFAULT_CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE; } function normalizeHotmailServiceMode(value = '') { if (typeof normalizeHotmailServiceModeFromUtils === 'function') { return normalizeHotmailServiceModeFromUtils(value); } return String(value || '').trim().toLowerCase() === HOTMAIL_SERVICE_MODE_REMOTE ? HOTMAIL_SERVICE_MODE_REMOTE : HOTMAIL_SERVICE_MODE_LOCAL; } function normalizeAccountRunHistoryHelperBaseUrlValue(value = '') { const trimmed = String(value || '').trim(); if (!trimmed) { return DEFAULT_ACCOUNT_RUN_HISTORY_HELPER_BASE_URL; } try { const parsed = new URL(trimmed); if (!['http:', 'https:'].includes(parsed.protocol)) { return DEFAULT_ACCOUNT_RUN_HISTORY_HELPER_BASE_URL; } if (parsed.pathname === '/append-account-log' || parsed.pathname === '/sync-account-run-records') { parsed.pathname = ''; parsed.search = ''; parsed.hash = ''; } return parsed.toString().replace(/\/$/, ''); } catch { return DEFAULT_ACCOUNT_RUN_HISTORY_HELPER_BASE_URL; } } function normalizePhoneSmsProvider(value = '') { const rootScope = typeof window !== 'undefined' ? window : globalThis; if (rootScope.PhoneSmsProviderRegistry?.normalizeProviderId) { return rootScope.PhoneSmsProviderRegistry.normalizeProviderId(value); } const nexSmsProvider = typeof PHONE_SMS_PROVIDER_NEXSMS !== 'undefined' ? PHONE_SMS_PROVIDER_NEXSMS : 'nexsms'; const maDaoProvider = typeof PHONE_SMS_PROVIDER_MADAO !== 'undefined' ? PHONE_SMS_PROVIDER_MADAO : 'madao'; const smsBowerProvider = typeof PHONE_SMS_PROVIDER_SMS_BOWER !== 'undefined' ? PHONE_SMS_PROVIDER_SMS_BOWER : 'sms-bower'; const normalized = String(value || '').trim().toLowerCase(); if (normalized === PHONE_SMS_PROVIDER_FIVE_SIM) { return PHONE_SMS_PROVIDER_FIVE_SIM; } if (normalized === nexSmsProvider) { return nexSmsProvider; } if (normalized === maDaoProvider) { return maDaoProvider; } if (normalized === smsBowerProvider) { return smsBowerProvider; } return PHONE_SMS_PROVIDER_HERO_SMS; } function setPhoneSmsProviderSelectValue(provider) { const normalizedProvider = normalizePhoneSmsProvider(provider); if (selectPhoneSmsProvider) { selectPhoneSmsProvider.value = normalizedProvider; if (selectPhoneSmsProvider.dataset) { selectPhoneSmsProvider.dataset.activeProvider = normalizedProvider; } } return normalizedProvider; } function getLastAppliedPhoneSmsProvider() { return normalizePhoneSmsProvider( selectPhoneSmsProvider?.dataset?.activeProvider || latestState?.phoneSmsProvider || selectPhoneSmsProvider?.value ); } function getSelectedPhoneSmsProvider() { return normalizePhoneSmsProvider(selectPhoneSmsProvider?.value || latestState?.phoneSmsProvider); } function getPhoneSmsProviderLabel(provider = getSelectedPhoneSmsProvider()) { if (typeof window !== 'undefined' && window.PhoneSmsProviderRegistry?.getProviderLabel) { return window.PhoneSmsProviderRegistry.getProviderLabel(provider); } const normalizedProvider = normalizePhoneSmsProvider(provider); if (normalizedProvider === PHONE_SMS_PROVIDER_FIVE_SIM) { return '5sim'; } if (normalizedProvider === PHONE_SMS_PROVIDER_NEXSMS) { return 'NexSMS'; } if (normalizedProvider === PHONE_SMS_PROVIDER_MADAO) { return 'MaDao'; } if (normalizedProvider === PHONE_SMS_PROVIDER_SMS_BOWER) { return 'SMS Bower'; } return 'HeroSMS'; } function isFiveSimProviderSelected() { return getSelectedPhoneSmsProvider() === PHONE_SMS_PROVIDER_FIVE_SIM; } function normalizePhoneSmsCountryId(value, provider = getSelectedPhoneSmsProvider()) { const normalizedProvider = normalizePhoneSmsProvider(provider); if (normalizedProvider === PHONE_SMS_PROVIDER_FIVE_SIM) { return normalizeFiveSimCountryId(value); } if (normalizedProvider === PHONE_SMS_PROVIDER_NEXSMS) { return normalizeNexSmsCountryIdValue(value, -1); } if (normalizedProvider === PHONE_SMS_PROVIDER_MADAO) { return normalizeMaDaoCountry(value); } return normalizeHeroSmsCountryId(value); } function normalizePhoneSmsCountryLabel(value = '', provider = getSelectedPhoneSmsProvider()) { const normalizedProvider = normalizePhoneSmsProvider(provider); if (normalizedProvider === PHONE_SMS_PROVIDER_FIVE_SIM) { return normalizeFiveSimCountryLabel(value); } if (normalizedProvider === PHONE_SMS_PROVIDER_NEXSMS) { return normalizeNexSmsCountryLabel(value); } if (normalizedProvider === PHONE_SMS_PROVIDER_MADAO) { return String(value || '').trim(); } return normalizeHeroSmsCountryLabel(value); } function normalizePhoneSmsMaxPriceValue(value = '', provider = getSelectedPhoneSmsProvider()) { const normalizedProvider = normalizePhoneSmsProvider(provider); if (normalizedProvider === PHONE_SMS_PROVIDER_FIVE_SIM) { return normalizeFiveSimMaxPriceValue(value); } if (normalizedProvider === PHONE_SMS_PROVIDER_MADAO) { return normalizeMaDaoPriceValue(value); } if (normalizedProvider === PHONE_SMS_PROVIDER_SMS_BOWER && typeof window !== 'undefined' && window.PhoneSmsBowerProvider?.normalizeSmsBowerMaxPrice) { return window.PhoneSmsBowerProvider.normalizeSmsBowerMaxPrice(value); } return normalizeHeroSmsMaxPriceValue(value); } function normalizePhoneSmsMinPriceValue(value = '', provider = getSelectedPhoneSmsProvider()) { const normalizedProvider = normalizePhoneSmsProvider(provider); if (normalizedProvider === PHONE_SMS_PROVIDER_FIVE_SIM) { return normalizeFiveSimMaxPriceValue(value); } if (normalizedProvider === PHONE_SMS_PROVIDER_MADAO) { return normalizeMaDaoPriceValue(value); } if (normalizedProvider === PHONE_SMS_PROVIDER_SMS_BOWER && typeof window !== 'undefined' && window.PhoneSmsBowerProvider?.normalizeSmsBowerMaxPrice) { return window.PhoneSmsBowerProvider.normalizeSmsBowerMaxPrice(value); } return normalizeHeroSmsMaxPriceValue(value); } function normalizeSmsBowerCountryIdValue(value, fallback = 6) { if (typeof window !== 'undefined' && window.PhoneSmsBowerProvider?.normalizeSmsBowerCountryId) { return window.PhoneSmsBowerProvider.normalizeSmsBowerCountryId(value, fallback); } const parsed = Math.floor(Number(value)); if (Number.isFinite(parsed) && parsed >= 0) { return parsed; } const fallbackParsed = Math.floor(Number(fallback)); return Number.isFinite(fallbackParsed) && fallbackParsed >= 0 ? fallbackParsed : 6; } function normalizeSmsBowerCountryLabelValue(value = '', fallback = 'Indonesia') { if (typeof window !== 'undefined' && window.PhoneSmsBowerProvider?.normalizeSmsBowerCountryLabel) { return window.PhoneSmsBowerProvider.normalizeSmsBowerCountryLabel(value, fallback); } return String(value || '').trim() || fallback; } function normalizeSmsBowerCountryOrderValue(value = []) { const source = Array.isArray(value) ? value : String(value || '').split(/[\r\n,,;;]+/).map((entry) => String(entry || '').trim()).filter(Boolean); const seen = new Set(); const normalized = []; source.forEach((entry) => { const rawId = entry && typeof entry === 'object' && !Array.isArray(entry) ? (entry.id ?? entry.countryId) : String(entry || '').trim().match(/^(\d+)/)?.[1]; const id = normalizeSmsBowerCountryIdValue(rawId, -1); if (id < 0 || seen.has(id)) { return; } seen.add(id); const rawLabel = entry && typeof entry === 'object' && !Array.isArray(entry) ? (entry.label ?? entry.countryLabel) : String(entry || '').trim().replace(/^\d+\s*(?:[:|/-]\s*)?/, ''); normalized.push({ id, label: normalizeSmsBowerCountryLabelValue(rawLabel, id === 6 ? 'Indonesia' : `Country #${id}`) }); }); return normalized; } function getPhoneSmsProviderCount() { const rootScope = typeof window !== 'undefined' ? window : globalThis; const registryIds = rootScope.PhoneSmsProviderRegistry?.getProviderIds?.(); if (Array.isArray(registryIds) && registryIds.length) { return registryIds.length; } return DEFAULT_PHONE_SMS_PROVIDER_ORDER.length; } function normalizeFiveSimCountryId(value, fallback = DEFAULT_FIVE_SIM_COUNTRY_ID) { const fallbackSource = fallback === undefined || fallback === null ? DEFAULT_FIVE_SIM_COUNTRY_ID : fallback; const normalizedFallback = String(fallbackSource).trim().toLowerCase().replace(/[^a-z0-9_-]+/g, ''); const rawNormalized = typeof window !== 'undefined' && window.PhoneSmsFiveSimProvider?.normalizeFiveSimCountryId ? window.PhoneSmsFiveSimProvider.normalizeFiveSimCountryId(value, '') : String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, ''); const normalized = String(rawNormalized || '').trim().toLowerCase(); if (normalized) { return normalized; } if (!normalizedFallback) { return ''; } return normalizedFallback || DEFAULT_FIVE_SIM_COUNTRY_ID; } function normalizeFiveSimCountryLabel(value = '', fallback = DEFAULT_FIVE_SIM_COUNTRY_LABEL) { if (typeof window !== 'undefined' && window.PhoneSmsFiveSimProvider?.normalizeFiveSimCountryLabel) { return window.PhoneSmsFiveSimProvider.normalizeFiveSimCountryLabel(value, fallback); } return String(value || '').trim() || fallback; } function formatFiveSimCountryDisplayLabel(id = '', englishValue = '', fallback = DEFAULT_FIVE_SIM_COUNTRY_LABEL) { if (typeof window !== 'undefined' && window.PhoneSmsFiveSimProvider?.formatFiveSimCountryLabel) { return window.PhoneSmsFiveSimProvider.formatFiveSimCountryLabel(id, englishValue, fallback); } const countryId = normalizeFiveSimCountryId(id, ''); const english = normalizeFiveSimCountryLabel(englishValue || countryId || fallback, fallback); const chinese = FIVE_SIM_COUNTRY_CN_BY_ID[countryId] || ''; if (chinese && english) { if (String(english).includes(chinese)) { return english; } if (chinese.toLowerCase() !== english.toLowerCase()) { return `${chinese} (${english})`; } } return chinese || english; } function normalizeFiveSimOperator(value = '', fallback = DEFAULT_FIVE_SIM_OPERATOR) { if (typeof window !== 'undefined' && window.PhoneSmsFiveSimProvider?.normalizeFiveSimOperator) { return window.PhoneSmsFiveSimProvider.normalizeFiveSimOperator(value || fallback); } return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || fallback; } function normalizeFiveSimMaxPriceValue(value = '') { if (typeof window !== 'undefined' && window.PhoneSmsFiveSimProvider?.normalizeFiveSimMaxPrice) { return window.PhoneSmsFiveSimProvider.normalizeFiveSimMaxPrice(value); } const rawValue = String(value ?? '').trim(); if (!rawValue) { return ''; } const numeric = Number(rawValue); if (!Number.isFinite(numeric) || numeric <= 0) { return ''; } return String(Math.round(numeric * 10000) / 10000); } function normalizeFiveSimCountryFallbackList(value = []) { if (typeof window !== 'undefined' && window.PhoneSmsFiveSimProvider?.normalizeFiveSimCountryFallback) { return window.PhoneSmsFiveSimProvider.normalizeFiveSimCountryFallback(value); } const source = Array.isArray(value) ? value : String(value || '') .split(/[\r\n,,;;]+/) .map((entry) => String(entry || '').trim()) .filter(Boolean); const seen = new Set(); const normalized = []; source.forEach((entry) => { let id = ''; let label = ''; if (entry && typeof entry === 'object' && !Array.isArray(entry)) { id = normalizeFiveSimCountryId(entry.id ?? entry.countryId ?? entry.slug, ''); label = normalizeFiveSimCountryLabel( entry.label ?? entry.countryLabel ?? formatFiveSimCountryDisplayLabel(id, entry.text_en ?? entry.name, id), id ); } else { const text = String(entry || '').trim(); const structured = text.match(/^([a-z0-9_-]+)\s*(?:[:|/-]\s*(.+))?$/i); id = normalizeFiveSimCountryId(structured?.[1] || text, ''); label = String(structured?.[2] || '').trim() || formatFiveSimCountryDisplayLabel(id, id, id); } if (!id || seen.has(id)) { return; } seen.add(id); normalized.push({ id, label: label || formatFiveSimCountryDisplayLabel(id, id, id) }); }); return normalized; } function normalizeHeroSmsCountryId(value, fallback = DEFAULT_HERO_SMS_COUNTRY_ID) { const parsed = Math.floor(Number(value)); if (Number.isFinite(parsed) && parsed > 0) { return parsed; } const fallbackParsed = Math.floor(Number(fallback)); return Number.isFinite(fallbackParsed) && fallbackParsed > 0 ? fallbackParsed : DEFAULT_HERO_SMS_COUNTRY_ID; } function normalizeHeroSmsCountryLabel(value = '') { return String(value || '').trim() || DEFAULT_HERO_SMS_COUNTRY_LABEL; } function normalizeHeroSmsMaxPriceValue(value = '') { const rawValue = String(value ?? '').trim(); if (!rawValue) { return ''; } const numeric = Number(rawValue); if (!Number.isFinite(numeric) || numeric <= 0) { return ''; } return String(Math.round(numeric * 10000) / 10000); } function normalizeMaDaoBaseUrlValue(value = '') { const rawValue = String(value || DEFAULT_MADAO_BASE_URL).trim(); try { const parsed = new URL(rawValue); if (!['http:', 'https:'].includes(parsed.protocol)) { return DEFAULT_MADAO_BASE_URL; } const endpointPath = parsed.pathname.replace(/\/+$/g, ''); if (['/api/acquire', '/api/poll', '/api/release', '/api/routing/replace'].includes(endpointPath)) { parsed.pathname = ''; parsed.search = ''; parsed.hash = ''; } return parsed.toString().replace(/\/+$/, ''); } catch { return DEFAULT_MADAO_BASE_URL; } } function normalizeMaDaoModeValue(value = '') { return String(value || '').trim().toLowerCase() === MADAO_MODE_DIRECT ? MADAO_MODE_DIRECT : DEFAULT_MADAO_MODE; } function normalizeMaDaoIdentifierValue(value = '') { return String(value || '').trim(); } function normalizeMaDaoRoutingPlanIdValue(value = '') { return normalizeMaDaoIdentifierValue(value); } function normalizeMaDaoProviderIdValue(value = '') { return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, ''); } function normalizeMaDaoOperatorValue(value = '') { const rawValue = String(value || '').trim(); const compactValue = rawValue.toLowerCase().replace(/[^a-z0-9]+/g, ''); if (!rawValue || compactValue === 'any' || compactValue === 'anyoperator') { return ''; } return rawValue.toLowerCase().replace(/[^a-z0-9_-]+/g, ''); } function normalizeMaDaoCountry(value = '') { const trimmed = String(value || '').trim(); if (!trimmed) { return ''; } const lowered = trimmed.toLowerCase(); if (lowered === 'any' || lowered === 'local') { return lowered; } if (/^[a-z]{2}$/i.test(trimmed)) { return trimmed.toUpperCase(); } return lowered.replace(/[^a-z0-9_-]+/g, ''); } function formatMaDaoCountryDisplayLabel(value = '', label = '', labelZh = '') { const country = normalizeMaDaoCountry(value); const sourceLabelZh = String(labelZh || '').trim(); const sourceLabel = String(label || '').trim(); if (!country) { return sourceLabelZh || sourceLabel; } if (country === 'local') { return sourceLabelZh || '本地'; } if (country === 'any') { return sourceLabelZh || '任意国家'; } return sourceLabelZh || sourceLabel || country; } function normalizeMaDaoPriceValue(value = '') { const rawValue = String(value ?? '').trim(); if (!rawValue) { return ''; } const numeric = Number(rawValue); if (!Number.isFinite(numeric) || numeric <= 0) { return ''; } return String(Math.round(numeric * 10000) / 10000); } function createSelectOptionElement(value = '', label = '', title = '') { const option = typeof document !== 'undefined' && typeof document.createElement === 'function' ? document.createElement('option') : { value: '', textContent: '', title: '', selected: false }; option.value = String(value || ''); option.textContent = String(label || value || '').trim(); const normalizedTitle = String(title || '').trim(); if (normalizedTitle) { option.title = normalizedTitle; } return option; } function setSelectOptions(selectEl, items = [], options = {}) { if (!selectEl) { return; } const placeholder = String(options.placeholder || '').trim(); const includeEmpty = options.includeEmpty !== false; const emptyValue = options.emptyValue !== undefined ? String(options.emptyValue) : ''; const selectedValue = String(options.value !== undefined ? options.value : selectEl.value || '').trim(); const optionItems = []; if (includeEmpty) { optionItems.push({ value: emptyValue, label: placeholder || '请选择', }); } (Array.isArray(items) ? items : []).forEach((item) => { const value = String(item?.value ?? '').trim(); if (!value) { return; } optionItems.push({ value, label: String(item?.label || value).trim(), hint: String(item?.hint || '').trim(), }); }); if (typeof selectEl.replaceChildren === 'function') { selectEl.replaceChildren(...optionItems.map((item) => createSelectOptionElement(item.value, item.label, item.hint))); } else if (typeof selectEl.appendChild === 'function') { selectEl.innerHTML = ''; optionItems.forEach((item) => { selectEl.appendChild(createSelectOptionElement(item.value, item.label, item.hint)); }); } else { selectEl.options = optionItems.map((item) => createSelectOptionElement(item.value, item.label, item.hint)); } const values = new Set(optionItems.map((item) => item.value)); const nextValue = values.has(selectedValue) ? selectedValue : (includeEmpty ? emptyValue : (optionItems[0]?.value || '')); selectEl.value = nextValue; Array.from(selectEl.options || []).forEach((option) => { option.selected = String(option.value || '') === nextValue; }); } function buildMaDaoRoutingPlanOptions(items = [], selectedValue = '') { const selectedPlanId = normalizeMaDaoRoutingPlanIdValue(selectedValue); const seen = new Set(); const normalizedItems = []; (Array.isArray(items) ? items : []).forEach((item) => { const value = normalizeMaDaoRoutingPlanIdValue( item?.value || item?.id || item?.routing_plan_id || item?.routingPlanId || '' ); if (!value || seen.has(value)) { return; } seen.add(value); normalizedItems.push({ value, label: String(item?.label || item?.name || value).trim() || value, hint: String(item?.hint || item?.description || '').trim(), service: String(item?.service || '').trim().toLowerCase(), }); }); if (selectedPlanId && !seen.has(selectedPlanId)) { normalizedItems.unshift({ value: selectedPlanId, label: selectedPlanId, hint: '已保存的路由计划', }); } return normalizedItems; } function setMaDaoRoutingPlanSelectOptions(selectedValue = latestState?.madaoRoutingPlanId || '') { const normalizedSelected = normalizeMaDaoRoutingPlanIdValue(selectedValue); const options = buildMaDaoRoutingPlanOptions(maDaoRoutingPlanOptions, normalizedSelected); setSelectOptions(selectMaDaoRoutingPlanId, options, { placeholder: '请先刷新路由计划', value: normalizedSelected, }); } function normalizeMaDaoOptionListItems(items = [], selectedValue = '', normalizeValue = normalizeMaDaoIdentifierValue, selectedHint = '已保存的选项') { const normalizedSelected = normalizeValue(selectedValue); const seen = new Set(); const normalizedItems = []; (Array.isArray(items) ? items : []).forEach((item) => { const value = normalizeValue( item?.value || item?.id || item?.provider || item?.provider_value || item?.providerValue || item?.country || item?.operator || '' ); if (!value || seen.has(value)) { return; } seen.add(value); normalizedItems.push({ value, label: String(item?.label || item?.name || item?.display_name || item?.displayName || value).trim() || value, labelZh: String(item?.label_zh || item?.labelZh || item?.display_name_zh || item?.displayNameZh || '').trim(), hint: String(item?.hint || item?.description || item?.provider_value || item?.providerValue || '').trim(), enabled: item?.enabled !== false, }); }); if (normalizedSelected && !seen.has(normalizedSelected)) { normalizedItems.unshift({ value: normalizedSelected, label: normalizedSelected, hint: selectedHint, enabled: true, }); } return normalizedItems.filter((item) => item.enabled !== false); } function resolveMaDaoOptionSelectedValue(items = [], selectedValue = '', normalizeValue = normalizeMaDaoIdentifierValue) { const normalizedSelected = normalizeValue(selectedValue); if (!normalizedSelected) { return ''; } const sourceItems = Array.isArray(items) ? items : []; for (const item of sourceItems) { const value = normalizeValue( item?.value || item?.id || item?.provider || item?.provider_value || item?.providerValue || item?.country || item?.operator || '' ); if (!value) { continue; } const aliases = [ item?.value, item?.id, item?.provider, item?.provider_value, item?.providerValue, item?.country, item?.operator, item?.label, item?.label_zh, item?.labelZh, item?.name, item?.display_name, item?.displayName, ]; if (aliases.some((alias) => normalizeValue(alias || '') === normalizedSelected)) { return value; } } return normalizedSelected; } function setMaDaoProviderSelectOptions(selectedValue = latestState?.madaoProviderId || '') { const normalizedSelected = resolveMaDaoOptionSelectedValue( maDaoProviderOptions, selectedValue, normalizeMaDaoProviderIdValue ); const options = normalizeMaDaoOptionListItems( maDaoProviderOptions, normalizedSelected, normalizeMaDaoProviderIdValue, '已保存的服务商' ); setSelectOptions(selectMaDaoProviderId, options, { placeholder: '请先刷新服务商', value: normalizedSelected, }); } function setMaDaoCountrySelectOptions(selectedValue = latestState?.madaoCountry || '') { const normalizedSelected = resolveMaDaoOptionSelectedValue( maDaoCountryOptions, selectedValue, normalizeMaDaoCountry ); const options = normalizeMaDaoOptionListItems( maDaoCountryOptions, normalizedSelected, normalizeMaDaoCountry, '已保存的国家' ).map((item) => ({ ...item, label: formatMaDaoCountryDisplayLabel(item.value, item.label, item.labelZh), })); setSelectOptions(selectMaDaoCountry, options, { placeholder: '请先选择服务商', value: normalizedSelected, }); } function setMaDaoOperatorSelectOptions(selectedValue = latestState?.madaoOperator || '') { const normalizedSelected = resolveMaDaoOptionSelectedValue( maDaoOperatorOptions, selectedValue, normalizeMaDaoOperatorValue ); const options = normalizeMaDaoOptionListItems( maDaoOperatorOptions, normalizedSelected, normalizeMaDaoOperatorValue, '已保存的线路' ); setSelectOptions(selectMaDaoOperator, options, { placeholder: '任意线路', value: normalizedSelected, }); } function buildMaDaoRequestUrl(path = '', baseUrl = '') { const normalizedBaseUrl = normalizeMaDaoBaseUrlValue(baseUrl || latestState?.madaoBaseUrl || DEFAULT_MADAO_BASE_URL); return new URL(String(path || '').replace(/^\/+/, ''), `${normalizedBaseUrl.replace(/\/+$/, '')}/`).toString(); } function buildMaDaoRequestHeaders(httpSecret = '') { const headers = { Accept: 'application/json' }; const normalizedSecret = String(httpSecret || '').trim(); if (normalizedSecret) { headers.Authorization = `Bearer ${normalizedSecret}`; } return headers; } async function fetchMaDaoJson(path = '', options = {}) { const baseUrl = typeof inputMaDaoBaseUrl !== 'undefined' && inputMaDaoBaseUrl ? normalizeMaDaoBaseUrlValue(inputMaDaoBaseUrl.value || latestState?.madaoBaseUrl) : normalizeMaDaoBaseUrlValue(latestState?.madaoBaseUrl); const httpSecret = typeof inputMaDaoHttpSecret !== 'undefined' && inputMaDaoHttpSecret ? String(inputMaDaoHttpSecret.value || '') : String(latestState?.madaoHttpSecret || ''); const response = await fetch(buildMaDaoRequestUrl(path, baseUrl), { method: options.method || 'GET', headers: { ...buildMaDaoRequestHeaders(httpSecret), ...(options.body !== undefined ? { 'Content-Type': 'application/json' } : {}), }, body: options.body !== undefined ? JSON.stringify(options.body) : undefined, cache: 'no-store', }); const text = await response.text(); let payload = null; if (text) { try { payload = JSON.parse(text); } catch { payload = { message: text }; } } if (!response.ok) { const message = String(payload?.message || payload?.error || response.statusText || `HTTP ${response.status}`).trim(); throw new Error(message || 'MaDao 请求失败'); } return payload; } function getMaDaoRoutingPlansFromPayload(payload = {}) { if (Array.isArray(payload)) { return payload; } if (Array.isArray(payload?.plans)) { return payload.plans; } if (Array.isArray(payload?.data?.plans)) { return payload.data.plans; } if (Array.isArray(payload?.data)) { return payload.data; } if (Array.isArray(payload?.items)) { return payload.items; } return []; } function getMaDaoProvidersFromPayload(payload = {}) { if (Array.isArray(payload)) { return payload; } if (Array.isArray(payload?.providers)) { return payload.providers; } if (Array.isArray(payload?.data?.providers)) { return payload.data.providers; } if (Array.isArray(payload?.data)) { return payload.data; } if (Array.isArray(payload?.items)) { return payload.items; } return []; } function getMaDaoOptionItemsFromPayload(payload = {}) { if (Array.isArray(payload)) { return payload; } if (Array.isArray(payload?.items)) { return payload.items; } if (Array.isArray(payload?.data?.items)) { return payload.data.items; } if (Array.isArray(payload?.data)) { return payload.data; } return []; } function getSelectedMaDaoProviderId() { return normalizeMaDaoProviderIdValue( typeof selectMaDaoProviderId !== 'undefined' && selectMaDaoProviderId ? selectMaDaoProviderId.value : latestState?.madaoProviderId ); } function getSelectedMaDaoCountry() { return normalizeMaDaoCountry( typeof selectMaDaoCountry !== 'undefined' && selectMaDaoCountry ? selectMaDaoCountry.value : latestState?.madaoCountry ); } async function loadMaDaoRoutingPlans(options = {}) { const selectedValue = normalizeMaDaoRoutingPlanIdValue( typeof selectMaDaoRoutingPlanId !== 'undefined' && selectMaDaoRoutingPlanId ? selectMaDaoRoutingPlanId.value : latestState?.madaoRoutingPlanId ); const payload = await fetchMaDaoJson('/api/routing-plans'); const plans = getMaDaoRoutingPlansFromPayload(payload); maDaoRoutingPlanOptions = plans .filter((plan) => plan?.enabled !== false) .filter((plan) => { const service = String(plan?.service || '').trim().toLowerCase(); return !service || service === 'openai'; }) .map((plan) => ({ value: normalizeMaDaoRoutingPlanIdValue(plan?.id || plan?.routing_plan_id || plan?.routingPlanId || plan?.value || ''), label: String(plan?.name || plan?.label || plan?.id || plan?.routing_plan_id || '').trim(), hint: String(plan?.description || plan?.hint || '').trim(), service: String(plan?.service || '').trim().toLowerCase(), })) .filter((plan) => plan.value); setMaDaoRoutingPlanSelectOptions(selectedValue); updateHeroSmsPlatformDisplay(); if (!options.silent && typeof showToast === 'function') { showToast('已刷新 MaDao 路由计划。', 'info', 1600); } return maDaoRoutingPlanOptions; } async function loadMaDaoProviders(options = {}) { const selectedValue = normalizeMaDaoProviderIdValue( (typeof selectMaDaoProviderId !== 'undefined' && selectMaDaoProviderId ? selectMaDaoProviderId.value : '') || latestState?.madaoProviderId ); const payload = await fetchMaDaoJson('/api/providers'); maDaoProviderOptions = getMaDaoProvidersFromPayload(payload) .filter((provider) => provider?.enabled !== false) .map((provider) => ({ value: normalizeMaDaoProviderIdValue(provider?.id || provider?.value || provider?.provider || ''), label: String(provider?.name || provider?.label || provider?.id || provider?.value || '').trim(), hint: String(provider?.description || provider?.hint || provider?.protocol_label || provider?.protocolLabel || provider?.kind || '').trim(), enabled: provider?.enabled !== false, })) .filter((provider) => provider.value); setMaDaoProviderSelectOptions(selectedValue); if (!options.skipChildren && getSelectedMaDaoProviderId()) { await loadMaDaoCountries({ silent: true }).catch(() => { setMaDaoCountrySelectOptions(latestState?.madaoCountry || ''); setMaDaoOperatorSelectOptions(latestState?.madaoOperator || ''); }); } updateHeroSmsPlatformDisplay(); if (!options.silent && typeof showToast === 'function') { showToast('已刷新 MaDao 服务商。', 'info', 1600); } return maDaoProviderOptions; } async function loadMaDaoCountries(options = {}) { const providerId = getSelectedMaDaoProviderId(); const selectedValue = normalizeMaDaoCountry( (typeof selectMaDaoCountry !== 'undefined' && selectMaDaoCountry ? selectMaDaoCountry.value : '') || latestState?.madaoCountry ); if (!providerId) { maDaoCountryOptions = []; maDaoOperatorOptions = []; setMaDaoCountrySelectOptions(selectedValue); setMaDaoOperatorSelectOptions(latestState?.madaoOperator || ''); return maDaoCountryOptions; } const payload = await fetchMaDaoJson(`/api/providers/${encodeURIComponent(providerId)}/countries`); maDaoCountryOptions = getMaDaoOptionItemsFromPayload(payload); setMaDaoCountrySelectOptions(selectedValue); if (!options.skipChildren) { await loadMaDaoOperators({ silent: true }).catch(() => { setMaDaoOperatorSelectOptions(latestState?.madaoOperator || ''); }); } updateHeroSmsPlatformDisplay(); if (!options.silent && typeof showToast === 'function') { showToast('已刷新 MaDao 国家。', 'info', 1600); } return maDaoCountryOptions; } async function loadMaDaoOperators(options = {}) { const providerId = getSelectedMaDaoProviderId(); const country = getSelectedMaDaoCountry(); const selectedValue = normalizeMaDaoOperatorValue( typeof selectMaDaoOperator !== 'undefined' && selectMaDaoOperator ? selectMaDaoOperator.value : latestState?.madaoOperator ); if (!providerId) { maDaoOperatorOptions = []; setMaDaoOperatorSelectOptions(selectedValue); return maDaoOperatorOptions; } const payload = await fetchMaDaoJson(`/api/providers/${encodeURIComponent(providerId)}/operators`, { method: 'POST', body: country ? { country } : {}, }); maDaoOperatorOptions = getMaDaoOptionItemsFromPayload(payload); setMaDaoOperatorSelectOptions(selectedValue); updateHeroSmsPlatformDisplay(); if (!options.silent && typeof showToast === 'function') { showToast('已刷新 MaDao 线路。', 'info', 1600); } return maDaoOperatorOptions; } function getSelectedMaDaoRoutingPlanLabel(value = '') { const selectedValue = normalizeMaDaoRoutingPlanIdValue( value || (typeof selectMaDaoRoutingPlanId !== 'undefined' && selectMaDaoRoutingPlanId ? selectMaDaoRoutingPlanId.value : latestState?.madaoRoutingPlanId) || '' ); if (!selectedValue) { return 'routing plan'; } const matched = maDaoRoutingPlanOptions.find((item) => normalizeMaDaoRoutingPlanIdValue(item?.value) === selectedValue); return String(matched?.label || selectedValue).trim() || 'routing plan'; } function normalizePhoneSmsProviderValue(value = '') { const rootScope = typeof window !== 'undefined' ? window : globalThis; if (rootScope.PhoneSmsProviderRegistry?.normalizeProviderId) { return rootScope.PhoneSmsProviderRegistry.normalizeProviderId(value); } return normalizePhoneSmsProvider(value); } function normalizePhoneSmsProviderOrderValue(value = [], fallbackOrder = []) { const rootScope = typeof window !== 'undefined' ? window : globalThis; if (rootScope.PhoneSmsProviderRegistry?.normalizeProviderOrder) { return rootScope.PhoneSmsProviderRegistry.normalizeProviderOrder(value, fallbackOrder); } const providerLimit = getPhoneSmsProviderCount(); const source = Array.isArray(value) ? value : String(value || '') .split(/[\r\n,]+/) .map((entry) => String(entry || '').trim()) .filter(Boolean); const normalized = []; const seen = new Set(); source.forEach((entry) => { const provider = normalizePhoneSmsProviderValue(entry); if (seen.has(provider)) { return; } seen.add(provider); normalized.push(provider); }); if (normalized.length) { return normalized.slice(0, providerLimit); } const fallback = Array.isArray(fallbackOrder) ? fallbackOrder : []; if (!fallback.length) { return []; } const fallbackNormalized = []; fallback.forEach((entry) => { const provider = normalizePhoneSmsProviderValue(entry); if (!provider || fallbackNormalized.includes(provider)) { return; } fallbackNormalized.push(provider); }); return fallbackNormalized.slice(0, providerLimit); } function formatPhoneSmsProviderOrderSummary(order = []) { const normalized = normalizePhoneSmsProviderOrderValue(order, []); if (!normalized.length) { return '未设置'; } return normalized .map((provider, index) => `${index + 1}. ${getPhoneSmsProviderLabel(provider)}`) .join(' → '); } function updatePhoneSmsProviderOrderSummary(order = []) { const normalized = normalizePhoneSmsProviderOrderValue(order, []); if (displayPhoneSmsProviderOrder) { displayPhoneSmsProviderOrder.textContent = formatPhoneSmsProviderOrderSummary(normalized); } if (btnPhoneSmsProviderOrderMenu) { const providerCount = getPhoneSmsProviderCount(); btnPhoneSmsProviderOrderMenu.textContent = normalized.length ? `${normalized.map((provider) => getPhoneSmsProviderLabel(provider)).join(' / ')} (${normalized.length}/${providerCount})` : `未选择 (0/${providerCount})`; } } function resolveNormalizedProviderOrderForRuntime(state = {}) { const rawOrder = Array.isArray(state?.phoneSmsProviderOrder) ? state.phoneSmsProviderOrder : []; const normalizedOrder = normalizePhoneSmsProviderOrderValue(rawOrder, []); if (normalizedOrder.length) { return normalizedOrder; } const fallbackProvider = normalizePhoneSmsProviderValue( state?.phoneSmsProvider || selectPhoneSmsProvider?.value || DEFAULT_PHONE_SMS_PROVIDER ); return [fallbackProvider]; } function setPhoneSmsProviderOrderMenuOpen(open) { const nextOpen = Boolean(open); if (btnPhoneSmsProviderOrderMenu) { btnPhoneSmsProviderOrderMenu.setAttribute('aria-expanded', String(nextOpen)); } if (phoneSmsProviderOrderMenu) { phoneSmsProviderOrderMenu.hidden = !nextOpen; } } function renderPhoneSmsProviderOrderMenu() { if (!phoneSmsProviderOrderMenu || !selectPhoneSmsProviderOrder) { return; } phoneSmsProviderOrderMenu.innerHTML = ''; const selectedOrder = normalizePhoneSmsProviderOrderValue(phoneSmsProviderOrderSelection, []); const selectedSet = new Set(selectedOrder); Array.from(selectPhoneSmsProviderOrder.options || []).forEach((option) => { const provider = normalizePhoneSmsProviderValue(option.value || ''); const active = selectedSet.has(provider); const orderIndex = active ? selectedOrder.findIndex((entry) => entry === provider) + 1 : 0; const item = document.createElement('button'); item.type = 'button'; item.className = 'header-dropdown-item hero-sms-country-menu-item'; item.classList.toggle('is-active', active); const labelText = document.createElement('span'); labelText.className = 'hero-sms-country-menu-item-label'; labelText.textContent = getPhoneSmsProviderLabel(provider); const badge = document.createElement('span'); badge.className = 'hero-sms-country-menu-item-badge'; badge.textContent = active ? `✓ ${orderIndex}` : ''; item.appendChild(labelText); item.appendChild(badge); item.addEventListener('click', () => { option.selected = !option.selected; const nextOrder = syncPhoneSmsProviderOrderFromSelect({ enforceMax: true, ensureDefault: false, showLimitToast: false, syncProvider: false, }); updatePhoneSmsProviderOrderSummary(nextOrder); markSettingsDirty(true); saveSettings({ silent: true }).catch(() => { }); }); phoneSmsProviderOrderMenu.appendChild(item); }); } function syncPhoneSmsProviderOrderFromSelect(options = {}) { const selectionLimit = Math.max(1, Math.floor(Number(options.maxSelection) || getPhoneSmsProviderCount())); const enforceMax = options.enforceMax !== false; const ensureDefault = options.ensureDefault !== false; const syncProvider = Boolean(options.syncProvider); const showLimitToast = Boolean(options.showLimitToast); if (!selectPhoneSmsProviderOrder) { phoneSmsProviderOrderSelection = []; updatePhoneSmsProviderOrderSummary([]); return []; } const selectedProviders = Array.from(selectPhoneSmsProviderOrder.options) .filter((option) => option.selected) .map((option) => normalizePhoneSmsProviderValue(option.value || '')) .filter(Boolean); const selectedSet = new Set(selectedProviders); let nextOrder = phoneSmsProviderOrderSelection.filter((provider) => selectedSet.has(provider)); selectedProviders.forEach((provider) => { if (!nextOrder.includes(provider)) { nextOrder.push(provider); } }); if (ensureDefault && !nextOrder.length) { const fallbackProvider = getSelectedPhoneSmsProvider(); nextOrder = normalizePhoneSmsProviderOrderValue([fallbackProvider], []); } if (enforceMax && nextOrder.length > selectionLimit) { const droppedCount = nextOrder.length - selectionLimit; nextOrder = nextOrder.slice(0, selectionLimit); if (showLimitToast && droppedCount > 0 && typeof showToast === 'function') { showToast(`服务商顺序最多 ${selectionLimit} 个,已保留前 ${selectionLimit} 个。`, 'warn', 2200); } } const nextOrderSet = new Set(nextOrder); Array.from(selectPhoneSmsProviderOrder.options).forEach((option) => { const provider = normalizePhoneSmsProviderValue(option.value || ''); option.selected = nextOrderSet.has(provider); }); phoneSmsProviderOrderSelection = [...nextOrder]; if (syncProvider && nextOrder.length && selectPhoneSmsProvider) { selectPhoneSmsProvider.value = normalizePhoneSmsProviderValue(nextOrder[0]); } renderPhoneSmsProviderOrderMenu(); updatePhoneSmsProviderOrderSummary(nextOrder); return nextOrder; } function applyPhoneSmsProviderOrderSelection(order = [], options = {}) { const ensureDefault = options.ensureDefault !== false; const syncProvider = Boolean(options.syncProvider); const normalizedOrder = normalizePhoneSmsProviderOrderValue(order, []); phoneSmsProviderOrderSelection = [...normalizedOrder]; if (selectPhoneSmsProviderOrder) { const selectedSet = new Set(normalizedOrder); Array.from(selectPhoneSmsProviderOrder.options).forEach((option) => { const provider = normalizePhoneSmsProviderValue(option.value || ''); option.selected = selectedSet.has(provider); }); } return syncPhoneSmsProviderOrderFromSelect({ ensureDefault, enforceMax: true, syncProvider, showLimitToast: false, }); } function setPhoneSmsProviderOrderPrimary(provider = DEFAULT_PHONE_SMS_PROVIDER) { const normalizedProvider = normalizePhoneSmsProviderValue(provider); const currentOrder = normalizePhoneSmsProviderOrderValue(phoneSmsProviderOrderSelection, []); const nextOrder = [ normalizedProvider, ...currentOrder.filter((entry) => normalizePhoneSmsProviderValue(entry) !== normalizedProvider), ]; return applyPhoneSmsProviderOrderSelection(nextOrder, { ensureDefault: false, syncProvider: false, }); } function clearPhoneSmsProviderOrderSelection(options = {}) { const syncProvider = Boolean(options.syncProvider); phoneSmsProviderOrderSelection = []; if (selectPhoneSmsProviderOrder) { Array.from(selectPhoneSmsProviderOrder.options || []).forEach((option) => { option.selected = false; }); } const nextOrder = syncPhoneSmsProviderOrderFromSelect({ ensureDefault: false, enforceMax: true, syncProvider, showLimitToast: false, }); if (!nextOrder.length && !syncProvider && selectPhoneSmsProvider) { selectPhoneSmsProvider.value = normalizePhoneSmsProviderValue( selectPhoneSmsProvider.value || DEFAULT_PHONE_SMS_PROVIDER ); } return nextOrder; } function getSelectedPhoneSmsProviderOrder() { const normalized = syncPhoneSmsProviderOrderFromSelect({ ensureDefault: false, enforceMax: true, syncProvider: false, showLimitToast: false, }); return normalizePhoneSmsProviderOrderValue(normalized, []); } function getSelectedPhoneSmsProvider() { if (!selectPhoneSmsProvider) { return DEFAULT_PHONE_SMS_PROVIDER; } const normalized = normalizePhoneSmsProviderValue(selectPhoneSmsProvider.value); selectPhoneSmsProvider.value = normalized; return normalized; } function normalizeFiveSimCountryCode(value = '', fallback = 'thailand') { const normalized = String(value || '') .trim() .toLowerCase() .replace(/[^a-z0-9_-]/g, ''); return normalized || fallback; } function normalizeFiveSimCountryOrderValue(value = []) { const source = Array.isArray(value) ? value : String(value || '') .split(/[\r\n,,;;]+/) .map((entry) => String(entry || '').trim()) .filter(Boolean); const normalized = []; const seen = new Set(); source.forEach((entry) => { const code = normalizeFiveSimCountryCode( entry && typeof entry === 'object' && !Array.isArray(entry) ? (entry.code || entry.country || entry.id || '') : entry, '' ); if (!code || seen.has(code)) { return; } seen.add(code); normalized.push(code); }); if (!normalized.length) { return []; } return normalized.slice(0, 10); } function formatFiveSimCountryOrderValue(value = []) { return normalizeFiveSimCountryOrderValue(value).join(','); } function normalizeFiveSimOperatorValue(value = '') { return normalizeFiveSimCountryCode(value, DEFAULT_FIVE_SIM_OPERATOR); } function normalizeFiveSimProductValue(value = '') { return normalizeFiveSimCountryCode(value, DEFAULT_FIVE_SIM_PRODUCT); } function normalizeNexSmsCountryIdValue(value, fallback = 0) { const parsed = Math.floor(Number(value)); if (Number.isFinite(parsed) && parsed >= 0) { return parsed; } const fallbackParsed = Math.floor(Number(fallback)); if (Number.isFinite(fallbackParsed) && fallbackParsed >= 0) { return fallbackParsed; } return 0; } function normalizeNexSmsCountryOrderValue(value = []) { const source = Array.isArray(value) ? value : String(value || '') .split(/[\r\n,,;;]+/) .map((entry) => String(entry || '').trim()) .filter(Boolean); const normalized = []; const seen = new Set(); source.forEach((entry) => { const countryId = normalizeNexSmsCountryIdValue( entry && typeof entry === 'object' && !Array.isArray(entry) ? (entry.id || entry.countryId || entry.country || '') : entry, -1 ); if (countryId < 0 || seen.has(countryId)) { return; } seen.add(countryId); normalized.push(countryId); }); if (!normalized.length) { return []; } return normalized.slice(0, 10); } function normalizeNexSmsServiceCodeValue(value = '') { const normalized = String(value || '') .trim() .toLowerCase() .replace(/[^a-z0-9_-]/g, ''); return normalized || DEFAULT_NEX_SMS_SERVICE_CODE; } function normalizeFiveSimCountryLabel(value = '', fallback = 'Thailand') { return String(value || '').trim() || fallback; } function normalizeFiveSimCountryFallbackList(value = []) { const source = Array.isArray(value) ? value : String(value || '') .split(/[\r\n,,;;]+/) .map((entry) => String(entry || '').trim()) .filter(Boolean); const normalized = []; const seen = new Set(); source.forEach((entry) => { let code = ''; let label = ''; if (entry && typeof entry === 'object' && !Array.isArray(entry)) { code = normalizeFiveSimCountryCode(entry.code || entry.id || entry.country || '', ''); label = normalizeFiveSimCountryLabel(entry.label || entry.countryLabel || ''); } else { code = normalizeFiveSimCountryCode(entry, ''); } if (!code || seen.has(code)) { return; } seen.add(code); normalized.push({ id: code, code, label: label || code, }); }); return normalized; } function normalizeNexSmsCountryLabel(value = '', fallback = '') { return String(value || '').trim() || fallback; } function normalizeNexSmsCountryFallbackList(value = []) { const source = Array.isArray(value) ? value : String(value || '') .split(/[\r\n,,;;]+/) .map((entry) => String(entry || '').trim()) .filter(Boolean); const normalized = []; const seen = new Set(); source.forEach((entry) => { let countryId = -1; let label = ''; if (entry && typeof entry === 'object' && !Array.isArray(entry)) { countryId = normalizeNexSmsCountryIdValue(entry.id || entry.countryId || entry.country || '', -1); label = normalizeNexSmsCountryLabel(entry.label || entry.countryLabel || ''); } else { countryId = normalizeNexSmsCountryIdValue(entry, -1); } if (countryId < 0 || seen.has(countryId)) { return; } seen.add(countryId); normalized.push({ id: countryId, label: label || `Country #${countryId}`, }); }); return normalized; } function getFiveSimCountryDisplayNameByIso(isoCode = '') { const normalizedIso = String(isoCode || '').trim().toUpperCase(); if (!normalizedIso || typeof Intl === 'undefined' || typeof Intl.DisplayNames !== 'function') { return ''; } try { const zhDisplayNames = new Intl.DisplayNames(['zh-CN'], { type: 'region' }); return String(zhDisplayNames.of(normalizedIso) || '').trim(); } catch { return ''; } } function buildFiveSimCountryDisplayLabel(country = {}) { const code = normalizeFiveSimCountryCode(country.code || country.id || country.country || '', 'thailand'); const english = String(country.text_en || country.eng || country.english || code).trim(); const iso = String( country.isoCode || country.iso || country.countryCode || '' ).trim().toUpperCase(); const chinese = getFiveSimCountryDisplayNameByIso(iso); const base = chinese && english ? `${chinese} (${english})` : (chinese || english || code); return iso ? `${base} [${iso}]` : base; } function buildFiveSimCountrySearchText(country = {}, label = '', code = '') { const tokens = new Set(); const iso = String(country.isoCode || country.iso || country.countryCode || '').trim().toUpperCase(); const chinese = getFiveSimCountryDisplayNameByIso(iso); [label, code, iso, chinese, country.text_en, country.english, country.text_ru].forEach((entry) => { const token = String(entry || '').trim(); if (token) { tokens.add(token); } }); return Array.from(tokens).join(' '); } function parseFiveSimCountriesPayload(payload = {}) { if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { return []; } const entries = Object.entries(payload) .map(([countryCode, details]) => { const isoObject = details?.iso && typeof details.iso === 'object' ? details.iso : {}; const isoCode = Object.keys(isoObject)[0] || ''; const normalizedCode = normalizeFiveSimCountryCode(countryCode, ''); if (!normalizedCode) { return null; } const option = { code: normalizedCode, id: normalizedCode, isoCode: String(isoCode || '').trim().toUpperCase(), text_en: String(details?.text_en || normalizedCode).trim(), }; const label = buildFiveSimCountryDisplayLabel(option); return { id: normalizedCode, code: normalizedCode, isoCode: option.isoCode, label, searchText: buildFiveSimCountrySearchText(option, label, normalizedCode), }; }) .filter(Boolean); return entries.sort((left, right) => String(left.label || '').localeCompare(String(right.label || ''))); } function normalizePhoneVerificationReplacementLimit(value, fallback = DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT) { const rawValue = String(value ?? '').trim(); const parsed = Number.parseInt(rawValue, 10); if (!Number.isFinite(parsed)) { return Math.max( PHONE_REPLACEMENT_LIMIT_MIN, Math.min(PHONE_REPLACEMENT_LIMIT_MAX, Number(fallback) || DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT) ); } return Math.max(PHONE_REPLACEMENT_LIMIT_MIN, Math.min(PHONE_REPLACEMENT_LIMIT_MAX, parsed)); } function normalizePhoneCodeWaitSecondsValue(value, fallback = DEFAULT_PHONE_CODE_WAIT_SECONDS) { const rawValue = String(value ?? '').trim(); const parsed = Number.parseInt(rawValue, 10); if (!Number.isFinite(parsed)) { return Math.max( PHONE_CODE_WAIT_SECONDS_MIN, Math.min(PHONE_CODE_WAIT_SECONDS_MAX, Number(fallback) || DEFAULT_PHONE_CODE_WAIT_SECONDS) ); } return Math.max(PHONE_CODE_WAIT_SECONDS_MIN, Math.min(PHONE_CODE_WAIT_SECONDS_MAX, parsed)); } function normalizePhoneCodeTimeoutWindowsValue(value, fallback = DEFAULT_PHONE_CODE_TIMEOUT_WINDOWS) { const rawValue = String(value ?? '').trim(); const parsed = Number.parseInt(rawValue, 10); if (!Number.isFinite(parsed)) { return Math.max( PHONE_CODE_TIMEOUT_WINDOWS_MIN, Math.min(PHONE_CODE_TIMEOUT_WINDOWS_MAX, Number(fallback) || DEFAULT_PHONE_CODE_TIMEOUT_WINDOWS) ); } return Math.max(PHONE_CODE_TIMEOUT_WINDOWS_MIN, Math.min(PHONE_CODE_TIMEOUT_WINDOWS_MAX, parsed)); } function normalizePhoneCodePollIntervalSecondsValue(value, fallback = DEFAULT_PHONE_CODE_POLL_INTERVAL_SECONDS) { const rawValue = String(value ?? '').trim(); const parsed = Number.parseInt(rawValue, 10); if (!Number.isFinite(parsed)) { return Math.max( PHONE_CODE_POLL_INTERVAL_SECONDS_MIN, Math.min(PHONE_CODE_POLL_INTERVAL_SECONDS_MAX, Number(fallback) || DEFAULT_PHONE_CODE_POLL_INTERVAL_SECONDS) ); } return Math.max(PHONE_CODE_POLL_INTERVAL_SECONDS_MIN, Math.min(PHONE_CODE_POLL_INTERVAL_SECONDS_MAX, parsed)); } function normalizePhoneCodePollMaxRoundsValue(value, fallback = DEFAULT_PHONE_CODE_POLL_MAX_ROUNDS) { const rawValue = String(value ?? '').trim(); const parsed = Number.parseInt(rawValue, 10); if (!Number.isFinite(parsed)) { return Math.max( PHONE_CODE_POLL_MAX_ROUNDS_MIN, Math.min(PHONE_CODE_POLL_MAX_ROUNDS_MAX, Number(fallback) || DEFAULT_PHONE_CODE_POLL_MAX_ROUNDS) ); } return Math.max(PHONE_CODE_POLL_MAX_ROUNDS_MIN, Math.min(PHONE_CODE_POLL_MAX_ROUNDS_MAX, parsed)); } function normalizeHeroSmsReuseEnabledValue(value, fallbackValue = undefined) { if (value === undefined || value === null) { if (fallbackValue !== undefined && fallbackValue !== null) { return Boolean(fallbackValue); } return DEFAULT_HERO_SMS_REUSE_ENABLED; } return Boolean(value); } function normalizeHeroSmsAcquirePriority(value = '') { const normalized = String(value || '').trim().toLowerCase(); if (normalized === HERO_SMS_ACQUIRE_PRIORITY_PRICE) { return HERO_SMS_ACQUIRE_PRIORITY_PRICE; } if (normalized === HERO_SMS_ACQUIRE_PRIORITY_PRICE_HIGH) { return HERO_SMS_ACQUIRE_PRIORITY_PRICE_HIGH; } return HERO_SMS_ACQUIRE_PRIORITY_COUNTRY; } function normalizeHeroSmsOperatorValue(value = '', fallback = DEFAULT_HERO_SMS_OPERATOR) { const normalized = String(value || '') .trim() .toLowerCase() .replace(/[^a-z0-9_-]+/g, ''); if (normalized) { return normalized; } const fallbackNormalized = String(fallback || '') .trim() .toLowerCase() .replace(/[^a-z0-9_-]+/g, ''); return fallbackNormalized || DEFAULT_HERO_SMS_OPERATOR; } function normalizeHeroSmsCountryFallbackList(value = []) { const source = Array.isArray(value) ? value : String(value || '') .split(/[\r\n,,;;]+/) .map((entry) => String(entry || '').trim()) .filter(Boolean); const seen = new Set(); const normalized = []; source.forEach((entry) => { let id = 0; let label = ''; if (entry && typeof entry === 'object' && !Array.isArray(entry)) { const parsedId = Math.floor(Number(entry.id ?? entry.countryId)); id = Number.isFinite(parsedId) && parsedId > 0 ? parsedId : 0; label = normalizeHeroSmsCountryLabel(entry.label ?? entry.countryLabel); } else { const text = String(entry || '').trim(); const structured = text.match(/^(\d+)\s*(?:[:|/-]\s*(.+))?$/); if (structured) { const parsedId = Math.floor(Number(structured[1])); id = Number.isFinite(parsedId) && parsedId > 0 ? parsedId : 0; label = normalizeHeroSmsCountryLabel(structured[2]); } else { const parsedId = Math.floor(Number(text)); id = Number.isFinite(parsedId) && parsedId > 0 ? parsedId : 0; } } if (!id || seen.has(id)) { return; } seen.add(id); normalized.push({ id, label: label || `Country #${id}`, }); }); return normalized; } function collectHeroSmsCountrySearchTokens(value, tokens, depth = 0) { if (depth > 2 || value === null || value === undefined) { return; } if (typeof value === 'string') { const normalized = value.trim(); if (normalized) { tokens.add(normalized); } return; } if (Array.isArray(value)) { value.forEach((entry) => collectHeroSmsCountrySearchTokens(entry, tokens, depth + 1)); return; } if (typeof value === 'object') { Object.values(value).forEach((entry) => collectHeroSmsCountrySearchTokens(entry, tokens, depth + 1)); } } function normalizeHeroSmsCountryAliasKey(value = '') { return String(value || '') .trim() .toLowerCase() .replace(/[’'`]/g, '') .replace(/&/g, ' and ') .replace(/\(.*?\)/g, ' ') .replace(/[^a-z0-9]+/g, ' ') .replace(/\s+/g, ' ') .trim(); } function collectHeroSmsCountryCodeAliases(country = {}, label = '') { const aliases = new Set(); const candidateLabels = [ String(label || '').trim(), String(country?.eng || '').trim(), String(country?.name || '').trim(), String(country?.country || '').trim(), ].filter(Boolean); candidateLabels.forEach((candidate) => { const normalized = normalizeHeroSmsCountryAliasKey(candidate); if (!normalized) { return; } const code = HERO_SMS_COUNTRY_ISO_CODE_BY_NAME.get(normalized); if (code) { aliases.add(code); } const overrideAliases = HERO_SMS_COUNTRY_CODE_ALIAS_OVERRIDES[normalized]; if (Array.isArray(overrideAliases)) { overrideAliases.forEach((entry) => { const token = String(entry || '').trim().toUpperCase(); if (token) { aliases.add(token); } }); } }); return Array.from(aliases); } function buildHeroSmsCountrySearchText(country = {}, label = '', id = '') { const tokens = new Set(); collectHeroSmsCountrySearchTokens(country, tokens, 0); if (label) { tokens.add(String(label).trim()); } if (id) { tokens.add(String(id).trim()); } collectHeroSmsCountryCodeAliases(country, label).forEach((alias) => tokens.add(alias)); return Array.from(tokens).join(' '); } function buildHeroSmsCountryDisplayLabel(country = {}) { const english = String(country?.eng || '').trim(); const chinese = String(country?.chn || '').trim(); if (chinese && english) { if (chinese.toLowerCase() === english.toLowerCase()) { return english; } return `${chinese} (${english})`; } return chinese || english; } function normalizeHeroSmsFetchErrorMessage(error) { const message = String(error?.message || error || '').trim(); if (!message) { return '未知网络错误'; } if (/aborted|abort|timed out|timeout/i.test(message)) { return '请求超时,请稍后重试'; } if (/failed to fetch|networkerror|network request failed/i.test(message)) { return '网络不可用或被拦截'; } return message; } function normalizeHeroSmsCountryPayloadEntries(source) { if (Array.isArray(source)) { return source; } if (!source || typeof source !== 'object') { return []; } return Object.values(source) .filter((entry) => entry && typeof entry === 'object' && !Array.isArray(entry)); } function parseHeroSmsCountryPayload(payload) { const candidateSources = [ payload?.value, payload?.data, payload?.countries, payload?.result, payload, ]; for (const source of candidateSources) { const entries = normalizeHeroSmsCountryPayloadEntries(source); if (entries.length) { return entries; } } return []; } function parseHeroSmsOperatorsPayload(payload) { const source = payload?.countryOperators || payload?.operators || payload?.data || payload?.result || payload; if (!source || typeof source !== 'object' || Array.isArray(source)) { return new Map(); } const result = new Map(); Object.entries(source).forEach(([countryId, operators]) => { const normalizedCountryId = Math.floor(Number(countryId)); if (!Number.isFinite(normalizedCountryId) || normalizedCountryId <= 0 || !Array.isArray(operators)) { return; } const seen = new Set(); const normalizedOperators = operators .map((operator) => normalizeHeroSmsOperatorValue(operator, '')) .filter(Boolean) .filter((operator) => { if (seen.has(operator)) { return false; } seen.add(operator); return true; }); if (normalizedOperators.length) { result.set(String(normalizedCountryId), normalizedOperators); } }); return result; } function normalizeHeroSmsPriceForPreview(value) { const direct = Number(value); if (Number.isFinite(direct) && direct >= 0) { return direct; } const text = String(value ?? '').trim(); if (!text) { return null; } const matched = text.match(/-?\d+(?:[.,]\d+)?/); if (!matched) { return null; } const parsed = Number(String(matched[0] || '').replace(',', '.')); if (!Number.isFinite(parsed) || parsed < 0) { return null; } return parsed; } function formatHeroSmsPriceForPreview(value) { const price = Number(value); if (!Number.isFinite(price) || price < 0) { return ''; } const rounded = Math.round(price * 10000) / 10000; return rounded.toFixed(4).replace(/\.?0+$/, ''); } function isHeroSmsPreviewEmptyPayload(payload) { if (payload === undefined || payload === null) { return true; } if (typeof payload === 'string') { return !payload.trim(); } if (Array.isArray(payload)) { return payload.length === 0; } if (typeof payload === 'object') { return Object.keys(payload).length === 0; } return false; } function collectHeroSmsPriceEntriesForPreview(payload, entries = []) { if (Array.isArray(payload)) { payload.forEach((entry) => collectHeroSmsPriceEntriesForPreview(entry, entries)); return entries; } if (!payload || typeof payload !== 'object') { return entries; } const cost = normalizeHeroSmsPriceForPreview(payload.cost ?? payload.price ?? payload.Price); if (cost !== null) { const count = Number(payload.count ?? payload.qty ?? payload.Qty); const physicalCount = Number(payload.physicalCount); const hasCount = Number.isFinite(count); const hasPhysicalCount = Number.isFinite(physicalCount); const stockCount = hasPhysicalCount ? physicalCount : (hasCount ? count : 0); const hasStockField = hasCount || hasPhysicalCount; entries.push({ cost, hasStockField, stockCount: Number.isFinite(stockCount) ? stockCount : 0, inStock: !hasStockField || stockCount > 0, }); } const pushTierMapEntries = (tierMap) => { if (!tierMap || typeof tierMap !== 'object') { return; } Object.entries(tierMap).forEach(([priceKey, countRaw]) => { const priceValue = normalizeHeroSmsPriceForPreview(priceKey); if (priceValue === null) { return; } const stockCount = Number(countRaw); if (!Number.isFinite(stockCount)) { return; } entries.push({ cost: priceValue, hasStockField: true, stockCount: Math.max(0, stockCount), inStock: stockCount > 0, }); }); }; pushTierMapEntries(payload.freePriceMap); pushTierMapEntries(payload.priceMap); // Some HeroSMS payloads encode tiers as object keys, e.g.: // { "0.05": { count: 0 }, "0.35": { count: 12 } } // Parse those keyed tiers as well, so preview does not miss higher-price stock. Object.entries(payload).forEach(([key, value]) => { const keyedPrice = normalizeHeroSmsPriceForPreview(key); if (keyedPrice === null) { return; } if (value && typeof value === 'object') { const stockCandidates = [ value.count, value.physicalCount, value.stock, value.available, value.quantity, value.qty, value.left, value.free, ] .map((raw) => Number(raw)) .filter((raw) => Number.isFinite(raw)); const hasStockField = stockCandidates.length > 0; const trimmedKey = String(key || '').trim(); const looksLikeCountryOrServiceKey = /^[0-9]{1,4}$/.test(trimmedKey); const looksLikeDecimalPriceKey = /^-?\d+[.,]\d+$/.test(trimmedKey) || /^[^\d-]*-?\d+[.,]\d+[^\d]*$/.test(trimmedKey); // Guard against country/service IDs encoded as numeric keys (e.g. "52", "16"). // For numeric keys we require explicit stock fields, while decimal price keys can // still be consumed even when the provider only returns a scalar quantity. if (looksLikeCountryOrServiceKey && !looksLikeDecimalPriceKey) { return; } const stockCount = hasStockField ? Math.max(...stockCandidates) : 0; entries.push({ cost: keyedPrice, hasStockField, stockCount: Number.isFinite(stockCount) ? stockCount : 0, inStock: stockCount > 0, }); return; } const numericCount = Number(value); if (Number.isFinite(numericCount)) { entries.push({ cost: keyedPrice, hasStockField: true, stockCount: Math.max(0, numericCount), inStock: numericCount > 0, }); } }); Object.values(payload).forEach((entry) => collectHeroSmsPriceEntriesForPreview(entry, entries)); return entries; } function collectHeroSmsPriceEntriesFromTopCountriesPayload(payload, countryId, entries = []) { const normalizedCountryId = normalizeHeroSmsCountryId(countryId, 0); if (normalizedCountryId <= 0) { return entries; } if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { return entries; } Object.values(payload).forEach((entry) => { if (!entry || typeof entry !== 'object' || Array.isArray(entry)) { return; } const entryCountryId = normalizeHeroSmsCountryId( entry.country ?? entry.countryId ?? entry.country_id ?? entry.id, 0 ); if (entryCountryId !== normalizedCountryId) { return; } collectHeroSmsPriceEntriesForPreview(entry, entries); }); return entries; } function collectHeroSmsPriceEntriesFromVerificationPayload(payload, countryId, serviceCode = 'dr', entries = []) { const normalizedCountryId = normalizeHeroSmsCountryId(countryId, 0); if (normalizedCountryId <= 0) { return entries; } if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { return entries; } const normalizedServiceCode = String(serviceCode || '').trim(); if (!normalizedServiceCode) { return entries; } const serviceNode = payload?.[normalizedServiceCode]; if (!serviceNode || typeof serviceNode !== 'object' || Array.isArray(serviceNode)) { return entries; } const countryNode = serviceNode?.[String(normalizedCountryId)]; if (!countryNode || typeof countryNode !== 'object' || Array.isArray(countryNode)) { return entries; } const priceValue = normalizeHeroSmsPriceForPreview(countryNode.price); if (priceValue === null) { return entries; } const stockCount = Number(countryNode.count); entries.push({ cost: priceValue, hasStockField: Number.isFinite(stockCount), stockCount: Number.isFinite(stockCount) ? Math.max(0, stockCount) : 0, inStock: !Number.isFinite(stockCount) || stockCount > 0, }); return entries; } function collectHeroSmsPriceCandidatesForPreview(payload, candidates = []) { collectHeroSmsPriceEntriesForPreview(payload, []) .filter((entry) => entry.inStock) .forEach((entry) => { candidates.push(entry.cost); }); return candidates; } function formatPhoneSmsPriceEntriesSummary(payload) { const entries = collectHeroSmsPriceEntriesForPreview(payload, []) .filter((entry) => Number.isFinite(Number(entry.cost)) && Number(entry.cost) > 0); const inStockPrices = Array.from(new Set( entries .filter((entry) => entry.inStock) .map((entry) => Math.round(Number(entry.cost) * 10000) / 10000) )).sort((left, right) => left - right); const allPrices = Array.from(new Set( entries.map((entry) => Math.round(Number(entry.cost) * 10000) / 10000) )).sort((left, right) => left - right); return { entries, inStockPrices, allPrices }; } function describeHeroSmsPreviewPayload(payload) { if (payload === undefined || payload === null) { return ''; } if (typeof payload === 'string') { return payload.trim(); } if (typeof payload === 'number' || typeof payload === 'boolean') { return String(payload); } if (typeof payload === 'object') { const directMessage = String( payload.message || payload.msg || payload.error || payload.title || payload.statusText || '' ).trim(); if (directMessage) { const extra = String(payload?.info?.text || payload?.info?.description || '').trim(); return extra ? `${directMessage}: ${extra}` : directMessage; } try { return JSON.stringify(payload); } catch { return '[object]'; } } return String(payload); } function summarizeHeroSmsPreviewError(payload, responseStatus = 0) { if (isHeroSmsPreviewEmptyPayload(payload)) { return '未返回有效价格'; } const text = describeHeroSmsPreviewPayload(payload); if (text === '{}' || text === '[]') { return '未返回有效价格'; } if (/UNPROCESSABLE_ENTITY/i.test(text) && /api_key/i.test(text) && /REQUIRED/i.test(text)) { return '请先填写接码 API Key'; } if (/BAD_KEY|WRONG_KEY|INVALID_KEY/i.test(text)) { return 'API Key 无效'; } if (/NO_BALANCE|NOT_ENOUGH_BALANCE/i.test(text)) { return '余额不足'; } if (/BANNED|ACCOUNT_BANNED/i.test(text)) { return '账号已被封禁'; } if (/WRONG_SERVICE|SERVICE_NOT_FOUND/i.test(text)) { return '服务代码无效'; } if (/WRONG_COUNTRY|COUNTRY_NOT_FOUND/i.test(text)) { return '国家参数无效'; } if (/NO_NUMBERS/i.test(text)) { return '暂无可用号源'; } if (responseStatus && responseStatus >= 400) { return `HTTP ${responseStatus}`; } return text || '未知错误'; } function resolvePhoneSmsPricePreviewRange(provider = '') { const activeProvider = provider || ( typeof getSelectedPhoneSmsProvider === 'function' ? getSelectedPhoneSmsProvider() : (typeof DEFAULT_PHONE_SMS_PROVIDER !== 'undefined' ? DEFAULT_PHONE_SMS_PROVIDER : 'hero-sms') ); const normalizedProvider = normalizePhoneSmsProviderValue(activeProvider); const rawMinPrice = normalizedProvider === PHONE_SMS_PROVIDER_MADAO ? (typeof inputMaDaoMinPrice !== 'undefined' && inputMaDaoMinPrice ? inputMaDaoMinPrice.value : '') : (typeof inputHeroSmsMinPrice !== 'undefined' && inputHeroSmsMinPrice ? inputHeroSmsMinPrice.value : ''); const rawMaxPrice = normalizedProvider === PHONE_SMS_PROVIDER_MADAO ? (typeof inputMaDaoMaxPrice !== 'undefined' && inputMaDaoMaxPrice ? inputMaDaoMaxPrice.value : '') : (typeof inputHeroSmsMaxPrice !== 'undefined' && inputHeroSmsMaxPrice ? inputHeroSmsMaxPrice.value : ''); const minPriceText = typeof normalizePhoneSmsMinPriceValue === 'function' ? normalizePhoneSmsMinPriceValue(rawMinPrice, activeProvider) : normalizeHeroSmsMaxPriceValue(rawMinPrice); const maxPriceText = typeof normalizePhoneSmsMaxPriceValue === 'function' ? normalizePhoneSmsMaxPriceValue(rawMaxPrice, activeProvider) : normalizeHeroSmsMaxPriceValue(rawMaxPrice); const minPrice = minPriceText ? Number(minPriceText) : null; const maxPrice = maxPriceText ? Number(maxPriceText) : null; return { minPrice, maxPrice, hasMinPrice: Number.isFinite(minPrice) && minPrice > 0, hasMaxPrice: Number.isFinite(maxPrice) && maxPrice > 0, invalid: Number.isFinite(minPrice) && minPrice > 0 && Number.isFinite(maxPrice) && maxPrice > 0 && minPrice > maxPrice, }; } function isPhoneSmsPriceWithinPreviewRange(price, range = {}) { const numeric = Number(price); if (!Number.isFinite(numeric) || numeric <= 0 || range?.invalid) { return false; } const normalized = Math.round(numeric * 10000) / 10000; if (range?.hasMinPrice && normalized < Number(range.minPrice)) { return false; } if (range?.hasMaxPrice && normalized > Number(range.maxPrice)) { return false; } return true; } function filterPhoneSmsPriceEntriesForPreviewRange(entries = [], range = {}) { if (!range?.hasMinPrice && !range?.hasMaxPrice && !range?.invalid) { return Array.isArray(entries) ? [...entries] : []; } return (Array.isArray(entries) ? entries : []).filter((entry) => ( isPhoneSmsPriceWithinPreviewRange(entry?.price ?? entry?.cost, range) )); } function filterPhoneSmsPriceValuesForPreviewRange(values = [], range = {}) { if (!range?.hasMinPrice && !range?.hasMaxPrice && !range?.invalid) { return Array.isArray(values) ? [...values] : []; } return (Array.isArray(values) ? values : []).filter((price) => ( isPhoneSmsPriceWithinPreviewRange(price, range) )); } function formatPhoneSmsPriceRangePreviewText(range = {}) { const minText = range?.hasMinPrice ? (formatHeroSmsPriceForPreview(range.minPrice) || String(range.minPrice)) : ''; const maxText = range?.hasMaxPrice ? (formatHeroSmsPriceForPreview(range.maxPrice) || String(range.maxPrice)) : ''; if (minText && maxText) { return `${minText}~${maxText}`; } if (minText) { return `${minText}~`; } if (maxText) { return `~${maxText}`; } return ''; } function buildPhoneSmsPriceRangePreviewMessage(range = {}) { const rangeText = formatPhoneSmsPriceRangePreviewText(range); if (range?.invalid) { return `价格区间无效:最低购买价 ${formatHeroSmsPriceForPreview(range.minPrice) || range.minPrice} 高于价格上限 ${formatHeroSmsPriceForPreview(range.maxPrice) || range.maxPrice}`; } return rangeText ? `区间内无可用号源(当前 ${rangeText})` : '暂无可用号源'; } function formatPriceTiersForPreview(entries = [], options = {}) { const maxPrice = Number(options?.maxPrice); const hasMaxPrice = Number.isFinite(maxPrice) && maxPrice > 0; const normalized = Array.from( new Map( (Array.isArray(entries) ? entries : []) .map((entry) => { const price = Number(entry?.price ?? entry?.cost); if (!Number.isFinite(price) || price <= 0) { return null; } const normalizedPrice = Math.round(price * 10000) / 10000; const countRaw = Number(entry?.count); const count = Number.isFinite(countRaw) ? Math.max(0, Math.floor(countRaw)) : null; return [normalizedPrice, count]; }) .filter(Boolean) ).entries() ) .map(([price, count]) => ({ price: Number(price), count })) .sort((left, right) => left.price - right.price); if (!normalized.length) { return ''; } const limit = 16; const displayed = normalized.slice(0, limit); const suffix = normalized.length > limit ? ` ... +${normalized.length - limit} 档` : ''; return displayed.map((entry) => { const priceText = formatHeroSmsPriceForPreview(entry.price) || String(entry.price); const countText = entry.count === null ? '' : `x${entry.count}`; const overLimitText = hasMaxPrice && entry.price > maxPrice ? '↑' : ''; return `${priceText}${countText ? `(${countText})` : ''}${overLimitText}`; }).join(', ') + suffix; } function formatPriceTiersWithZeroStockForPreview(entries = [], options = {}) { const maxPrice = Number(options?.maxPrice); const hasMaxPrice = Number.isFinite(maxPrice) && maxPrice > 0; const normalized = Array.from( new Map( (Array.isArray(entries) ? entries : []) .map((entry) => { const price = Number(entry?.price ?? entry?.cost); if (!Number.isFinite(price) || price <= 0) { return null; } const normalizedPrice = Math.round(price * 10000) / 10000; const countRaw = Number(entry?.count); const count = Number.isFinite(countRaw) ? Math.max(0, Math.floor(countRaw)) : null; return [normalizedPrice, count]; }) .filter(Boolean) ).entries() ) .map(([price, count]) => ({ price: Number(price), count })) .sort((left, right) => left.price - right.price); if (!normalized.length) { return ''; } const limit = 16; const displayed = normalized.slice(0, limit); const suffix = normalized.length > limit ? ` ... +${normalized.length - limit} 档` : ''; return displayed.map((entry) => { const priceText = formatHeroSmsPriceForPreview(entry.price) || String(entry.price); const countText = entry.count === null ? '' : `x${entry.count}`; const overLimitText = hasMaxPrice && entry.price > maxPrice ? '↑' : ''; return `${priceText}${countText ? `(${countText})` : ''}${overLimitText}`; }).join(', ') + suffix; } function describeNexSmsPreviewPayload(payload) { if (payload === undefined || payload === null) { return ''; } if (typeof payload === 'string') { return payload.trim(); } if (typeof payload === 'number' || typeof payload === 'boolean') { return String(payload); } if (typeof payload === 'object') { const directMessage = String( payload.message || payload.msg || payload.error || payload.title || payload.statusText || payload?.data?.message || payload?.data?.error || '' ).trim(); if (directMessage) { return directMessage; } try { return JSON.stringify(payload); } catch { return '[object]'; } } return String(payload); } function getSelectedHeroSmsCountryOption() { const selectedCountries = syncHeroSmsFallbackSelectionOrderFromSelect({ enforceMax: true, ensureDefault: false, showLimitToast: false, }); if (selectedCountries.length) { return selectedCountries[0]; } return isFiveSimProviderSelected() ? { id: DEFAULT_FIVE_SIM_COUNTRY_ID, label: DEFAULT_FIVE_SIM_COUNTRY_LABEL } : { id: DEFAULT_HERO_SMS_COUNTRY_ID, label: DEFAULT_HERO_SMS_COUNTRY_LABEL }; } function getFiveSimCountryOptionLabel(code = '') { const normalizedCode = normalizeFiveSimCountryCode(code, ''); if (!normalizedCode) { return ''; } const matched = Array.from(selectFiveSimCountry?.options || []) .find((option) => normalizeFiveSimCountryCode(option.value, '') === normalizedCode); if (matched) { return normalizeFiveSimCountryLabel(matched.textContent, normalizedCode); } const fallback = FIVE_SIM_SUPPORTED_COUNTRY_ITEMS.find((item) => item.id === normalizedCode); return fallback ? formatFiveSimCountryDisplayLabel(fallback.id, fallback.eng, fallback.id) : normalizedCode; } function getNexSmsCountryLabelById(id) { const countryId = normalizeNexSmsCountryIdValue(id, -1); if (countryId < 0) { return ''; } const matched = Array.from(selectNexSmsCountry?.options || []) .find((option) => normalizeNexSmsCountryIdValue(option.value, -1) === countryId); return normalizeNexSmsCountryLabel(matched?.textContent || '', `Country #${countryId}`); } function getSelectedFiveSimCountries() { return syncFiveSimCountrySelectionOrderFromSelect({ enforceMax: true, ensureDefault: false, showLimitToast: false, }); } function getSelectedNexSmsCountries() { return syncNexSmsCountrySelectionOrderFromSelect({ enforceMax: true, ensureDefault: false, showLimitToast: false, }); } function getSelectedSmsBowerCountries() { const selected = Array.from(selectSmsBowerCountry?.options || []) .filter((option) => option.selected) .map((option) => ({ id: normalizeSmsBowerCountryIdValue(option.value, -1), label: normalizeSmsBowerCountryLabelValue(option.textContent, `Country #${option.value}`), })) .filter((country) => country.id >= 0); return selected.length ? selected : [{ id: 6, label: 'Indonesia' }]; } function renderSmsBowerCountryFallbackOrder(countries = getSelectedSmsBowerCountries()) { if (!displaySmsBowerCountryFallbackOrder) { return; } const normalized = normalizeSmsBowerCountryOrderValue(countries); displaySmsBowerCountryFallbackOrder.textContent = normalized.length ? normalized.map((country, index) => `${index + 1}. ${country.label}(${country.id})`).join(' → ') : 'Indonesia #6'; } function updateHeroSmsPlatformDisplay() { if (!displayHeroSmsPlatform) { return; } const provider = getSelectedPhoneSmsProvider(); const selected = provider === PHONE_SMS_PROVIDER_FIVE_SIM ? (getSelectedFiveSimCountries()[0] || { id: DEFAULT_FIVE_SIM_COUNTRY_ID, label: DEFAULT_FIVE_SIM_COUNTRY_LABEL }) : (provider === PHONE_SMS_PROVIDER_NEXSMS ? (getSelectedNexSmsCountries()[0] || { id: DEFAULT_NEX_SMS_COUNTRY_ORDER[0], label: `Country #${DEFAULT_NEX_SMS_COUNTRY_ORDER[0]}` }) : (provider === PHONE_SMS_PROVIDER_SMS_BOWER ? (getSelectedSmsBowerCountries()[0] || { id: 6, label: 'Indonesia' }) : (provider === PHONE_SMS_PROVIDER_MADAO ? { id: '', label: normalizeMaDaoModeValue(selectMaDaoMode?.value || latestState?.madaoMode) === MADAO_MODE_DIRECT ? 'direct' : getSelectedMaDaoRoutingPlanLabel() } : getSelectedHeroSmsCountryOption()))); const countryText = selected?.label ? ` / ${selected.label}` : ''; displayHeroSmsPlatform.textContent = `${getPhoneSmsProviderLabel(provider)} / OpenAI${countryText}`; if (inputHeroSmsApiKey) { inputHeroSmsApiKey.placeholder = provider === PHONE_SMS_PROVIDER_FIVE_SIM ? '请输入 5sim API Key' : (provider === PHONE_SMS_PROVIDER_NEXSMS ? '请输入 NexSMS API Key' : '请输入 HeroSMS API Key'); } } function getHeroSmsCountryLabelById(id) { const targetId = String(id || '').trim(); const countrySelect = selectHeroSmsCountry || selectHeroSmsCountryFallback; if (!targetId || !countrySelect) { return ''; } const matched = Array.from(countrySelect.options).find((option) => option.value === targetId); return String(matched?.textContent || '').trim() || (isFiveSimProviderSelected() ? targetId : `Country #${targetId}`); } function renderHeroSmsCountryFallbackOrder(countries = []) { if (!displayHeroSmsCountryFallbackOrder) { return; } displayHeroSmsCountryFallbackOrder.textContent = ''; const normalized = isFiveSimProviderSelected() ? normalizeFiveSimCountryFallbackList(countries) : normalizeHeroSmsCountryFallbackList(countries); if (!normalized.length) { displayHeroSmsCountryFallbackOrder.textContent = '未设置'; return; } normalized.forEach((country, index) => { const chip = document.createElement('span'); chip.className = 'country-order-chip'; const label = document.createElement('span'); label.className = 'country-order-chip-label'; label.textContent = `${index + 1}. ${country.label}(${country.id})`; const removeBtn = document.createElement('button'); removeBtn.type = 'button'; removeBtn.className = 'country-order-remove'; removeBtn.title = `移除 ${country.label}`; removeBtn.setAttribute('aria-label', `移除 ${country.label}`); removeBtn.textContent = '×'; removeBtn.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); removeHeroSmsCountryFromOrder(country.id); }); chip.appendChild(label); chip.appendChild(removeBtn); displayHeroSmsCountryFallbackOrder.appendChild(chip); if (index < normalized.length - 1) { const separator = document.createElement('span'); separator.className = 'country-order-separator'; separator.textContent = '→'; displayHeroSmsCountryFallbackOrder.appendChild(separator); } }); } function setHeroSmsCountryMenuOpen(open) { const nextOpen = Boolean(open); if (btnHeroSmsCountryMenu) { btnHeroSmsCountryMenu.setAttribute('aria-expanded', String(nextOpen)); } if (heroSmsCountryMenu) { heroSmsCountryMenu.hidden = !nextOpen; if (nextOpen) { const searchInput = heroSmsCountryMenu.querySelector('.hero-sms-country-menu-search-input'); if (searchInput) { // Always reset previous keyword on open to avoid accidental "empty list" state. heroSmsCountryMenuSearchKeyword = ''; searchInput.value = ''; applyHeroSmsCountryMenuFilter(''); setTimeout(() => { searchInput.focus(); searchInput.select(); }, 0); } } } } function applyHeroSmsCountryMenuFilter(keyword = '') { if (!heroSmsCountryMenu) { return; } const normalizedKeyword = String(keyword || '').trim().toLowerCase(); const items = Array.from(heroSmsCountryMenu.querySelectorAll('.hero-sms-country-menu-item')); let visibleCount = 0; items.forEach((item) => { const haystack = String(item.dataset.searchText || '').toLowerCase(); const visible = !normalizedKeyword || haystack.includes(normalizedKeyword); item.hidden = !visible; if (visible) { visibleCount += 1; } }); let empty = heroSmsCountryMenu.querySelector('.hero-sms-country-menu-empty'); if (visibleCount === 0) { if (!empty) { empty = document.createElement('span'); empty.className = 'data-value hero-sms-country-menu-empty'; empty.textContent = '没有匹配国家'; heroSmsCountryMenu.appendChild(empty); } } else if (empty) { empty.remove(); } } function updateHeroSmsCountryMenuSummary(selectedCountries = []) { if (!btnHeroSmsCountryMenu) { return; } const normalized = isFiveSimProviderSelected() ? normalizeFiveSimCountryFallbackList(selectedCountries) : normalizeHeroSmsCountryFallbackList(selectedCountries); if (!normalized.length) { btnHeroSmsCountryMenu.textContent = `\u672a\u9009\u62e9 (0/${HERO_SMS_COUNTRY_SELECTION_MAX})`; return; } const labels = normalized.map((country) => country.label); btnHeroSmsCountryMenu.textContent = `${labels.join(' / ')} (${normalized.length}/${HERO_SMS_COUNTRY_SELECTION_MAX})`; } function renderHeroSmsCountryChoiceButtons() { if (!heroSmsCountryMenu || !selectHeroSmsCountry) { return; } const options = Array.from(selectHeroSmsCountry.options || []); const selectedOrder = [...heroSmsCountrySelectionOrder]; const selectedSet = new Set(selectedOrder.map((id) => String(id))); heroSmsCountryMenu.innerHTML = ''; const searchWrap = document.createElement('div'); searchWrap.className = 'hero-sms-country-menu-search'; const searchInput = document.createElement('input'); searchInput.type = 'search'; searchInput.className = 'data-input mono hero-sms-country-menu-search-input'; searchInput.placeholder = '搜索国家(中/英/代码/ID)'; searchInput.value = heroSmsCountryMenuSearchKeyword; searchInput.addEventListener('input', () => { heroSmsCountryMenuSearchKeyword = String(searchInput.value || '').trim(); applyHeroSmsCountryMenuFilter(heroSmsCountryMenuSearchKeyword); }); searchWrap.appendChild(searchInput); heroSmsCountryMenu.appendChild(searchWrap); if (!options.length) { const empty = document.createElement('span'); empty.className = 'data-value hero-sms-country-menu-empty'; empty.textContent = '暂无国家选项'; heroSmsCountryMenu.appendChild(empty); updateHeroSmsCountryMenuSummary([]); return; } options.forEach((option) => { const countryId = String(option.value || '').trim(); if (!countryId) { return; } const item = document.createElement('button'); item.type = 'button'; item.className = 'header-dropdown-item hero-sms-country-menu-item'; const active = selectedSet.has(countryId); const orderIndex = active ? selectedOrder.findIndex((id) => String(id) === countryId) + 1 : 0; const label = String(option.textContent || '').trim() || `Country #${countryId}`; item.classList.toggle('is-active', active); const labelText = document.createElement('span'); labelText.className = 'hero-sms-country-menu-item-label'; labelText.textContent = label; const badge = document.createElement('span'); badge.className = 'hero-sms-country-menu-item-badge'; badge.textContent = active ? `✓ ${orderIndex}` : ''; item.appendChild(labelText); item.appendChild(badge); item.dataset.searchText = `${label} ${countryId} ${heroSmsCountrySearchTextById.get(countryId) || ''}`; item.addEventListener('click', () => { option.selected = !option.selected; syncHeroSmsFallbackSelectionOrderFromSelect({ enforceMax: true, ensureDefault: false, showLimitToast: true, }); updateHeroSmsPlatformDisplay(); markSettingsDirty(true); saveSettings({ silent: true }).catch(() => { }); }); heroSmsCountryMenu.appendChild(item); }); applyHeroSmsCountryMenuFilter(heroSmsCountryMenuSearchKeyword); updateHeroSmsCountryMenuSummary( selectedOrder.map((id) => ({ id, label: getHeroSmsCountryLabelById(id), })) ); } function syncHeroSmsFallbackSelectionOrderFromSelect(options = {}) { const countrySelect = selectHeroSmsCountry || selectHeroSmsCountryFallback; const selectionLimit = Math.max(1, Math.floor(Number(options.maxSelection) || HERO_SMS_COUNTRY_SELECTION_MAX)); const enforceMax = options.enforceMax !== false; const ensureDefault = options.ensureDefault !== false; const showLimitToast = Boolean(options.showLimitToast); if (!countrySelect) { const defaultCountry = isFiveSimProviderSelected() ? { id: DEFAULT_FIVE_SIM_COUNTRY_ID, label: DEFAULT_FIVE_SIM_COUNTRY_LABEL } : { id: normalizeHeroSmsCountryId(DEFAULT_HERO_SMS_COUNTRY_ID), label: DEFAULT_HERO_SMS_COUNTRY_LABEL }; heroSmsCountrySelectionOrder = [defaultCountry.id]; renderHeroSmsCountryFallbackOrder([defaultCountry]); return [defaultCountry]; } const selectedIds = Array.from(countrySelect.options) .filter((option) => option.selected) .map((option) => normalizePhoneSmsCountryId(option.value, getSelectedPhoneSmsProvider())) .filter(Boolean); if (!selectedIds.length && !countrySelect.multiple) { const fallbackId = normalizePhoneSmsCountryId(countrySelect.value, getSelectedPhoneSmsProvider()); if (fallbackId) { selectedIds.push(fallbackId); } } const selectedSet = new Set(selectedIds); let nextOrder = heroSmsCountrySelectionOrder.filter((id) => selectedSet.has(id)); selectedIds.forEach((id) => { if (!nextOrder.includes(id)) { nextOrder.push(id); } }); if (ensureDefault && !nextOrder.length) { const defaultId = normalizePhoneSmsCountryId(countrySelect.value || (isFiveSimProviderSelected() ? DEFAULT_FIVE_SIM_COUNTRY_ID : DEFAULT_HERO_SMS_COUNTRY_ID)); nextOrder = [defaultId]; } if (enforceMax && nextOrder.length > selectionLimit) { const droppedCount = nextOrder.length - selectionLimit; nextOrder = nextOrder.slice(0, selectionLimit); if (showLimitToast && droppedCount > 0 && typeof showToast === 'function') { showToast(`接码国家最多选择 ${selectionLimit} 个,已保留前 ${selectionLimit} 个。`, 'warn', 2200); } } const nextOrderSet = new Set(nextOrder.map((id) => String(id))); Array.from(countrySelect.options).forEach((option) => { option.selected = nextOrderSet.has(String(option.value)); }); heroSmsCountrySelectionOrder = nextOrder; const selectedCountries = heroSmsCountrySelectionOrder.map((id) => ({ id, label: getHeroSmsCountryLabelById(id), })); renderHeroSmsCountryFallbackOrder(selectedCountries); renderHeroSmsCountryChoiceButtons(); return selectedCountries; } function applyHeroSmsFallbackSelection(countries = [], options = {}) { const includePrimary = Boolean(options.includePrimary); const normalizeFallbackForCurrentProvider = isFiveSimProviderSelected() ? normalizeFiveSimCountryFallbackList : normalizeHeroSmsCountryFallbackList; const sourceCountries = includePrimary ? countries : [ getSelectedHeroSmsCountryOption(), ...normalizeFallbackForCurrentProvider(countries), ]; const normalized = (isFiveSimProviderSelected() ? normalizeFiveSimCountryFallbackList(sourceCountries) : normalizeHeroSmsCountryFallbackList(sourceCountries) ).slice(0, HERO_SMS_COUNTRY_SELECTION_MAX); const selectedIds = normalized .map((entry) => normalizePhoneSmsCountryId(entry.id, getSelectedPhoneSmsProvider())) .filter(Boolean); const countrySelect = selectHeroSmsCountry || selectHeroSmsCountryFallback; if (countrySelect) { const selectedSet = new Set(selectedIds.map((id) => String(id))); Array.from(countrySelect.options).forEach((option) => { option.selected = selectedSet.has(String(option.value)); }); } heroSmsCountrySelectionOrder = [...selectedIds]; return syncHeroSmsFallbackSelectionOrderFromSelect({ enforceMax: true, ensureDefault: false, showLimitToast: false, }); } function removeHeroSmsCountryFromOrder(id) { const provider = getSelectedPhoneSmsProvider(); const normalizedId = normalizePhoneSmsCountryId(id, provider); if (!normalizedId) { return []; } heroSmsCountrySelectionOrder = heroSmsCountrySelectionOrder .filter((entry) => String(entry) !== String(normalizedId)); [selectHeroSmsCountry, selectHeroSmsCountryFallback].forEach((selectEl) => { if (!selectEl) { return; } Array.from(selectEl.options || []).forEach((option) => { if (String(normalizePhoneSmsCountryId(option.value, provider)) === String(normalizedId)) { option.selected = false; } }); }); const nextOrder = syncHeroSmsFallbackSelectionOrderFromSelect({ enforceMax: true, ensureDefault: false, showLimitToast: false, }); updateHeroSmsPlatformDisplay(); markSettingsDirty(true); saveSettings({ silent: true }).catch(() => { }); return nextOrder; } function normalizePhoneActivationState(record = {}) { if (!record || typeof record !== 'object' || Array.isArray(record)) { return null; } const provider = normalizePhoneSmsProviderValue(record.provider || latestState?.phoneSmsProvider || PHONE_SMS_PROVIDER_HERO); const activationId = String(record.activationId ?? record.id ?? record.activation ?? '').trim(); const phoneNumber = String(record.phoneNumber ?? record.phone ?? record.number ?? '').trim(); if (!activationId || !phoneNumber) { return null; } const normalized = { provider, activationId, phoneNumber, serviceCode: String(record.serviceCode || '').trim(), successfulUses: Math.max(0, Math.floor(Number(record.successfulUses) || 0)), maxUses: Math.max(1, Math.floor(Number(record.maxUses) || 3)), }; if (provider === PHONE_SMS_PROVIDER_FIVE_SIM) { normalized.countryId = normalizeFiveSimCountryCode( record.countryCode || record.countryId || '', '' ); } else if (provider === PHONE_SMS_PROVIDER_NEXSMS) { normalized.countryId = normalizeNexSmsCountryId(record.countryId, -1); } else { normalized.countryId = normalizeHeroSmsCountryId(record.countryId, 0); } const countryLabel = String(record.countryLabel || '').trim(); if (countryLabel) { normalized.countryLabel = countryLabel; } const expiresAt = Number(record.expiresAt); if (Number.isFinite(expiresAt) && expiresAt > 0) { normalized.expiresAt = Math.floor(expiresAt); } return normalized; } function buildPhoneActivationOptionKey(activation) { const normalized = normalizePhoneActivationState(activation); if (!normalized) { return ''; } return [ normalized.provider, String(normalized.activationId || '').trim(), String(normalized.phoneNumber || '').trim(), ].join('::'); } function resolvePhoneActivationCountryLabel(activation = null) { const normalized = normalizePhoneActivationState(activation); if (!normalized) { return ''; } if (normalized.countryLabel) { return normalized.countryLabel; } if (normalized.provider === PHONE_SMS_PROVIDER_FIVE_SIM) { return String(normalized.countryId || '').trim(); } if (normalized.provider === PHONE_SMS_PROVIDER_NEXSMS) { const countryId = normalizeNexSmsCountryId(normalized.countryId, -1); return countryId >= 0 ? `Country #${countryId}` : ''; } return normalizeHeroSmsCountryLabel( getHeroSmsCountryLabelById(normalized.countryId || '') ); } function getPhoneSmsProviderLabel(provider = '') { if (typeof window !== 'undefined' && window.PhoneSmsProviderRegistry?.getProviderLabel) { return window.PhoneSmsProviderRegistry.getProviderLabel(provider); } const normalized = normalizePhoneSmsProviderValue(provider); if (normalized === PHONE_SMS_PROVIDER_FIVE_SIM) { return '5sim'; } if (normalized === PHONE_SMS_PROVIDER_NEXSMS) { return 'NexSMS'; } if (normalized === PHONE_SMS_PROVIDER_MADAO) { return 'MaDao'; } return 'HeroSMS'; } function buildPhoneActivationOptionLabel(activation = null, sourceLabel = '') { const normalized = normalizePhoneActivationState(activation); if (!normalized) { return ''; } const countryLabel = resolvePhoneActivationCountryLabel(normalized); const providerLabel = getPhoneSmsProviderLabel(normalized.provider); const reuseLabel = normalized.maxUses > 1 ? ` / ${Math.max(0, normalized.successfulUses) + 1}/${normalized.maxUses}` : ''; const sourcePrefix = sourceLabel ? `[${sourceLabel}] ` : ''; return `${sourcePrefix}${normalized.phoneNumber} / ${providerLabel}${countryLabel ? ` / ${countryLabel}` : ''} (#${normalized.activationId})${reuseLabel}`; } function stopPhoneRuntimeCountdownTicker() { clearInterval(phoneRuntimeCountdownTimer); phoneRuntimeCountdownTimer = null; } function renderPhoneRuntimeCountdown() { if (!displayHeroSmsCurrentCountdown) { return; } const hasActivation = Boolean(normalizePhoneActivationState(latestState?.currentPhoneActivation)); const endsAt = Number(phoneRuntimeCountdownEndsAt) || 0; const now = Date.now(); if (!hasActivation || !Number.isFinite(endsAt) || endsAt <= 0) { displayHeroSmsCurrentCountdown.textContent = hasActivation ? '等待中' : '未启动'; stopPhoneRuntimeCountdownTicker(); return; } const remainingMs = endsAt - now; const windowHint = ( phoneRuntimeCountdownWindowTotal > 0 ? `(${Math.max(1, phoneRuntimeCountdownWindowIndex)}/${phoneRuntimeCountdownWindowTotal})` : '' ); if (remainingMs <= 0) { displayHeroSmsCurrentCountdown.textContent = `00:00:00${windowHint}`; stopPhoneRuntimeCountdownTicker(); return; } displayHeroSmsCurrentCountdown.textContent = `${formatCountdown(remainingMs)}${windowHint}`; } function syncPhoneRuntimeCountdown(state = {}) { phoneRuntimeCountdownEndsAt = Math.max(0, Number(state?.currentPhoneVerificationCountdownEndsAt) || 0); phoneRuntimeCountdownWindowIndex = Math.max(0, Math.floor(Number(state?.currentPhoneVerificationCountdownWindowIndex) || 0)); phoneRuntimeCountdownWindowTotal = Math.max(0, Math.floor(Number(state?.currentPhoneVerificationCountdownWindowTotal) || 0)); renderPhoneRuntimeCountdown(); if (phoneRuntimeCountdownEndsAt > Date.now()) { if (!phoneRuntimeCountdownTimer) { phoneRuntimeCountdownTimer = setInterval(() => { renderPhoneRuntimeCountdown(); }, 1000); } } else { stopPhoneRuntimeCountdownTicker(); } } function renderPhonePreferredActivationOptions(state = {}) { if (!selectHeroSmsPreferredActivation) { return; } const provider = normalizePhoneSmsProviderValue( state?.phoneSmsProvider || latestState?.phoneSmsProvider || getSelectedPhoneSmsProvider() ); const selectedPreferred = normalizePhoneActivationState(state?.phonePreferredActivation || latestState?.phonePreferredActivation); const reusablePool = Array.isArray(state?.phoneReusableActivationPool) ? state.phoneReusableActivationPool : (Array.isArray(latestState?.phoneReusableActivationPool) ? latestState.phoneReusableActivationPool : []); const optionEntries = []; const seen = new Set(); const pushActivationOption = (activation, sourceLabel) => { const normalized = normalizePhoneActivationState(activation); if (!normalized || normalized.provider !== provider) { return; } const key = buildPhoneActivationOptionKey(normalized); if (!key || seen.has(key)) { return; } seen.add(key); optionEntries.push({ key, activation: normalized, label: buildPhoneActivationOptionLabel(normalized, sourceLabel), }); }; pushActivationOption(state?.currentPhoneActivation || latestState?.currentPhoneActivation, '当前'); pushActivationOption(state?.reusablePhoneActivation || latestState?.reusablePhoneActivation, '复用'); reusablePool.forEach((activation) => pushActivationOption(activation, '池')); pushActivationOption(selectedPreferred, '已选'); phonePreferredActivationOptionMap.clear(); selectHeroSmsPreferredActivation.innerHTML = ''; const autoOption = document.createElement('option'); autoOption.value = ''; autoOption.textContent = '自动(先复用已有可用号,再创建新号)'; selectHeroSmsPreferredActivation.appendChild(autoOption); optionEntries.forEach((entry) => { phonePreferredActivationOptionMap.set(entry.key, entry.activation); const option = document.createElement('option'); option.value = entry.key; option.textContent = entry.label; selectHeroSmsPreferredActivation.appendChild(option); }); const selectedKey = buildPhoneActivationOptionKey(selectedPreferred); if (selectedKey && phonePreferredActivationOptionMap.has(selectedKey)) { selectHeroSmsPreferredActivation.value = selectedKey; } else { selectHeroSmsPreferredActivation.value = ''; } } function getSelectedPhonePreferredActivation() { if (!selectHeroSmsPreferredActivation) { return null; } const selectedKey = String(selectHeroSmsPreferredActivation.value || '').trim(); if (!selectedKey) { return null; } const activation = phonePreferredActivationOptionMap.get(selectedKey); return activation ? { ...activation } : null; } function updateHeroSmsRuntimeDisplay(state = {}) { if (displayHeroSmsCurrentNumber) { const activation = normalizePhoneActivationState(state?.currentPhoneActivation || latestState?.currentPhoneActivation); const phoneNumber = String(activation?.phoneNumber || '').trim(); const activationId = String(activation?.activationId || '').trim(); const countryLabel = normalizePhoneSmsCountryLabel( activation?.countryLabel || getHeroSmsCountryLabelById(activation?.countryId || ''), activation?.provider || getSelectedPhoneSmsProvider() ); displayHeroSmsCurrentNumber.textContent = phoneNumber ? `${phoneNumber}${activationId ? ` (#${activationId})` : ''}${countryLabel ? ` / ${countryLabel}` : ''}` : '未分配'; } if (displayHeroSmsCurrentCode) { const code = String(state?.currentPhoneVerificationCode ?? latestState?.currentPhoneVerificationCode ?? '').trim(); displayHeroSmsCurrentCode.textContent = code || '未获取'; } if (displayFreeReusablePhone || displayFreeReusablePhoneCountry || inputFreeReusablePhone) { const activation = state?.freeReusablePhoneActivation ?? latestState?.freeReusablePhoneActivation ?? null; const phoneNumber = String(activation?.phoneNumber || '').trim(); const activationId = String(activation?.activationId || '').trim(); const successfulUses = Number.isFinite(Number(activation?.successfulUses)) ? Number(activation.successfulUses) : null; const maxUses = Number.isFinite(Number(activation?.maxUses)) ? Number(activation.maxUses) : null; const countryLabel = normalizePhoneSmsCountryLabel( activation?.countryLabel || getHeroSmsCountryLabelById(activation?.countryId || '') || state?.heroSmsCountryLabel || latestState?.heroSmsCountryLabel || getHeroSmsCountryLabelById(state?.heroSmsCountryId || latestState?.heroSmsCountryId || ''), activation?.provider || getSelectedPhoneSmsProvider() ); const usesText = successfulUses !== null || maxUses !== null ? ` / ${successfulUses ?? 0}/${maxUses ?? 3}` : ''; if (displayFreeReusablePhone) { displayFreeReusablePhone.textContent = phoneNumber ? `${phoneNumber}${activationId ? ` (#${activationId})` : ''}${usesText}` : '未保存'; } if (displayFreeReusablePhoneCountry) { displayFreeReusablePhoneCountry.textContent = phoneNumber ? `地区:${countryLabel || '未保存'}` : '地区:未保存'; } if (inputFreeReusablePhone) { inputFreeReusablePhone.value = phoneNumber; } } syncPhoneRuntimeCountdown(state); renderPhonePreferredActivationOptions(state); } function applyFreeReusablePhoneMutationResult(refreshedState = {}, mutationResult = {}, options = {}) { const { clear = false } = options; const explicitActivation = clear ? null : ( mutationResult?.freeReusablePhoneActivation || mutationResult?.state?.freeReusablePhoneActivation || null ); const statePatch = { ...(refreshedState || {}), ...(mutationResult?.state || {}), ...(clear || explicitActivation ? { freeReusablePhoneActivation: explicitActivation } : {}), }; syncLatestState(statePatch); updateHeroSmsRuntimeDisplay(latestState || statePatch); } async function refreshFreeReusablePhoneStateFallback(mutationResult = {}, options = {}) { if (mutationResult?.state || mutationResult?.freeReusablePhoneActivation || options.clear) { applyFreeReusablePhoneMutationResult(latestState, mutationResult, options); return; } try { const refreshedState = await chrome.runtime.sendMessage({ type: 'GET_STATE', source: 'sidepanel' }); applyFreeReusablePhoneMutationResult(refreshedState || latestState, mutationResult, options); } catch (error) { console.warn('Failed to refresh free reusable phone state:', error); applyFreeReusablePhoneMutationResult(latestState, mutationResult, options); } } async function loadHeroSmsCountries(options = {}) { const countrySelect = selectHeroSmsCountry || selectHeroSmsCountryFallback; if (!countrySelect) { return; } const silent = Boolean(options?.silent); const preferFallbackOnly = Boolean(options?.preferFallbackOnly); const provider = getSelectedPhoneSmsProvider(); const previousSelectionOrder = [...heroSmsCountrySelectionOrder]; const previousSelectedIds = previousSelectionOrder.length ? previousSelectionOrder : Array.from(countrySelect.options) .filter((option) => option.selected) .map((option) => normalizePhoneSmsCountryId(option.value, provider)) .filter(Boolean); const applyOptions = (optionItems = [], selectEl) => { if (!selectEl) { return; } selectEl.innerHTML = ''; optionItems.forEach((entry) => { const option = document.createElement('option'); option.value = String(entry.id); option.textContent = entry.label; selectEl.appendChild(option); }); }; const applyFiveSimFallbackOptions = () => { const fallbackItems = FIVE_SIM_SUPPORTED_COUNTRY_ITEMS.map((item) => ({ id: item.id, label: formatFiveSimCountryDisplayLabel(item.id, item.eng), searchText: item.searchText, })); applyOptions(fallbackItems, selectHeroSmsCountry); applyOptions(fallbackItems, selectHeroSmsCountryFallback); heroSmsCountrySearchTextById.clear(); fallbackItems.forEach((entry) => heroSmsCountrySearchTextById.set(String(entry.id), entry.searchText)); }; const applyHeroSmsFallbackOptions = () => { const fallbackItems = HERO_SMS_FALLBACK_COUNTRY_ITEMS .map((item) => { const id = normalizeHeroSmsCountryId(item.id); const label = buildHeroSmsCountryDisplayLabel(item); return { id, label: String(label || '').trim() || `Country #${id}`, searchText: buildHeroSmsCountrySearchText(item, label, String(id)), }; }) .filter((item) => item.id > 0); if (!fallbackItems.some((item) => item.id === DEFAULT_HERO_SMS_COUNTRY_ID)) { fallbackItems.unshift({ id: DEFAULT_HERO_SMS_COUNTRY_ID, label: DEFAULT_HERO_SMS_COUNTRY_LABEL, searchText: `${DEFAULT_HERO_SMS_COUNTRY_LABEL} ${DEFAULT_HERO_SMS_COUNTRY_ID}`, }); } applyOptions(fallbackItems, selectHeroSmsCountry); applyOptions(fallbackItems, selectHeroSmsCountryFallback); heroSmsCountrySearchTextById.clear(); fallbackItems.forEach((entry) => { heroSmsCountrySearchTextById.set(String(entry.id), entry.searchText); }); }; if (provider === PHONE_SMS_PROVIDER_FIVE_SIM && preferFallbackOnly) { applyFiveSimFallbackOptions(); } else if (provider === PHONE_SMS_PROVIDER_FIVE_SIM) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 10000); const response = await fetch('https://5sim.net/v1/guest/countries', { signal: controller.signal, cache: 'no-store', headers: { Accept: 'application/json' }, }); clearTimeout(timeoutId); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const payload = await response.json(); const countriesPayload = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload : {}; const optionItems = Object.entries(countriesPayload) .map(([slug, entry]) => { const id = normalizeFiveSimCountryId(slug, ''); const label = formatFiveSimCountryDisplayLabel(id, entry?.text_en || slug, slug); return { id, label, searchText: [ slug, label, entry?.text_en, entry?.text_ru, Object.keys(entry?.iso || {}).join(' '), Object.keys(entry?.prefix || {}).join(' '), ].filter(Boolean).join(' '), }; }) .filter((entry) => entry.id) .sort((left, right) => String(left.label || '').localeCompare(String(right.label || ''))); if (!optionItems.length) { throw new Error('国家列表为空'); } heroSmsCountrySearchTextById.clear(); optionItems.forEach((entry) => heroSmsCountrySearchTextById.set(String(entry.id), entry.searchText)); applyOptions(optionItems, selectHeroSmsCountry); applyOptions(optionItems, selectHeroSmsCountryFallback); } catch (error) { applyFiveSimFallbackOptions(); if (!silent && typeof showToast === 'function') { showToast(`5sim 国家列表加载失败:${normalizeHeroSmsFetchErrorMessage(error)}(已切换为内置国家列表)`, 'warn', 2800); } } } else if (preferFallbackOnly) { applyHeroSmsFallbackOptions(); } else try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 10000); const response = await fetch('https://hero-sms.com/stubs/handler_api.php?action=getCountries', { signal: controller.signal, cache: 'no-store', }); clearTimeout(timeoutId); const payload = await response.json(); const countries = parseHeroSmsCountryPayload(payload); if (!countries.length) { throw new Error('国家列表为空'); } const optionItems = countries .filter((item) => Number(item?.id) > 0 && (String(item?.eng || '').trim() || String(item?.chn || '').trim())) .sort((left, right) => String(left.eng || '').localeCompare(String(right.eng || ''))) .map((item) => { const id = normalizeHeroSmsCountryId(item.id); const label = buildHeroSmsCountryDisplayLabel(item); return { id, label: String(label || '').trim() || `Country #${id}`, searchText: buildHeroSmsCountrySearchText(item, label, String(id)), }; }); if (!optionItems.length) { throw new Error('国家列表为空'); } heroSmsCountrySearchTextById.clear(); optionItems.forEach((entry) => { heroSmsCountrySearchTextById.set(String(entry.id), entry.searchText); }); applyOptions(optionItems, selectHeroSmsCountry); applyOptions(optionItems, selectHeroSmsCountryFallback); } catch (error) { applyHeroSmsFallbackOptions(); if (!silent && typeof showToast === 'function') { showToast(`国家列表加载失败:${normalizeHeroSmsFetchErrorMessage(error)}(已切换为内置国家列表)`, 'warn', 2800); } } const availableIds = new Set(Array.from(countrySelect.options).map((option) => String(option.value))); const normalizedSelectedIds = previousSelectedIds .map((id) => String(id)) .filter((id) => availableIds.has(id)) .map((id) => normalizePhoneSmsCountryId(id, provider)); heroSmsCountrySelectionOrder = normalizedSelectedIds; const selectedSet = new Set(normalizedSelectedIds.map((id) => String(id))); Array.from(countrySelect.options).forEach((option) => { option.selected = selectedSet.has(String(option.value)); }); syncHeroSmsFallbackSelectionOrderFromSelect({ enforceMax: true, ensureDefault: false, showLimitToast: false, }); updateHeroSmsPlatformDisplay(); setHeroSmsOperatorSelectValue(latestState?.heroSmsOperator); refreshHeroSmsOperatorOptions({ silent: true, selectedOperator: latestState?.heroSmsOperator }); } function getHeroSmsOperatorCountryId() { const countrySelect = selectHeroSmsCountry || selectHeroSmsCountryFallback; if (heroSmsCountrySelectionOrder.length) { return normalizeHeroSmsCountryId(heroSmsCountrySelectionOrder[0], DEFAULT_HERO_SMS_COUNTRY_ID); } const selectedOption = countrySelect ? Array.from(countrySelect.options || []).find((option) => option.selected) : null; return normalizeHeroSmsCountryId( selectedOption?.value || latestState?.heroSmsCountryId, DEFAULT_HERO_SMS_COUNTRY_ID ); } async function loadHeroSmsOperators(options = {}) { const silent = Boolean(options?.silent); const force = Boolean(options?.force); if ( !force && heroSmsOperatorsByCountryId instanceof Map && heroSmsOperatorsByCountryId.size && Date.now() - heroSmsOperatorsLoadedAt < 10 * 60 * 1000 ) { return heroSmsOperatorsByCountryId; } try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 3500); const response = await fetch(HERO_SMS_OPERATORS_URL, { signal: controller.signal, cache: 'no-store', headers: { Accept: 'application/json' }, }); clearTimeout(timeoutId); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const payload = await response.json(); const parsed = parseHeroSmsOperatorsPayload(payload); if (!parsed.size) { throw new Error('运营商列表为空'); } heroSmsOperatorsByCountryId = parsed; heroSmsOperatorsLoadedAt = Date.now(); } catch (error) { if (!(heroSmsOperatorsByCountryId instanceof Map)) { heroSmsOperatorsByCountryId = new Map(); } if (!silent && typeof showToast === 'function') { showToast(`HeroSMS 运营商列表加载失败:${normalizeHeroSmsFetchErrorMessage(error)}(已保留“不限”)`, 'warn', 2600); } } return heroSmsOperatorsByCountryId; } function renderHeroSmsOperatorOptions(selectedOperator = null) { if (!selectHeroSmsOperator || isRenderingHeroSmsOperatorOptions) { return; } isRenderingHeroSmsOperatorOptions = true; try { const currentValue = normalizeHeroSmsOperatorValue( selectedOperator !== null && selectedOperator !== undefined ? selectedOperator : (selectHeroSmsOperator.value || latestState?.heroSmsOperator), DEFAULT_HERO_SMS_OPERATOR ); const countryId = String(getHeroSmsOperatorCountryId()); const operators = heroSmsOperatorsByCountryId instanceof Map ? (heroSmsOperatorsByCountryId.get(countryId) || []) : []; selectHeroSmsOperator.innerHTML = ''; const anyOption = document.createElement('option'); anyOption.value = DEFAULT_HERO_SMS_OPERATOR; anyOption.textContent = '不限(any)'; selectHeroSmsOperator.appendChild(anyOption); operators.forEach((operator) => { const option = document.createElement('option'); option.value = operator; option.textContent = operator; selectHeroSmsOperator.appendChild(option); }); if ( currentValue && currentValue !== DEFAULT_HERO_SMS_OPERATOR && !Array.from(selectHeroSmsOperator.options).some((option) => option.value === currentValue) ) { const selectedOption = document.createElement('option'); selectedOption.value = currentValue; selectedOption.textContent = currentValue; selectHeroSmsOperator.appendChild(selectedOption); } selectHeroSmsOperator.value = currentValue || DEFAULT_HERO_SMS_OPERATOR; selectHeroSmsOperator.disabled = operators.length === 0; } finally { isRenderingHeroSmsOperatorOptions = false; } } function setHeroSmsOperatorSelectValue(operator = latestState?.heroSmsOperator) { if (!selectHeroSmsOperator) { return DEFAULT_HERO_SMS_OPERATOR; } const normalized = normalizeHeroSmsOperatorValue(operator, DEFAULT_HERO_SMS_OPERATOR); if ( normalized && normalized !== DEFAULT_HERO_SMS_OPERATOR && !Array.from(selectHeroSmsOperator.options || []).some((option) => option.value === normalized) ) { const selectedOption = document.createElement('option'); selectedOption.value = normalized; selectedOption.textContent = normalized; selectHeroSmsOperator.appendChild(selectedOption); } selectHeroSmsOperator.value = normalized; return normalized; } function refreshHeroSmsOperatorOptions(options = {}) { if (!selectHeroSmsOperator) { return Promise.resolve(heroSmsOperatorsByCountryId); } return loadHeroSmsOperators(options) .then(() => { renderHeroSmsOperatorOptions(options?.selectedOperator); return heroSmsOperatorsByCountryId; }) .catch(() => { renderHeroSmsOperatorOptions(options?.selectedOperator); return heroSmsOperatorsByCountryId; }); } function getFiveSimCountryLabelByCode(code = '') { const normalizedCode = normalizeFiveSimCountryCode(code, ''); if (!normalizedCode || !selectFiveSimCountry) { return ''; } const matched = Array.from(selectFiveSimCountry.options || []) .find((option) => String(option.value || '').trim() === normalizedCode); return normalizeFiveSimCountryLabel(matched?.textContent || '', normalizedCode); } function renderFiveSimCountryFallbackOrder(countries = []) { if (!displayFiveSimCountryFallbackOrder) { return; } const normalized = normalizeFiveSimCountryFallbackList(countries); displayFiveSimCountryFallbackOrder.textContent = ''; if (!normalized.length) { displayFiveSimCountryFallbackOrder.textContent = '未设置'; return; } normalized.forEach((country, index) => { const chip = document.createElement('span'); chip.className = 'country-order-chip'; const label = document.createElement('span'); label.className = 'country-order-chip-label'; label.textContent = `${index + 1}. ${country.label}`; const removeBtn = document.createElement('button'); removeBtn.type = 'button'; removeBtn.className = 'country-order-remove'; removeBtn.title = `移除 ${country.label}`; removeBtn.setAttribute('aria-label', `移除 ${country.label}`); removeBtn.textContent = '×'; removeBtn.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); removeFiveSimCountryFromOrder(country.code || country.id); }); chip.appendChild(label); chip.appendChild(removeBtn); displayFiveSimCountryFallbackOrder.appendChild(chip); if (index < normalized.length - 1) { const separator = document.createElement('span'); separator.className = 'country-order-separator'; separator.textContent = '→'; displayFiveSimCountryFallbackOrder.appendChild(separator); } }); } function setFiveSimCountryMenuOpen(open) { const nextOpen = Boolean(open); if (btnFiveSimCountryMenu) { btnFiveSimCountryMenu.setAttribute('aria-expanded', String(nextOpen)); } if (fiveSimCountryMenu) { fiveSimCountryMenu.hidden = !nextOpen; if (nextOpen) { const searchInput = fiveSimCountryMenu.querySelector('.hero-sms-country-menu-search-input'); if (searchInput) { fiveSimCountryMenuSearchKeyword = ''; searchInput.value = ''; applyFiveSimCountryMenuFilter(''); setTimeout(() => { searchInput.focus(); searchInput.select(); }, 0); } } } } function applyFiveSimCountryMenuFilter(keyword = '') { if (!fiveSimCountryMenu) { return; } const normalizedKeyword = String(keyword || '').trim().toLowerCase(); const items = Array.from(fiveSimCountryMenu.querySelectorAll('.hero-sms-country-menu-item')); let visibleCount = 0; items.forEach((item) => { const haystack = String(item.dataset.searchText || '').toLowerCase(); const visible = !normalizedKeyword || haystack.includes(normalizedKeyword); item.hidden = !visible; if (visible) { visibleCount += 1; } }); let empty = fiveSimCountryMenu.querySelector('.hero-sms-country-menu-empty'); if (visibleCount === 0) { if (!empty) { empty = document.createElement('span'); empty.className = 'data-value hero-sms-country-menu-empty'; empty.textContent = '没有匹配国家'; fiveSimCountryMenu.appendChild(empty); } } else if (empty) { empty.remove(); } } function updateFiveSimCountryMenuSummary(selectedCountries = []) { if (!btnFiveSimCountryMenu) { return; } const normalized = normalizeFiveSimCountryFallbackList(selectedCountries); if (!normalized.length) { btnFiveSimCountryMenu.textContent = `未选择 (0/${HERO_SMS_COUNTRY_SELECTION_MAX})`; return; } const labels = normalized.map((country) => country.label); btnFiveSimCountryMenu.textContent = `${labels.join(' / ')} (${normalized.length}/${HERO_SMS_COUNTRY_SELECTION_MAX})`; } function renderFiveSimCountryChoiceButtons() { if (!fiveSimCountryMenu || !selectFiveSimCountry) { return; } const options = Array.from(selectFiveSimCountry.options || []); const selectedOrder = [...fiveSimCountrySelectionOrder]; const selectedSet = new Set(selectedOrder); fiveSimCountryMenu.innerHTML = ''; const searchWrap = document.createElement('div'); searchWrap.className = 'hero-sms-country-menu-search'; const searchInput = document.createElement('input'); searchInput.type = 'search'; searchInput.className = 'data-input mono hero-sms-country-menu-search-input'; searchInput.placeholder = '搜索国家(中/英/代码)'; searchInput.value = fiveSimCountryMenuSearchKeyword; searchInput.addEventListener('input', () => { fiveSimCountryMenuSearchKeyword = String(searchInput.value || '').trim(); applyFiveSimCountryMenuFilter(fiveSimCountryMenuSearchKeyword); }); searchWrap.appendChild(searchInput); fiveSimCountryMenu.appendChild(searchWrap); if (!options.length) { const empty = document.createElement('span'); empty.className = 'data-value hero-sms-country-menu-empty'; empty.textContent = '暂无国家选项'; fiveSimCountryMenu.appendChild(empty); updateFiveSimCountryMenuSummary([]); return; } options.forEach((option) => { const countryId = normalizeFiveSimCountryId(option.value, ''); if (!countryId) { return; } const item = document.createElement('button'); item.type = 'button'; item.className = 'hero-sms-country-menu-item'; item.dataset.countryId = countryId; item.dataset.searchText = fiveSimCountrySearchTextByCode.get(countryId) || `${countryId} ${option.textContent || ''}`; const selected = selectedSet.has(countryId); item.setAttribute('aria-pressed', selected ? 'true' : 'false'); item.textContent = option.textContent || countryId; item.addEventListener('click', (event) => { event.preventDefault(); toggleFiveSimCountryInOrder(countryId); }); fiveSimCountryMenu.appendChild(item); }); applyFiveSimCountryMenuFilter(fiveSimCountryMenuSearchKeyword); } function syncFiveSimCountrySelectionOrderFromSelect(options = {}) { const selectionLimit = Math.max(1, Math.floor(Number(options.maxSelection) || HERO_SMS_COUNTRY_SELECTION_MAX)); const enforceMax = options.enforceMax !== false; const ensureDefault = options.ensureDefault !== false; const showLimitToast = Boolean(options.showLimitToast); const countrySelect = selectFiveSimCountry; if (!countrySelect) { fiveSimCountrySelectionOrder = []; renderFiveSimCountryFallbackOrder([]); updateFiveSimCountryMenuSummary([]); return []; } const selectedCodes = Array.from(countrySelect.options || []) .filter((option) => option.selected) .map((option) => normalizeFiveSimCountryCode(option.value, '')) .filter(Boolean); const selectedSet = new Set(selectedCodes); let nextOrder = fiveSimCountrySelectionOrder.filter((code) => selectedSet.has(code)); selectedCodes.forEach((code) => { if (!nextOrder.includes(code)) { nextOrder.push(code); } }); if (ensureDefault && !nextOrder.length) { nextOrder = [normalizeFiveSimCountryCode(countrySelect.value || DEFAULT_FIVE_SIM_COUNTRY_ID)]; } if (enforceMax && nextOrder.length > selectionLimit) { const droppedCount = nextOrder.length - selectionLimit; nextOrder = nextOrder.slice(0, selectionLimit); if (showLimitToast && droppedCount > 0 && typeof showToast === 'function') { showToast(`5sim 国家最多选择 ${selectionLimit} 个,已保留前 ${selectionLimit} 个。`, 'warn', 2200); } } const nextOrderSet = new Set(nextOrder); Array.from(countrySelect.options || []).forEach((option) => { option.selected = nextOrderSet.has(normalizeFiveSimCountryCode(option.value, '')); }); fiveSimCountrySelectionOrder = [...nextOrder]; const selectedCountries = fiveSimCountrySelectionOrder.map((code) => ({ id: code, code, label: getFiveSimCountryOptionLabel(code), })); renderFiveSimCountryFallbackOrder(selectedCountries); updateFiveSimCountryMenuSummary(selectedCountries); renderFiveSimCountryChoiceButtons(); return selectedCountries; } function applyFiveSimCountrySelection(countries = [], options = {}) { const normalized = normalizeFiveSimCountryFallbackList(countries).slice(0, HERO_SMS_COUNTRY_SELECTION_MAX); const selectedCodes = normalized .map((entry) => normalizeFiveSimCountryCode(entry.code || entry.id, '')) .filter(Boolean); fiveSimCountrySelectionOrder = [...selectedCodes]; if (selectFiveSimCountry) { const selectedSet = new Set(selectedCodes); Array.from(selectFiveSimCountry.options || []).forEach((option) => { option.selected = selectedSet.has(normalizeFiveSimCountryCode(option.value, '')); }); } return syncFiveSimCountrySelectionOrderFromSelect({ ensureDefault: options.ensureDefault !== false, enforceMax: true, showLimitToast: false, }); } function toggleFiveSimCountryInOrder(code = '') { const normalizedCode = normalizeFiveSimCountryCode(code, ''); if (!normalizedCode || !selectFiveSimCountry) { return []; } const option = Array.from(selectFiveSimCountry.options || []) .find((entry) => normalizeFiveSimCountryCode(entry.value, '') === normalizedCode); if (option) { option.selected = !option.selected; } const nextOrder = syncFiveSimCountrySelectionOrderFromSelect({ enforceMax: true, ensureDefault: false, showLimitToast: true, }); markSettingsDirty(true); saveSettings({ silent: true }).catch(() => { }); return nextOrder; } function removeFiveSimCountryFromOrder(code = '') { const normalizedCode = normalizeFiveSimCountryCode(code, ''); fiveSimCountrySelectionOrder = fiveSimCountrySelectionOrder.filter((entry) => entry !== normalizedCode); if (selectFiveSimCountry) { Array.from(selectFiveSimCountry.options || []).forEach((option) => { if (normalizeFiveSimCountryCode(option.value, '') === normalizedCode) { option.selected = false; } }); } const nextOrder = syncFiveSimCountrySelectionOrderFromSelect({ enforceMax: true, ensureDefault: false, showLimitToast: false, }); markSettingsDirty(true); saveSettings({ silent: true }).catch(() => { }); return nextOrder; } async function loadFiveSimCountries(options = {}) { if (!selectFiveSimCountry) { return; } const silent = Boolean(options?.silent); const preferFallbackOnly = Boolean(options?.preferFallbackOnly); const previousOrder = [...fiveSimCountrySelectionOrder]; const applyOptions = (items = []) => { selectFiveSimCountry.innerHTML = ''; fiveSimCountrySearchTextByCode.clear(); items.forEach((entry) => { const code = normalizeFiveSimCountryCode(entry.code || entry.id, ''); if (!code) { return; } const option = document.createElement('option'); option.value = code; option.textContent = entry.label || getFiveSimCountryOptionLabel(code) || code; selectFiveSimCountry.appendChild(option); fiveSimCountrySearchTextByCode.set(code, entry.searchText || `${option.textContent} ${code}`); }); }; const fallbackItems = FIVE_SIM_SUPPORTED_COUNTRY_ITEMS.map((item) => ({ id: item.id, code: item.id, label: formatFiveSimCountryDisplayLabel(item.id, item.eng, item.id), searchText: item.searchText, })); if (preferFallbackOnly) { applyOptions(fallbackItems); } else try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 10000); const response = await fetch('https://5sim.net/v1/guest/countries', { signal: controller.signal, cache: 'no-store', headers: { Accept: 'application/json' }, }); clearTimeout(timeoutId); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const payload = await response.json(); const items = parseFiveSimCountriesPayload(payload); applyOptions(items.length ? items : fallbackItems); } catch (error) { applyOptions(fallbackItems); if (!silent && typeof showToast === 'function') { showToast(`5sim 国家列表加载失败:${normalizeHeroSmsFetchErrorMessage(error)}(已切换为内置国家列表)`, 'warn', 2800); } } applyFiveSimCountrySelection(previousOrder.length ? previousOrder : latestState?.fiveSimCountryOrder || [], { ensureDefault: false, }); } function renderNexSmsCountryFallbackOrder(countries = []) { if (!displayNexSmsCountryFallbackOrder) { return; } const normalized = normalizeNexSmsCountryFallbackList(countries); displayNexSmsCountryFallbackOrder.textContent = ''; if (!normalized.length) { displayNexSmsCountryFallbackOrder.textContent = '未设置'; return; } normalized.forEach((country, index) => { const chip = document.createElement('span'); chip.className = 'country-order-chip'; const label = document.createElement('span'); label.className = 'country-order-chip-label'; label.textContent = `${index + 1}. ${country.label}`; const removeBtn = document.createElement('button'); removeBtn.type = 'button'; removeBtn.className = 'country-order-remove'; removeBtn.title = `移除 ${country.label}`; removeBtn.setAttribute('aria-label', `移除 ${country.label}`); removeBtn.textContent = '×'; removeBtn.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); removeNexSmsCountryFromOrder(country.id); }); chip.appendChild(label); chip.appendChild(removeBtn); displayNexSmsCountryFallbackOrder.appendChild(chip); if (index < normalized.length - 1) { const separator = document.createElement('span'); separator.className = 'country-order-separator'; separator.textContent = '→'; displayNexSmsCountryFallbackOrder.appendChild(separator); } }); } function setNexSmsCountryMenuOpen(open) { const nextOpen = Boolean(open); if (btnNexSmsCountryMenu) { btnNexSmsCountryMenu.setAttribute('aria-expanded', String(nextOpen)); } if (nexSmsCountryMenu) { nexSmsCountryMenu.hidden = !nextOpen; if (nextOpen) { const searchInput = nexSmsCountryMenu.querySelector('.hero-sms-country-menu-search-input'); if (searchInput) { nexSmsCountryMenuSearchKeyword = ''; searchInput.value = ''; applyNexSmsCountryMenuFilter(''); setTimeout(() => { searchInput.focus(); searchInput.select(); }, 0); } } } } function applyNexSmsCountryMenuFilter(keyword = '') { if (!nexSmsCountryMenu) { return; } const normalizedKeyword = String(keyword || '').trim().toLowerCase(); const items = Array.from(nexSmsCountryMenu.querySelectorAll('.hero-sms-country-menu-item')); let visibleCount = 0; items.forEach((item) => { const haystack = String(item.dataset.searchText || '').toLowerCase(); const visible = !normalizedKeyword || haystack.includes(normalizedKeyword); item.hidden = !visible; if (visible) { visibleCount += 1; } }); let empty = nexSmsCountryMenu.querySelector('.hero-sms-country-menu-empty'); if (visibleCount === 0) { if (!empty) { empty = document.createElement('span'); empty.className = 'data-value hero-sms-country-menu-empty'; empty.textContent = '没有匹配国家'; nexSmsCountryMenu.appendChild(empty); } } else if (empty) { empty.remove(); } } function updateNexSmsCountryMenuSummary(selectedCountries = []) { if (!btnNexSmsCountryMenu) { return; } const normalized = normalizeNexSmsCountryFallbackList(selectedCountries); btnNexSmsCountryMenu.textContent = normalized.length ? `${normalized.map((country) => country.label).join(' / ')} (${normalized.length}/${HERO_SMS_COUNTRY_SELECTION_MAX})` : `未选择 (0/${HERO_SMS_COUNTRY_SELECTION_MAX})`; } function renderNexSmsCountryChoiceButtons() { if (!nexSmsCountryMenu || !selectNexSmsCountry) { return; } const options = Array.from(selectNexSmsCountry.options || []); const selectedOrder = [...nexSmsCountrySelectionOrder]; const selectedSet = new Set(selectedOrder); nexSmsCountryMenu.innerHTML = ''; const searchWrap = document.createElement('div'); searchWrap.className = 'hero-sms-country-menu-search'; const searchInput = document.createElement('input'); searchInput.type = 'search'; searchInput.className = 'data-input mono hero-sms-country-menu-search-input'; searchInput.placeholder = '搜索国家 ID'; searchInput.value = nexSmsCountryMenuSearchKeyword; searchInput.addEventListener('input', () => { nexSmsCountryMenuSearchKeyword = String(searchInput.value || '').trim(); applyNexSmsCountryMenuFilter(nexSmsCountryMenuSearchKeyword); }); searchWrap.appendChild(searchInput); nexSmsCountryMenu.appendChild(searchWrap); if (!options.length) { const empty = document.createElement('span'); empty.className = 'data-value hero-sms-country-menu-empty'; empty.textContent = '暂无国家选项'; nexSmsCountryMenu.appendChild(empty); updateNexSmsCountryMenuSummary([]); return; } options.forEach((option) => { const countryId = normalizeNexSmsCountryIdValue(option.value, -1); if (countryId < 0) { return; } const item = document.createElement('button'); item.type = 'button'; item.className = 'hero-sms-country-menu-item'; item.dataset.countryId = String(countryId); item.dataset.searchText = nexSmsCountrySearchTextById.get(countryId) || `${countryId} ${option.textContent || ''}`; const selected = selectedSet.has(countryId); item.setAttribute('aria-pressed', selected ? 'true' : 'false'); item.textContent = option.textContent || `Country #${countryId}`; item.addEventListener('click', (event) => { event.preventDefault(); toggleNexSmsCountryInOrder(countryId); }); nexSmsCountryMenu.appendChild(item); }); applyNexSmsCountryMenuFilter(nexSmsCountryMenuSearchKeyword); } function syncNexSmsCountrySelectionOrderFromSelect(options = {}) { const selectionLimit = Math.max(1, Math.floor(Number(options.maxSelection) || HERO_SMS_COUNTRY_SELECTION_MAX)); const enforceMax = options.enforceMax !== false; const ensureDefault = options.ensureDefault !== false; const showLimitToast = Boolean(options.showLimitToast); const countrySelect = selectNexSmsCountry; if (!countrySelect) { nexSmsCountrySelectionOrder = []; renderNexSmsCountryFallbackOrder([]); updateNexSmsCountryMenuSummary([]); return []; } const selectedIds = Array.from(countrySelect.options || []) .filter((option) => option.selected) .map((option) => normalizeNexSmsCountryIdValue(option.value, -1)) .filter((id) => id >= 0); const selectedSet = new Set(selectedIds); let nextOrder = nexSmsCountrySelectionOrder.filter((id) => selectedSet.has(id)); selectedIds.forEach((id) => { if (!nextOrder.includes(id)) { nextOrder.push(id); } }); if (ensureDefault && !nextOrder.length) { nextOrder = [normalizeNexSmsCountryIdValue(DEFAULT_NEX_SMS_COUNTRY_ORDER[0], 1)]; } if (enforceMax && nextOrder.length > selectionLimit) { const droppedCount = nextOrder.length - selectionLimit; nextOrder = nextOrder.slice(0, selectionLimit); if (showLimitToast && droppedCount > 0 && typeof showToast === 'function') { showToast(`NexSMS 国家最多选择 ${selectionLimit} 个,已保留前 ${selectionLimit} 个。`, 'warn', 2200); } } const nextOrderSet = new Set(nextOrder); Array.from(countrySelect.options || []).forEach((option) => { option.selected = nextOrderSet.has(normalizeNexSmsCountryIdValue(option.value, -1)); }); nexSmsCountrySelectionOrder = [...nextOrder]; const selectedCountries = nexSmsCountrySelectionOrder.map((id) => ({ id, label: getNexSmsCountryLabelById(id), })); renderNexSmsCountryFallbackOrder(selectedCountries); updateNexSmsCountryMenuSummary(selectedCountries); renderNexSmsCountryChoiceButtons(); return selectedCountries; } function applyNexSmsCountrySelection(countries = [], options = {}) { const normalized = normalizeNexSmsCountryFallbackList(countries).slice(0, HERO_SMS_COUNTRY_SELECTION_MAX); const selectedIds = normalized .map((entry) => normalizeNexSmsCountryIdValue(entry.id, -1)) .filter((id) => id >= 0); nexSmsCountrySelectionOrder = [...selectedIds]; if (selectNexSmsCountry) { const selectedSet = new Set(selectedIds); Array.from(selectNexSmsCountry.options || []).forEach((option) => { option.selected = selectedSet.has(normalizeNexSmsCountryIdValue(option.value, -1)); }); } return syncNexSmsCountrySelectionOrderFromSelect({ ensureDefault: options.ensureDefault !== false, enforceMax: true, showLimitToast: false, }); } function toggleNexSmsCountryInOrder(id = -1) { const countryId = normalizeNexSmsCountryIdValue(id, -1); if (countryId < 0 || !selectNexSmsCountry) { return []; } const option = Array.from(selectNexSmsCountry.options || []) .find((entry) => normalizeNexSmsCountryIdValue(entry.value, -1) === countryId); if (option) { option.selected = !option.selected; } const nextOrder = syncNexSmsCountrySelectionOrderFromSelect({ enforceMax: true, ensureDefault: false, showLimitToast: true, }); markSettingsDirty(true); saveSettings({ silent: true }).catch(() => { }); return nextOrder; } function removeNexSmsCountryFromOrder(id = -1) { const countryId = normalizeNexSmsCountryIdValue(id, -1); nexSmsCountrySelectionOrder = nexSmsCountrySelectionOrder.filter((entry) => entry !== countryId); if (selectNexSmsCountry) { Array.from(selectNexSmsCountry.options || []).forEach((option) => { if (normalizeNexSmsCountryIdValue(option.value, -1) === countryId) { option.selected = false; } }); } const nextOrder = syncNexSmsCountrySelectionOrderFromSelect({ enforceMax: true, ensureDefault: false, showLimitToast: false, }); markSettingsDirty(true); saveSettings({ silent: true }).catch(() => { }); return nextOrder; } async function loadNexSmsCountries() { if (!selectNexSmsCountry) { return; } const previousOrder = [...nexSmsCountrySelectionOrder]; selectNexSmsCountry.innerHTML = ''; nexSmsCountrySearchTextById.clear(); NEX_SMS_FALLBACK_COUNTRY_ITEMS.forEach((entry) => { const id = normalizeNexSmsCountryIdValue(entry.id, -1); if (id < 0) { return; } const option = document.createElement('option'); option.value = String(id); option.textContent = entry.label || `Country #${id}`; selectNexSmsCountry.appendChild(option); nexSmsCountrySearchTextById.set(id, entry.searchText || `${option.textContent} ${id}`); }); applyNexSmsCountrySelection(previousOrder.length ? previousOrder : latestState?.nexSmsCountryOrder || [], { ensureDefault: false, }); } async function buildNexSmsPricePreviewLines(options = {}) { const countryIds = ( typeof getSelectedNexSmsCountries === 'function' ? getSelectedNexSmsCountries() : normalizeNexSmsCountryFallbackList( Array.isArray(latestState?.nexSmsCountryOrder) ? latestState.nexSmsCountryOrder : [] ) ) .map((country) => normalizeNexSmsCountryIdValue(country.id, -1)) .filter((id) => id >= 0); const serviceCode = normalizeNexSmsServiceCodeValue( inputNexSmsServiceCode?.value || latestState?.nexSmsServiceCode || DEFAULT_NEX_SMS_SERVICE_CODE ); const priceRange = resolvePhoneSmsPricePreviewRange( typeof PHONE_SMS_PROVIDER_NEXSMS !== 'undefined' ? PHONE_SMS_PROVIDER_NEXSMS : 'nexsms' ); const maxPrice = priceRange.maxPrice; const apiKey = String(inputNexSmsApiKey?.value || '').trim(); const providerLabel = String(options?.providerLabel || 'NexSMS').trim(); if (!apiKey) { return [`${providerLabel}: 请先填写 NexSMS API Key`]; } if (priceRange.invalid) { return [`${providerLabel}: ${buildPhoneSmsPriceRangePreviewMessage(priceRange)}`]; } if (!countryIds.length) { return [`${providerLabel}: 请先选择至少 1 个国家`]; } const previews = []; for (const countryId of countryIds) { const countryLabel = getNexSmsCountryLabelById(countryId) || `Country #${countryId}`; try { const url = new URL('/api/getCountryByService', 'https://api.nexsms.net'); url.searchParams.set('apiKey', apiKey); url.searchParams.set('serviceCode', serviceCode); url.searchParams.set('countryId', String(countryId)); const response = await fetch(url.toString(), { cache: 'no-store' }); const payload = await response.json().catch(() => ({})); if (!response.ok || Number(payload?.code) !== 0) { previews.push(`${countryLabel}: ${describeNexSmsPreviewPayload(payload) || `HTTP ${response.status}`}`); continue; } const data = payload?.data || {}; const tierEntries = data?.priceMap && typeof data.priceMap === 'object' ? Object.entries(data.priceMap) .map(([price, count]) => { const numericPrice = Number(price); const numericCount = Number(count); if (!Number.isFinite(numericPrice) || numericPrice <= 0) { return null; } return { price: Math.round(numericPrice * 10000) / 10000, count: Number.isFinite(numericCount) ? Math.max(0, Math.floor(numericCount)) : null, }; }) .filter(Boolean) : []; const availablePrices = tierEntries .filter((entry) => entry.count === null || entry.count > 0) .map((entry) => entry.price); const uniqueSorted = Array.from(new Set(availablePrices)).sort((left, right) => left - right); const fallbackMin = Number(data?.minPrice); const filteredTierEntries = filterPhoneSmsPriceEntriesForPreviewRange(tierEntries, priceRange); const rangePrices = filterPhoneSmsPriceValuesForPreviewRange(uniqueSorted, priceRange); const fallbackMinPrice = Number.isFinite(fallbackMin) && fallbackMin > 0 ? Math.round(fallbackMin * 10000) / 10000 : null; const rangeFallbackMin = isPhoneSmsPriceWithinPreviewRange(fallbackMinPrice, priceRange) ? fallbackMinPrice : null; const lowest = rangePrices.length ? rangePrices[0] : rangeFallbackMin; if (!Number.isFinite(lowest)) { previews.push(`${countryLabel}: ${buildPhoneSmsPriceRangePreviewMessage(priceRange)}`); continue; } const tierText = formatPriceTiersForPreview(filteredTierEntries, { maxPrice }); const lowestLabel = priceRange.hasMinPrice || priceRange.hasMaxPrice ? '区间内最低' : '最低'; previews.push(`${countryLabel}: ${lowestLabel} ${lowest}${tierText ? `;档位:${tierText}` : ''}`); } catch (error) { previews.push(`${countryLabel}: 查询失败(${normalizeHeroSmsFetchErrorMessage(error)})`); } } if (!previews.length) { previews.push('未获取'); } return [`${providerLabel}:`, ...previews]; } async function previewFiveSimPriceTiers() { if (!displayHeroSmsPriceTiers) { return; } const countryCodes = ( typeof getSelectedFiveSimCountries === 'function' ? getSelectedFiveSimCountries() : normalizeFiveSimCountryFallbackList( Array.isArray(latestState?.fiveSimCountryOrder) ? latestState.fiveSimCountryOrder : [] ) ) .map((country) => normalizeFiveSimCountryCode(country.code || country.id, '')) .filter(Boolean); const product = normalizeFiveSimProductValue( inputFiveSimProduct?.value || latestState?.fiveSimProduct || DEFAULT_FIVE_SIM_PRODUCT ); const priceRange = resolvePhoneSmsPricePreviewRange( typeof PHONE_SMS_PROVIDER_FIVE_SIM !== 'undefined' ? PHONE_SMS_PROVIDER_FIVE_SIM : '5sim' ); const maxPrice = priceRange.maxPrice; displayHeroSmsPriceTiers.textContent = '查询中...'; if (rowHeroSmsPriceTiers) { rowHeroSmsPriceTiers.style.display = ''; } if (priceRange.invalid) { displayHeroSmsPriceTiers.textContent = buildPhoneSmsPriceRangePreviewMessage(priceRange); return; } if (!countryCodes.length) { displayHeroSmsPriceTiers.textContent = '请先选择至少 1 个国家,再查询价格'; return; } const collectPriceEntries = (payload, entries = []) => { if (Array.isArray(payload)) { payload.forEach((entry) => collectPriceEntries(entry, entries)); return entries; } if (!payload || typeof payload !== 'object') { return entries; } const cost = Number(payload.cost); const count = Number(payload.count); if (Number.isFinite(cost) && cost > 0) { entries.push({ cost: Math.round(cost * 10000) / 10000, count: Number.isFinite(count) ? count : 0, }); } Object.entries(payload).forEach(([key, value]) => { const keyedPrice = Number(key); if (!Number.isFinite(keyedPrice) || keyedPrice <= 0) { return; } if (value && typeof value === 'object') { const keyedCount = Number(value.count); entries.push({ cost: Math.round(keyedPrice * 10000) / 10000, count: Number.isFinite(keyedCount) ? keyedCount : 0, }); return; } const numericCount = Number(value); entries.push({ cost: Math.round(keyedPrice * 10000) / 10000, count: Number.isFinite(numericCount) ? numericCount : 0, }); }); Object.values(payload).forEach((entry) => collectPriceEntries(entry, entries)); return entries; }; const previews = []; for (const countryCode of countryCodes) { const countryLabel = getFiveSimCountryLabelByCode(countryCode) || countryCode; try { const url = new URL('https://5sim.net/v1/guest/prices'); url.searchParams.set('country', countryCode); url.searchParams.set('product', product); const response = await fetch(url.toString(), { cache: 'no-store' }); const payload = await response.json().catch(() => ({})); if (!response.ok) { previews.push(`${countryLabel}: HTTP ${response.status}`); continue; } const productRoot = payload?.[product] || payload; const countryRoot = productRoot?.[countryCode] || productRoot; const tierEntries = collectPriceEntries(countryRoot, []) .filter((entry) => Number.isFinite(entry.cost) && entry.cost > 0) .map((entry) => ({ price: entry.cost, count: Number.isFinite(entry.count) ? Math.max(0, Math.floor(entry.count)) : null, })); const prices = tierEntries .filter((entry) => entry.count === null || entry.count > 0) .map((entry) => entry.price); const uniqueSorted = Array.from(new Set(prices)).sort((left, right) => left - right); const rangePrices = filterPhoneSmsPriceValuesForPreviewRange(uniqueSorted, priceRange); const filteredTierEntries = filterPhoneSmsPriceEntriesForPreviewRange(tierEntries, priceRange); if (!rangePrices.length) { previews.push(`${countryLabel}: ${buildPhoneSmsPriceRangePreviewMessage(priceRange)}`); continue; } const lowest = rangePrices[0]; const tierText = formatPriceTiersForPreview(filteredTierEntries, { maxPrice }); const lowestLabel = priceRange.hasMinPrice || priceRange.hasMaxPrice ? '区间内最低' : '最低'; previews.push(`${countryLabel}: ${lowestLabel} ${lowest}${tierText ? `;档位:${tierText}` : ''}`); } catch (error) { previews.push(`${countryLabel}: 查询失败(${normalizeHeroSmsFetchErrorMessage(error)})`); } } displayHeroSmsPriceTiers.textContent = previews.join('\n') || '未获取'; } async function buildFiveSimPricePreviewLines(options = {}) { const countryCodes = ( typeof getSelectedFiveSimCountries === 'function' ? getSelectedFiveSimCountries() : normalizeFiveSimCountryFallbackList( Array.isArray(latestState?.fiveSimCountryOrder) ? latestState.fiveSimCountryOrder : [] ) ) .map((country) => normalizeFiveSimCountryCode(country.code || country.id, '')) .filter(Boolean); const product = normalizeFiveSimProductValue( inputFiveSimProduct?.value || latestState?.fiveSimProduct || DEFAULT_FIVE_SIM_PRODUCT ); const priceRange = resolvePhoneSmsPricePreviewRange( typeof PHONE_SMS_PROVIDER_FIVE_SIM !== 'undefined' ? PHONE_SMS_PROVIDER_FIVE_SIM : '5sim' ); const maxPrice = priceRange.maxPrice; const providerLabel = String(options?.providerLabel || '5sim').trim(); if (!countryCodes.length) { return [`${providerLabel}: 请先选择至少 1 个国家`]; } if (priceRange.invalid) { return [`${providerLabel}: ${buildPhoneSmsPriceRangePreviewMessage(priceRange)}`]; } const collectPriceEntries = (payload, entries = []) => { if (Array.isArray(payload)) { payload.forEach((entry) => collectPriceEntries(entry, entries)); return entries; } if (!payload || typeof payload !== 'object') { return entries; } const cost = Number(payload.cost); const count = Number(payload.count); if (Number.isFinite(cost) && cost > 0) { entries.push({ cost: Math.round(cost * 10000) / 10000, count: Number.isFinite(count) ? count : 0, }); } Object.entries(payload).forEach(([key, value]) => { const keyedPrice = Number(key); if (!Number.isFinite(keyedPrice) || keyedPrice <= 0) { return; } if (value && typeof value === 'object') { const keyedCount = Number(value.count); entries.push({ cost: Math.round(keyedPrice * 10000) / 10000, count: Number.isFinite(keyedCount) ? keyedCount : 0, }); return; } const numericCount = Number(value); entries.push({ cost: Math.round(keyedPrice * 10000) / 10000, count: Number.isFinite(numericCount) ? numericCount : 0, }); }); Object.values(payload).forEach((entry) => collectPriceEntries(entry, entries)); return entries; }; const previews = []; for (const countryCode of countryCodes) { const countryLabel = getFiveSimCountryLabelByCode(countryCode) || countryCode; try { const url = new URL('https://5sim.net/v1/guest/prices'); url.searchParams.set('country', countryCode); url.searchParams.set('product', product); const response = await fetch(url.toString(), { cache: 'no-store' }); const payload = await response.json().catch(() => ({})); if (!response.ok) { previews.push(`${countryLabel}: HTTP ${response.status}`); continue; } const productRoot = payload?.[product] || payload; const countryRoot = productRoot?.[countryCode] || productRoot; const tierEntries = collectPriceEntries(countryRoot, []) .filter((entry) => Number.isFinite(entry.cost) && entry.cost > 0) .map((entry) => ({ price: entry.cost, count: Number.isFinite(entry.count) ? Math.max(0, Math.floor(entry.count)) : null, })); const prices = tierEntries .filter((entry) => entry.count === null || entry.count > 0) .map((entry) => entry.price); const uniqueSorted = Array.from(new Set(prices)).sort((left, right) => left - right); const rangePrices = filterPhoneSmsPriceValuesForPreviewRange(uniqueSorted, priceRange); const filteredTierEntries = filterPhoneSmsPriceEntriesForPreviewRange(tierEntries, priceRange); if (!rangePrices.length) { previews.push(`${countryLabel}: ${buildPhoneSmsPriceRangePreviewMessage(priceRange)}`); continue; } const lowest = rangePrices[0]; const tierText = formatPriceTiersForPreview(filteredTierEntries, { maxPrice }); const lowestLabel = priceRange.hasMinPrice || priceRange.hasMaxPrice ? '区间内最低' : '最低'; previews.push(`${countryLabel}: ${lowestLabel} ${lowest}${tierText ? `;档位:${tierText}` : ''}`); } catch (error) { previews.push(`${countryLabel}: 查询失败(${normalizeHeroSmsFetchErrorMessage(error)})`); } } if (!previews.length) { previews.push('未获取'); } return [`${providerLabel}:`, ...previews]; } async function previewHeroSmsPriceTiers() { const normalizeProvider = typeof normalizePhoneSmsProviderValue === 'function' ? normalizePhoneSmsProviderValue : ((value = '') => { const normalized = String(value || '').trim().toLowerCase(); if (normalized === '5sim') return '5sim'; if (normalized === 'nexsms') return 'nexsms'; return 'hero-sms'; }); const fiveSimProviderValue = typeof PHONE_SMS_PROVIDER_FIVE_SIM !== 'undefined' ? PHONE_SMS_PROVIDER_FIVE_SIM : '5sim'; const nexSmsProviderValue = typeof PHONE_SMS_PROVIDER_NEXSMS !== 'undefined' ? PHONE_SMS_PROVIDER_NEXSMS : 'nexsms'; const heroProviderValue = typeof PHONE_SMS_PROVIDER_HERO !== 'undefined' ? PHONE_SMS_PROVIDER_HERO : 'hero-sms'; const defaultProviderValue = typeof DEFAULT_PHONE_SMS_PROVIDER !== 'undefined' ? DEFAULT_PHONE_SMS_PROVIDER : 'hero-sms'; const activeProvider = typeof getSelectedPhoneSmsProvider === 'function' ? getSelectedPhoneSmsProvider() : normalizeProvider(selectPhoneSmsProvider?.value || defaultProviderValue); displayHeroSmsPriceTiers.textContent = '查询中...'; if (rowHeroSmsPriceTiers) { rowHeroSmsPriceTiers.style.display = ''; } if (!displayHeroSmsPriceTiers) { return; } const selectedProviderOrder = ( typeof getSelectedPhoneSmsProviderOrder === 'function' ? getSelectedPhoneSmsProviderOrder() : normalizePhoneSmsProviderOrderValue(phoneSmsProviderOrderSelection, []) ); const providerOrder = selectedProviderOrder.length ? selectedProviderOrder : [normalizeProvider(activeProvider)]; const previews = []; for (const provider of providerOrder) { if (provider === fiveSimProviderValue) { const lines = await buildFiveSimPricePreviewLines({ providerLabel: '5sim' }); previews.push(...lines, ''); continue; } if (provider === nexSmsProviderValue) { const lines = await buildNexSmsPricePreviewLines({ providerLabel: 'NexSMS' }); previews.push(...lines, ''); continue; } if (provider !== heroProviderValue) { continue; } const selectedCountries = syncHeroSmsFallbackSelectionOrderFromSelect({ enforceMax: true, ensureDefault: false, showLimitToast: false, }); const candidates = selectedCountries .map((country) => ({ id: normalizeHeroSmsCountryId(country?.id, 0), label: normalizeHeroSmsCountryLabel(country?.label, ''), })) .filter((country) => country.id > 0); const priceRange = resolvePhoneSmsPricePreviewRange(heroProviderValue); const maxPrice = priceRange.maxPrice; const apiKey = String(inputHeroSmsApiKey?.value || '').trim(); const heroLines = ['HeroSMS:']; if (!apiKey) { heroLines.push('请先填写接码 API Key'); previews.push(...heroLines, ''); continue; } if (priceRange.invalid) { heroLines.push(buildPhoneSmsPriceRangePreviewMessage(priceRange)); previews.push(...heroLines, ''); continue; } if (!candidates.length) { heroLines.push('请先选择至少 1 个国家'); previews.push(...heroLines, ''); continue; } for (const country of candidates) { const countryId = normalizeHeroSmsCountryId(country.id, 0); if (countryId <= 0) { continue; } const countryLabel = normalizeHeroSmsCountryLabel( country.label || getHeroSmsCountryLabelById(countryId), `Country #${countryId}` ); try { const parsedPayloads = []; const failedPriceActions = []; const fetchPriceAction = async (action) => { const url = new URL('https://hero-sms.com/stubs/handler_api.php'); url.searchParams.set('action', action); url.searchParams.set('service', 'dr'); url.searchParams.set('country', String(countryId)); if (action === 'getPricesExtended') { url.searchParams.set('freePrice', 'true'); } if (apiKey) { url.searchParams.set('api_key', apiKey); } const response = await fetch(url.toString(), { cache: 'no-store' }); const rawText = await response.text(); let payload = rawText; try { payload = rawText ? JSON.parse(rawText) : ''; } catch { payload = rawText; } if (!response.ok) { const reason = summarizeHeroSmsPreviewError(payload, response.status); failedPriceActions.push(`${action}: ${reason}`); return; } parsedPayloads.push(payload); }; const fetchTopCountriesPayload = async () => { const url = new URL('https://hero-sms.com/stubs/handler_api.php'); url.searchParams.set('action', 'getTopCountriesByService'); url.searchParams.set('service', 'dr'); url.searchParams.set('freePrice', 'true'); if (apiKey) { url.searchParams.set('api_key', apiKey); } const response = await fetch(url.toString(), { cache: 'no-store' }); const rawText = await response.text(); let payload = rawText; try { payload = rawText ? JSON.parse(rawText) : ''; } catch { payload = rawText; } if (!response.ok) { const reason = summarizeHeroSmsPreviewError(payload, response.status); failedPriceActions.push(`getTopCountriesByService: ${reason}`); return null; } return payload; }; const fetchVerificationPricesPayload = async () => { const url = new URL('https://hero-sms.com/stubs/handler_api.php'); url.searchParams.set('action', 'getPricesVerification'); url.searchParams.set('service', 'dr'); url.searchParams.set('country', String(countryId)); if (apiKey) { url.searchParams.set('api_key', apiKey); } const response = await fetch(url.toString(), { cache: 'no-store' }); const rawText = await response.text(); let payload = rawText; try { payload = rawText ? JSON.parse(rawText) : ''; } catch { payload = rawText; } if (!response.ok) { const reason = summarizeHeroSmsPreviewError(payload, response.status); failedPriceActions.push(`getPricesVerification: ${reason}`); return null; } return payload; }; await fetchPriceAction('getPricesExtended'); await fetchPriceAction('getPrices'); // Additional visibility probe: some regions expose richer tier maps on getPricesForVerification. await fetchPriceAction('getPricesForVerification'); const [topCountriesPayload, verificationPayload] = await Promise.all([ fetchTopCountriesPayload(), fetchVerificationPricesPayload(), ]); const priceEntries = parsedPayloads .flatMap((payload) => collectHeroSmsPriceEntriesForPreview(payload, [])) .concat( collectHeroSmsPriceEntriesFromTopCountriesPayload(topCountriesPayload, countryId, []), collectHeroSmsPriceEntriesFromVerificationPayload(verificationPayload, countryId, 'dr', []) ) .filter((entry) => Number.isFinite(Number(entry.cost)) && Number(entry.cost) > 0); const tierStockByPrice = new Map(); for (const entry of priceEntries) { const cost = Math.round(Number(entry.cost) * 10000) / 10000; if (!Number.isFinite(cost) || cost <= 0) { continue; } const stockCount = Number.isFinite(Number(entry.stockCount)) ? Math.max(0, Number(entry.stockCount)) : 0; const previous = tierStockByPrice.get(cost); tierStockByPrice.set(cost, Number.isFinite(previous) ? Math.max(previous, stockCount) : stockCount); } const allPrices = Array.from(tierStockByPrice.keys()).sort((left, right) => left - right); const inStockPrices = allPrices.filter((price) => Number(tierStockByPrice.get(price)) > 0); const tierEntries = allPrices.map((price) => ({ price, count: Math.max(0, Number(tierStockByPrice.get(price)) || 0), })); const rangeAllPrices = filterPhoneSmsPriceValuesForPreviewRange(allPrices, priceRange); const rangeInStockPrices = filterPhoneSmsPriceValuesForPreviewRange(inStockPrices, priceRange); const filteredTierEntries = filterPhoneSmsPriceEntriesForPreviewRange(tierEntries, priceRange); const tierPreviewText = formatPriceTiersWithZeroStockForPreview(filteredTierEntries, { maxPrice }); if (!rangeInStockPrices.length) { if ((priceRange.hasMinPrice || priceRange.hasMaxPrice) && allPrices.length && !rangeAllPrices.length) { heroLines.push(`${countryLabel}: ${buildPhoneSmsPriceRangePreviewMessage(priceRange)}`); continue; } if (rangeAllPrices.length || allPrices.length) { const lowestKnownPrice = rangeAllPrices.length ? rangeAllPrices[0] : allPrices[0]; const lowestKnown = formatHeroSmsPriceForPreview(lowestKnownPrice) || String(lowestKnownPrice); heroLines.push(`${countryLabel}: 全档位均无库存(最低标价 ${lowestKnown})${tierPreviewText ? `;档位:${tierPreviewText}` : ''}`); continue; } if (failedPriceActions.length) { heroLines.push(`${countryLabel}: ${failedPriceActions.join(' | ')}`); continue; } heroLines.push(`${countryLabel}: 无可用价格`); continue; } const lowestWithinRange = rangeInStockPrices[0]; const lowestText = formatHeroSmsPriceForPreview(lowestWithinRange) || String(lowestWithinRange); const lowestLabel = priceRange.hasMinPrice || priceRange.hasMaxPrice ? '区间内最低' : '最低'; heroLines.push(`${countryLabel}: ${lowestLabel} ${lowestText}${tierPreviewText ? `;档位:${tierPreviewText}` : ''}`); } catch (error) { heroLines.push(`${countryLabel}: 查询失败(${normalizeHeroSmsFetchErrorMessage(error)})`); } } if (heroLines.length === 1) { heroLines.push('未获取'); } previews.push(...heroLines, ''); } while (previews.length && previews[previews.length - 1] === '') { previews.pop(); } displayHeroSmsPriceTiers.textContent = previews.join('\n') || '未获取'; } async function previewPhoneSmsBalance() { if (!displayPhoneSmsBalance) { return; } const provider = getSelectedPhoneSmsProvider(); const apiKey = String(inputHeroSmsApiKey?.value || '').trim(); if (!apiKey) { displayPhoneSmsBalance.textContent = '请先填写接码 API Key'; if (rowHeroSmsPriceTiers) rowHeroSmsPriceTiers.style.display = ''; return; } displayPhoneSmsBalance.textContent = '余额查询中...'; if (rowHeroSmsPriceTiers) rowHeroSmsPriceTiers.style.display = ''; try { const url = provider === PHONE_SMS_PROVIDER_FIVE_SIM ? new URL('https://5sim.net/v1/user/profile') : new URL('https://hero-sms.com/stubs/handler_api.php'); const requestOptions = {}; if (provider === PHONE_SMS_PROVIDER_FIVE_SIM) { requestOptions.headers = { Accept: 'application/json', Authorization: `Bearer ${apiKey}`, }; } else { url.searchParams.set('action', 'getBalance'); url.searchParams.set('api_key', apiKey); } const response = await fetch(url.toString(), requestOptions); const rawText = await response.text(); let payload = rawText; try { payload = rawText ? JSON.parse(rawText) : ''; } catch { payload = rawText; } if (!response.ok) { displayPhoneSmsBalance.textContent = `余额查询失败:${summarizeHeroSmsPreviewError(payload, response.status)}`; return; } if (provider === PHONE_SMS_PROVIDER_FIVE_SIM) { const balance = Number(payload?.balance); const frozen = Number(payload?.frozen_balance); displayPhoneSmsBalance.textContent = Number.isFinite(balance) ? `5sim 余额 ${formatHeroSmsPriceForPreview(balance) || balance}${Number.isFinite(frozen) ? `,冻结 ${formatHeroSmsPriceForPreview(frozen) || frozen}` : ''}` : `5sim 余额:${describeHeroSmsPreviewPayload(payload) || '未知'}`; } else { const text = describeHeroSmsPreviewPayload(payload).replace(/^ACCESS_BALANCE:/i, '').trim(); displayPhoneSmsBalance.textContent = `HeroSMS 余额 ${text || '未知'}`; } } catch (error) { displayPhoneSmsBalance.textContent = `余额查询失败:${normalizeHeroSmsFetchErrorMessage(error)}`; } } function getSelectedLocalCpaStep9Mode() { const activeButton = localCpaStep9ModeButtons.find((button) => button.classList.contains('is-active')); return normalizeLocalCpaStep9Mode(activeButton?.dataset.localCpaStep9Mode); } function setLocalCpaStep9Mode(mode) { const resolvedMode = normalizeLocalCpaStep9Mode(mode); localCpaStep9ModeButtons.forEach((button) => { const active = button.dataset.localCpaStep9Mode === resolvedMode; button.classList.toggle('is-active', active); button.setAttribute('aria-pressed', String(active)); }); } function getSelectedMail2925Mode() { const activeButton = mail2925ModeButtons.find((button) => button.classList.contains('is-active')); return normalizeMail2925Mode(activeButton?.dataset.mail2925Mode); } function setMail2925Mode(mode) { const resolvedMode = normalizeMail2925Mode(mode); mail2925ModeButtons.forEach((button) => { const active = button.dataset.mail2925Mode === resolvedMode; button.classList.toggle('is-active', active); button.setAttribute('aria-pressed', String(active)); }); } function getSelectedCloudflareTempEmailLookupMode() { const activeButton = tempEmailLookupModeButtons.find((button) => button.classList.contains('is-active')); return normalizeCloudflareTempEmailLookupMode(activeButton?.dataset.tempEmailLookupMode); } function setCloudflareTempEmailLookupMode(mode) { const resolvedMode = normalizeCloudflareTempEmailLookupMode(mode); tempEmailLookupModeButtons.forEach((button) => { const active = button.dataset.tempEmailLookupMode === resolvedMode; button.classList.toggle('is-active', active); button.setAttribute('aria-pressed', String(active)); }); } function syncCloudflareTempEmailSubdomainModeButtons(mode) { const resolvedMode = normalizeCloudflareTempEmailSubdomainMode(mode); tempEmailSubdomainModeButtons.forEach((button) => { const active = button.dataset.tempEmailSubdomainMode === resolvedMode; button.classList.toggle('is-active', active); button.setAttribute('aria-pressed', String(active)); }); } function getSelectedCloudflareTempEmailSubdomainMode() { if (inputTempEmailUseFixedSubdomain?.checked) { return CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_FIXED; } if (inputTempEmailUseRandomSubdomain?.checked) { return CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_RANDOM; } const activeButton = tempEmailSubdomainModeButtons.find((button) => button.classList.contains('is-active')); return normalizeCloudflareTempEmailSubdomainMode(activeButton?.dataset.tempEmailSubdomainMode); } function setCloudflareTempEmailSubdomainMode(mode) { const resolvedMode = normalizeCloudflareTempEmailSubdomainMode(mode); if (inputTempEmailUseRandomSubdomain) { inputTempEmailUseRandomSubdomain.checked = resolvedMode === CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_RANDOM; } if (inputTempEmailUseFixedSubdomain) { inputTempEmailUseFixedSubdomain.checked = resolvedMode === CLOUDFLARE_TEMP_EMAIL_SUBDOMAIN_MODE_FIXED; } syncCloudflareTempEmailSubdomainModeButtons(resolvedMode); } function getSelectedHotmailServiceMode() { const activeButton = hotmailServiceModeButtons.find((button) => button.classList.contains('is-active')); return normalizeHotmailServiceMode(activeButton?.dataset.hotmailServiceMode); } function setHotmailServiceMode(mode) { const resolvedMode = normalizeHotmailServiceMode(mode); hotmailServiceModeButtons.forEach((button) => { const active = button.dataset.hotmailServiceMode === resolvedMode; button.disabled = false; button.setAttribute('aria-disabled', 'false'); button.classList.toggle('is-active', active); button.setAttribute('aria-pressed', String(active)); }); } function updateAccountRunHistorySettingsUI() { if (!rowAccountRunHistoryHelperBaseUrl) { return; } rowAccountRunHistoryHelperBaseUrl.style.display = 'none'; } function normalizeSignupMethod(value = '') { return String(value || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email'; } function isPhoneSignupReuseLocked(state = latestState, options = {}) { const hasOptionMethod = Object.prototype.hasOwnProperty.call(options || {}, 'signupMethod'); const rawMethod = hasOptionMethod ? options.signupMethod : (typeof getSelectedSignupMethod === 'function' ? getSelectedSignupMethod() : (state?.signupMethod || DEFAULT_SIGNUP_METHOD)); const selectedMethod = normalizeSignupMethod(rawMethod); if (selectedMethod === SIGNUP_METHOD_PHONE) { return true; } if (!options?.includeRuntimeIdentity) { return false; } const identifierType = String( options?.accountIdentifierType ?? state?.accountIdentifierType ?? '' ).trim().toLowerCase(); return identifierType === 'phone'; } function getStoredPhoneSmsReuseEnabled(state = latestState) { return normalizeHeroSmsReuseEnabledValue( state?.phoneSmsReuseEnabled, state?.heroSmsReuseEnabled ); } function getStoredFreePhoneReuseEnabled(state = latestState) { return Boolean(state?.freePhoneReuseEnabled); } function getStoredFreePhoneReuseAutoEnabled(state = latestState) { return Boolean(state?.freePhoneReuseAutoEnabled); } function restorePhoneReuseControlsFromState(state = latestState) { if (typeof inputHeroSmsReuseEnabled !== 'undefined' && inputHeroSmsReuseEnabled) { inputHeroSmsReuseEnabled.checked = getStoredPhoneSmsReuseEnabled(state); } if (typeof inputFreePhoneReuseEnabled !== 'undefined' && inputFreePhoneReuseEnabled) { inputFreePhoneReuseEnabled.checked = getStoredFreePhoneReuseEnabled(state); } if (typeof inputFreePhoneReuseAutoEnabled !== 'undefined' && inputFreePhoneReuseAutoEnabled) { inputFreePhoneReuseAutoEnabled.checked = getStoredFreePhoneReuseAutoEnabled(state); } } function setElementReuseLockedState(element, locked, title = PHONE_SIGNUP_REUSE_LOCK_TITLE) { if (!element) { return; } element.classList?.toggle?.('is-disabled', Boolean(locked)); if (locked) { element.title = title; } else if (element.title === title) { element.title = ''; } } function normalizePanelMode(value = '') { const normalized = String(value || '').trim().toLowerCase(); if (normalized === 'sub2api' || normalized === 'codex2api') { return normalized; } return 'cpa'; } let flowRegistry = null; let settingsSchema = null; let flowCapabilityRegistry = null; function getFlowRegistry() { if (flowRegistry) { return flowRegistry; } const rootScope = typeof window !== 'undefined' ? window : globalThis; flowRegistry = rootScope.MultiPageFlowRegistry || null; return flowRegistry; } function getSettingsSchema() { if (settingsSchema) { return settingsSchema; } const rootScope = typeof window !== 'undefined' ? window : globalThis; const registry = getFlowRegistry(); settingsSchema = rootScope.MultiPageSettingsSchema?.createSettingsSchema?.({ defaultFlowId: DEFAULT_ACTIVE_FLOW_ID, flowRegistry: registry || undefined, }) || null; return settingsSchema; } function normalizeFlowId(value = '', fallback = DEFAULT_ACTIVE_FLOW_ID) { const registry = getFlowRegistry(); if (registry?.normalizeFlowId) { return registry.normalizeFlowId(value, fallback); } const normalized = String(value || '').trim().toLowerCase(); if (normalized === 'codex') { return DEFAULT_ACTIVE_FLOW_ID; } const fallbackValue = String(fallback || '').trim().toLowerCase(); return normalized || fallbackValue || DEFAULT_ACTIVE_FLOW_ID; } function getDefaultTargetIdForFlow(flowId = DEFAULT_ACTIVE_FLOW_ID) { const registry = getFlowRegistry(); if (registry?.getDefaultTargetId) { return registry.getDefaultTargetId(normalizeFlowId(flowId)); } return normalizeFlowId(flowId) === 'kiro' ? 'kiro-rs' : 'cpa'; } function normalizeTargetIdForFlow(flowId = DEFAULT_ACTIVE_FLOW_ID, targetId = '', fallback = '') { const normalizedFlowId = normalizeFlowId(flowId); const registry = getFlowRegistry(); const fallbackTargetId = fallback || getDefaultTargetIdForFlow(normalizedFlowId); if (registry?.normalizeTargetId) { return registry.normalizeTargetId(normalizedFlowId, targetId, fallbackTargetId); } if (normalizedFlowId === DEFAULT_ACTIVE_FLOW_ID) { return normalizePanelMode(targetId || fallbackTargetId); } const normalized = String(targetId || '').trim().toLowerCase(); return normalized || String(fallbackTargetId || '').trim().toLowerCase() || 'kiro-rs'; } function getSelectedFlowId(state = latestState) { const selectedValue = typeof selectFlow !== 'undefined' && selectFlow ? selectFlow.value : ''; return normalizeFlowId( selectedValue || state?.activeFlowId || state?.flowId || DEFAULT_ACTIVE_FLOW_ID, state?.activeFlowId || state?.flowId || DEFAULT_ACTIVE_FLOW_ID ); } function getSelectedTargetIdForState(state = latestState, flowId = getSelectedFlowId(state)) { const normalizedFlowId = normalizeFlowId(flowId); const schema = getSettingsSchema(); if (schema?.getSelectedTargetId) { return schema.getSelectedTargetId(state || {}, normalizedFlowId); } return normalizeTargetIdForFlow( normalizedFlowId, state?.targetId || '', getDefaultTargetIdForFlow(normalizedFlowId) ); } function getSelectedTargetId(flowId = getSelectedFlowId()) { const normalizedFlowId = normalizeFlowId(flowId); const selectedValue = typeof selectPanelMode !== 'undefined' && selectPanelMode ? selectPanelMode.value : ''; return normalizeTargetIdForFlow( normalizedFlowId, selectedValue || latestState?.targetId || '', getDefaultTargetIdForFlow(normalizedFlowId) ); } function getTargetRepositoryUrl(flowId = getSelectedFlowId(), targetId = getSelectedTargetId(flowId)) { const normalizedFlowId = normalizeFlowId(flowId); const normalizedTargetId = normalizeTargetIdForFlow( normalizedFlowId, targetId, getDefaultTargetIdForFlow(normalizedFlowId) ); return TARGET_REPOSITORY_URLS[normalizedFlowId]?.[normalizedTargetId] || ''; } function updateTargetRepositoryButton(flowId = getSelectedFlowId(), targetId = getSelectedTargetId(flowId)) { if (!btnOpenTargetRepository) { return; } const repositoryUrl = getTargetRepositoryUrl(flowId, targetId); const hasRepositoryUrl = Boolean(repositoryUrl); btnOpenTargetRepository.hidden = !hasRepositoryUrl; btnOpenTargetRepository.disabled = !hasRepositoryUrl; btnOpenTargetRepository.dataset.repositoryUrl = repositoryUrl; const targetLabel = getFlowRegistry()?.getTargetLabel?.(flowId, targetId) || targetId; const title = hasRepositoryUrl ? `打开 ${targetLabel} 项目 GitHub 仓库` : `${targetLabel} 暂未配置项目仓库`; btnOpenTargetRepository.setAttribute('aria-label', title); btnOpenTargetRepository.title = title; } function renderFlowSelectorOptions(selectedFlowId = getSelectedFlowId()) { if (!selectFlow) { return []; } const registry = getFlowRegistry(); const flowIds = Array.isArray(registry?.getRegisteredFlowIds?.()) ? registry.getRegisteredFlowIds() : [DEFAULT_ACTIVE_FLOW_ID]; const normalizedSelectedFlowId = normalizeFlowId(selectedFlowId); selectFlow.innerHTML = ''; flowIds.forEach((flowId) => { const option = document.createElement('option'); option.value = flowId; option.textContent = registry?.getFlowLabel?.(flowId) || flowId; selectFlow.appendChild(option); }); selectFlow.value = normalizedSelectedFlowId; return flowIds; } function renderTargetSelectorOptions(flowId = getSelectedFlowId(), selectedTargetId = '') { if (!selectPanelMode) { return []; } const registry = getFlowRegistry(); const normalizedFlowId = normalizeFlowId(flowId); const targetOptions = Array.isArray(registry?.getTargetOptions?.(normalizedFlowId)) ? registry.getTargetOptions(normalizedFlowId) : []; const normalizedTargetId = normalizeTargetIdForFlow( normalizedFlowId, selectedTargetId, getDefaultTargetIdForFlow(normalizedFlowId) ); selectPanelMode.innerHTML = ''; targetOptions.forEach((targetOption) => { const option = document.createElement('option'); option.value = targetOption.id; option.textContent = targetOption.label || targetOption.id; selectPanelMode.appendChild(option); }); if (labelSourceSelector) { labelSourceSelector.textContent = '来源'; } selectPanelMode.disabled = targetOptions.length <= 1; if (targetOptions.length > 0) { selectPanelMode.value = normalizedTargetId; } return targetOptions; } function collectVisibleSettingsTargets(visibleGroupIds = []) { const registry = getFlowRegistry(); const visibleGroupIdSet = new Set( Array.isArray(visibleGroupIds) ? visibleGroupIds.map((groupId) => String(groupId || '').trim()).filter(Boolean) : [] ); const groupDefinitions = registry?.getSettingsGroupDefinitions?.() || {}; const rowIds = new Set(); const sectionIds = new Set(); Object.entries(groupDefinitions).forEach(([groupId, definition]) => { if (!visibleGroupIdSet.has(groupId)) { return; } (definition?.rowIds || []).forEach((rowId) => rowIds.add(rowId)); (definition?.sectionIds || []).forEach((sectionId) => sectionIds.add(sectionId)); }); return { rowIds: Array.from(rowIds), sectionIds: Array.from(sectionIds), }; } function applyFlowSettingsGroupVisibility(visibleGroupIds = []) { const registry = getFlowRegistry(); const groupDefinitions = registry?.getSettingsGroupDefinitions?.() || {}; const { rowIds: visibleRowIds, sectionIds: visibleSectionIds } = collectVisibleSettingsTargets(visibleGroupIds); const visibleRowIdSet = new Set(visibleRowIds); const visibleSectionIdSet = new Set(visibleSectionIds); const allRowIds = new Set(); const allSectionIds = new Set(); Object.values(groupDefinitions).forEach((definition) => { (definition?.rowIds || []).forEach((rowId) => allRowIds.add(rowId)); (definition?.sectionIds || []).forEach((sectionId) => allSectionIds.add(sectionId)); }); allRowIds.forEach((rowId) => { const element = document.getElementById(rowId); if (!element) { return; } element.style.display = visibleRowIdSet.has(rowId) ? '' : 'none'; }); allSectionIds.forEach((sectionId) => { const element = document.getElementById(sectionId); if (!element) { return; } element.style.display = visibleSectionIdSet.has(sectionId) ? '' : 'none'; }); return { rowIds: visibleRowIds, sectionIds: visibleSectionIds, }; } function syncFlowSelectorsFromState(state = latestState) { const activeFlowId = normalizeFlowId(state?.activeFlowId || state?.flowId || DEFAULT_ACTIVE_FLOW_ID); renderFlowSelectorOptions(activeFlowId); const targetId = getSelectedTargetIdForState(state, activeFlowId); renderTargetSelectorOptions(activeFlowId, targetId); return { activeFlowId, targetId, }; } function getFlowCapabilityRegistry() { if (flowCapabilityRegistry) { return flowCapabilityRegistry; } const rootScope = typeof window !== 'undefined' ? window : globalThis; flowCapabilityRegistry = rootScope.MultiPageFlowCapabilities?.createFlowCapabilityRegistry?.({ defaultFlowId: DEFAULT_ACTIVE_FLOW_ID, }) || null; return flowCapabilityRegistry; } function resolveCurrentSidepanelCapabilities(options = {}) { const registry = getFlowCapabilityRegistry(); if (!registry?.resolveSidepanelCapabilities) { return null; } const activeFlowId = normalizeFlowId( options?.activeFlowId ?? options?.state?.activeFlowId ?? latestState?.activeFlowId ?? latestState?.flowId ?? DEFAULT_ACTIVE_FLOW_ID ); const state = { ...(latestState || {}), ...(options?.state || {}), activeFlowId, }; const targetId = options?.targetId !== undefined ? options.targetId : (state?.targetId ?? getSelectedTargetIdForState(state, activeFlowId)); state.targetId = normalizeTargetIdForFlow( activeFlowId, targetId || state?.targetId || '', getDefaultTargetIdForFlow(activeFlowId) ); return registry.resolveSidepanelCapabilities({ activeFlowId, targetId: state.targetId, signupMethod: options?.signupMethod ?? state?.signupMethod, state, }); } function resolveStepDefinitionCapabilityState(state = latestState, options = {}) { const nextState = { ...(state || {}), ...(options?.state || {}), }; const capabilityState = resolveCurrentSidepanelCapabilities({ activeFlowId: options?.activeFlowId ?? nextState?.activeFlowId, targetId: options?.targetId ?? nextState?.targetId, signupMethod: options?.signupMethod ?? nextState?.signupMethod, state: nextState, }); return { capabilityState, plusModeEnabled: capabilityState ? Boolean(capabilityState.runtimeLocks?.plusModeEnabled) : Boolean(nextState?.plusModeEnabled), plusAccountAccessStrategy: capabilityState?.effectivePlusAccountAccessStrategy || normalizePlusAccountAccessStrategy( nextState?.plusAccountAccessStrategy || DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY ), signupMethod: capabilityState?.effectiveSignupMethod || normalizeSignupMethod((options?.signupMethod ?? nextState?.signupMethod) || DEFAULT_SIGNUP_METHOD), phoneVerificationEnabled: capabilityState ? Boolean(capabilityState.runtimeLocks?.phoneVerificationEnabled) : Boolean(nextState?.phoneVerificationEnabled), }; } function getSelectedPanelMode(state = latestState) { const resolvedPanelMode = normalizePanelMode( typeof getSelectedTargetIdForState === 'function' ? getSelectedTargetIdForState({ ...(typeof latestState !== 'undefined' ? latestState : {}), ...(state || {}), activeFlowId: DEFAULT_ACTIVE_FLOW_ID, flowId: DEFAULT_ACTIVE_FLOW_ID, }, DEFAULT_ACTIVE_FLOW_ID) : ( typeof selectPanelMode !== 'undefined' && selectPanelMode ? selectPanelMode.value : (typeof state !== 'undefined' ? state?.targetId : '') ) ); const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function' ? resolveCurrentSidepanelCapabilities({ activeFlowId: DEFAULT_ACTIVE_FLOW_ID, targetId: resolvedPanelMode, state: { ...(typeof latestState !== 'undefined' ? latestState : {}), ...(state || {}), activeFlowId: DEFAULT_ACTIVE_FLOW_ID, flowId: DEFAULT_ACTIVE_FLOW_ID, targetId: resolvedPanelMode, }, }) : null; return normalizePanelMode(capabilityState?.effectiveTargetId || resolvedPanelMode); } function getSelectedSignupMethod() { const activeButton = signupMethodButtons.find((button) => button.classList.contains('is-active')); return normalizeSignupMethod(activeButton?.dataset.signupMethod || latestState?.signupMethod || DEFAULT_SIGNUP_METHOD); } function setSignupMethod(method) { const resolvedMethod = normalizeSignupMethod(method); signupMethodButtons.forEach((button) => { const active = normalizeSignupMethod(button.dataset.signupMethod) === resolvedMethod; button.classList.toggle('is-active', active); button.setAttribute('aria-pressed', String(active)); }); syncLatestState({ signupMethod: resolvedMethod }); return resolvedMethod; } function canSelectPhoneSignupMethod() { const phoneEnabled = Boolean(inputPhoneVerificationEnabled?.checked); const plusModeEnabled = typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled ? Boolean(inputPlusModeEnabled.checked) : Boolean(latestState?.plusModeEnabled); const accountContributionEnabled = typeof isContributionModeActiveForFlow === 'function' ? isContributionModeActiveForFlow(latestState) : Boolean(latestState?.accountContributionEnabled); const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function' ? resolveCurrentSidepanelCapabilities({ targetId: typeof getSelectedTargetId === 'function' ? getSelectedTargetId(getSelectedFlowId(latestState)) : latestState?.targetId, state: { ...(typeof latestState !== 'undefined' ? latestState : {}), phoneVerificationEnabled: phoneEnabled, plusModeEnabled, accountContributionEnabled, }, }) : (() => { const rootScope = typeof window !== 'undefined' ? window : globalThis; const registry = rootScope.MultiPageFlowCapabilities?.createFlowCapabilityRegistry?.({ defaultFlowId: typeof DEFAULT_ACTIVE_FLOW_ID === 'string' ? DEFAULT_ACTIVE_FLOW_ID : 'openai', }) || null; return registry?.resolveSidepanelCapabilities ? registry.resolveSidepanelCapabilities({ activeFlowId: typeof latestState !== 'undefined' ? latestState?.activeFlowId : '', targetId: typeof getSelectedTargetId === 'function' ? getSelectedTargetId(getSelectedFlowId(latestState)) : latestState?.targetId, state: { ...(typeof latestState !== 'undefined' ? latestState : {}), phoneVerificationEnabled: phoneEnabled, plusModeEnabled, accountContributionEnabled, }, }) : null; })(); if (capabilityState && typeof capabilityState.canSelectPhoneSignup === 'boolean') { return capabilityState.canSelectPhoneSignup; } return phoneEnabled && !plusModeEnabled && !accountContributionEnabled; } function isSignupMethodSwitchLocked() { return isAutoRunLockedPhase() || isAutoRunPausedPhase(); } function updateSignupMethodUI(options = {}) { if (!signupMethodButtons.length) { if (typeof syncSignupPhoneInputFromState === 'function') { syncSignupPhoneInputFromState(latestState); } return; } const showSignupMethod = Boolean(inputPhoneVerificationEnabled?.checked); if (rowSignupMethod) { rowSignupMethod.style.display = showSignupMethod ? '' : 'none'; } let selectedMethod = normalizeSignupMethod(getSelectedSignupMethod()); const phoneSelectable = canSelectPhoneSignupMethod(); const accountContributionEnabled = typeof isContributionModeActiveForFlow === 'function' ? isContributionModeActiveForFlow(latestState) : Boolean(latestState?.accountContributionEnabled); if (!phoneSelectable && selectedMethod === SIGNUP_METHOD_PHONE) { selectedMethod = setSignupMethod(SIGNUP_METHOD_EMAIL); if (options.notify && typeof showToast === 'function') { showToast('已切回邮箱注册', 'info', 1600); } } else { setSignupMethod(selectedMethod); } const locked = isSignupMethodSwitchLocked(); signupMethodButtons.forEach((button) => { const method = normalizeSignupMethod(button.dataset.signupMethod); const disabled = locked || (method === SIGNUP_METHOD_PHONE && !phoneSelectable); button.disabled = disabled; button.setAttribute('aria-disabled', String(disabled)); if (method === SIGNUP_METHOD_PHONE) { if (!Boolean(inputPhoneVerificationEnabled?.checked)) { button.title = '开启接码后可选择手机号注册'; } else if (typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled?.checked) { button.title = 'Plus 模式暂不支持手机号注册'; } else if (accountContributionEnabled) { button.title = '账号贡献开启时不能使用手机号注册'; } else if (locked) { button.title = '自动流程运行中不能切换注册方式'; } else { button.title = ''; } } }); const stepDefinitionState = typeof resolveStepDefinitionCapabilityState === 'function' ? resolveStepDefinitionCapabilityState({ ...(latestState || {}), plusModeEnabled: typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled ? Boolean(inputPlusModeEnabled.checked) : Boolean(latestState?.plusModeEnabled), signupMethod: selectedMethod, }, { signupMethod: selectedMethod, }) : { plusModeEnabled: typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled ? Boolean(inputPlusModeEnabled.checked) : Boolean(latestState?.plusModeEnabled), signupMethod: selectedMethod, }; syncStepDefinitionsForMode(stepDefinitionState.plusModeEnabled, { plusPaymentMethod: getSelectedPlusPaymentMethod(latestState), signupMethod: selectedMethod, phoneSignupReloginAfterBindEmailEnabled: typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail ? Boolean(inputPhoneSignupReloginAfterBindEmail.checked) : currentPhoneSignupReloginAfterBindEmailEnabled, }); if (typeof syncSignupPhoneInputFromState === 'function') { syncSignupPhoneInputFromState(latestState); } } function updatePhoneVerificationSettingsUI() { const rawEnabled = Boolean(inputPhoneVerificationEnabled?.checked); const rawPlusModeEnabled = typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled ? Boolean(inputPlusModeEnabled.checked) : Boolean(latestState?.plusModeEnabled); const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function' ? resolveCurrentSidepanelCapabilities({ targetId: typeof getSelectedTargetId === 'function' ? getSelectedTargetId(getSelectedFlowId(latestState)) : latestState?.targetId, signupMethod: typeof getSelectedSignupMethod === 'function' ? getSelectedSignupMethod() : latestState?.signupMethod, state: { ...(latestState || {}), phoneVerificationEnabled: rawEnabled, plusModeEnabled: rawPlusModeEnabled, }, }) : (() => { const rootScope = typeof window !== 'undefined' ? window : globalThis; const registry = rootScope.MultiPageFlowCapabilities?.createFlowCapabilityRegistry?.({ defaultFlowId: typeof DEFAULT_ACTIVE_FLOW_ID === 'string' ? DEFAULT_ACTIVE_FLOW_ID : 'openai', }) || null; return registry?.resolveSidepanelCapabilities ? registry.resolveSidepanelCapabilities({ activeFlowId: latestState?.activeFlowId, targetId: typeof getSelectedTargetId === 'function' ? getSelectedTargetId(getSelectedFlowId(latestState)) : latestState?.targetId, signupMethod: typeof getSelectedSignupMethod === 'function' ? getSelectedSignupMethod() : latestState?.signupMethod, state: { ...(latestState || {}), phoneVerificationEnabled: rawEnabled, plusModeEnabled: rawPlusModeEnabled, }, }) : null; })(); const canShowPhoneSettings = capabilityState ? Boolean(capabilityState.canShowPhoneSettings) : true; const enabled = canShowPhoneSettings && rawEnabled; const showSettings = enabled && phoneVerificationSectionExpanded; const selectedSignupMethodForPhoneSettings = typeof getSelectedSignupMethod === 'function' ? getSelectedSignupMethod() : normalizeSignupMethod(latestState?.signupMethod || DEFAULT_SIGNUP_METHOD); const showPhoneSignupReloginAfterBindEmail = showSettings && selectedSignupMethodForPhoneSettings === SIGNUP_METHOD_PHONE; const heroProviderValue = typeof PHONE_SMS_PROVIDER_HERO !== 'undefined' ? PHONE_SMS_PROVIDER_HERO : 'hero-sms'; const fiveSimProviderValue = typeof PHONE_SMS_PROVIDER_FIVE_SIM !== 'undefined' ? PHONE_SMS_PROVIDER_FIVE_SIM : '5sim'; const nexSmsProviderValue = typeof PHONE_SMS_PROVIDER_NEXSMS !== 'undefined' ? PHONE_SMS_PROVIDER_NEXSMS : 'nexsms'; const maDaoProviderValue = typeof PHONE_SMS_PROVIDER_MADAO !== 'undefined' ? PHONE_SMS_PROVIDER_MADAO : 'madao'; const providerOrderForDisplay = resolveNormalizedProviderOrderForRuntime(latestState || {}); const provider = ( Array.isArray(latestState?.phoneSmsProviderOrder) && latestState.phoneSmsProviderOrder.length > 0 ? providerOrderForDisplay[0] : null ) || ( typeof getSelectedPhoneSmsProvider === 'function' ? getSelectedPhoneSmsProvider() : normalizePhoneSmsProviderValue(selectPhoneSmsProvider?.value || latestState?.phoneSmsProvider || heroProviderValue) ); const heroProvider = provider === heroProviderValue; if (rowPhoneVerificationEnabled) { rowPhoneVerificationEnabled.style.display = canShowPhoneSettings ? '' : 'none'; } if (rowHeroSmsPlatform) { rowHeroSmsPlatform.style.display = canShowPhoneSettings ? '' : 'none'; } updateSignupMethodUI(); if (btnTogglePhoneVerificationSection) { btnTogglePhoneVerificationSection.disabled = !enabled; btnTogglePhoneVerificationSection.textContent = showSettings ? '收起设置' : '展开设置'; btnTogglePhoneVerificationSection.title = enabled ? (showSettings ? '收起接码设置' : '展开接码设置') : '开启接码后可展开设置'; btnTogglePhoneVerificationSection.setAttribute('aria-expanded', String(showSettings)); } if (rowPhoneVerificationFold) { rowPhoneVerificationFold.style.display = showSettings ? '' : 'none'; } const sharedPhoneVerificationRows = [ typeof rowPhoneSmsProvider !== 'undefined' ? rowPhoneSmsProvider : null, typeof rowPhoneSmsProviderOrder !== 'undefined' ? rowPhoneSmsProviderOrder : null, typeof rowPhoneSmsProviderOrderActions !== 'undefined' ? rowPhoneSmsProviderOrderActions : null, typeof rowPhoneCodeSettingsGroup !== 'undefined' ? rowPhoneCodeSettingsGroup : null, typeof rowPhoneVerificationResendCount !== 'undefined' ? rowPhoneVerificationResendCount : null, typeof rowPhoneReplacementLimit !== 'undefined' ? rowPhoneReplacementLimit : null, typeof rowPhoneCodeWaitSeconds !== 'undefined' ? rowPhoneCodeWaitSeconds : null, typeof rowPhoneCodeTimeoutWindows !== 'undefined' ? rowPhoneCodeTimeoutWindows : null, typeof rowPhoneCodePollIntervalSeconds !== 'undefined' ? rowPhoneCodePollIntervalSeconds : null, typeof rowPhoneCodePollMaxRounds !== 'undefined' ? rowPhoneCodePollMaxRounds : null, ]; sharedPhoneVerificationRows.forEach((row) => { if (row) { row.style.display = showSettings ? '' : 'none'; } }); getAllProviderUiRows().forEach((row) => { row.style.display = 'none'; }); getProviderUiRows(provider).forEach((row) => { row.style.display = showSettings ? '' : 'none'; }); updateProviderPriceControls(provider, showSettings); if (typeof rowPhoneSignupReloginAfterBindEmail !== 'undefined' && rowPhoneSignupReloginAfterBindEmail) { rowPhoneSignupReloginAfterBindEmail.style.display = showPhoneSignupReloginAfterBindEmail ? '' : 'none'; } if (typeof rowFreePhoneReuseEnabled !== 'undefined' && rowFreePhoneReuseEnabled) { rowFreePhoneReuseEnabled.style.display = showSettings && heroProvider ? '' : 'none'; } if (typeof rowFreePhoneReuseAutoEnabled !== 'undefined' && rowFreePhoneReuseAutoEnabled) { rowFreePhoneReuseAutoEnabled.style.display = showSettings && heroProvider ? '' : 'none'; } const phoneSignupReuseLocked = typeof isPhoneSignupReuseLocked === 'function' ? isPhoneSignupReuseLocked(latestState, { signupMethod: typeof getSelectedSignupMethod === 'function' ? getSelectedSignupMethod() : latestState?.signupMethod, }) : false; if (!phoneSignupReuseLocked && phoneSignupReuseUiWasLocked) { restorePhoneReuseControlsFromState(latestState); } if (phoneSignupReuseLocked) { if (typeof inputHeroSmsReuseEnabled !== 'undefined' && inputHeroSmsReuseEnabled) { inputHeroSmsReuseEnabled.checked = false; } if (typeof inputFreePhoneReuseEnabled !== 'undefined' && inputFreePhoneReuseEnabled) { inputFreePhoneReuseEnabled.checked = false; } if (typeof inputFreePhoneReuseAutoEnabled !== 'undefined' && inputFreePhoneReuseAutoEnabled) { inputFreePhoneReuseAutoEnabled.checked = false; } } phoneSignupReuseUiWasLocked = phoneSignupReuseLocked; const settingsLocked = isAutoRunLockedPhase(); if (typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail) { inputPhoneSignupReloginAfterBindEmail.disabled = settingsLocked || !showPhoneSignupReloginAfterBindEmail; } if (typeof rowPhoneSignupReloginAfterBindEmail !== 'undefined' && rowPhoneSignupReloginAfterBindEmail) { rowPhoneSignupReloginAfterBindEmail.classList.toggle('is-disabled', settingsLocked || !showPhoneSignupReloginAfterBindEmail); } const freePhoneReuseEnabled = Boolean( heroProvider && showSettings && !phoneSignupReuseLocked && typeof inputFreePhoneReuseEnabled !== 'undefined' && inputFreePhoneReuseEnabled?.checked ); const freePhoneReuseAutoAvailable = showSettings && heroProvider && !phoneSignupReuseLocked && freePhoneReuseEnabled; if (typeof inputHeroSmsReuseEnabled !== 'undefined' && inputHeroSmsReuseEnabled) { inputHeroSmsReuseEnabled.disabled = settingsLocked || phoneSignupReuseLocked; } if (typeof inputFreePhoneReuseAutoEnabled !== 'undefined' && inputFreePhoneReuseAutoEnabled) { inputFreePhoneReuseAutoEnabled.disabled = settingsLocked || phoneSignupReuseLocked || !freePhoneReuseAutoAvailable; if (!freePhoneReuseAutoAvailable) { inputFreePhoneReuseAutoEnabled.checked = false; } } setFreePhoneReuseControlsLocked(settingsLocked || phoneSignupReuseLocked); if (typeof selectHeroSmsPreferredActivation !== 'undefined' && selectHeroSmsPreferredActivation) { selectHeroSmsPreferredActivation.disabled = settingsLocked || phoneSignupReuseLocked; } if (typeof inputFreeReusablePhone !== 'undefined' && inputFreeReusablePhone) { inputFreeReusablePhone.disabled = settingsLocked || phoneSignupReuseLocked; } if (typeof btnSaveFreeReusablePhone !== 'undefined' && btnSaveFreeReusablePhone) { btnSaveFreeReusablePhone.disabled = settingsLocked || phoneSignupReuseLocked; } if (typeof btnClearFreeReusablePhone !== 'undefined' && btnClearFreeReusablePhone) { btnClearFreeReusablePhone.disabled = settingsLocked || phoneSignupReuseLocked; } const heroSmsReuseRow = typeof inputHeroSmsReuseEnabled !== 'undefined' && inputHeroSmsReuseEnabled?.closest ? inputHeroSmsReuseEnabled.closest('.hero-sms-price-control') : null; setElementReuseLockedState(heroSmsReuseRow, phoneSignupReuseLocked); setElementReuseLockedState( typeof rowFreePhoneReuseEnabled !== 'undefined' ? rowFreePhoneReuseEnabled : null, phoneSignupReuseLocked ); setElementReuseLockedState( typeof rowFreePhoneReuseAutoEnabled !== 'undefined' ? rowFreePhoneReuseAutoEnabled : null, phoneSignupReuseLocked ); setElementReuseLockedState( typeof rowHeroSmsPreferredActivation !== 'undefined' ? rowHeroSmsPreferredActivation : null, phoneSignupReuseLocked ); setElementReuseLockedState( typeof rowFreeReusablePhone !== 'undefined' ? rowFreeReusablePhone : null, phoneSignupReuseLocked ); if (typeof rowFreePhoneReuseAutoEnabled !== 'undefined' && rowFreePhoneReuseAutoEnabled) { rowFreePhoneReuseAutoEnabled.classList.toggle('is-disabled', phoneSignupReuseLocked || !freePhoneReuseAutoAvailable); } const runtimeVisible = enabled; [ typeof rowHeroSmsRuntimePair !== 'undefined' ? rowHeroSmsRuntimePair : null, typeof rowHeroSmsCurrentNumber !== 'undefined' ? rowHeroSmsCurrentNumber : null, typeof rowHeroSmsCurrentCountdown !== 'undefined' ? rowHeroSmsCurrentCountdown : null, typeof rowHeroSmsCurrentCode !== 'undefined' ? rowHeroSmsCurrentCode : null, typeof rowFreeReusablePhone !== 'undefined' ? rowFreeReusablePhone : null, typeof rowHeroSmsPreferredActivation !== 'undefined' ? rowHeroSmsPreferredActivation : null, ].forEach((row) => { if (row) { row.style.display = runtimeVisible ? '' : 'none'; } }); if (typeof syncSignupPhoneInputFromState === 'function') { syncSignupPhoneInputFromState(latestState); } if (!showSettings && typeof rowHeroSmsPriceTiers !== 'undefined' && rowHeroSmsPriceTiers) { rowHeroSmsPriceTiers.style.display = 'none'; } updateHeroSmsPlatformDisplay(); } function updatePlusModeUI() { const paypalValue = typeof PLUS_PAYMENT_METHOD_PAYPAL !== 'undefined' ? PLUS_PAYMENT_METHOD_PAYPAL : 'paypal'; const paypalHostedValue = typeof PLUS_PAYMENT_METHOD_PAYPAL_HOSTED !== 'undefined' ? PLUS_PAYMENT_METHOD_PAYPAL_HOSTED : 'paypal-hosted'; const noneValue = typeof PLUS_PAYMENT_METHOD_NONE !== 'undefined' ? PLUS_PAYMENT_METHOD_NONE : 'none'; const gopayValue = typeof PLUS_PAYMENT_METHOD_GOPAY !== 'undefined' ? PLUS_PAYMENT_METHOD_GOPAY : 'gopay'; const gpcValue = typeof PLUS_PAYMENT_METHOD_GPC_HELPER !== 'undefined' ? PLUS_PAYMENT_METHOD_GPC_HELPER : 'gpc-helper'; const oauthStrategyValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH !== 'undefined' ? PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH : 'oauth'; const sub2apiSessionStrategyValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION !== 'undefined' ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : 'sub2api_codex_session'; const cpaSessionStrategyValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION !== 'undefined' ? PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION : 'cpa_codex_session'; const sessionUiStrategyValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_CODEX_SESSION_UI !== 'undefined' ? PLUS_ACCOUNT_ACCESS_STRATEGY_CODEX_SESSION_UI : 'codex_session'; const defaultMethod = typeof DEFAULT_PLUS_PAYMENT_METHOD !== 'undefined' ? DEFAULT_PLUS_PAYMENT_METHOD : paypalValue; const resolveStrategyTargetId = typeof normalizePlusStrategyTargetId === 'function' ? normalizePlusStrategyTargetId : ((value = '') => { const normalized = String(value || '').trim().toLowerCase(); if (normalized === 'sub2api') { return 'sub2api'; } if (normalized === 'codex2api') { return 'codex2api'; } return 'cpa'; }); const describePlusAccountAccessStrategy = typeof getPlusAccountAccessStrategyDescription === 'function' ? getPlusAccountAccessStrategyDescription : ((strategy = '', targetId = '') => { const normalizedStrategy = normalizePlusAccountAccessStrategy(strategy); const normalizedTargetId = resolveStrategyTargetId(targetId); if (normalizedStrategy === sub2apiSessionStrategyValue) { return '复用当前 Plus 已登录会话,直接导入到 SUB2API'; } if (normalizedStrategy === cpaSessionStrategyValue) { return '复用当前 Plus 已登录会话,直接导入到 CPA'; } if (normalizedTargetId === 'sub2api') { return '通过 OAuth 回调创建 SUB2API 账号'; } if (normalizedTargetId === 'codex2api') { return '通过 OAuth 回调创建 Codex2API 账号'; } return '通过 OAuth 回调创建 CPA 账号'; }); const normalizeStrategyUiValue = typeof normalizePlusAccountAccessStrategyUiValue === 'function' ? normalizePlusAccountAccessStrategyUiValue : ((value = '') => { const normalized = String(value || '').trim().toLowerCase(); if ( normalized === sessionUiStrategyValue || normalized === sub2apiSessionStrategyValue || normalized === cpaSessionStrategyValue ) { return sessionUiStrategyValue; } return oauthStrategyValue; }); const requestedPlusAccountAccessStrategy = typeof getRequestedPlusAccountAccessStrategy === 'function' ? getRequestedPlusAccountAccessStrategy(latestState) : normalizePlusAccountAccessStrategy(latestState?.plusAccountAccessStrategy || DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY); const rawEnabled = typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled ? Boolean(inputPlusModeEnabled.checked) : false; const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function' ? resolveCurrentSidepanelCapabilities({ targetId: typeof getSelectedTargetId === 'function' ? getSelectedTargetId(getSelectedFlowId(latestState)) : latestState?.targetId, state: { ...(latestState || {}), plusModeEnabled: rawEnabled, plusAccountAccessStrategy: requestedPlusAccountAccessStrategy, }, }) : (() => { const rootScope = typeof window !== 'undefined' ? window : globalThis; const registry = rootScope.MultiPageFlowCapabilities?.createFlowCapabilityRegistry?.({ defaultFlowId: typeof DEFAULT_ACTIVE_FLOW_ID === 'string' ? DEFAULT_ACTIVE_FLOW_ID : 'openai', }) || null; return registry?.resolveSidepanelCapabilities ? registry.resolveSidepanelCapabilities({ activeFlowId: latestState?.activeFlowId, targetId: typeof getSelectedTargetId === 'function' ? getSelectedTargetId(getSelectedFlowId(latestState)) : latestState?.targetId, state: { ...(latestState || {}), plusModeEnabled: rawEnabled, plusAccountAccessStrategy: requestedPlusAccountAccessStrategy, }, }) : null; })(); const supportsPlusMode = capabilityState ? Boolean(capabilityState.canShowPlusSettings) : true; const enabled = supportsPlusMode && rawEnabled; const canEditPlusAccountAccessStrategy = Boolean(capabilityState?.canEditPlusAccountAccessStrategy); const availablePlusAccountAccessStrategies = Array.isArray(capabilityState?.availablePlusAccountAccessStrategies) && capabilityState.availablePlusAccountAccessStrategies.length > 0 ? capabilityState.availablePlusAccountAccessStrategies : [oauthStrategyValue]; const effectivePlusAccountAccessStrategy = capabilityState?.effectivePlusAccountAccessStrategy || requestedPlusAccountAccessStrategy || oauthStrategyValue; const effectiveTargetId = resolveStrategyTargetId( capabilityState?.effectiveTargetId || (typeof getSelectedPanelMode === 'function' ? getSelectedPanelMode(latestState) : latestState?.targetId) || 'cpa' ); const method = enabled ? getSelectedPlusPaymentMethod() : defaultMethod; const selectedMethod = typeof selectPlusPaymentMethod !== 'undefined' && selectPlusPaymentMethod?.value ? normalizePlusPaymentMethod(selectPlusPaymentMethod.value) : method; const hostedRowsVisible = enabled && selectedMethod === paypalHostedValue; const gpcRowsVisible = enabled && selectedMethod === gpcValue; if (typeof rowPlusMode !== 'undefined' && rowPlusMode) { rowPlusMode.style.display = supportsPlusMode ? '' : 'none'; } if (typeof selectPlusPaymentMethod !== 'undefined' && selectPlusPaymentMethod) { selectPlusPaymentMethod.value = method; if (selectPlusPaymentMethod.style) { selectPlusPaymentMethod.style.display = enabled ? '' : 'none'; } } if (typeof plusPaymentMethodCaption !== 'undefined' && plusPaymentMethodCaption) { plusPaymentMethodCaption.textContent = method === gpcValue ? 'GPC 网页充值链路' : method === gopayValue ? 'GoPay 印尼订阅链路' : method === noneValue ? '已有 Plus,无需配置支付链路' : method === paypalHostedValue ? 'PayPal 无卡直绑链路' : 'PayPal 订阅链路'; } [ typeof rowPlusPaymentMethod !== 'undefined' ? rowPlusPaymentMethod : null, ].forEach((row) => { if (!row) { return; } row.style.display = enabled ? '' : 'none'; }); [ typeof rowPlusAccountAccessStrategy !== 'undefined' ? rowPlusAccountAccessStrategy : null, ].forEach((row) => { if (!row) { return; } row.style.display = enabled ? '' : 'none'; }); if (typeof selectPlusAccountAccessStrategy !== 'undefined' && selectPlusAccountAccessStrategy) { const availableStrategyUiValueSet = new Set(availablePlusAccountAccessStrategies.map(normalizeStrategyUiValue)); Array.from(selectPlusAccountAccessStrategy.options || []).forEach((option) => { const optionValue = normalizeStrategyUiValue(option?.value || ''); const optionSupported = availableStrategyUiValueSet.has(optionValue); option.hidden = enabled ? !optionSupported : false; option.disabled = enabled ? !optionSupported : false; }); selectPlusAccountAccessStrategy.dataset.requestedValue = capabilityState?.runtimeLocks?.accountContribution ? requestedPlusAccountAccessStrategy : effectivePlusAccountAccessStrategy; selectPlusAccountAccessStrategy.value = normalizeStrategyUiValue(effectivePlusAccountAccessStrategy); selectPlusAccountAccessStrategy.disabled = !enabled || !canEditPlusAccountAccessStrategy; selectPlusAccountAccessStrategy.setAttribute('aria-disabled', String(selectPlusAccountAccessStrategy.disabled)); } if (typeof plusAccountAccessStrategyCaption !== 'undefined' && plusAccountAccessStrategyCaption) { if (!enabled) { plusAccountAccessStrategyCaption.textContent = '当前来源仅支持 OAuth'; } else if (!canEditPlusAccountAccessStrategy) { plusAccountAccessStrategyCaption.textContent = '当前来源仅支持 OAuth'; } else if (effectivePlusAccountAccessStrategy === sub2apiSessionStrategyValue) { plusAccountAccessStrategyCaption.textContent = '复用当前 Plus 已登录会话,直接导入到 SUB2API'; } else if (effectivePlusAccountAccessStrategy === oauthStrategyValue) { plusAccountAccessStrategyCaption.textContent = '通过 OAuth 回调创建 SUB2API 账号'; } else { plusAccountAccessStrategyCaption.textContent = '当前来源仅支持 OAuth'; } } if (typeof plusAccountAccessStrategyCaption !== 'undefined' && plusAccountAccessStrategyCaption) { if (!enabled || !canEditPlusAccountAccessStrategy) { plusAccountAccessStrategyCaption.textContent = '当前来源仅支持 OAuth'; } else { plusAccountAccessStrategyCaption.textContent = describePlusAccountAccessStrategy( effectivePlusAccountAccessStrategy, effectiveTargetId ); } } if (typeof plusAccountAccessStrategyCaption !== 'undefined' && plusAccountAccessStrategyCaption) { plusAccountAccessStrategyCaption.textContent = !enabled ? '当前来源仅支持 OAuth' : ((effectivePlusAccountAccessStrategy !== oauthStrategyValue || canEditPlusAccountAccessStrategy) ? describePlusAccountAccessStrategy( effectivePlusAccountAccessStrategy, effectiveTargetId ) : '当前来源仅支持 OAuth'); } if (enabled && effectivePlusAccountAccessStrategy === sub2apiSessionStrategyValue) { [ typeof rowSub2ApiUrl !== 'undefined' ? rowSub2ApiUrl : null, typeof rowSub2ApiEmail !== 'undefined' ? rowSub2ApiEmail : null, typeof rowSub2ApiPassword !== 'undefined' ? rowSub2ApiPassword : null, typeof rowSub2ApiGroup !== 'undefined' ? rowSub2ApiGroup : null, typeof rowSub2ApiAccountPriority !== 'undefined' ? rowSub2ApiAccountPriority : null, typeof rowSub2ApiDefaultProxy !== 'undefined' ? rowSub2ApiDefaultProxy : null, ].forEach((row) => { if (row) row.style.display = ''; }); } if (enabled && effectivePlusAccountAccessStrategy === cpaSessionStrategyValue) { [ typeof rowVpsUrl !== 'undefined' ? rowVpsUrl : null, typeof rowVpsPassword !== 'undefined' ? rowVpsPassword : null, typeof rowLocalCpaStep9Mode !== 'undefined' ? rowLocalCpaStep9Mode : null, ].forEach((row) => { if (row) row.style.display = ''; }); } [ typeof rowPayPalAccount !== 'undefined' ? rowPayPalAccount : null, ].forEach((row) => { if (!row) { return; } row.style.display = enabled && selectedMethod === paypalValue ? '' : 'none'; }); [ typeof rowHostedCheckoutVerificationUrl !== 'undefined' ? rowHostedCheckoutVerificationUrl : null, typeof rowHostedCheckoutPhone !== 'undefined' ? rowHostedCheckoutPhone : null, typeof rowPlusHostedCheckoutOauthDelay !== 'undefined' ? rowPlusHostedCheckoutOauthDelay : null, ].forEach((row) => { if (!row) { return; } row.style.display = hostedRowsVisible ? '' : 'none'; }); if (typeof rowGpcCardKey !== 'undefined' && rowGpcCardKey) { rowGpcCardKey.style.display = gpcRowsVisible ? '' : 'none'; } if (typeof btnGpcCardKeyPurchase !== 'undefined' && btnGpcCardKeyPurchase) { btnGpcCardKeyPurchase.style.display = gpcRowsVisible ? '' : 'none'; } [ typeof rowGoPayCountryCode !== 'undefined' ? rowGoPayCountryCode : null, typeof rowGoPayPhone !== 'undefined' ? rowGoPayPhone : null, typeof rowGoPayOtp !== 'undefined' ? rowGoPayOtp : null, typeof rowGoPayPin !== 'undefined' ? rowGoPayPin : null, ].forEach((row) => { if (!row) { return; } row.style.display = enabled && selectedMethod === gopayValue ? '' : 'none'; }); } function setSettingsCardLocked(locked) { if (!settingsCard) { return; } settingsCard.classList.toggle('is-locked', locked); settingsCard.toggleAttribute('inert', false); Array.from(settingsCard.children).forEach((child) => { const keepInteractive = child?.id === 'row-custom-email-pool'; child.toggleAttribute('inert', Boolean(locked && !keepInteractive)); }); } function setFreePhoneReuseControlsLocked(locked) { if (inputFreePhoneReuseEnabled) { inputFreePhoneReuseEnabled.disabled = locked; } if (inputFreePhoneReuseAutoEnabled) { inputFreePhoneReuseAutoEnabled.disabled = locked || !Boolean(inputFreePhoneReuseEnabled?.checked) || !Boolean(inputPhoneVerificationEnabled?.checked && phoneVerificationSectionExpanded); } } async function setRuntimeEmailState(email) { const normalizedEmail = String(email || '').trim() || null; const response = await chrome.runtime.sendMessage({ type: 'SET_EMAIL_STATE', source: 'sidepanel', payload: { email: normalizedEmail }, }); if (response?.error) { throw new Error(response.error); } return normalizedEmail; } function getRuntimeSignupPhoneValue(state = latestState) { const identifierType = String(state?.accountIdentifierType || '').trim().toLowerCase(); return String( state?.signupPhoneNumber || (identifierType === 'phone' ? state?.accountIdentifier : '') || '' ).trim(); } function shouldExecuteStep3WithSignupPhoneIdentity(state = latestState) { const identifierType = String(state?.accountIdentifierType || '').trim().toLowerCase(); const resolvedMethod = normalizeSignupMethod( state?.resolvedSignupMethod || state?.signupMethod || (typeof getSelectedSignupMethod === 'function' ? getSelectedSignupMethod() : DEFAULT_SIGNUP_METHOD) ); return identifierType === 'phone' || Boolean(getRuntimeSignupPhoneValue(state)) || resolvedMethod === SIGNUP_METHOD_PHONE; } function getSignupPhoneInputValue() { return typeof inputSignupPhone !== 'undefined' && inputSignupPhone ? String(inputSignupPhone.value || '').trim() : ''; } function shouldPreserveSignupPhoneInputValue(stateSignupPhone = '') { if (typeof inputSignupPhone === 'undefined' || !inputSignupPhone || !signupPhoneInputDirty) { return false; } if (getSignupPhoneInputValue() === String(stateSignupPhone || '').trim()) { signupPhoneInputDirty = false; return false; } return signupPhoneInputFocused || (typeof document !== 'undefined' && document.activeElement === inputSignupPhone); } function syncSignupPhoneInputFromState(state = latestState) { const signupPhone = getRuntimeSignupPhoneValue(state); if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) { if (!shouldPreserveSignupPhoneInputValue(signupPhone)) { inputSignupPhone.value = signupPhone; } } if (typeof rowSignupPhone !== 'undefined' && rowSignupPhone) { const phoneVerificationEnabled = typeof inputPhoneVerificationEnabled !== 'undefined' && inputPhoneVerificationEnabled ? Boolean(inputPhoneVerificationEnabled.checked) : Boolean(state?.phoneVerificationEnabled || latestState?.phoneVerificationEnabled); const rawSignupMethod = state?.signupMethod || ( typeof getSelectedSignupMethod === 'function' ? getSelectedSignupMethod() : 'email' ); const selectedMethod = typeof normalizeSignupMethod === 'function' ? normalizeSignupMethod(rawSignupMethod) : (String(rawSignupMethod || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email'); const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function' ? resolveCurrentSidepanelCapabilities({ targetId: typeof getSelectedTargetIdForState === 'function' ? getSelectedTargetIdForState({ ...(latestState || {}), ...(state || {}), }, state?.activeFlowId || latestState?.activeFlowId || DEFAULT_ACTIVE_FLOW_ID) : (state?.targetId || latestState?.targetId), signupMethod: selectedMethod, state: { ...(latestState || {}), ...(state || {}), phoneVerificationEnabled, }, }) : (() => { const rootScope = typeof window !== 'undefined' ? window : globalThis; const registry = rootScope.MultiPageFlowCapabilities?.createFlowCapabilityRegistry?.({ defaultFlowId: typeof DEFAULT_ACTIVE_FLOW_ID === 'string' ? DEFAULT_ACTIVE_FLOW_ID : 'openai', }) || null; return registry?.resolveSidepanelCapabilities ? registry.resolveSidepanelCapabilities({ activeFlowId: state?.activeFlowId || latestState?.activeFlowId, targetId: typeof getSelectedTargetIdForState === 'function' ? getSelectedTargetIdForState({ ...(latestState || {}), ...(state || {}), }, state?.activeFlowId || latestState?.activeFlowId || DEFAULT_ACTIVE_FLOW_ID) : (state?.targetId || latestState?.targetId), signupMethod: selectedMethod, state: { ...(latestState || {}), ...(state || {}), phoneVerificationEnabled, }, }) : null; })(); const canShowPhoneSettings = capabilityState ? Boolean(capabilityState.canShowPhoneSettings) : true; rowSignupPhone.style.display = canShowPhoneSettings && phoneVerificationEnabled && (selectedMethod === 'phone' || Boolean(signupPhone) || Boolean(getSignupPhoneInputValue()) || signupPhoneInputDirty) ? '' : 'none'; } } async function setRuntimeSignupPhoneState(phoneNumber) { const normalizedPhone = String(phoneNumber || '').trim() || null; const response = await chrome.runtime.sendMessage({ type: 'SET_SIGNUP_PHONE_STATE', source: 'sidepanel', payload: { phoneNumber: normalizedPhone }, }); if (response?.error) { throw new Error(response.error); } return normalizedPhone; } async function persistSignupPhoneInputValue(options = {}) { const { final = true, silent = true } = options; if (typeof inputSignupPhone === 'undefined' || !inputSignupPhone) { return getRuntimeSignupPhoneValue(latestState); } if (signupPhoneInputPersistPromise) { return signupPhoneInputPersistPromise; } const phoneNumber = getSignupPhoneInputValue(); inputSignupPhone.value = phoneNumber; const currentPhone = getRuntimeSignupPhoneValue(latestState); if (!signupPhoneInputDirty && phoneNumber === currentPhone) { return phoneNumber; } signupPhoneInputPersistPromise = (async () => { const response = await chrome.runtime.sendMessage({ type: final ? 'SAVE_SIGNUP_PHONE' : 'SET_SIGNUP_PHONE_STATE', source: 'sidepanel', payload: { phoneNumber }, }); if (response?.error) { throw new Error(response.error); } const normalizedPhone = String(response?.phoneNumber || phoneNumber || '').trim(); signupPhoneInputDirty = getSignupPhoneInputValue() !== normalizedPhone; syncLatestState({ signupPhoneNumber: normalizedPhone, phoneNumber: '', ...(normalizedPhone ? { accountIdentifierType: 'phone', accountIdentifier: normalizedPhone, } : (String(latestState?.accountIdentifierType || '').trim().toLowerCase() === 'phone' ? { accountIdentifierType: null, accountIdentifier: '', } : {})), }); syncSignupPhoneInputFromState(latestState); if (!silent) { showToast(normalizedPhone ? '注册手机号已保存。' : '注册手机号已清空。', 'success', 1600); } return normalizedPhone; })(); try { return await signupPhoneInputPersistPromise; } finally { signupPhoneInputPersistPromise = null; } } async function persistSignupPhoneInputForAction() { if (typeof inputSignupPhone === 'undefined' || !inputSignupPhone) { return; } const phoneNumber = getSignupPhoneInputValue(); const currentPhone = getRuntimeSignupPhoneValue(latestState); if (!signupPhoneInputDirty && phoneNumber === currentPhone) { return; } await persistSignupPhoneInputValue({ final: true, silent: true }); } function isGpcHelperCheckoutSelected() { const gpcValue = typeof PLUS_PAYMENT_METHOD_GPC_HELPER !== 'undefined' ? PLUS_PAYMENT_METHOD_GPC_HELPER : 'gpc-helper'; const plusEnabled = typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled ? Boolean(inputPlusModeEnabled.checked) : Boolean(latestState?.plusModeEnabled); return plusEnabled && getSelectedPlusPaymentMethod() === gpcValue; } function normalizeGpcCardKeyInput(value = '') { if (window.GoPayUtils?.normalizeGpcCardKey) { return window.GoPayUtils.normalizeGpcCardKey(value); } return String(value || '').trim().toUpperCase(); } function isGpcCardKeyInputFormat(value = '') { if (window.GoPayUtils?.isGpcCardKeyFormat) { return window.GoPayUtils.isGpcCardKeyFormat(value); } return /^GPC-[A-F0-9]{8}-[A-F0-9]{8}-[A-F0-9]{8}$/.test(normalizeGpcCardKeyInput(value)); } function setGpcCardKeyStatus(message = '', tone = '') { if (!displayGpcCardKeyStatus) { return; } displayGpcCardKeyStatus.textContent = message || '等待输入'; displayGpcCardKeyStatus.dataset.tone = tone || ''; } function formatGpcCardKeyBalanceStatus(result = {}) { const remaining = result?.remainingUses; if (remaining !== undefined && remaining !== null && String(remaining).trim() !== '') { return `剩余 ${remaining} 次`; } return String(result?.balance || '').trim() || '卡密可用'; } async function refreshGpcCardKeyStatus(options = {}) { const rawCardKey = String(inputGpcCardKey?.value || '').trim(); const cardKey = normalizeGpcCardKeyInput(rawCardKey); if (!rawCardKey) { setGpcCardKeyStatus('等待输入', ''); return null; } if (inputGpcCardKey && inputGpcCardKey.value !== cardKey) { inputGpcCardKey.value = cardKey; } if (!isGpcCardKeyInputFormat(cardKey)) { setGpcCardKeyStatus('格式应为 GPC-XXXXXXXX-XXXXXXXX-XXXXXXXX', 'error'); return null; } const requestId = (refreshGpcCardKeyStatus.requestId || 0) + 1; refreshGpcCardKeyStatus.requestId = requestId; setGpcCardKeyStatus('正在检测...', 'running'); try { const response = await sendSidepanelMessage({ type: 'REFRESH_GPC_CARD_BALANCE', payload: { gpcCardKey: cardKey, reason: options.reason || 'input', }, }); if (requestId !== refreshGpcCardKeyStatus.requestId) { return response; } if (response?.error) { throw new Error(response.error); } setGpcCardKeyStatus(formatGpcCardKeyBalanceStatus(response), 'ok'); syncLatestState({ gpcCardKey: cardKey, gpcBalance: response?.balance || latestState?.gpcBalance || '', gpcBalancePayload: response?.data || latestState?.gpcBalancePayload || null, gpcBalanceUpdatedAt: response?.updatedAt || Date.now(), gpcBalanceError: '', gpcRemainingUses: Number(response?.remainingUses) || 0, gpcCardStatus: String(response?.cardStatus || '').trim(), }); return response; } catch (error) { if (requestId === refreshGpcCardKeyStatus.requestId) { setGpcCardKeyStatus(error?.message || '卡密检测失败', 'error'); } return null; } } function scheduleGpcCardKeyStatusRefresh() { clearTimeout(scheduleGpcCardKeyStatusRefresh.timer); scheduleGpcCardKeyStatusRefresh.timer = setTimeout(() => { refreshGpcCardKeyStatus({ reason: 'input' }).catch(() => { }); }, 1000); } async function showGpcStartBlockedDialog(message) { await openConfirmModal({ title: 'GPC 页面流程无法开启', message, confirmLabel: '知道了', }); } async function ensureGpcCardKeyReadyForStart(options = {}) { if (!isGpcHelperCheckoutSelected()) { return true; } const cardKey = normalizeGpcCardKeyInput(inputGpcCardKey?.value || latestState?.gpcCardKey || ''); if (!cardKey) { await showGpcStartBlockedDialog('请先填写 GPC 卡密。'); return false; } if (!isGpcCardKeyInputFormat(cardKey)) { await showGpcStartBlockedDialog('GPC 卡密格式不正确,应类似 GPC-6C9F1A32-45734795-914E6F00。'); setGpcCardKeyStatus('格式应为 GPC-XXXXXXXX-XXXXXXXX-XXXXXXXX', 'error'); return false; } if (inputGpcCardKey) { inputGpcCardKey.value = cardKey; } if (options?.notify) { showToast('GPC 卡密已填写。', 'success', 1800); } return true; } async function openPlusManualConfirmationDialog(options = {}) { const method = String(options.method || '').trim().toLowerCase(); const gopayValue = typeof PLUS_PAYMENT_METHOD_GOPAY !== 'undefined' ? PLUS_PAYMENT_METHOD_GOPAY : 'gopay'; const activeFlowId = String(latestState?.activeFlowId || latestState?.flowId || 'openai').trim().toLowerCase(); const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function' ? normalizeTargetIdForFlow : ((flowId, targetId = '', fallback = '') => { const normalizedFlowId = String(flowId || '').trim().toLowerCase() || 'openai'; if (normalizedFlowId === 'openai') { const normalizedTargetId = String(targetId || fallback || '').trim().toLowerCase(); return normalizedTargetId === 'sub2api' || normalizedTargetId === 'codex2api' ? normalizedTargetId : 'cpa'; } const normalizedTargetId = String(targetId || '').trim().toLowerCase(); return normalizedTargetId || String(fallback || '').trim().toLowerCase() || 'kiro-rs'; }); const getDefaultTargetIdForFlowSafe = typeof getDefaultTargetIdForFlow === 'function' ? getDefaultTargetIdForFlow : ((flowId = 'openai') => (String(flowId || '').trim().toLowerCase() === 'openai' ? 'cpa' : 'kiro-rs')); const normalizePlusStrategyTargetIdSafe = typeof normalizePlusStrategyTargetId === 'function' ? normalizePlusStrategyTargetId : ((value = '') => { const normalized = String(value || '').trim().toLowerCase(); if (normalized === 'sub2api' || normalized === 'codex2api') { return normalized; } return 'cpa'; }); const targetId = normalizePlusStrategyTargetIdSafe( typeof getSelectedTargetIdForState === 'function' ? getSelectedTargetIdForState(latestState, activeFlowId) : normalizeTargetIdForFlowSafe( activeFlowId, latestState?.targetId || '', getDefaultTargetIdForFlowSafe(activeFlowId) ) ); const signupMethod = String(latestState?.resolvedSignupMethod || latestState?.signupMethod || 'email').trim().toLowerCase(); const plusModeEnabled = latestState?.plusModeEnabled === undefined ? true : Boolean(latestState.plusModeEnabled); const plusAccountAccessStrategy = String(latestState?.plusAccountAccessStrategy || 'oauth').trim().toLowerCase(); const useSub2ApiSessionImport = plusModeEnabled && activeFlowId === 'openai' && targetId === 'sub2api' && signupMethod === 'email' && plusAccountAccessStrategy === 'sub2api_codex_session'; const continuationActionLabel = useSub2ApiSessionImport ? '导入当前 ChatGPT 会话到 SUB2API' : 'OAuth 登录'; const title = String(options.title || '').trim() || (method === gopayValue ? 'GoPay 订阅确认' : '手动确认'); const message = String(options.message || '').trim() || (method === gopayValue ? '请在当前订阅页中手动完成 GoPay 订阅,完成后点击“我已完成订阅”继续。' : '请先在页面中完成当前手动操作,完成后点击确认继续。'); return openActionModal({ title, message, actions: [ { id: 'cancel', label: '取消等待', variant: 'btn-ghost' }, { id: 'confirm', label: '我已完成订阅', variant: 'btn-primary' }, ], alert: method === gopayValue ? { text: `确认后流程会直接继续到 Plus 模式后续的${continuationActionLabel}。`, tone: 'info' } : null, }); } async function syncPlusManualConfirmationDialog() { const gopayValue = typeof PLUS_PAYMENT_METHOD_GOPAY !== 'undefined' ? PLUS_PAYMENT_METHOD_GOPAY : 'gopay'; const requestId = String(latestState?.plusManualConfirmationRequestId || '').trim(); const pending = Boolean(latestState?.plusManualConfirmationPending); if (!pending || !requestId || plusManualConfirmationDialogInFlight || activePlusManualConfirmationRequestId === requestId) { return; } const step = Number(latestState?.plusManualConfirmationStep) || 0; const method = String(latestState?.plusManualConfirmationMethod || '').trim().toLowerCase(); const activeFlowId = String(latestState?.activeFlowId || latestState?.flowId || 'openai').trim().toLowerCase(); const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function' ? normalizeTargetIdForFlow : ((flowId, targetId = '', fallback = '') => { const normalizedFlowId = String(flowId || '').trim().toLowerCase() || 'openai'; if (normalizedFlowId === 'openai') { const normalizedTargetId = String(targetId || fallback || '').trim().toLowerCase(); return normalizedTargetId === 'sub2api' || normalizedTargetId === 'codex2api' ? normalizedTargetId : 'cpa'; } const normalizedTargetId = String(targetId || '').trim().toLowerCase(); return normalizedTargetId || String(fallback || '').trim().toLowerCase() || 'kiro-rs'; }); const getDefaultTargetIdForFlowSafe = typeof getDefaultTargetIdForFlow === 'function' ? getDefaultTargetIdForFlow : ((flowId = 'openai') => (String(flowId || '').trim().toLowerCase() === 'openai' ? 'cpa' : 'kiro-rs')); const normalizePlusStrategyTargetIdSafe = typeof normalizePlusStrategyTargetId === 'function' ? normalizePlusStrategyTargetId : ((value = '') => { const normalized = String(value || '').trim().toLowerCase(); if (normalized === 'sub2api' || normalized === 'codex2api') { return normalized; } return 'cpa'; }); const targetId = normalizePlusStrategyTargetIdSafe( typeof getSelectedTargetIdForState === 'function' ? getSelectedTargetIdForState(latestState, activeFlowId) : normalizeTargetIdForFlowSafe( activeFlowId, latestState?.targetId || '', getDefaultTargetIdForFlowSafe(activeFlowId) ) ); const signupMethod = String(latestState?.resolvedSignupMethod || latestState?.signupMethod || 'email').trim().toLowerCase(); const plusModeEnabled = latestState?.plusModeEnabled === undefined ? true : Boolean(latestState.plusModeEnabled); const plusAccountAccessStrategy = String(latestState?.plusAccountAccessStrategy || 'oauth').trim().toLowerCase(); const useSub2ApiSessionImport = plusModeEnabled && activeFlowId === 'openai' && targetId === 'sub2api' && signupMethod === 'email' && plusAccountAccessStrategy === 'sub2api_codex_session'; const continuationActionLabel = useSub2ApiSessionImport ? '导入当前 ChatGPT 会话到 SUB2API' : 'OAuth 登录'; const title = latestState?.plusManualConfirmationTitle; const message = latestState?.plusManualConfirmationMessage; activePlusManualConfirmationRequestId = requestId; plusManualConfirmationDialogInFlight = true; let shouldReopenDialog = false; try { const choice = await openPlusManualConfirmationDialog({ method, title, message, }); const currentRequestId = String(latestState?.plusManualConfirmationRequestId || '').trim(); const stillPending = Boolean(latestState?.plusManualConfirmationPending); if (!stillPending || currentRequestId !== requestId) { return; } if (choice == null) { shouldReopenDialog = true; showToast('当前订阅确认仍在等待中,将重新弹出确认窗口。', 'info', 1800); return; } const confirmed = choice === 'confirm'; const response = await chrome.runtime.sendMessage({ type: 'RESOLVE_PLUS_MANUAL_CONFIRMATION', source: 'sidepanel', payload: { step, requestId, confirmed, }, }); if (response?.error) { throw new Error(response.error); } if (confirmed) { showToast(method === gopayValue ? `GoPay 订阅已确认,正在继续${continuationActionLabel}...` : '已确认,流程继续执行中...', 'info', 2200); } else { showToast(method === gopayValue ? '已取消 GoPay 订阅等待。' : '已取消当前手动确认。', 'warn', 2200); } } catch (error) { showToast(error?.message || String(error || '未知错误'), 'error'); } finally { if (activePlusManualConfirmationRequestId === requestId) { activePlusManualConfirmationRequestId = ''; } plusManualConfirmationDialogInFlight = false; if ( shouldReopenDialog && latestState?.plusManualConfirmationPending && String(latestState?.plusManualConfirmationRequestId || '').trim() === requestId ) { setTimeout(() => { void syncPlusManualConfirmationDialog(); }, 0); } } } async function openPlusManualConfirmationDialog(options = {}) { const method = String(options.method || '').trim().toLowerCase(); const gopayValue = typeof PLUS_PAYMENT_METHOD_GOPAY !== 'undefined' ? PLUS_PAYMENT_METHOD_GOPAY : 'gopay'; const continuationActionLabel = resolvePlusManualContinuationActionLabelFromState(latestState); const title = String(options.title || '').trim() || (method === gopayValue ? 'GoPay subscription confirmation' : 'Manual confirmation'); const message = String(options.message || '').trim() || (method === gopayValue ? 'Complete the GoPay subscription on the current page, then continue.' : 'Finish the current manual action on the page, then continue.'); return openActionModal({ title, message, actions: [ { id: 'cancel', label: 'Cancel', variant: 'btn-ghost' }, { id: 'confirm', label: 'Continue', variant: 'btn-primary' }, ], alert: method === gopayValue ? { text: `After confirmation, the Plus flow will continue with ${continuationActionLabel}.`, tone: 'info' } : null, }); } async function clearRegistrationEmail(options = {}) { const { silent = false } = options; if (!inputEmail.value.trim() && !latestState?.email) { return; } inputEmail.value = ''; syncLatestState({ email: null }); try { await setRuntimeEmailState(null); } catch (err) { if (!silent) { showToast(`清空邮箱失败:${err.message}`, 'error'); } throw err; } } async function clearRegistrationSignupPhone(options = {}) { const { silent = false } = options; if (!getRuntimeSignupPhoneValue(latestState)) { if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) { inputSignupPhone.value = ''; } signupPhoneInputDirty = false; syncSignupPhoneInputFromState(latestState); return; } if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) { inputSignupPhone.value = ''; } signupPhoneInputDirty = false; syncLatestState({ signupPhoneNumber: '', ...(String(latestState?.accountIdentifierType || '').trim().toLowerCase() === 'phone' ? { accountIdentifierType: null, accountIdentifier: '', } : {}), }); syncSignupPhoneInputFromState(latestState); try { await setRuntimeSignupPhoneState(null); } catch (err) { if (!silent) { showToast(`清空注册手机号失败:${err.message}`, 'error'); } throw err; } } function markSettingsDirty(isDirty = true) { settingsDirty = isDirty; if (isDirty) { settingsSaveRevision += 1; } updateSettingsSaveState(); } function updateSettingsSaveState() { updateConfigMenuControls(); } function isEditableElementInSettingsCard(element) { if (!element || !(element instanceof Element)) { return false; } const tagName = String(element.tagName || '').toLowerCase(); const isEditableInput = ( tagName === 'textarea' || (tagName === 'input' && !['checkbox', 'radio', 'button', 'submit', 'reset', 'range', 'file', 'color'].includes(String(element.type || '').toLowerCase())) || Boolean(element.isContentEditable) ); if (!isEditableInput) { return false; } return !settingsCard || settingsCard.contains(element); } function scheduleSettingsAutoSave() { clearTimeout(settingsAutoSaveTimer); settingsAutoSaveTimer = setTimeout(() => { saveSettings({ silent: true, source: 'autosave' }).catch(() => { }); }, 1200); } async function saveSettings(options = {}) { const { silent = false, force = false, source = '' } = options; clearTimeout(settingsAutoSaveTimer); if (!force && !settingsDirty && !settingsSaveInFlight && silent) { return; } const payload = collectSettingsPayload(); const saveRevision = settingsSaveRevision; settingsSaveInFlight = true; updateSettingsSaveState(); const shouldSkipStateApplyForFocusedEditor = (() => { if (!silent || source !== 'autosave') { return false; } const activeEl = typeof document !== 'undefined' ? document.activeElement : null; return isEditableElementInSettingsCard(activeEl); })(); try { const response = await chrome.runtime.sendMessage({ type: 'SAVE_SETTING', source: 'sidepanel', payload, }); if (response?.error) { throw new Error(response.error); } if (response?.state && saveRevision === settingsSaveRevision) { if (shouldSkipStateApplyForFocusedEditor) { syncLatestState(response.state); markSettingsDirty(false); } else { applySettingsState(response.state); } } else { syncLatestState(payload); if (saveRevision === settingsSaveRevision) { markSettingsDirty(false); } updatePanelModeUI(); updateMailProviderUI(); updateButtonStates(); } if (!silent) { showToast('配置已保存', 'success', 1800); } } catch (err) { markSettingsDirty(true); if (!silent) { showToast(`保存失败:${err.message}`, 'error'); } throw err; } finally { settingsSaveInFlight = false; updateSettingsSaveState(); } } async function persistCurrentSettingsForAction() { clearTimeout(settingsAutoSaveTimer); await waitForSettingsSaveIdle(); await persistSignupPhoneInputForAction(); await saveSettings({ silent: true, force: true }); } function applyAutoRunStatus(payload = currentAutoRun) { syncAutoRunState(payload); const runLabel = getAutoRunLabel(currentAutoRun); const locked = isAutoRunLockedPhase(); const paused = isAutoRunPausedPhase(); const settingsCardLocked = locked; setSettingsCardLocked(settingsCardLocked); setFreePhoneReuseControlsLocked(settingsCardLocked); inputRunCount.disabled = currentAutoRun.autoRunning || ( typeof shouldLockRunCountToEmailPool === 'function' ? shouldLockRunCountToEmailPool() : getLockedRunCountFromEmailPool() > 0 ); btnAutoRun.disabled = currentAutoRun.autoRunning; btnFetchEmail.disabled = locked || isCustomMailProvider() || usesCustomEmailPoolGenerator(); inputEmail.disabled = locked; if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) { inputSignupPhone.disabled = locked; } if (typeof inputSub2ApiAccountPriority !== 'undefined' && inputSub2ApiAccountPriority) { inputSub2ApiAccountPriority.disabled = locked; } inputAutoSkipFailures.disabled = locked; const lockedRunCount = typeof getLockedRunCountFromEmailPool === 'function' ? getLockedRunCountFromEmailPool() : 0; const isSyncPhase = typeof isAutoRunSourceSyncPhase === 'function' ? isAutoRunSourceSyncPhase : (phase) => ['running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase); const shouldSyncRunCount = typeof shouldSyncRunCountFromAutoRunSource === 'function' ? shouldSyncRunCountFromAutoRunSource(currentAutoRun) : (currentAutoRun.autoRunning || isSyncPhase(currentAutoRun.phase)); if (lockedRunCount > 0) { inputRunCount.value = String(lockedRunCount); } else if (shouldSyncRunCount && currentAutoRun.totalRuns > 0) { inputRunCount.value = String(currentAutoRun.totalRuns); } switch (currentAutoRun.phase) { case 'waiting_step': autoContinueBar.style.display = 'none'; btnAutoRun.textContent = `等待中${runLabel}`; break; case 'waiting_email': autoContinueBar.style.display = 'flex'; btnAutoRun.textContent = `已暂停${runLabel}`; break; case 'running': autoContinueBar.style.display = 'none'; btnAutoRun.textContent = `运行中${runLabel}`; break; case 'retrying': autoContinueBar.style.display = 'none'; btnAutoRun.textContent = `重试中${runLabel}`; break; case 'waiting_interval': autoContinueBar.style.display = 'none'; btnAutoRun.textContent = `等待中${runLabel}`; break; default: autoContinueBar.style.display = 'none'; setDefaultAutoRunButton(); inputEmail.disabled = false; if (!locked) { btnFetchEmail.disabled = isCustomMailProvider() || usesCustomEmailPoolGenerator(); } break; } updateFallbackThreadIntervalInputState(); syncAutoRunCountdownTicker(); updateStopButtonState(paused || locked || Object.values(getStepStatuses()).some(status => status === 'running')); updateConfigMenuControls(); renderContributionMode(); } function initializeManualStepActions() { document.querySelectorAll('.step-row').forEach((row) => { if (row.querySelector('.step-actions')) { return; } const step = Number(row.dataset.step); const nodeId = String(row.dataset.nodeId || getNodeIdByStepForCurrentMode(step) || '').trim(); const statusEl = row.querySelector('.step-status'); if (!statusEl) return; const actions = document.createElement('div'); actions.className = 'step-actions'; const manualBtn = document.createElement('button'); manualBtn.type = 'button'; manualBtn.className = 'step-manual-btn'; manualBtn.dataset.step = String(step); manualBtn.dataset.nodeId = nodeId; manualBtn.title = '跳过此节点'; manualBtn.setAttribute('aria-label', `跳过节点 ${nodeId || step}`); manualBtn.innerHTML = ''; manualBtn.addEventListener('click', async (event) => { event.stopPropagation(); try { await handleSkipNode(nodeId || getNodeIdByStepForCurrentMode(step)); } catch (err) { showToast(err.message, 'error'); } }); statusEl.parentNode.replaceChild(actions, statusEl); actions.appendChild(manualBtn); actions.appendChild(statusEl); }); } function renderStepsList() { if (!stepsList) return; stepsList.innerHTML = workflowNodes.map((node) => { const step = getStepIdByNodeIdForCurrentMode(node.nodeId); const nodeId = String(node.nodeId || '').trim(); return `