优化了 gui

This commit is contained in:
yml2213
2026-06-21 23:38:52 +08:00
parent af3951ec6f
commit 220fcd4052
4 changed files with 125 additions and 20 deletions
+5 -2
View File
@@ -5,6 +5,8 @@ from pathlib import Path
from dataclasses import dataclass
from typing import List, Optional
from .email_verifier import get_email_config_for_account
@dataclass
class Account:
@@ -45,13 +47,14 @@ class Config:
"""获取账号列表"""
accounts = []
for acc in self._config.get('accounts', []):
email_config = get_email_config_for_account(acc['email'])
accounts.append(Account(
username=acc['username'],
password=acc['password'],
email=acc['email'],
email_password=acc['email_password'],
email_imap_server=acc.get('email_imap_server', ''),
email_imap_port=acc.get('email_imap_port', 993),
email_imap_server=acc.get('email_imap_server') or email_config['server'],
email_imap_port=acc.get('email_imap_port') or email_config['port'],
))
return accounts
+13
View File
@@ -40,6 +40,7 @@ class EmailVerifier:
"""连接IMAP服务器"""
try:
logger.info(f"连接IMAP服务器: {self.imap_server}:{self.imap_port}")
self._validate_login_text()
self._connection = imaplib.IMAP4_SSL(
self.imap_server,
self.imap_port,
@@ -51,6 +52,17 @@ class EmailVerifier:
logger.error(f"IMAP连接失败: {e}")
raise
def _validate_login_text(self) -> None:
"""提前检查IMAP登录字段,给出比ascii编码异常更明确的提示。"""
for label, value in (("邮箱账号", self.username), ("邮箱密码/授权码", self.password)):
try:
value.encode("ascii")
except UnicodeEncodeError as exc:
raise ValueError(
f"{label}包含中文或其他非ASCII字符,IMAP无法登录;"
"请检查导入格式是否为:用户名|密码|邮箱|邮箱密码"
) from exc
def disconnect(self) -> None:
"""断开IMAP连接"""
if self._connection:
@@ -324,6 +336,7 @@ class EmailVerifier:
def get_email_config_for_account(email_address: str) -> dict:
"""根据邮箱地址自动配置IMAP服务器"""
configs = {
'bdhg.xyz': {'server': 'mail.bdhg.xyz', 'port': 993},
'qq.com': {'server': 'imap.qq.com', 'port': 993},
'163.com': {'server': 'imap.163.com', 'port': 993},
'126.com': {'server': 'imap.126.com', 'port': 993},
+81 -2
View File
@@ -9,11 +9,26 @@ from pathlib import Path
from queue import Queue, Empty
from typing import List
from loguru import logger
from douyu.config import Account, ProxyConfig
from utils import setup_logger
from .login_worker import LoginWorker, parse_accounts_text
from .widgets import AccountTable, LogPanel
LOG_LEVELS = ('DEBUG', 'INFO', 'WARNING', 'ERROR')
LOG_LEVEL_TAGS = {
'TRACE': 'debug',
'DEBUG': 'debug',
'INFO': 'info',
'SUCCESS': 'success',
'WARNING': 'warning',
'ERROR': 'error',
'CRITICAL': 'error',
}
class DouyuLoginApp:
"""斗鱼批量登录GUI应用"""
@@ -31,6 +46,8 @@ class DouyuLoginApp:
# 加载配置
self.proxy_config = self._load_proxy_config()
self.geetest_retries = self._load_geetest_retries()
self.log_level = self._load_log_level()
self._configure_logging(self.log_level)
# 创建主窗口
self.root = tk.Tk()
@@ -77,6 +94,45 @@ class DouyuLoginApp:
except:
return 5
def _load_log_level(self) -> str:
"""从配置文件加载默认日志等级。"""
try:
import yaml
with open(self.config_path, 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
level = config.get('log', {}).get('level', 'INFO').upper()
return level if level in LOG_LEVELS else 'INFO'
except:
return 'INFO'
def _configure_logging(self, level: str) -> None:
"""配置终端和GUI日志输出等级。"""
self.log_level = level if level in LOG_LEVELS else 'INFO'
setup_logger(level=self.log_level)
logger.add(
self._enqueue_log_record,
level=self.log_level,
format='{message}',
colorize=False,
)
def _enqueue_log_record(self, message) -> None:
"""把loguru日志转发到GUI日志队列。"""
record = message.record
level = record['level'].name
tag = LOG_LEVEL_TAGS.get(level, 'info')
text = (
f"{record['name']}:{record['function']}:{record['line']} | "
f"{record['message']}"
)
self.log_queue.put((tag, text))
def _on_log_level_change(self, _event=None) -> None:
"""切换日志等级。"""
level = self.log_level_var.get().upper()
self._configure_logging(level)
self.log_panel.append_log('info', f'日志等级已切换为: {level}')
def _create_ui(self):
"""创建用户界面"""
# 主容器
@@ -163,9 +219,13 @@ class DouyuLoginApp:
self.proxy_https_entry = ttk.Entry(static_frame, textvariable=self.proxy_https_var, width=20)
self.proxy_https_entry.pack(side='left', padx=(5, 0))
# 右侧配置:极验和日志
side_frame = ttk.Frame(config_frame)
side_frame.pack(side='right', fill='y', padx=(5, 0))
# 极验配置
geetest_frame = ttk.LabelFrame(config_frame, text='极验配置', padding=10)
geetest_frame.pack(side='right', fill='y', padx=(5, 0))
geetest_frame = ttk.LabelFrame(side_frame, text='极验配置', padding=10)
geetest_frame.pack(fill='x')
ttk.Label(geetest_frame, text='最大重试:').pack(anchor='w')
self.geetest_retries_var = tk.IntVar(value=self.geetest_retries)
@@ -173,6 +233,22 @@ class DouyuLoginApp:
textvariable=self.geetest_retries_var)
retries_spin.pack(anchor='w', pady=(5, 0))
# 日志配置
log_frame = ttk.LabelFrame(side_frame, text='日志配置', padding=10)
log_frame.pack(fill='x', pady=(8, 0))
ttk.Label(log_frame, text='等级:').pack(anchor='w')
self.log_level_var = tk.StringVar(value=self.log_level)
log_level_combo = ttk.Combobox(
log_frame,
values=LOG_LEVELS,
textvariable=self.log_level_var,
state='readonly',
width=10,
)
log_level_combo.pack(anchor='w', pady=(5, 0))
log_level_combo.bind('<<ComboboxSelected>>', self._on_log_level_change)
# 初始状态
self._on_proxy_toggle()
@@ -333,6 +409,8 @@ class DouyuLoginApp:
# 获取当前GUI配置
proxy_config = self._get_proxy_config()
geetest_retries = self.geetest_retries_var.get()
log_level = self.log_level_var.get().upper()
self._configure_logging(log_level)
# 验证代理配置
if proxy_config.enabled:
@@ -366,6 +444,7 @@ class DouyuLoginApp:
self.log_panel.append_log('info', '未使用代理')
self.log_panel.append_log('info', f'极验最大重试: {geetest_retries}')
self.log_panel.append_log('info', f'日志等级: {log_level}')
self.progress_var.set('登录中...')
# 启动登录线程
+26 -16
View File
@@ -1,5 +1,6 @@
"""登录工作线程模块"""
import re
import threading
import time
from queue import Queue
@@ -8,33 +9,31 @@ from loguru import logger
from douyu import DouyuLogin
from douyu.config import Account, ProxyConfig
from douyu.email_verifier import get_email_config_for_account
# IMAP服务器映射
IMAP_SERVERS = {
'qq.com': 'imap.qq.com',
'163.com': 'imap.163.com',
'126.com': 'imap.126.com',
'gmail.com': 'imap.gmail.com',
'outlook.com': 'outlook.office365.com',
'hotmail.com': 'outlook.office365.com',
'yeah.net': 'imap.yeah.net',
'sohu.com': 'imap.sohu.com',
'sina.com': 'imap.sina.com',
}
EMAIL_PATTERN = re.compile(r'^[^\s@|]+@[^\s@|]+\.[^\s@|]+$')
def get_imap_server(email: str) -> str:
"""从邮箱地址推导IMAP服务器"""
domain = email.split('@')[-1].lower()
return IMAP_SERVERS.get(domain, f'imap.{domain}')
return get_email_config_for_account(email)['server']
def _is_ascii(value: str) -> bool:
"""检查字符串是否为ASCII。"""
try:
value.encode('ascii')
return True
except UnicodeEncodeError:
return False
def parse_accounts_text(text: str) -> list[Account]:
"""
解析账号文本
格式:用户名|密码|邮箱|邮箱密码(每行一个)
格式:用户名|密码|邮箱|邮箱密码
Returns:
Account列表
@@ -47,7 +46,7 @@ def parse_accounts_text(text: str) -> list[Account]:
continue
parts = line.split('|')
if len(parts) < 4:
if len(parts) != 4:
logger.warning(f"{line_num}行格式错误,需要4个字段,实际{len(parts)}个: {line}")
continue
@@ -57,6 +56,17 @@ def parse_accounts_text(text: str) -> list[Account]:
logger.warning(f"{line_num}行存在空字段: {line}")
continue
if not EMAIL_PATTERN.match(email) or not _is_ascii(email):
logger.warning(
f"{line_num}行邮箱格式不正确: {email}"
"请确认格式为:用户名|密码|邮箱|邮箱密码"
)
continue
if not _is_ascii(email_password):
logger.warning(f"{line_num}行邮箱密码/授权码包含非ASCII字符,IMAP可能无法登录")
continue
accounts.append(Account(
username=username,
password=password,