diff --git a/.codex_tmp_mihomo_config.yaml b/.codex_tmp_mihomo_config.yaml new file mode 100644 index 0000000..6a0d11b --- /dev/null +++ b/.codex_tmp_mihomo_config.yaml @@ -0,0 +1,298 @@ +allow-lan: true +mode: rule +bind-address: "0.0.0.0" +mixed-port: 7891 +socks-port: 7890 +ipv6: true +unified-delay: true +log-level: warning +find-process-mode: strict +tcp-concurrent: true +keep-alive-idle: 600 +keep-alive-interval: 15 + +profile: + store-selected: true + store-fake-ip: true + +external-controller: 0.0.0.0:9090 + +tun: + enable: true + stack: mixed + auto-route: true + auto-detect-interface: true + dns-hijack: + - any:53 + +NodeParam: &NodeParam + type: http + interval: 86400 + health-check: + enable: true + url: https://www.gstatic.com/generate_204 + interval: 300 + +proxy-providers: + airport: + <<: *NodeParam + url: "http://172.30.0.2:25500/sub?target=clash&new_name=true&url=https%3A%2F%2Fdash.pqjc.site%2Fapi%2Fv1%2Fpq%2Fdce38d7c4a39d9b8fb6818a658e2f554" + path: ./providers/airport.yaml + +dns: + enable: true + cache-algorithm: arc + listen: 0.0.0.0:1053 + ipv6: true + respect-rules: true + use-hosts: true + use-system-hosts: true + enhanced-mode: fake-ip + fake-ip-range: 198.18.0.1/16 + fake-ip-filter-mode: blacklist + fake-ip-filter: + - rule-set:fakeipfilter + nameserver: + - https://223.5.5.5/dns-query + - https://doh.pub/dns-query + proxy-server-nameserver: + - https://223.5.5.5/dns-query + - https://doh.pub/dns-query + +sniffer: + enable: true + parse-pure-ip: true + sniff: + HTTP: + ports: [80, 8080-8880] + override-destination: true + TLS: + ports: [443, 8443] + QUIC: + ports: [443, 8443] + skip-domain: + - "Mijia Cloud" + - "+.push.apple.com" + +FilterUS: &FilterUS '^(?=.*?(?i)(United States|America|\bUS\b|硅谷|家宽|洛杉矶|西雅图|纽约|芝加哥|拉斯维加斯|美帝|美国)).*(?]*>", text)) + + +def _normalize_email_body(body): + if isinstance(body, list): + return "\n\n".join(str(item) for item in body if item is not None) + if body is None: + return "" + return str(body) + + +def _html_to_plain_text(html_text): + text = QTextDocumentFragment.fromHtml(html_text).toPlainText().strip() + if text: + return text + return unescape(re.sub(r"<[^>]+>", " ", html_text)).strip() + + +def _extract_verification_code(text): + if not text: + return "" + match = re.search(r"(?' + f"{code}" + ), + html_text, + count=1, + ) + + def _generate_password(length=14): import random import string @@ -183,10 +234,13 @@ class MainWindow(QMainWindow): self.reload_accounts_btn.clicked.connect(self._on_reload_accounts) self.search_input.textChanged.connect(self._apply_account_filter) self.show_passwords_check.toggled.connect(self._on_toggle_password_visibility) - self.backend_type_input.currentTextChanged.connect(self._on_backend_type_changed) + self.backend_type_input.currentTextChanged.connect( + self._on_backend_type_changed + ) self.db_type_input.currentTextChanged.connect(self._on_db_type_changed) self.test_db_btn.clicked.connect(self._on_test_db_connection) self.init_db_btn.clicked.connect(self._on_init_db) + self.copy_verification_code_btn.clicked.connect(self._copy_verification_code) def _migrate_legacy_config_if_needed(self): if CONFIG_PATH.exists(): @@ -227,13 +281,13 @@ class MainWindow(QMainWindow): details_group = QGroupBox("账号详情") details_layout = QFormLayout(details_group) - self.detail_email = QLabel("-") - self.detail_mail_password = QLabel("-") - self.detail_chatgpt_password = QLabel("-") - self.detail_name = QLabel("-") - self.detail_birthdate = QLabel("-") - self.detail_mail_token = QLabel("-") - self.detail_source = QLabel("postgres") + self.detail_email = self._create_copyable_label("-") + self.detail_mail_password = self._create_copyable_label("-") + self.detail_chatgpt_password = self._create_copyable_label("-") + self.detail_name = self._create_copyable_label("-") + self.detail_birthdate = self._create_copyable_label("-") + self.detail_mail_token = self._create_copyable_label("-") + self.detail_source = self._create_copyable_label("postgres") details_layout.addRow("邮箱", self.detail_email) details_layout.addRow("邮箱密码", self.detail_mail_password) details_layout.addRow("Mail Token", self.detail_mail_token) @@ -248,6 +302,18 @@ class MainWindow(QMainWindow): right_layout.addWidget(QLabel("邮件列表")) self.emails_list = QListWidget() right_layout.addWidget(self.emails_list) + code_layout = QHBoxLayout() + self.verification_code_label = QLabel("验证码") + self.verification_code_value = QLineEdit() + self.verification_code_value.setReadOnly(True) + self.verification_code_value.setPlaceholderText("自动提取 6 位验证码") + self.verification_code_value.setFocusPolicy(Qt.StrongFocus) + self.copy_verification_code_btn = QPushButton("复制") + self.copy_verification_code_btn.setEnabled(False) + code_layout.addWidget(self.verification_code_label) + code_layout.addWidget(self.verification_code_value) + code_layout.addWidget(self.copy_verification_code_btn) + right_layout.addLayout(code_layout) right_layout.addWidget(QLabel("邮件内容")) self.email_content = QTextEdit() self.email_content.setReadOnly(True) @@ -360,11 +426,27 @@ class MainWindow(QMainWindow): layout.addStretch(1) return page + def _create_copyable_label(self, text=""): + label = QLabel(text) + label.setTextInteractionFlags(Qt.TextSelectableByMouse) + label.setCursor(Qt.IBeamCursor) + label.setWordWrap(True) + return label + def _setup_timer(self): self.timer = QTimer(self) self.timer.setInterval(self.refresh_interval.value() * 1000) self.timer.timeout.connect(self._auto_refresh) + def _copy_verification_code(self): + code = self.verification_code_value.text().strip() + if not code: + return + QApplication.clipboard().setText(code) + self.verification_code_value.setFocus() + self.verification_code_value.selectAll() + self._log(f"验证码已复制: {code}") + def _log(self, message): ts = time.strftime("%H:%M:%S") self.log_box.append(f"[{ts}] {message}") @@ -408,9 +490,14 @@ class MainWindow(QMainWindow): def _load_config(self): data = load_config(CONFIG_PATH) - self.api_base_input.setText(data.get("api_base", "https://temp-email-api.example.com")) + self.api_base_input.setText( + data.get("api_base", "https://temp-email-api.example.com") + ) self.domain_input.setText(data.get("domain", "example.com")) - backend_type = str(data.get("backend_type", "cloudflare_temp_email")).strip().lower() or "cloudflare_temp_email" + backend_type = ( + str(data.get("backend_type", "cloudflare_temp_email")).strip().lower() + or "cloudflare_temp_email" + ) backend_index = self.backend_type_input.findText(backend_type) if backend_index >= 0: self.backend_type_input.setCurrentIndex(backend_index) @@ -418,18 +505,33 @@ class MainWindow(QMainWindow): self.proxy_input.setText(data.get("proxy", "")) self.show_passwords_check.setChecked(bool(data.get("show_passwords", False))) self.auto_refresh_check.setChecked(bool(data.get("auto_refresh", False))) - self.refresh_interval.setValue(max(10, min(300, int(data.get("refresh_interval", 30) or 30)))) - self.pg_enabled_check.setChecked(bool(data.get("db_enabled", data.get("pg_enabled", True)))) + self.refresh_interval.setValue( + max(10, min(300, int(data.get("refresh_interval", 30) or 30))) + ) + self.pg_enabled_check.setChecked( + bool(data.get("db_enabled", data.get("pg_enabled", True))) + ) db_type = str(data.get("db_type", "postgresql")).strip().lower() or "postgresql" index = self.db_type_input.findText(db_type) if index >= 0: self.db_type_input.setCurrentIndex(index) self.pg_host_input.setText(data.get("db_host", data.get("pg_host", ""))) - self.pg_port_input.setValue(int(data.get("db_port", data.get("pg_port", 5432 if db_type != "mysql" else 3306)) or (3306 if db_type == "mysql" else 5432))) - self.pg_db_input.setText(data.get("db_name", data.get("pg_db", "mail_accounts_db"))) + self.pg_port_input.setValue( + int( + data.get( + "db_port", data.get("pg_port", 5432 if db_type != "mysql" else 3306) + ) + or (3306 if db_type == "mysql" else 5432) + ) + ) + self.pg_db_input.setText( + data.get("db_name", data.get("pg_db", "mail_accounts_db")) + ) self.db_table_input.setText(data.get("db_table", "registered_accounts")) self.pg_user_input.setText(data.get("db_user", data.get("pg_user", ""))) - self.pg_password_input.setText(data.get("db_password", data.get("pg_password", ""))) + self.pg_password_input.setText( + data.get("db_password", data.get("pg_password", "")) + ) self.db_auto_create_check.setChecked(bool(data.get("db_auto_create", False))) self._on_toggle_password_visibility(self.show_passwords_check.isChecked()) @@ -522,7 +624,11 @@ class MainWindow(QMainWindow): def _apply_account_filter(self): keyword = self.search_input.text().strip().lower() - filtered = [item for item in self.all_accounts if self._account_matches_search(item, keyword)] + filtered = [ + item + for item in self.all_accounts + if self._account_matches_search(item, keyword) + ] self._render_accounts_list(filtered) if not filtered: self.current_mail_token = "" @@ -538,7 +644,9 @@ class MainWindow(QMainWindow): try: self.all_accounts = load_accounts(config) self._apply_account_filter() - self._log(f"已加载 {config.get('db_type', 'postgresql')} 账号: {len(self.all_accounts)} 个") + self._log( + f"已加载 {config.get('db_type', 'postgresql')} 账号: {len(self.all_accounts)} 个" + ) except Exception as e: self.all_accounts = [] self._apply_account_filter() @@ -635,9 +743,13 @@ class MainWindow(QMainWindow): return token_status = "已存在" if account.get("mail_token", "") else "缺失" self.detail_email.setText(account.get("email", "")) - self.detail_mail_password.setText(self._mask_secret(account.get("mail_password", ""), "(管理员模式下可留空)")) + self.detail_mail_password.setText( + self._mask_secret(account.get("mail_password", ""), "(管理员模式下可留空)") + ) self.detail_mail_token.setText(token_status) - self.detail_chatgpt_password.setText(self._mask_secret(account.get("chatgpt_password", ""))) + self.detail_chatgpt_password.setText( + self._mask_secret(account.get("chatgpt_password", "")) + ) self.detail_name.setText(account.get("name", "")) self.detail_birthdate.setText(account.get("birthdate", "")) self.detail_source.setText(account.get("source", "postgres")) @@ -710,7 +822,9 @@ class MainWindow(QMainWindow): if not items: return email_item = items[0].data(Qt.UserRole) - msg_id = email_item.get("id") or email_item.get("@id") or email_item.get("messageId") + msg_id = ( + email_item.get("id") or email_item.get("@id") or email_item.get("messageId") + ) body = ( email_item.get("text") or email_item.get("message") @@ -725,7 +839,33 @@ class MainWindow(QMainWindow): body = _email_text_from_detail(detail) except Exception as e: body = f"(获取邮件详情失败: {e})" - self.email_content.setPlainText(str(body or "(无正文)")) + content = _normalize_email_body(body) + if not content: + self.verification_code_value.clear() + self.copy_verification_code_btn.setEnabled(False) + self.verification_code_label.setText("验证码") + self.email_content.setToolTip("") + self.email_content.setPlainText("(无正文)") + return + plain_text = ( + _html_to_plain_text(content) if _looks_like_html(content) else content + ) + verification_code = _extract_verification_code(plain_text) + self.verification_code_value.setText(verification_code) + self.copy_verification_code_btn.setEnabled(bool(verification_code)) + self.verification_code_label.setText( + "验证码 (已提取)" if verification_code else "验证码" + ) + display_text = plain_text + if verification_code: + display_text = re.sub( + rf"(?