Gmail/main.py

1667 lines
69 KiB
Python
Raw Normal View History

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
2024-11-23 14:27:50 +00:00
from multiprocessing import Queue, Pool
from account import AccountManagerSQLite
from typing import List, Tuple
import win32gui
2024-11-26 16:30:00 +00:00
from mail import GoogleCodeReceiver, EmailClient
from proxy import ProxyManagerSQLite, classifier_smartproxy
from datetime import datetime, timedelta, timezone
# Windows 消息常量
WM_MOUSEMOVE = 0x0200
WM_LBUTTONDOWN = 0x0201
WM_LBUTTONUP = 0x0202
MK_LBUTTON = 0x0001
2024-11-26 16:30:00 +00:00
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")
2024-11-26 16:30:00 +00:00
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")
2024-11-26 16:30:00 +00:00
# 全局写入锁
write_lock = multiprocessing.Lock()
ua_templates = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/{webkit_version} (KHTML, like Gecko) Chrome/{chrome_version} Safari/{safari_version}",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/{webkit_version} (KHTML, like Gecko) Chrome/{chrome_version} Safari/{safari_version}",
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/{webkit_version} (KHTML, like Gecko) Chrome/{chrome_version} Safari/{safari_version}",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/{webkit_version} (KHTML, like Gecko) Chrome/{chrome_version} Safari/{safari_version}",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/{webkit_version} (KHTML, like Gecko) Chrome/{chrome_version} Safari/{safari_version}",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/{webkit_version} (KHTML, like Gecko) Chrome/{chrome_version} Safari/{safari_version}",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/{webkit_version} (KHTML, like Gecko) Chrome/{chrome_version} Safari/{safari_version}",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/{webkit_version} (KHTML, like Gecko) Chrome/{chrome_version} Safari/{safari_version}",
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:95.0) AppleWebKit/{webkit_version} (KHTML, like Gecko) Chrome/{chrome_version} Safari/{safari_version}",
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/{webkit_version} (KHTML, like Gecko) Chrome/{chrome_version} Safari/{safari_version}"
]
# 自定义异常
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 generate_user_agent():
# 随机选择一个Chrome的UA模板
selected_template = random.choice(ua_templates)
# 随机生成 WebKit、Safari 和 Chrome 版本号
webkit_version = f"{random.randint(500, 599)}.{random.randint(0, 99)}"
safari_version = f"{random.randint(500, 599)}.{random.randint(0, 99)}"
chrome_version = f"{random.randint(40, 99)}.{random.randint(0, 2999)}.{random.randint(0, 99)}"
# 填充模板中的动态部分并生成完整的UA
user_agent = selected_template.format(
webkit_version=webkit_version,
safari_version=safari_version,
chrome_version=chrome_version
)
return user_agent
def random_sleep(tab, min_seconds=1, max_seconds=3):
"""在 min_seconds 和 max_seconds 之间随机停顿"""
tab.wait(random.uniform(min_seconds, max_seconds))
2024-11-26 16:30:00 +00:00
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)) # 随机延迟
2024-11-26 16:30:00 +00:00
next_input = tab.ele('@@tag()=span@@text()=下一步', timeout=15)
next_input.click(by_js=True)
2024-11-26 16:30:00 +00:00
print("到这里我已经点完下一步,进入修改辅助邮箱的地方了")
tab.wait.load_start()
tab.wait(1)
retry = tab.ele("@text()=您输入的电子邮件地址不正确,请重试。",timeout=15)
if retry:
return True
2024-11-26 16:30:00 +00:00
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)
2024-11-23 14:27:50 +00:00
# 用来标记账号是否完成
2024-11-23 14:27:50 +00:00
def update_status_in_db(email: str, status: str):
"""
2024-11-23 14:27:50 +00:00
更新数据库中指定账户的状态
"""
2024-11-23 14:27:50 +00:00
# 更新数据库中对应账户的状态
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, '初始化中')
2024-11-26 16:30:00 +00:00
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
random_user_agent = generate_user_agent()
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)
2024-11-26 16:30:00 +00:00
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 (
2024-11-26 16:30:00 +00:00
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 (
2024-11-26 16:30:00 +00:00
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}")
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, "请求错误,请重试")
return (
2024-11-26 16:30:00 +00:00
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}")
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, "请求错误,请重试")
return (
2024-11-26 16:30:00 +00:00
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:
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, '接码')
browser.quit()
2024-11-26 16:30:00 +00:00
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}")
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, "请求错误,请重试")
return (
2024-11-26 16:30:00 +00:00
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
2024-11-26 16:30:00 +00:00
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}")
2024-11-26 16:30:00 +00:00
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}")
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, "请求错误,请重试")
return (
2024-11-26 16:30:00 +00:00
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:
# 查看密码是否更改
2024-11-26 16:30:00 +00:00
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)
2024-11-26 16:30:00 +00:00
save_log(random_number, f"输入新密码完毕")
tab.wait(7)
if tab.wait.ele_deleted('#passwordNext', timeout=10) == False:
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, '被盗')
2024-11-23 14:27:50 +00:00
return email_account
password_change = True
print("密码已经被更改过")
save_log(random_number, "密码已经被更改过")
except ElementNotFoundError as e:
print(f"找不到密码输入框的元素{e}")
save_log(random_number, f"找不到密码输入框的元素:{e}")
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, "请求错误,请重试")
return (
2024-11-26 16:30:00 +00:00
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}")
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, "请求错误,请重试")
return (
2024-11-26 16:30:00 +00:00
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) # 使用传入的旧辅助邮箱
2024-11-26 16:30:00 +00:00
print(f"是否有修改辅助邮箱账号:{auxiliary_email_account_change}")
tab.wait(5)
# 如果没有被修改则进行修改
if not auxiliary_email_account_change:
2024-11-26 16:30:00 +00:00
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("没有修改辅助邮箱的界面,跳过")
2024-11-26 16:30:00 +00:00
# auxiliary_email_account_change = True
except ElementNotFoundError as e:
# 捕获并打印异常,不中断后续代码执行
print(f"修改辅助邮箱的时候找不到元素: {e}")
save_log(random_number, f"修改辅助邮箱的时候找不到元素: {e}")
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, "请求错误,请重试")
return (
2024-11-26 16:30:00 +00:00
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)
2024-11-26 16:30:00 +00:00
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, '已更改')
2024-11-23 14:27:50 +00:00
return email_account
tab.handle_alert(accept=True)
# # 点击继续按钮
# continue_div1 = tab.ele('@text()=Continue with smart features', timeout=15)
# if continue_div1:
# try:
# click_continue_button(tab, continue_div1)
# 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)
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 (
2024-11-26 16:30:00 +00:00
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)
try:
# 如果密码没被修改
if not password_change:
# 找到修改密码的按钮
find_and_visit_href2(tab)
# 修改密码
input_password_new(tab, new_password) # 使用传入的新密码
# 点击确认修改
click_span_with_class(tab)
except ElementNotFoundError as e:
print(f"找不到修改密码的元素:{e}")
save_log(random_number, f"找不到修改密码的元素:{e}")
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, "请求错误,请重试")
return (
2024-11-26 16:30:00 +00:00
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)
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, '已更改')
2024-11-23 14:27:50 +00:00
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)
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, '已更改')
2024-11-23 14:27:50 +00:00
return email_account
else:
# 初始化
client = EmailClient(server, username, password)
client.connect()
code_receiver = GoogleCodeReceiver(client)
2024-11-26 16:30:00 +00:00
# 最多尝试3次循环
for attempt in range(3):
print(f"{attempt + 1} 次尝试检查验证码...")
save_log(random_number, f"{attempt + 1} 次尝试检查验证码...")
2024-11-26 16:30:00 +00:00
# 获取当前程序运行的时间
current_time = datetime.now(timezone.utc)
print(f"当前时间为{current_time}")
save_log(random_number, f"当前时间为{current_time}")
2024-11-26 16:30:00 +00:00
# 检查验证码超时时间为300秒使用当前时间作为 start_time
try:
code = code_receiver.wait_code(username=f"{email_account}", timeout=300, interval=5,
start_time=datetime(2024, 11, 27))
2024-11-26 16:30:00 +00:00
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, "点击确定")
2024-11-26 16:30:00 +00:00
# 检查是否有报错提示
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}")
2024-11-26 16:30:00 +00:00
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:
2024-11-26 16:30:00 +00:00
# 如果未找到验证码,点击重新发送按钮并进入下一次循环
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)
2024-11-26 16:30:00 +00:00
if sent_code_button:
sent_code_button.click(by_js=True)
except ElementNotFoundError as e:
print(f"更改辅助邮箱账号的时候找不到元素:{e}")
save_log(random_number, f"更改辅助邮箱账号的时候找不到元素:{e}")
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, "请求错误,请重试")
return (
2024-11-26 16:30:00 +00:00
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}")
2024-11-26 16:30:00 +00:00
update_status_in_db(email_account, f'出现未知错误:{e}请重试')
return (
2024-11-26 16:30:00 +00:00
email_account, email_password, old_recovery_email, new_password, new_recovery_email, proxy_host,
proxy_port,
proxy_username, proxy_password, region)
2024-11-26 16:30:00 +00:00
finally:
try:
browser.quit()
# 释放资源
time.sleep(5)
delete_folder(plugin_path)
delete_folder(user_dir)
except Exception:
print("出现没有代理")
save_log(random_number, "出现没有代理")
2024-11-26 16:30:00 +00:00
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)
2024-11-26 16:30:00 +00:00
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"
2024-11-26 16:30:00 +00:00
# 获取并显示每个国家的代理数量
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}")
# 更新界面上的代理统计标签
2024-11-26 16:30:00 +00:00
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)
2024-11-26 16:30:00 +00:00
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()
2024-11-26 16:30:00 +00:00
# 检查文件路径和 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() # 清除停止标志
2024-11-23 14:27:50 +00:00
exec_thread = threading.Thread(target=parallel_execution_with_db,
2024-11-26 16:30:00 +00:00
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) # 强制退出程序
2024-11-26 16:30:00 +00:00
def parallel_execution_with_db(file_path, max_concurrency, max_retries):
progress_queue = Queue()
all_rows = read_data_from_db() # 第一次读取数据
2024-11-26 16:30:00 +00:00
print(f"数据库中的数据:{all_rows}")
total_rows = len(all_rows)
completed_count = 0
failed_count = 0
progress_bar['maximum'] = total_rows
retry_count = 0
2024-11-26 16:30:00 +00:00
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
2024-11-26 16:30:00 +00:00
# 获取邮箱和区域信息
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}")
2024-11-26 16:30:00 +00:00
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)
2024-11-26 16:30:00 +00:00
for result in results:
try:
retry_row = result.get()
print(f"main函数返回的值: {retry_row}")
2024-11-23 14:27:50 +00:00
if not retry_row: # 如果返回False表示任务失败
failed_count += 1
elif isinstance(retry_row, tuple): # 返回的是元组,表示需要重试
failed_count += 1 # 增加失败计数
2024-11-23 14:27:50 +00:00
else: # 返回的是单个 account表示成功
completed_count += 1
# 更新进度条和显示的状态
progress_bar['value'] = completed_count
lbl_progress_status.config(text=f"完成:{completed_count}/{total_rows},失败:{failed_count}")
2024-11-23 14:27:50 +00:00
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]:
2024-11-23 14:27:50 +00:00
"""
从数据库读取数据同时保持与旧的接口和逻辑一致
2024-11-26 16:30:00 +00:00
根据账号区域自动分配代理并导入到数据库
去除代理分配和数据库更新代理信息的部分
2024-11-23 14:27:50 +00:00
"""
# 初始化数据库管理器
skip_keywords = {"已更改", "接码", "被盗"}
data = []
2024-11-23 14:27:50 +00:00
# 从数据库中查询所有数据
accounts = db_manager.export_data()
global total_tasks, completed_tasks, failed_tasks # 全局变量以更新状态
2024-11-26 16:30:00 +00:00
# 计算任务数量
2024-11-23 14:27:50 +00:00
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
2024-11-23 14:27:50 +00:00
for i, account in enumerate(accounts):
if not account.email:
break
2024-11-26 16:30:00 +00:00
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
2024-11-26 16:30:00 +00:00
))
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")
2024-11-26 16:30:00 +00:00
# 添加显示代理统计信息的标签
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)
2024-11-26 16:30:00 +00:00
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()