update
This commit is contained in:
184
gui.py
184
gui.py
@@ -1,9 +1,11 @@
|
||||
from html import unescape
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
from PySide6.QtCore import QTimer, Qt
|
||||
from PySide6.QtGui import QFont
|
||||
from PySide6.QtGui import QFont, QTextDocumentFragment
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
QCheckBox,
|
||||
@@ -65,6 +67,55 @@ def _email_text_from_detail(detail):
|
||||
return str(detail.get("raw") or "")
|
||||
|
||||
|
||||
def _looks_like_html(value):
|
||||
text = str(value or "").strip()
|
||||
if not text:
|
||||
return False
|
||||
if "<html" in text.lower() or "<body" in text.lower():
|
||||
return True
|
||||
return bool(re.search(r"<\s*[a-zA-Z][^>]*>", 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"(?<!\d)(\d{6})(?!\d)", text)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return ""
|
||||
|
||||
|
||||
def _highlight_verification_code_html(html_text, code):
|
||||
if not html_text or not code:
|
||||
return html_text
|
||||
pattern = re.compile(rf"(?<!\d){re.escape(code)}(?!\d)")
|
||||
return pattern.sub(
|
||||
(
|
||||
'<span style="background:#fff1bf;color:#8a3b12;'
|
||||
"font-weight:700;padding:2px 6px;border-radius:6px;"
|
||||
'letter-spacing:1px;">'
|
||||
f"{code}</span>"
|
||||
),
|
||||
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"(?<!\d)({re.escape(verification_code)})(?!\d)",
|
||||
r"[验证码: \1]",
|
||||
plain_text,
|
||||
count=1,
|
||||
)
|
||||
self.email_content.setToolTip("")
|
||||
self.email_content.setPlainText(display_text)
|
||||
|
||||
def _on_auto_refresh_toggle(self, checked):
|
||||
if checked:
|
||||
|
||||
Reference in New Issue
Block a user