Gmail/main.py

1637 lines
67 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import json
import shutil
import socket
import sys
import threading
from multiprocessing import freeze_support
from DrissionPage import ChromiumOptions, ChromiumPage
from DrissionPage.errors import *
import random
import time
import os
from filelock import FileLock
from openpyxl import load_workbook
import ctypes
from ctypes import wintypes
import uuid
import multiprocessing
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import string
from multiprocessing import Queue, Pool
from account import AccountManagerSQLite
from typing import List, Tuple
import win32gui
from mail import GoogleCodeReceiver, EmailClient
from proxy import ProxyManagerSQLite, classifier_smartproxy
from datetime import datetime, timedelta, timezone
from fake_useragent import UserAgent
# Windows 消息常量
WM_MOUSEMOVE = 0x0200
WM_LBUTTONDOWN = 0x0201
WM_LBUTTONUP = 0x0202
MK_LBUTTON = 0x0001
server = "server-10474.cuiqiu.vip" # 替换为你的 IMAP 服务器地址
username = "gmailvinted@mailezu.com"
password = "g1l2o0hld84"
# 停止标志
stop_event = threading.Event()
user32 = ctypes.windll.user32
root_plugin_dir = os.path.join(os.getcwd(), 'proxy_auth_plugin')
proxy_manager = ProxyManagerSQLite(db_path="proxies.db")
db_manager = AccountManagerSQLite(db_path="accounts.db")
def init_worker():
global proxy_manager, db_manager
# 在每个子进程中初始化数据库管理器
proxy_manager = ProxyManagerSQLite(db_path="proxies.db")
db_manager = AccountManagerSQLite(db_path="accounts.db")
# 全局写入锁
write_lock = multiprocessing.Lock()
# 自定义异常
class WindowMinimizedError(Exception):
def __init__(self, message="浏览器窗口已最小化,无法执行操作。"):
self.message = message
super().__init__(self.message)
class GoogleCaptchaError(Exception):
"""出现谷歌文字验证错误"""
pass
def is_window_minimized(hwnd):
"""检查窗口是否最小化"""
return win32gui.IsIconic(hwnd)
def random_sleep(tab, min_seconds=1, max_seconds=3):
"""在 min_seconds 和 max_seconds 之间随机停顿"""
tab.wait(random.uniform(min_seconds, max_seconds))
def get_free_port():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', 0)) # 使用端口0让操作系统自动选择一个未占用的端口
port = s.getsockname()[1]
s.close()
return port
# 设置代理ip
def create_proxy_auth_extension(proxy_host, proxy_port, proxy_username, proxy_password, scheme='PROXY', proxy_domains=None):
# Define a root directory
root_plugin_dir = os.path.join(os.getcwd(), 'proxy_auth_plugin')
if not os.path.exists(root_plugin_dir):
os.makedirs(root_plugin_dir) # Create root directory
# Use a unique identifier to generate a separate plugin directory
plugin_path = os.path.join(root_plugin_dir, f'plugin_{uuid.uuid4().hex}')
os.makedirs(plugin_path) # Create plugin directory
# Contents of manifest.json for the plugin
manifest_json = """
{
"version": "1.0.0",
"manifest_version": 2,
"name": "Proxy Auth Extension",
"permissions": [
"proxy",
"tabs",
"unlimitedStorage",
"storage",
"<all_urls>",
"webRequest",
"webRequestBlocking"
],
"background": {
"scripts": ["background.js"]
},
"minimum_chrome_version":"22.0.0"
}
"""
# Ensure proxy_domains is a list
if proxy_domains is None:
proxy_domains = []
# Generate domains_list for PAC script
# Since we can only match on hostnames, we use the domain names
domains_list = ', '.join('"{}"'.format(domain) for domain in proxy_domains)
# Generate PAC script
pac_script = string.Template(
'''
function FindProxyForURL(url, host) {
var proxy = "${scheme} ${proxy_host}:${proxy_port}";
var direct = "DIRECT";
var domains = [${domains_list}];
for (var i = 0; i < domains.length; i++) {
if (dnsDomainIs(host, domains[i])) {
return proxy;
}
}
return direct;
}
'''
).substitute(
scheme=scheme.upper(),
proxy_host=proxy_host,
proxy_port=proxy_port,
domains_list=domains_list
)
# Since we cannot inspect the full URL in onAuthRequired, we'll listen to all URLs
# But we'll check if the proxy is challenging for authentication
# We can do this by checking the 'isProxy' flag in the details
# However, 'isProxy' is only available in certain contexts
# Alternatively, we can restrict the listener to the proxy server
# Contents of background.js, using string templates to insert dynamic configuration
background_js = string.Template(
'''
var config = {
mode: "pac_script",
pacScript: {
data: $pac_script
}
};
chrome.proxy.settings.set({value: config, scope: "regular"}, function() {});
function callbackFn(details) {
if (details.challenger && details.challenger.host === "${proxy_host}" && details.challenger.port == ${proxy_port}) {
return {
authCredentials: {
username: "${username}",
password: "${password}"
}
};
}
}
chrome.webRequest.onAuthRequired.addListener(
callbackFn,
{urls: ["<all_urls>"]},
['blocking']
);
'''
).substitute(
pac_script=json.dumps(pac_script),
username=proxy_username,
password=proxy_password,
proxy_host=proxy_host,
proxy_port=proxy_port
)
# Write manifest.json and background.js to the plugin directory
with open(os.path.join(plugin_path, 'manifest.json'), 'w') as f:
f.write(manifest_json)
with open(os.path.join(plugin_path, 'background.js'), 'w') as f:
f.write(background_js)
# Return the unique plugin path
return plugin_path
def delete_folder(file_path):
"""
删除指定的代理插件文件夹及其内容
:param plugin_path: 代理插件文件夹路径
"""
if os.path.exists(file_path):
shutil.rmtree(file_path)
# 获取窗口标题
def get_window_title(hwnd):
length = user32.GetWindowTextLengthW(hwnd)
if length > 0:
buffer = ctypes.create_unicode_buffer(length + 1)
user32.GetWindowTextW(hwnd, buffer, length + 1)
return buffer.value
return ""
# 枚举所有窗口
def enum_windows():
windows = []
def callback(hwnd, lparam):
title = get_window_title(hwnd)
if title and "Google Chrome" in title:
windows.append((hwnd, title))
return True
EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool, wintypes.HWND, ctypes.c_void_p)
user32.EnumWindows(EnumWindowsProc(callback), 0)
return windows
def find_gmail_window(windows, target_title):
for hwnd, title in windows:
if target_title in title: # 判断目标标题是否包含在窗口标题中
return hwnd
return None
# 模拟鼠标事件
def simulate_mouse(hwnd, x, y):
lParam = (y << 16) | x
user32.PostMessageW(hwnd, WM_MOUSEMOVE, 0, lParam)
user32.PostMessageW(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, lParam)
user32.PostMessageW(hwnd, WM_LBUTTONUP, 0, lParam)
def click_the_login_button(tab, login_a):
# 获取href属性
href = login_a.attr('href')
# 访问href属性指向的页面
tab.get(href)
def input_email(tab, hwnd, email, email_input, max_retries=3):
"""
输入账号并点击“下一步”按钮。如果两次尝试都未成功,退出。
:param tab: 浏览器对象
:param email: 用户的邮箱账号
:param email_input: 输入框对象
:param max_retries: 最大尝试次数
"""
if is_window_minimized(hwnd):
raise WindowMinimizedError("浏览器窗口已最小化,无法执行操作。")
email_input.clear()
# 模拟人类输入,每次输入一个字符,并随机延迟
for char in email:
email_input.input(char) # 输入一个字符
tab.wait(random.uniform(0.1, 0.3)) # 随机延迟 0.1 到 0.3 秒
tab.wait(1)
next_input = tab.ele('@type=button', timeout=15, index=3)
# 将按钮拖拽到页面顶部位置
next_input.drag_to((0, 0), 0)
next_input.set.style('position', 'absolute')
next_input.set.style('top', '0')
next_input.set.style('left', '0')
next_input.set.style('width', '1000px')
next_input.set.style('height', '1000px')
tab.wait(2)
retry_count = 0
while retry_count < max_retries:
# 模拟点击操作
simulate_mouse(hwnd, 650, 650)
# 等待页面加载开始
tab.wait.load_start()
# 等待7秒看按钮是否消失
flag = tab.wait.ele_deleted(next_input, timeout=7)
if flag:
print(f"{retry_count + 1} 次尝试成功,按钮已消失。")
return True
else:
retry_count += 1 # 增加尝试次数
print(f"{retry_count} 次尝试失败,按钮未消失,重新尝试...")
if retry_count >= max_retries:
# 两次尝试都失败调整按钮大小为10px
next_input.set.style('width', '10px')
next_input.set.style('height', '10px')
print(f"尝试 {max_retries} 次仍未成功,调整按钮大小并退出。")
raise GoogleCaptchaError("出现谷歌文字验证")
# 点击重试按钮
def recover(tab, recover_a):
recover_a.click(by_js=True)
def input_password(tab, hwnd, password, password_input, max_retries=2):
# 检查窗口是否最小化
if is_window_minimized(hwnd):
raise WindowMinimizedError("浏览器窗口已最小化,无法执行操作。")
password_input.clear()
"""
输入密码并点击“下一步”按钮
:param tab: 浏览器对象
:param password: 用户的密码
"""
# 模拟人类输入密码,每次输入一个字符,并随机延迟
for char in password:
password_input.input(char) # 输入一个字符
tab.wait(random.uniform(0.1, 0.3)) # 随机延迟 0.1 到 0.3 秒
random_sleep(tab)
next_input = tab.ele('@type=button', timeout=15, index=2)
# 将按钮拖拽到页面顶部位置
next_input.drag_to((0, 0), 0)
next_input.set.style('position', 'absolute')
next_input.set.style('top', '0')
next_input.set.style('left', '0')
next_input.set.style('width', '1000px')
next_input.set.style('height', '1000px')
tab.wait(2)
retry_count = 0
while retry_count < max_retries:
# 模拟点击操作
simulate_mouse(hwnd, 700, 700)
# 等待页面加载开始
tab.wait.load_start()
# 等待7秒看按钮是否消失
flag = tab.wait.ele_deleted(next_input, timeout=7)
if flag:
print(f"{retry_count + 1} 次尝试成功,按钮已消失。")
break # 按钮成功消失,跳出循环
else:
# 获取页面提示信息
text = ""
try:
text = tab.ele('@aria-live=polite', timeout=1).text
except Exception as e:
print("获取页面提示失败:", e)
print(f"页面提示: {text}")
if text == "":
retry_count += 1
print(f"页面提示为空,第 {retry_count} 次尝试失败,重新尝试...")
else:
print(f"页面提示不为空:{text}, 不进行重试,退出。")
break # 页面提示不为空,退出重试流程
if retry_count >= max_retries:
# 两次尝试都失败调整按钮大小为10px
next_input.set.style('width', '10px')
next_input.set.style('height', '10px')
print(f"尝试 {max_retries} 次仍未成功,调整按钮大小并退出。")
raise GoogleCaptchaError("输入密码后无法点击下一步")
def click_alternate_email_verification_button(tab, email_div):
email_div.click(by_js=True)
tab.wait.load_start()
def input_recovery_code(tab, recovery_code):
"""
输入辅助邮箱的验证码并点击“下一步”按钮
:param tab: 浏览器对象
:param recovery_code: 辅助邮箱的验证码
"""
# 第一步:通过 class 定位到辅助邮箱的输入框
recovery_input = tab.ele('@aria-label=输入辅助邮箱地址', timeout=15)
for char in recovery_code:
recovery_input.input(char) # 输入一个字符
tab.wait(random.uniform(0.1, 0.3)) # 随机延迟
next_input = tab.ele('@@tag()=span@@text()=下一步', timeout=15)
next_input.click(by_js=True)
print("到这里我已经点完下一步,进入修改辅助邮箱的地方了")
tab.wait.load_start()
tab.wait(1)
retry = tab.ele("@text()=您输入的电子邮件地址不正确,请重试。",timeout=15)
if retry:
return True
return False
# 找到修改邮箱的按钮
def click_use_other_account_button2(tab, next_span):
next_span.click(by_js=True)
tab.wait.load_start()
# 修改辅助邮箱账号
def modify_the_secondary_email1(tab, button, auxiliary_email_account):
button.click(by_js=True)
tab.wait(2)
input = button
input.clear()
for char in auxiliary_email_account:
input.input(char) # Enter one character at a time
tab.wait(random.uniform(0.1, 0.3)) # Random delay between 0.1 and 0.3 seconds
# 点击保存
save_span = tab.ele('@text()=Save', timeout=15)
save_span.click(by_js=True)
button = tab.ele('@text()=Not now',timeout=15)
button.click(by_js=True)
tab.wait.load_start()
def click_continue_button(tab, continue_div1):
# 点击出现的弹窗
tab.handle_alert(accept=True)
tab.wait(1)
continue_div1.click(by_js=True)
tab.wait(1)
next_button = tab.ele('@@tag()=button@@name=data_consent_dialog_next', timeout=15)
next_button.click(by_js=True)
tab.wait(1)
continue_div2 = tab.ele('@text()=Personalize other Google products with my Gmail, Chat, and Meet data', timeout=15)
continue_div2.click(by_js=True)
tab.wait(1)
Done_button = tab.ele('@name=data_consent_dialog_done', timeout=15)
Done_button.click(by_js=True)
tab.wait(15)
Reload = tab.ele('@text()=Reload', timeout=15)
if Reload:
Reload.click(by_js=True)
def find_and_visit_href2(tab):
password_a = tab.ele('@aria-label=Password', timeout=15)
print(password_a)
href = password_a.attr('href')
tab.get(href)
def input_password_new(tab, password):
"""
输入密码并点击“下一步”按钮
:param tab: 浏览器对象
:param password: 用户的密码
"""
password_input = tab.ele('@@tag()=input@@name=password')
# 模拟人类输入密码,每次输入一个字符,并随机延迟
for char in password:
password_input.input(char) # 输入一个字符
tab.wait(random.uniform(0.1, 0.3)) # 随机延迟 0.1 到 0.3 秒
confirm_new_password_input = tab.ele("@@tag()=input@@name=confirmation_password")
# 模拟人类输入密码,每次输入一个字符,并随机延迟
for char in password:
confirm_new_password_input.input(char) # 输入一个字符
tab.wait(random.uniform(0.1, 0.3)) # 随机延迟 0.1 到 0.3 秒
# 点击修改按钮
def click_span_with_class(tab):
# 找到 class 为 UywwFc vQzf8d 的元素
span_element = tab.ele('@text()=Change password', timeout=15)
# 用 JavaScript 点击该元素
span_element.click(by_js=True)
# 返回上个页面
tab.back()
# 修改辅助邮箱账号2
def modify_the_secondary_email2(tab, new_recovery_email, new_password, hwnd):
try:
# 定位恢复邮箱的元素
recovery_email = tab.ele("@aria-label=Recovery email", timeout=15)
if not recovery_email:
raise Exception("未找到恢复邮箱元素")
# 获取恢复邮箱的链接并导航
href = recovery_email.attr('href')
if not href:
raise Exception("恢复邮箱链接未找到")
tab.get(href)
password_input = tab.ele('@@aria-label=输入您的密码@@type=password', timeout=15)
if password_input:
input_password(tab, hwnd, new_password, password_input)
tab.wait.load_start() # 等待页面加载开始
# 定位邮箱输入框并清空内容
email_input = tab.ele("@type=email", timeout=15)
# 查看辅助邮箱账号是否已经被修改了
if email_input.value == new_recovery_email:
return True
if not email_input:
raise Exception("未找到邮箱输入框")
email_input.clear()
for char in new_recovery_email:
email_input.input(char) # 输入一个字符
tab.wait(random.uniform(0.1, 0.3)) # 随机延迟 0.1 到 0.3 秒
# 定位并点击验证按钮
verify_button = tab.ele("@text()=Verify", timeout=15)
print(f"验证按钮已经找到:{verify_button}")
if not verify_button:
raise Exception("未找到验证按钮")
verify_button.click(by_js=True)
return False # 成功返回 True
except Exception as e:
print(f"更新恢复邮箱时发生错误: {e}")
return False # 出现错误返回 False
# 定义保存到本地文件的函数
def save_to_file(entry, filename="code.txt"):
"""将条目保存到本地文件,避免重复"""
# 如果文件不存在,先创建空文件
if not os.path.exists(filename):
with open(filename, "w", encoding='utf-8') as file:
pass # 创建文件
# 读取文件内容,检查是否存在相同条目
with open(filename, "r", encoding='utf-8') as file:
existing_entries = file.readlines()
# 构造条目字符串
entry_str = f"验证码: {entry['验证码']}, 发送的邮箱账号: {entry['发送的邮箱账号']}, 收到的时间: {entry['收到的时间']}\n"
if entry_str not in existing_entries: # 如果条目不在文件中
# 以追加模式写入文件
with open(filename, "a", encoding='utf-8') as file:
file.write(entry_str)
print("--------------------------------------------------")
print(f"新条目已保存: {entry_str.strip()}")
def clean(text):
"""清理邮件主题或发件人内容中的特殊字符"""
return "".join(c if c.isalnum() else "_" for c in text)
# 用来标记账号是否完成
def update_status_in_db(email: str, status: str):
"""
更新数据库中指定账户的状态。
"""
# 更新数据库中对应账户的状态
db_manager.update_record(email=email, change_status=status)
# 输出日志信息
print(f"账号 {email} 的状态已更新为:{status}")
# 保存日志到指定文件
def save_log(random_number, log_content):
logs_dir = "logs"
# 创建文件名,使用传入的 random_number
log_file_path = os.path.join(logs_dir, f"{random_number}.txt")
# 将日志内容写入文件
with open(log_file_path, 'a', encoding='utf-8') as f:
f.write(log_content)
f.write("\n")
def retry_with_recovery(tab, hwnd, email_account, MAX_RETRIES=4, wait_time=5):
"""
封装重试逻辑,最多尝试 `MAX_RETRIES` 次,检查是否存在重试按钮,并重新输入邮箱地址。
:param tab: 页面操作对象,假设具备 `.ele` 方法用于获取元素,`.wait.load_start()` 方法用于等待加载
:param recover: 处理重试按钮点击的函数
:param email_account: 需要输入的邮箱账号
:param MAX_RETRIES: 最大重试次数默认为4次
:param wait_time: 等待时间默认为15秒
"""
attempt = 0 # 初始化尝试次数
while attempt < MAX_RETRIES:
recover_a = tab.ele("@aria-label=重试", timeout=wait_time) # 检查是否有 '重试' 按钮
print(f"Attempt {attempt + 1}: {recover_a}")
tab.wait.load_start() # 等待加载开始
if recover_a:
recover(tab, recover_a) # 调用 recover 函数处理重试按钮
email_input = tab.ele('@aria-label=电子邮件地址或电话号码', timeout=wait_time) # 找到邮箱输入框
input_email(tab, hwnd, email_account, email_input) # 重新输入邮箱账号
random_sleep(tab) # 随机等待,避免被检测到自动化
attempt += 1 # 增加尝试次数
else:
break # 如果没有 '重试' 按钮,则跳出循环
# 如果达到最大尝试次数,跳出循环
if attempt == MAX_RETRIES:
print("Reached maximum retries")
break
def get_absolute_path(relative_path):
"""获取绝对路径"""
# 获取当前执行文件的路径(脚本或.exe
if getattr(sys, 'frozen', False): # 检测是否为打包后的exe
base_path = sys._MEIPASS # PyInstaller打包后资源文件临时路径
exe_path = os.path.dirname(sys.executable)
else:
base_path = os.path.abspath(".")
exe_path = os.path.dirname(os.path.abspath(__file__))
# 如果需要拼接在exe路径上
absolute_path = os.path.join(exe_path, relative_path)
return absolute_path
def logutGoogle(tab) -> bool:
tab.get('https://accounts.google.com/Logout?hl=zh-CN')
if tab.ele('@@tag()=input@@type=email', timeout=0) != None: # 已注销就会出登录页元素
print("当前浏览器已退出,无需再次退出")
return True
# 还有没注销移除的账号就会显示已退出和账号元素
try:
tab.ele('@text()=移除账号', timeout=0).click()
tab.wait(2).ele('@text()=已退出', timeout=0).parent().parent().next().click() # 移除图标
tab.wait(3).ele('@@text()=移除@@tag()=button').click() # 移除确认框
return (tab.ele('@@tag()=input@@type=email', timeout=8) != None) # 检查最后界面是否是登录
except Exception as e:
print(f'发生错误:{e}')
return False
return False # 无用分支,不过为了避免函数太长回头修改时候忘记,还是写个 False 以防万一
def main(email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host, proxy_port, proxy_username, proxy_password,region):
update_status_in_db(email_account, '初始化中')
print("邮箱账户:", email_account) # 邮箱账户
print("邮箱密码:", email_password) # 邮箱密码
print("旧的恢复邮箱:", old_recovery_email) # 旧的恢复邮箱
print("新的密码:", new_password) # 新的密码
print("新的恢复邮箱:", new_recovery_email) # 新的恢复邮箱
print("代理主机:", proxy_host) # 代理主机
print("代理端口:", proxy_port) # 代理端口
print("代理用户名:", proxy_username) # 代理用户名
print("代理密码:", proxy_password) # 代理密码
print("代理区域:", region)
global browser, plugin_path, user_dir, attempt
# 生成一个 6 位的随机数
global random_number
try:
random_number = str(random.randint(100000000, 999999999))
"""
主函数,用于自动登录谷歌邮箱并修改账户和密码。
参数:
email_account (str): 邮箱账号
email_password (str): 邮箱密码
old_recovery_email (str): 旧的辅助邮箱账号
new_recovery_email (str): 新的辅助邮箱账号
new_password (str): 新密码
"""
if stop_event.is_set():
print("任务被强制停止,退出主函数")
return None # 直接返回 None跳过当前任务
if not proxy_host:
save_log(random_number, f"没有{region}区域的代理")
update_status_in_db(email_account, f"没有{region}区域代理")
return False
lock = FileLock("proxy_auth_extension.lock")
with lock: # 加锁,确保其他进程在解锁前无法操作
plugin_path = create_proxy_auth_extension(
proxy_host=proxy_host,
proxy_port=proxy_port,
proxy_username=proxy_username,
proxy_password=proxy_password,
proxy_domains=[
"accounts.google.com",
"accounts.youtube.com",
"content-autofill.googleapis.com",
"myaccount.google.com",
"apis.google.com",
"www.google.com",
"ogs.google.com",
"jsonip.com"
]
)
save_log(random_number, f"已经创建好代理插件:{plugin_path}")
# 创建ChromiumOptions对象并配置
options = ChromiumOptions()
options.add_extension(plugin_path)
# 使用随机生成的User-Agent
ua = UserAgent()
random_user_agent = ua.chrome
options.set_user_agent(user_agent=random_user_agent)
# 随机分配一个端口号
random_port = get_free_port()
print(f"现在占用的端口号是:{random_port}")
save_log(random_number, f"现在占用的端口号是:{random_port}")
options.set_paths(local_port=random_port)
# 示例使用
browser_path = get_absolute_path(r"chrome\chrome.exe")
options.set_paths(f"{browser_path}")
options.no_imgs(True).mute(True)
cache_path = os.path.join(os.getcwd(), 'cache')
temp_path = os.path.join(os.getcwd(), 'tempfile')
user_dir = os.path.join(os.getcwd(), 'userdata', f'{uuid.uuid4().hex}')
options.set_cache_path(cache_path)
options.set_tmp_path(temp_path)
options.set_user_data_path(user_dir)
print(cache_path)
print(temp_path)
print(user_dir)
options.set_pref(arg='profile.default_content_settings.popups', value='0')
options.set_pref('translate.enabled', False) # 禁用翻译功能
options.set_pref('translate.newtld', False) # 禁用新的翻译扩展功能
# 确保不显示翻译提示
options.set_pref('profile.content_settings.exceptions.translate', '{"pattern":"*","setting":2}')
browser = ChromiumPage(options)
# 清除缓存和cookies
browser.clear_cache(cookies=True)
# browser = Chromium(29581)
# 获取标签页对象
tab = browser.latest_tab
save_log(random_number, "已获取谷歌的最后一个页面")
# 访问谷歌邮箱页面
flag = tab.get('https://workspace.google.com/intl/zh-CN/gmail/')
save_log(random_number, f"访问谷歌邮箱页面的状态:{flag}")
if not flag:
update_status_in_db(email_account, "无法访问谷歌邮箱页面,请重试")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
save_log(random_number, "已经访问谷歌页面")
# 执行JavaScript清除localStorage
tab.run_js("window.localStorage.clear();")
# 进入登录流程
login_a = tab.ele('@aria-label=登录 Gmail', timeout=15)
if login_a:
click_the_login_button(tab, login_a)
random_sleep(tab)
login = tab.ele('@@tag()=span@@text()=登录', timeout=10)
if login:
random_sleep(tab)
# 将随机数设置为窗口标题
script = f"document.title = '{random_number}';"
# 执行 JS 脚本
tab.run_js(script)
print("登录运行完毕")
save_log(random_number, "已经进入登录界面")
else:
update_status_in_db(email_account, '登录超时')
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
random_sleep(tab)
actual_title = tab.run_js("return document.title;")
save_log(random_number, f"设置标题为: {random_number}, 实际标题为: {actual_title}\n")
# 获取所有 Chrome 窗口
chrome_windows = enum_windows()
save_log(random_number, f"当前谷歌浏览器的所有窗口为:{chrome_windows}")
hwnd = None
for win in chrome_windows:
if random_number in win[1]: # 假设 `win['title']` 是窗口标题
hwnd = win[0]
print(f"窗口句柄:{win[0]} 窗口标题:{win[1]}")
save_log(random_number, f"获取的窗口的句柄:{win[0]} 和标题:{win[1]}")
break
if hwnd is None:
print("无法找到窗口句柄")
save_log(random_number, "无法找到窗口句柄")
update_status_in_db(email_account, '无法找到窗口句柄')
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
try:
# 输入邮箱账号
email_input = tab.ele('@aria-label=电子邮件地址或电话号码', timeout=15)
if email_input:
flag = input_email(tab, hwnd, email_account, email_input) # 使用传入的邮箱账号
if not flag:
update_status_in_db(random_number, "出现谷歌文字验证")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
random_sleep(tab)
except ElementNotFoundError as e:
print(f"找不到输入邮箱账号的元素{e}")
save_log(random_number, f"找不到输入邮箱账号的元素:{e}")
update_status_in_db(email_account, "请求错误,请重试")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
print("输入邮箱账号运行完毕")
save_log(random_number, "输入邮箱账号运行完毕")
wrong = tab.ele('@text()=出了点问题', timeout=5)
if wrong:
next_span = tab.ele("@@tag()=span@@text()=下一步", timeout=15)
next_span.click(by_js=True)
tab.wait.load_start()
try:
# 输入邮箱账号
email_input = tab.ele('@aria-label=电子邮件地址或电话号码', timeout=15)
if email_input:
input_email(tab, hwnd, email_account, email_input) # 使用传入的邮箱账号
random_sleep(tab)
except ElementNotFoundError as e:
print(f"找不到输入邮箱账号的元素{e}")
save_log(random_number, f"找不到输入邮箱账号的元素:{e}")
update_status_in_db(email_account, "请求错误,请重试")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
retry_with_recovery(tab, hwnd, email_account)
print("检查是否出现了wrong运行完毕")
save_log(random_number, "检查是否出现了wrong运行完毕")
# 看下是否出现了手机号
telephone = tab.ele("@text()=请输入电话号码,以便通过短信接收验证码。", timeout=5)
if telephone:
update_status_in_db(email_account, '接码')
browser.quit()
return
print("检查出现手机号运行完毕")
save_log(random_number, "检查出现手机号运行完毕")
try:
# 输入密码
password_input = tab.ele('@@aria-label=输入您的密码@@type=password', timeout=15)
if password_input:
flag = input_password(tab, hwnd, email_password, password_input) # 使用传入的邮箱密码
if flag:
update_status_in_db(email_account, "密码的下一步无法点击")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
random_sleep(tab)
except ElementNotFoundError as e:
print(f"找不到密码输入框的元素:{e}")
save_log(random_number, f"找不到密码输入框的元素:{e}")
update_status_in_db(email_account, "请求错误,请重试")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
print("输入旧密码运行完毕")
save_log(random_number, "输入旧密码运行完毕")
wrong = tab.ele("@text()=抱歉,出了点问题。请重试。", timeout=15)
save_log(random_number, f"谷歌验证按钮是否出现:{wrong}")
if wrong:
next_span = tab.ele("@@tag()=span@@text()=下一步", timeout=15)
next_span.click(by_js=True)
tab.wait.load_start()
try:
# 输入邮箱账号
email_input = tab.ele('@aria-label=电子邮件地址或电话号码', timeout=15)
if email_input:
input_email(tab, hwnd, email_account, email_input) # 使用传入的邮箱账号
random_sleep(tab)
except ElementNotFoundError as e:
print(f"找不到输入邮箱账号的元素{e}")
save_log(random_number, f"找不到输入邮箱账号的元素:{e}")
update_status_in_db(email_account, "请求错误,请重试")
return (
email_account, email_password, old_recovery_email, new_recovery_email, new_password, proxy_host,
proxy_port,
proxy_username, proxy_password)
try:
# 输入密码
password_input = tab.ele('@@aria-label=输入您的密码@@type=password', timeout=15)
if password_input:
input_password(tab, hwnd, email_password, password_input) # 使用传入的邮箱密码
random_sleep(tab)
except ElementNotFoundError as e:
print(f"找不到密码输入框的元素:{e}")
save_log(random_number, f"找不到密码输入框的元素:{e}")
update_status_in_db(email_account, "请求错误,请重试")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
print("如果出现错误,输入密码运行完毕")
save_log(random_number, "如果出现错误后,输入密码运行完毕")
# 确定密码是否被修改的开关
password_change = False
try:
# 查看密码是否更改
if tab.wait.ele_deleted('#passwordNext', timeout=5) == False:
save_log(random_number, f"旧密码出错,使用新密码")
password_input = tab.ele('@@aria-label=输入您的密码@@type=password', timeout=15)
input_password(tab, hwnd, new_password, password_input)
save_log(random_number, f"输入新密码完毕")
tab.wait(7)
if tab.wait.ele_deleted('#passwordNext', timeout=10) == False:
update_status_in_db(email_account, '被盗')
return email_account
password_change = True
print("密码已经被更改过")
save_log(random_number, "密码已经被更改过")
except ElementNotFoundError as e:
print(f"找不到密码输入框的元素{e}")
save_log(random_number, f"找不到密码输入框的元素:{e}")
update_status_in_db(email_account, "请求错误,请重试")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
print("输入新密码运行完毕")
save_log(random_number, "输入新密码运行完毕")
# 看下是否出现了手机号
telephone = tab.ele("@text()=请输入电话号码,以便通过短信接收验证码。", timeout=5)
if telephone:
save_log(random_number, '接码')
update_status_in_db(email_account, '接码')
return email_account
print("检查手机号2运行完毕")
save_log(random_number, "检查手机号2运行完毕")
wrong = tab.ele('@@tag()=a@@text()=了解详情', timeout=5)
alternate_email_button = tab.ele('@text()=确认您的辅助邮箱', timeout=5)
if wrong and not alternate_email_button:
save_log(random_number, '出现账号状态异常')
update_status_in_db(email_account, '出现账号状态异常')
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
print("检查谷歌账号是否存在异常活动完毕")
save_log(random_number, "检查谷歌账号是否存在异常活动完毕")
try:
if alternate_email_button:
click_alternate_email_verification_button(tab, alternate_email_button)
random_sleep(tab)
except ElementNotFoundError as e:
print(f"找不到输入辅助邮箱的元素:{e}" )
save_log(random_number, f"找不到输入辅助邮箱的元素:{e}")
update_status_in_db(email_account, "请求错误,请重试")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
print("点击辅助邮箱验证运行完毕")
save_log(random_number, "点击辅助邮箱验证运行完毕")
# 确定辅助邮箱账号是否被更改
auxiliary_email_account_change = False
# 输入辅助邮箱账号
recovery_input = tab.ele('@aria-label=输入辅助邮箱地址', timeout=15)
if recovery_input:
auxiliary_email_account_change = input_recovery_code(tab, old_recovery_email) # 使用传入的旧辅助邮箱
print(f"是否有修改辅助邮箱账号:{auxiliary_email_account_change}")
tab.wait(5)
# 如果没有被修改则进行修改
if not auxiliary_email_account_change:
print("我要开始修改辅助邮箱账号了")
try:
button = tab.ele('@type=email', timeout=15)
if button:
modify_the_secondary_email1(tab,button, new_recovery_email) # 使用传入的新辅助邮箱
print("修改完成")
auxiliary_email_account_change = True
else:
print("没有修改辅助邮箱的界面,跳过")
# auxiliary_email_account_change = True
except ElementNotFoundError as e:
# 捕获并打印异常,不中断后续代码执行
print(f"修改辅助邮箱的时候找不到元素: {e}")
save_log(random_number, f"修改辅助邮箱的时候找不到元素: {e}")
update_status_in_db(email_account, "请求错误,请重试")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password)
else:
print("辅助邮箱账号已经被更改过")
save_log(random_number, "辅助邮箱账号已经被更改过")
recovery_input.clear()
input_recovery_code(tab, new_recovery_email)
print("修改辅助邮箱1运行完毕")
save_log(random_number, "修改辅助邮箱1运行完毕")
if password_change and auxiliary_email_account_change:
logutGoogle(tab)
tab.wait(3)
update_status_in_db(email_account, '已更改')
return email_account
tab.handle_alert(accept=True)
try:
# 安全设置
tab.handle_alert(accept=True)
tab.get("https://ogs.google.com/u/0/widget/account?cn=account")
tab.handle_alert(accept=True)
tab.get("https://myaccount.google.com/security?gar=WzEyMF0")
except ElementNotFoundError as e:
save_log(random_number, f"进入安全设置后出错:{e}")
update_status_in_db(email_account,"进入安全设置的时候请求错误,请重试")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
tab.wait(3)
wrong = tab.ele("@text()=要查看和调整您的安全设置并获取有助于确保您账号安全的建议,请登录您的账号", timeout=5)
if wrong:
print("进入安全设置后仍然未登录")
save_log(random_number, '进入安全设置后仍然未登录')
update_status_in_db(email_account, '登录失败')
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
password_change_time1 = tab.ele("@text():Last changed").inner_html
try:
# 如果密码没被修改
if not password_change:
# 找到修改密码的按钮
find_and_visit_href2(tab)
# 修改密码
input_password_new(tab, new_password) # 使用传入的新密码
# 点击确认修改
click_span_with_class(tab)
password_change_time2 = tab.ele("@text():Last changed").inner_html
if password_change_time2 != password_change_time1:
password_change = True
else:
save_log(random_number, '密码没有成功修改')
print("密码没有成功修改")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
except ElementNotFoundError as e:
print(f"找不到修改密码的元素:{e}")
save_log(random_number, f"找不到修改密码的元素:{e}")
update_status_in_db(email_account, "请求错误,请重试")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
tab.wait(1)
try:
if auxiliary_email_account_change:
logutGoogle(tab)
tab.wait(3)
update_status_in_db(email_account, '已更改')
return email_account
# 修改辅助邮箱2
flag = modify_the_secondary_email2(tab, new_recovery_email, new_password, hwnd)
save_log(random_number, f"辅助邮箱是否被修改:{flag}")
if flag:
logutGoogle(tab)
tab.wait(3)
update_status_in_db(email_account, '已更改')
return email_account
else:
# 初始化
client = EmailClient(server, username, password)
client.connect()
code_receiver = GoogleCodeReceiver(client)
# 最多尝试3次循环
for attempt in range(3):
print(f"{attempt + 1} 次尝试检查验证码...")
save_log(random_number, f"{attempt + 1} 次尝试检查验证码...")
# 获取当前程序运行的时间
current_time = datetime.now(timezone.utc)
print(f"当前时间为{current_time}")
save_log(random_number, f"当前时间为{current_time}")
# 检查验证码超时时间为300秒使用当前时间作为 start_time
try:
code = code_receiver.wait_code(username=f"{email_account}", timeout=300, interval=5,
start_time=datetime(2024, 11, 27))
print(f"检查的验证码是{code}")
save_log(random_number, f"检查的验证码是{code}")
if code:
# 如果找到验证码,填入验证码
verification_input = tab.eles("@type=text", timeout=15)
if len(verification_input) >= 1:
verification_input[1].clear()
for char in code:
verification_input[1].input(char)
tab.wait(random.uniform(0.1, 0.3)) # 随机延迟 0.1 到 0.3 秒
print("输入完验证码了")
save_log(random_number, "输入完验证码了")
# 点击 Verify 按钮
verify_button = tab.eles("@text()=Verify", timeout=15)
if verify_button and len(verify_button) > 1:
verify_button[1].click(by_js=True)
tab.wait(5)
print("点击确定")
save_log(random_number, "点击确定")
# 检查是否有报错提示
is_visible = tab.ele('@@id=c4@@tag()=p@@role=alert', timeout=1)
save_log(random_number, f"是否有报错提示:{is_visible}")
if is_visible:
# 如果有报错提示,点击重新发送按钮并进入下一次循环
sent_code_button = tab.ele('@text():Send a new code.', timeout=15)
if sent_code_button:
print(f"重新发送邮件的按钮为:{sent_code_button}")
save_log(random_number, f"重新发送邮件的按钮为:{sent_code_button}")
sent_code_button.click(by_js=True)
continue
else:
# 如果没有报错,退出
print("辅助邮箱账号已经更改完毕")
save_log(random_number, "辅助邮箱账号已经更改完毕")
logutGoogle(tab)
tab.wait(3)
update_status_in_db(email_account, "已更改")
return email_account
else:
# 如果未找到验证码,点击重新发送按钮并进入下一次循环
print("未找到验证码,点击重新发送按钮")
sent_code_button = tab.ele('@text():Send a new code.', timeout=15)
if sent_code_button:
sent_code_button.click(by_js=True)
continue
except TimeoutError:
print("超时未收到验证码,重新发送")
save_log(random_number, "超时未收到验证码,重新发送")
sent_code_button = tab.ele('@text():Send a new code.', timeout=15)
if sent_code_button:
sent_code_button.click(by_js=True)
except ElementNotFoundError as e:
print(f"更改辅助邮箱账号的时候找不到元素:{e}")
save_log(random_number, f"更改辅助邮箱账号的时候找不到元素:{e}")
update_status_in_db(email_account, "请求错误,请重试")
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
except Exception as e:
print(f"出现未知错误:{e}")
save_log(random_number, f"出现未知错误:{e}")
update_status_in_db(email_account, f'出现未知错误:{e}请重试')
return (
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
finally:
try:
browser.quit()
# 释放资源
time.sleep(5)
delete_folder(plugin_path)
delete_folder(user_dir)
except Exception:
print("出现没有代理")
save_log(random_number, "出现没有代理")
def run_gui():
stop_flag = threading.Event() # 用于控制任务中止的标志位
exec_thread = None # 用于存储后台线程的引用
def handle_file_select():
file_path = filedialog.askopenfilename(
title="选择 Excel 文件",
filetypes=[("Excel 文件", "*.xlsx"), ("所有文件", "*.*")]
)
if file_path:
entry_file_path.delete(0, tk.END)
entry_file_path.insert(0, file_path)
def update_proxy_stats():
try:
# 国家代码列表
countries = {
"AT": "奥地利", "BE": "比利时", "CZ": "捷克", "DE": "德国", "DK": "丹麦",
"ES": "西班牙", "FI": "芬兰", "FR": "法国", "GB": "英国", "HR": "克罗地亚",
"HU": "匈牙利", "IT": "意大利", "LT": "立陶宛", "LU": "卢森堡", "NL": "荷兰",
"PL": "波兰", "PT": "葡萄牙", "RO": "罗马尼亚", "SE": "瑞典", "SK": "斯洛伐克"
}
# 获取全部区域的代理数量
try:
total_proxies = proxy_manager.get_proxy_count("ALL")
except Exception as e:
total_proxies = "获取失败"
print(f"获取全部代理数时出错: {e}")
# 显示总代表数量
proxy_stats = f"总数: {total_proxies} 个代理\n\n"
# 获取并显示每个国家的代理数量
country_data = []
for country_code, country_name in countries.items():
try:
# 获取该国家的代理数量
country_proxy_count = proxy_manager.get_proxy_count(country_code)
country_data.append(f"{country_name}({country_code}): {country_proxy_count} 个代理")
except Exception as e:
country_data.append(f"{country_name}({country_code}): 获取代理数失败")
print(f"获取 {country_name} ({country_code}) 代理数时出错: {e}")
# 更新界面上的代理统计标签
lbl_proxy_stats.config(text=proxy_stats)
# 分列显示国家的代理统计信息
max_rows = 5 # 每列最多显示多少行
columns = 4 # 显示几列
for col in range(columns):
for row in range(min(max_rows, len(country_data) - col * max_rows)):
label = tk.Label(root, text=country_data[col * max_rows + row], justify="left")
label.grid(row=row + 9, column=col, padx=5, pady=5, sticky="w")
# 为每列添加居中对齐,使用 columnspan 来确保列宽
for col in range(columns):
root.grid_columnconfigure(col, weight=1, uniform="equal")
# 调整列之间的间距,通过 columnconfigure 设置每列最小宽度minsize
for col in range(columns):
root.grid_columnconfigure(col, minsize=5)
except Exception as e:
print(f"更新代理统计信息时出错: {e}")
lbl_proxy_stats.config(text="获取代理统计信息失败")
def start_processing():
file_path = entry_file_path.get()
sheet_name = entry_sheet_name.get()
max_concurrency = entry_concurrency.get()
max_retries = entry_retries.get()
# 检查文件路径和 Sheet 名称是否填写完整
if not file_path or not sheet_name or not max_concurrency or not max_retries:
messagebox.showwarning("警告", "请填写完整的文件路径、表格名称、并发数和重试次数!")
return
# 检查文件路径是否存在
if not os.path.exists(file_path):
if not file_path.endswith(".xlsx"):
messagebox.showerror("错误", "文件的格式必须是.xlsx")
else:
messagebox.showerror("错误", "没有找到该路径的文件,请检查文件是否存在!")
return
# 检查文件是否正在被打开
try:
with open(file_path, 'r+b') as file:
pass # 成功打开说明未被占用
except PermissionError:
messagebox.showerror("错误", "文件正在被占用,请先保存并关闭 Excel 程序!")
return
# 验证 Sheet 名称是否存在
try:
wb = load_workbook(file_path, read_only=True)
if sheet_name not in wb.sheetnames:
messagebox.showerror("错误", f"{sheet_name}”表格名字不存在,请打开 Excel 文件检查!")
return
except Exception as e:
messagebox.showerror("错误", f"无法打开文件:{e}")
return
# 验证并发数
try:
max_concurrency = int(max_concurrency)
if max_concurrency <= 0:
raise ValueError("并发数必须是大于0的整数")
except ValueError as e:
messagebox.showerror("错误", f"无效的并发数:{e}")
return
# 验证重试次数
try:
max_retries = int(max_retries)
if max_retries < 0 or max_retries > 3:
raise ValueError("重试次数必须是整数且在0到3之间")
except ValueError as e:
messagebox.showerror("错误", f"无效的重试次数:{e}")
return
db_manager.import_from_excel(file_path, clear_old=True)
proxy_manager.import_proxies_with_classifier("Ip.txt", classifier=classifier_smartproxy)
# 初始化 Excel 数据和进度条状态
all_rows = read_data_from_db() # 读取 Excel 数据
total_tasks = len(all_rows)
completed_count = 0
failed_count = 0
# 设置进度条和状态显示
progress_bar['maximum'] = total_tasks
progress_bar['value'] = completed_count # 初始化为0
lbl_progress_status.config(
text=f"完成:{completed_count}/{total_tasks},失败:{failed_count}"
)
stop_flag.clear() # 清除停止标志
exec_thread = threading.Thread(target=parallel_execution_with_db,
args=(file_path, max_concurrency, max_retries))
exec_thread.daemon = True # 设置为守护线程
exec_thread.start()
def stop_processing():
stop_flag.set() # 设置标志位为 True通知停止
messagebox.showinfo("提示", "等待当前任务完成,程序即将停止!")
def on_closing():
stop_flag.set() # 设置标志位为 True通知停止
if exec_thread and exec_thread.is_alive():
exec_thread.join(timeout=5) # 等待线程结束
root.destroy() # 销毁主窗口
sys.exit(0) # 强制退出程序
def parallel_execution_with_db(file_path, max_concurrency, max_retries):
progress_queue = Queue()
all_rows = read_data_from_db() # 第一次读取数据
print(f"数据库中的数据:{all_rows}")
total_rows = len(all_rows)
completed_count = 0
failed_count = 0
progress_bar['maximum'] = total_rows
retry_count = 0
with Pool(processes=max_concurrency, initializer=init_worker()) as pool:
while all_rows:
if stop_flag.is_set():
print("停止信号已接收,中止任务!")
break
results = []
for i, row in enumerate(all_rows):
if stop_flag.is_set():
print("停止信号已接收,中止任务!")
break
# 获取邮箱和区域信息
account_email = row[0]
region = row[9] # 假设第6列存储的是账号的区域信息
if not region:
region = "ALL" # 如果没有区域信息,则使用默认区域
# 获取一个随机代理并传递给当前账号
proxy = proxy_manager.get_random_proxy_by_region(region=region, remove_after_fetch=True)
if proxy: # 如果分配成功
proxy_host, proxy_port, proxy_user, proxy_pass = proxy['host'], proxy['port'], proxy['user'], \
proxy['password']
print(f"为账号 {account_email} 分配代理:{proxy_host}:{proxy_port}:{proxy_user}:{proxy_pass}")
db_manager.update_record(account_email,
proxy=f"{proxy_host}:{proxy_port}:{proxy_user}:{proxy_pass}")
row = row[:5] + (proxy_host, proxy_port, proxy_user, proxy_pass) + row[9:] # 将代理信息加入到数据行中
else:
print(f"为账号 {account_email} 分配代理失败,跳过此账号。")
with write_lock:
try:
print("更新剩余可用IP")
proxy_manager.export_proxies("剩下的可用IP.txt")
except Exception as e:
print(f"导出代理时发生错误: {e}")
update_proxy_stats()
# 传递锁给子进程,并将当前账号数据传递给 main 函数
result = pool.apply_async(main, args=(row), callback=lambda result: progress_queue.put(result))
results.append(result)
for result in results:
try:
retry_row = result.get()
print(f"main函数返回的值: {retry_row}")
if not retry_row: # 如果返回False表示任务失败
failed_count += 1
elif isinstance(retry_row, tuple): # 返回的是元组,表示需要重试
failed_count += 1 # 增加失败计数
else: # 返回的是单个 account表示成功
completed_count += 1
# 更新进度条和显示的状态
progress_bar['value'] = completed_count
lbl_progress_status.config(text=f"完成:{completed_count}/{total_rows},失败:{failed_count}")
time.sleep(2) # 模拟耗时操作
except Exception as e:
print(f"任务执行时发生错误: {e}")
failed_count += 1
lbl_progress_status.config(text=f"完成:{completed_count}/{total_rows},失败:{failed_count}")
messagebox.showwarning("任务执行错误", f"任务执行时发生错误: {e}")
# 如果停止信号被触发,则中止
if stop_flag.is_set():
print("停止信号已接收,中止任务!")
break
# 重试逻辑,重新从数据库读取失败的数据
if retry_count < max_retries:
retry_count += 1
all_rows = read_data_from_db() # 重新从数据库读取失败的记录
else:
print("达到最大重试次数,停止重试。")
break
else:
print("所有任务已完成。")
messagebox.showinfo('运行结束', '所有任务已经完成')
def read_data_from_db() -> List[Tuple]:
"""
从数据库读取数据,同时保持与旧的接口和逻辑一致。
根据账号区域自动分配代理,并导入到数据库。
(去除代理分配和数据库更新代理信息的部分)
"""
# 初始化数据库管理器
skip_keywords = {"已更改", "接码", "被盗"}
data = []
# 从数据库中查询所有数据
accounts = db_manager.export_data()
global total_tasks, completed_tasks, failed_tasks # 全局变量以更新状态
# 计算任务数量
completed_tasks = sum(account.change_status in skip_keywords for account in accounts)
failed_tasks = len(accounts) - completed_tasks - sum(account.change_status is None for account in accounts)
total_tasks = len(accounts) - completed_tasks
for i, account in enumerate(accounts):
if not account.email:
break
if account.change_status not in skip_keywords:
# 不再分配代理,只是记录账号信息
data.append((
account.email, account.original_password, account.original_aux_email,
account.new_password, account.new_aux_email, "", "", "", "",account.region
))
return data
root = tk.Tk()
root.title("程序控制界面")
tk.Label(root, text="Excel 文件路径:").grid(row=0, column=0, padx=10, pady=10)
entry_file_path = tk.Entry(root, width=50)
entry_file_path.grid(row=0, column=1, padx=10, pady=10)
btn_select_file = tk.Button(root, text="选择文件", command=handle_file_select)
btn_select_file.grid(row=0, column=2, padx=10, pady=10)
tk.Label(root, text="Sheet 名称:").grid(row=1, column=0, padx=10, pady=10)
entry_sheet_name = tk.Entry(root, width=50)
entry_sheet_name.grid(row=1, column=1, padx=10, pady=10)
entry_sheet_name.insert(0, "修改邮箱密码和辅助邮箱")
tk.Label(root, text="并发数:").grid(row=2, column=0, padx=10, pady=10)
entry_concurrency = tk.Entry(root, width=50)
entry_concurrency.grid(row=2, column=1, padx=10, pady=10)
entry_concurrency.insert(0, str(multiprocessing.cpu_count()))
tk.Label(root, text="重试次数:").grid(row=3, column=0, padx=10, pady=10)
entry_retries = tk.Entry(root, width=50)
entry_retries.grid(row=3, column=1, padx=10, pady=10)
entry_retries.insert(0, "3")
# 进度条
progress_bar = ttk.Progressbar(root, orient="horizontal", length=400, mode="determinate")
progress_bar.grid(row=4, column=0, columnspan=2, padx=10, pady=10)
# 进度状态显示
lbl_progress_status = tk.Label(root, text="完成0/0失败0")
lbl_progress_status.grid(row=5, column=0, columnspan=2, padx=10, pady=10)
btn_start = tk.Button(root, text="开始处理", command=start_processing)
btn_start.grid(row=6, column=0, columnspan=2, padx=20, pady=20, sticky="ew")
btn_stop = tk.Button(root, text="停止处理", command=stop_processing)
btn_stop.grid(row=7, column=0, columnspan=2, padx=20, pady=20, sticky="ew")
# 添加显示代理统计信息的标签
tk.Label(root, text="代理统计信息:").grid(row=8, column=0, padx=10, pady=10)
lbl_proxy_stats = tk.Label(root, text="代理统计信息未加载")
lbl_proxy_stats.grid(row=8, column=1, padx=5, pady=10, columnspan=2)
root.protocol("WM_DELETE_WINDOW", on_closing) # 绑定窗口关闭事件
root.mainloop()
# 示例运行方式
if __name__ == "__main__":
logs_dir = "logs"
# 确保 logs 文件夹存在,如果不存在则创建
if not os.path.exists(logs_dir):
os.makedirs(logs_dir)
# 清空 logs 文件夹中的所有文件
for filename in os.listdir("logs"):
file_path = os.path.join("logs", filename)
if os.path.isfile(file_path):
os.remove(file_path)
freeze_support()
run_gui()