1067 lines
33 KiB
Python
1067 lines
33 KiB
Python
|
import socket
|
|||
|
import warnings
|
|||
|
from hashlib import md5
|
|||
|
|
|||
|
import requests
|
|||
|
from sqlalchemy import create_engine
|
|||
|
import pandas as pd
|
|||
|
import logging
|
|||
|
import time
|
|||
|
import sys
|
|||
|
import smtplib
|
|||
|
from email.header import Header
|
|||
|
from email.mime.text import MIMEText
|
|||
|
from email.mime.image import MIMEImage
|
|||
|
from email.mime.multipart import MIMEMultipart
|
|||
|
from email.mime.application import MIMEApplication
|
|||
|
import getpass
|
|||
|
import os
|
|||
|
import uuid
|
|||
|
import redis
|
|||
|
import ctypes
|
|||
|
import platform
|
|||
|
import re
|
|||
|
import subprocess
|
|||
|
import winreg
|
|||
|
import random
|
|||
|
project_name = 'auto_trade_20230130'
|
|||
|
from hashlib import md5
|
|||
|
import sys
|
|||
|
import datetime
|
|||
|
|
|||
|
import pandas as pd
|
|||
|
import os
|
|||
|
from openpyxl import load_workbook
|
|||
|
|
|||
|
import subprocess
|
|||
|
import time
|
|||
|
import requests
|
|||
|
|
|||
|
import requests
|
|||
|
import subprocess
|
|||
|
import time
|
|||
|
# from alibabacloud_tea_openapi import models as open_api_models
|
|||
|
# from alibabacloud_credentials.client import Client as CredentialClient
|
|||
|
# from alibabacloud_credentials.models import Config as CredentialConfig
|
|||
|
# from alibabacloud_ocr_api20210707.client import Client as OcrClient
|
|||
|
# from alibabacloud_ocr_api20210707 import models as ocr_models
|
|||
|
# from alibabacloud_tea_util import models as util_models
|
|||
|
# from alibabacloud_tea_util.client import Client as StreamClient
|
|||
|
|
|||
|
def connect_to_wifi(ssid='MaxEntropy', password='cskj12345678'):
|
|||
|
# 先检测当前网络是否正常
|
|||
|
try:
|
|||
|
response = requests.get("https://www.baidu.com", timeout=5)
|
|||
|
if response.status_code == 200:
|
|||
|
print("当前网络连接正常,无需重连WiFi")
|
|||
|
return True
|
|||
|
except:
|
|||
|
print("当前网络不可用,将尝试连接WiFi...")
|
|||
|
|
|||
|
# 生成 WiFi 配置文件(XML格式)
|
|||
|
wifi_config = f"""<?xml version="1.0"?>
|
|||
|
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
|
|||
|
<name>{ssid}</name>
|
|||
|
<SSIDConfig>
|
|||
|
<SSID>
|
|||
|
<name>{ssid}</name>
|
|||
|
</SSID>
|
|||
|
</SSIDConfig>
|
|||
|
<connectionType>ESS</connectionType>
|
|||
|
<connectionMode>auto</connectionMode>
|
|||
|
<MSM>
|
|||
|
<security>
|
|||
|
<authEncryption>
|
|||
|
<authentication>WPA2PSK</authentication>
|
|||
|
<encryption>AES</encryption>
|
|||
|
<useOneX>false</useOneX>
|
|||
|
</authEncryption>
|
|||
|
<sharedKey>
|
|||
|
<keyType>passPhrase</keyType>
|
|||
|
<protected>false</protected>
|
|||
|
<keyMaterial>{password}</keyMaterial>
|
|||
|
</sharedKey>
|
|||
|
</security>
|
|||
|
</MSM>
|
|||
|
</WLANProfile>"""
|
|||
|
|
|||
|
try:
|
|||
|
# 保存配置文件
|
|||
|
with open("wifi_profile.xml", "w") as f:
|
|||
|
f.write(wifi_config.strip())
|
|||
|
|
|||
|
# 添加 WiFi 配置文件
|
|||
|
subprocess.run(
|
|||
|
["netsh", "wlan", "add", "profile", "filename=wifi_profile.xml"],
|
|||
|
check=True,
|
|||
|
shell=True,
|
|||
|
)
|
|||
|
|
|||
|
# 连接 WiFi
|
|||
|
subprocess.run(
|
|||
|
["netsh", "wlan", "connect", "name=" + ssid],
|
|||
|
check=True,
|
|||
|
shell=True,
|
|||
|
)
|
|||
|
|
|||
|
# 等待连接成功
|
|||
|
max_retries = 5
|
|||
|
for i in range(max_retries):
|
|||
|
try:
|
|||
|
response = requests.get("https://www.baidu.com", timeout=5)
|
|||
|
if response.status_code == 200:
|
|||
|
print(f"成功连接到 WiFi: {ssid}")
|
|||
|
return True
|
|||
|
except:
|
|||
|
if i < max_retries - 1:
|
|||
|
print(f"等待网络连接...({i + 1}/{max_retries})")
|
|||
|
time.sleep(2)
|
|||
|
|
|||
|
print("WiFi连接失败: 超过最大重试次数")
|
|||
|
return False
|
|||
|
|
|||
|
except subprocess.CalledProcessError as e:
|
|||
|
print(f"WiFi配置失败: {e}")
|
|||
|
return False
|
|||
|
except Exception as e:
|
|||
|
print(f"发生未知错误: {e}")
|
|||
|
if os.path.exists("wifi_profile.xml"):
|
|||
|
os.remove("wifi_profile.xml")
|
|||
|
return False
|
|||
|
|
|||
|
|
|||
|
|
|||
|
class ExcelDataWriter:
|
|||
|
"""
|
|||
|
Excel数据写入工具类(定制列名版)
|
|||
|
列名顺序:as_of_date, id, account, proxy_ip, ip_location, exit_ip,
|
|||
|
ip_check_result, usage_result, ip_switch_time,
|
|||
|
transaction_duration, ip_survival_time, remarks,
|
|||
|
platform_risk_response
|
|||
|
"""
|
|||
|
|
|||
|
def __init__(self, filename=r'C:\auto_trade_20230130\mysql_table\ip_tracking.xlsx',sheet_name = 'Sheet1'):
|
|||
|
"""
|
|||
|
初始化Excel写入器
|
|||
|
:param filename: Excel文件名(默认ip_tracking.xlsx)
|
|||
|
"""
|
|||
|
self.filename = filename
|
|||
|
self.sheet_name = sheet_name
|
|||
|
# 严格按照要求的列名和顺序
|
|||
|
self.columns = [
|
|||
|
'as_of_date',
|
|||
|
'broker',
|
|||
|
'id',
|
|||
|
'account',
|
|||
|
'proxy_ip',
|
|||
|
'proxy_port',
|
|||
|
'ip_location',
|
|||
|
'exit_ip',
|
|||
|
'ip_cross_check_result',
|
|||
|
'usage_result',
|
|||
|
'ip_switch_time',
|
|||
|
'transaction_duration',
|
|||
|
'ip_survival_time',
|
|||
|
'remarks',
|
|||
|
'platform_risk_response'
|
|||
|
]
|
|||
|
|
|||
|
# 如果文件不存在,创建带指定列名的空Excel文件
|
|||
|
if not os.path.exists(self.filename):
|
|||
|
pd.DataFrame(columns=self.columns).to_excel(self.filename, sheet_name=self.sheet_name,index=False)
|
|||
|
|
|||
|
def write_data(self, data_dict):
|
|||
|
"""追加单条数据到 Excel(自动补全缺失列)"""
|
|||
|
try:
|
|||
|
# 1. 补全缺失列
|
|||
|
full_data = {col: data_dict.get(col) for col in self.columns}
|
|||
|
|
|||
|
# 2. 读取现有数据
|
|||
|
if os.path.exists(self.filename):
|
|||
|
existing_df = pd.read_excel(self.filename)
|
|||
|
# 验证现有列是否匹配
|
|||
|
if list(existing_df.columns) != self.columns:
|
|||
|
raise ValueError("现有文件的列名不匹配,请使用新文件")
|
|||
|
else:
|
|||
|
existing_df = pd.DataFrame(columns=self.columns)
|
|||
|
|
|||
|
# 3. 创建新数据行
|
|||
|
new_row = pd.DataFrame([full_data])[self.columns]
|
|||
|
# 4. 合并数据
|
|||
|
updated_df = pd.concat([existing_df, new_row], ignore_index=True)
|
|||
|
|
|||
|
# 5. 写入文件
|
|||
|
updated_df.to_excel(self.filename, index=False)
|
|||
|
print('数据一')
|
|||
|
return True
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
print(f"Excel 写入失败: {str(e)}")
|
|||
|
return False
|
|||
|
|
|||
|
def update_latest_record(self, status='成功', remarks='', risk_response=''):
|
|||
|
"""
|
|||
|
更新最新记录的状态信息
|
|||
|
:param status: 使用结果(成功/失败)
|
|||
|
:param remarks: 备注信息
|
|||
|
:param risk_response: 平台风险响应
|
|||
|
:return: 成功返回True,失败返回False
|
|||
|
"""
|
|||
|
# 读取整个Excel文件
|
|||
|
df = pd.read_excel(self.filename, sheet_name=self.sheet_name)
|
|||
|
|
|||
|
if len(df) == 0:
|
|||
|
raise ValueError("Excel文件中没有记录可更新")
|
|||
|
|
|||
|
# 获取最新记录索引
|
|||
|
last_idx = df.index[-1]
|
|||
|
|
|||
|
# 计算交易持续时间
|
|||
|
if pd.notna(df.at[last_idx, 'as_of_date']):
|
|||
|
start_time = datetime.datetime.strptime(df.at[last_idx, 'as_of_date'], "%Y-%m-%d %H:%M:%S.%f").timestamp()
|
|||
|
transaction_duration = round(time.time() - start_time, 2)
|
|||
|
else:
|
|||
|
transaction_duration = 0
|
|||
|
|
|||
|
# 更新字段
|
|||
|
df['usage_result'] = df['usage_result'].astype(str)
|
|||
|
df['remarks'] = df['remarks'].astype(str)
|
|||
|
df['platform_risk_response'] = df['platform_risk_response'].astype(str)
|
|||
|
df.at[last_idx, 'usage_result'] = status
|
|||
|
df.at[last_idx, 'transaction_duration'] = transaction_duration
|
|||
|
df.at[last_idx, 'remarks'] = remarks
|
|||
|
df.at[last_idx, 'platform_risk_response'] = risk_response
|
|||
|
# 保存回文件
|
|||
|
df.to_excel(self.filename, sheet_name=self.sheet_name, index=False)
|
|||
|
|
|||
|
def get_unavailable_ips(self, broker, account):
|
|||
|
|
|||
|
"""目标:返回指定券商(brokerhao)下,指定账户(account)不能使用过的代理 IP 列表"""
|
|||
|
|
|||
|
# 读取整个Excel文件
|
|||
|
df = pd.read_excel(self.filename, sheet_name=self.sheet_name)
|
|||
|
|
|||
|
# 如果数据为空,返回空列表
|
|||
|
if len(df) == 0:
|
|||
|
return []
|
|||
|
|
|||
|
# 获取该券商下所有已使用的IP
|
|||
|
broker_used_ips = df[df['broker'] == broker]['proxy_ip'].tolist()
|
|||
|
|
|||
|
# 获取该券商下该账户已使用的IP
|
|||
|
account_used_ips = df[(df['broker'] == broker) & (df['account'] == account)]['proxy_ip'].tolist()
|
|||
|
|
|||
|
# 计算不能使用的IP = 该券商所有已用IP - 该账户已使用的IP
|
|||
|
unused_ips = list(set(broker_used_ips) - set(account_used_ips))
|
|||
|
|
|||
|
return unused_ips
|
|||
|
|
|||
|
# TempStdoutArea对象为为重定向标准输出准备的空间,以列表形式存在
|
|||
|
class TempStdoutArea:
|
|||
|
def __init__(self):
|
|||
|
self.buffer = []
|
|||
|
|
|||
|
def write(self, *args, **kwargs):
|
|||
|
self.buffer.append(args)
|
|||
|
|
|||
|
|
|||
|
# 实现获取指定控件坐标的对象
|
|||
|
class GetControlCoord:
|
|||
|
# app为 pywinauto框架实例化的Application对象
|
|||
|
def __init__(self, app):
|
|||
|
self.app = app
|
|||
|
|
|||
|
# 获取指定控件坐标的方法,control参数传入指定控件
|
|||
|
def get_coord(self, control):
|
|||
|
# 重定向标准输出,将原本的标准输出信息写入自定义的空间内,以获取控件信息
|
|||
|
stdout = sys.stdout
|
|||
|
sys.stdout = TempStdoutArea()
|
|||
|
# pywinauto框架提供的print_control_identifiers()方法,可以打印出应用程序的控件信息,其中就包含控件坐标
|
|||
|
self.app.print_control_identifiers()
|
|||
|
# 将控件信息转移到control_identifiers,并还原标准输出
|
|||
|
control_identifiers, sys.stdout = sys.stdout, stdout
|
|||
|
# print(control_identifiers.buffer)
|
|||
|
|
|||
|
all_coord = []
|
|||
|
# 遍历控件信息,获取指定控件的坐标
|
|||
|
for a_control_identifier in control_identifiers.buffer:
|
|||
|
# print(a_control_identifier)
|
|||
|
# print(a_control_identifier[0])
|
|||
|
a_control_identifier = a_control_identifier[0]
|
|||
|
|
|||
|
if a_control_identifier.find(control) > -1:
|
|||
|
# print(a_control_identifier)
|
|||
|
a_coord = []
|
|||
|
str_coord = a_control_identifier[
|
|||
|
a_control_identifier.find('(') + 1: a_control_identifier.find(')')].split(',')
|
|||
|
# print(str_coord)
|
|||
|
for i in str_coord:
|
|||
|
num = i.strip()[1:]
|
|||
|
a_coord.append(num)
|
|||
|
all_coord.append(a_coord)
|
|||
|
return all_coord
|
|||
|
|
|||
|
|
|||
|
def send_email(subject: str, message: str, receive_users: list = ['1055017575@qq.com', '2293908092@qq.com'],
|
|||
|
file_path: str = 'no_path'):
|
|||
|
'''
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
subject : str
|
|||
|
title.
|
|||
|
message : str
|
|||
|
message.
|
|||
|
receive_users : list, optional
|
|||
|
receive. The default is ['1055017575@qq.com'].
|
|||
|
file_path : str, optional
|
|||
|
path. The default is 'no_path'.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
None.
|
|||
|
|
|||
|
'''
|
|||
|
|
|||
|
send_user = 'it_ainvest@zohomail.cn' # zoho
|
|||
|
send_pwd = '2022@AIWANTE' # 密码
|
|||
|
receive_user = receive_users
|
|||
|
|
|||
|
msg = MIMEMultipart()
|
|||
|
msg['subject'] = subject
|
|||
|
msg['from'] = send_user
|
|||
|
msg['to'] = ','.join(receive_user)
|
|||
|
msg.attach(MIMEText(message, 'html', 'utf-8'))
|
|||
|
|
|||
|
if file_path != 'no_path':
|
|||
|
part = MIMEApplication(open(file_path, 'rb').read())
|
|||
|
file_name = file_path.split('/')[-1]
|
|||
|
part.add_header('Content-Disposition', 'attachment', filename=file_name)
|
|||
|
msg.attach(part)
|
|||
|
else:
|
|||
|
pass
|
|||
|
|
|||
|
server = smtplib.SMTP_SSL("smtp.zoho.com.cn", 465)
|
|||
|
server.login(send_user, send_pwd)
|
|||
|
server.sendmail(send_user, receive_user, msg.as_string())
|
|||
|
server.quit()
|
|||
|
print('邮件发送成功')
|
|||
|
|
|||
|
|
|||
|
def download_data_from_db(sql, schema_name):
|
|||
|
"""
|
|||
|
create connection with DB
|
|||
|
阿里云数据库连接
|
|||
|
:param sql: 查询语句
|
|||
|
:param schema_name: 数据库名称
|
|||
|
:return: 查询结果数据
|
|||
|
"""
|
|||
|
cnx = create_engine(
|
|||
|
'mysql+pymysql://cn_ainvest_db:cn_ainvest_sd3a1@rm-2zewagytttzk6f24xno.'
|
|||
|
'mysql.rds.aliyuncs.com:3306/%s?charset=utf8' %
|
|||
|
schema_name, encoding="utf-8", echo=False)
|
|||
|
df = pd.read_sql(sql, con=cnx)
|
|||
|
return df
|
|||
|
|
|||
|
|
|||
|
# 转换股票代码
|
|||
|
def tranTicker(tick):
|
|||
|
tick = str(tick)
|
|||
|
|
|||
|
if len(tick) == 8:
|
|||
|
tick = tick.strip('SH').strip('SZ')
|
|||
|
if tick.startswith('6') or tick.startswith('11') or tick.startswith('10') or tick.startswith('51'):
|
|||
|
tick = 'SH' + tick
|
|||
|
elif tick.startswith('0') or tick.startswith('3') or tick.startswith('15') or tick.startswith(
|
|||
|
'16') or tick.startswith(
|
|||
|
'12'):
|
|||
|
tick = 'SZ' + tick
|
|||
|
else:
|
|||
|
pass
|
|||
|
elif len(tick) == 6:
|
|||
|
if tick.startswith('6') or tick.startswith('11') or tick.startswith('10') or tick.startswith('51'):
|
|||
|
tick = 'SH' + tick
|
|||
|
elif tick.startswith('0') or tick.startswith('15') or tick.startswith('12') or tick.startswith(
|
|||
|
'16') or tick.startswith('3'):
|
|||
|
tick = 'SZ' + tick
|
|||
|
else:
|
|||
|
pass
|
|||
|
else:
|
|||
|
num = 6 - len(tick)
|
|||
|
tick = 'SZ' + num * '0' + tick
|
|||
|
|
|||
|
return tick
|
|||
|
def ocr_image(image_path):
|
|||
|
"""
|
|||
|
传入图片路径,返回识别到的文字内容
|
|||
|
"""
|
|||
|
|
|||
|
access_key_id = 'LTAI5tA4dy591hCgEBfsxZei'
|
|||
|
access_key_secret = 'edYHXSVYda8HomlEsnsyQzw1QGu1EN'
|
|||
|
|
|||
|
if not os.path.exists(image_path):
|
|||
|
raise FileNotFoundError(f"图片文件不存在: {image_path}")
|
|||
|
|
|||
|
# 第一步:用新版 credential 初始化
|
|||
|
credential_config = CredentialConfig(
|
|||
|
type='access_key',
|
|||
|
access_key_id=access_key_id,
|
|||
|
access_key_secret=access_key_secret
|
|||
|
)
|
|||
|
credential = CredentialClient(credential_config)
|
|||
|
|
|||
|
# 第二步:openapi config
|
|||
|
config = open_api_models.Config(
|
|||
|
credential=credential,
|
|||
|
endpoint='ocr-api.cn-hangzhou.aliyuncs.com'
|
|||
|
)
|
|||
|
|
|||
|
# 第三步:初始化 OCR client
|
|||
|
client = OcrClient(config)
|
|||
|
|
|||
|
# 第四步:准备请求参数
|
|||
|
body_stream = StreamClient.read_from_file_path(image_path)
|
|||
|
request = ocr_models.RecognizeAllTextRequest(
|
|||
|
body=body_stream,
|
|||
|
type='Advanced'
|
|||
|
)
|
|||
|
runtime = util_models.RuntimeOptions()
|
|||
|
|
|||
|
# 第五步:发起请求
|
|||
|
response = client.recognize_all_text_with_options(request, runtime)
|
|||
|
return response.body.data.content
|
|||
|
|
|||
|
def get_price(tick, level='1', op="buy"):
|
|||
|
level = int(level)
|
|||
|
str_ticker = tick.lower()
|
|||
|
|
|||
|
rep_data = requests.get("http://qt.gtimg.cn/q=" + str_ticker).text
|
|||
|
|
|||
|
stocks_detail = "".join(rep_data)
|
|||
|
stock_detail = stocks_detail.split(";")[0]
|
|||
|
if len(stock_detail) < 49:
|
|||
|
return
|
|||
|
stock = stock_detail.split("~")
|
|||
|
|
|||
|
buy1 = float(stock[9])
|
|||
|
buy2 = float(stock[11])
|
|||
|
buy3 = float(stock[13])
|
|||
|
buy4 = float(stock[15])
|
|||
|
buy5 = float(stock[17])
|
|||
|
|
|||
|
sell1 = float(stock[19])
|
|||
|
sell2 = float(stock[21])
|
|||
|
sell3 = float(stock[23])
|
|||
|
sell4 = float(stock[25])
|
|||
|
sell5 = float(stock[27])
|
|||
|
|
|||
|
if op == "buy" and level == 1:
|
|||
|
return buy1
|
|||
|
elif op == "buy" and level == 2:
|
|||
|
return buy2
|
|||
|
elif op == "buy" and level == 3:
|
|||
|
return buy3
|
|||
|
elif op == "buy" and level == 4:
|
|||
|
return buy4
|
|||
|
elif op == "buy" and level == 5:
|
|||
|
return buy5
|
|||
|
|
|||
|
elif op == "sell" and level == 1:
|
|||
|
return sell1
|
|||
|
elif op == "sell" and level == 2:
|
|||
|
return sell2
|
|||
|
elif op == "sell" and level == 3:
|
|||
|
return sell3
|
|||
|
elif op == "sell" and level == 4:
|
|||
|
return sell4
|
|||
|
elif op == "sell" and level == 5:
|
|||
|
return sell5
|
|||
|
else:
|
|||
|
return float(stock[3])
|
|||
|
|
|||
|
def get_host_ip():
|
|||
|
"""
|
|||
|
查询本机ip地址
|
|||
|
:return:
|
|||
|
"""
|
|||
|
s = uuid.UUID(int=uuid.getnode()).hex[-12:]
|
|||
|
|
|||
|
return s
|
|||
|
|
|||
|
|
|||
|
|
|||
|
def tryrun(func):
|
|||
|
def wrapper(*args, **kwargs):
|
|||
|
while True:
|
|||
|
countnum = 0
|
|||
|
try:
|
|||
|
res = func(*args, **kwargs)
|
|||
|
except RuntimeError:
|
|||
|
countnum += 1
|
|||
|
time.sleep(1)
|
|||
|
if countnum > 120:
|
|||
|
break
|
|||
|
return res
|
|||
|
|
|||
|
return wrapper
|
|||
|
def tryrun2(func):
|
|||
|
def wrapper(*args, **kwargs):
|
|||
|
while True:
|
|||
|
countnum = 0
|
|||
|
try:
|
|||
|
res = func(*args, **kwargs)
|
|||
|
except RuntimeError:
|
|||
|
countnum += 1
|
|||
|
time.sleep(10)
|
|||
|
if countnum > 90:
|
|||
|
break
|
|||
|
return res
|
|||
|
|
|||
|
return wrapper
|
|||
|
|
|||
|
def output(func):
|
|||
|
def wrapper(*args, **kwargs):
|
|||
|
res = func(*args, **kwargs)
|
|||
|
print(args[1])
|
|||
|
return res
|
|||
|
|
|||
|
return wrapper
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
class Logger():
|
|||
|
"""
|
|||
|
This is get log class
|
|||
|
"""
|
|||
|
|
|||
|
def __init__(self, path, name, Flevel=logging.DEBUG):
|
|||
|
self.time = time.strftime("%Y-%m-%d")
|
|||
|
self.fileName = path + '/' + name + ".log"
|
|||
|
self.logger = logging.getLogger(self.fileName)
|
|||
|
self.logger.setLevel(Flevel)
|
|||
|
if not self.logger.handlers:
|
|||
|
fmt = logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S')
|
|||
|
fh = logging.FileHandler(self.fileName, encoding='utf-8')
|
|||
|
fh.setFormatter(fmt)
|
|||
|
fh.setLevel(Flevel)
|
|||
|
self.logger.addHandler(fh)
|
|||
|
|
|||
|
@output
|
|||
|
def debug(self, message):
|
|||
|
"""
|
|||
|
This is debug
|
|||
|
"""
|
|||
|
self.logger.debug(message)
|
|||
|
|
|||
|
@output
|
|||
|
def info(self, message):
|
|||
|
"""
|
|||
|
This is info
|
|||
|
"""
|
|||
|
self.logger.info(message)
|
|||
|
|
|||
|
@output
|
|||
|
def warn(self, message):
|
|||
|
"""
|
|||
|
This is warn
|
|||
|
"""
|
|||
|
self.logger.warn(message)
|
|||
|
|
|||
|
@output
|
|||
|
def error(self, message):
|
|||
|
"""
|
|||
|
This is error
|
|||
|
"""
|
|||
|
self.logger.error(message)
|
|||
|
|
|||
|
@output
|
|||
|
def critical(self, message):
|
|||
|
"""
|
|||
|
This is critical
|
|||
|
"""
|
|||
|
self.logger.critical(message)
|
|||
|
|
|||
|
def softInfo(self, message):
|
|||
|
"""
|
|||
|
This is output
|
|||
|
"""
|
|||
|
self.logger.info(message)
|
|||
|
|
|||
|
|
|||
|
def init_redis(ip, port1, db1, psw=''):
|
|||
|
'''
|
|||
|
redis初始化(待修改为密码版本)
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
ip : TYPE
|
|||
|
DESCRIPTION.
|
|||
|
port1 : TYPE
|
|||
|
DESCRIPTION.
|
|||
|
db1 : TYPE
|
|||
|
DESCRIPTION.
|
|||
|
psw : TYPE, optional
|
|||
|
DESCRIPTION. The default is ''.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
TYPE
|
|||
|
DESCRIPTION.
|
|||
|
|
|||
|
'''
|
|||
|
global r
|
|||
|
time.sleep(5)
|
|||
|
# r = redis.Redis(host=ip, port=port1, password=psw)
|
|||
|
pool = redis.ConnectionPool(host=ip, port=port1, password=psw)
|
|||
|
r = redis.Redis(connection_pool=pool)
|
|||
|
return r.ping()
|
|||
|
|
|||
|
|
|||
|
def redis_lget(key,num):
|
|||
|
'''
|
|||
|
redis获取指定key的值
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
key : TYPE
|
|||
|
DESCRIPTION.
|
|||
|
num : TYPE
|
|||
|
DESCRIPTION.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
vec : TYPE
|
|||
|
DESCRIPTION.
|
|||
|
|
|||
|
'''
|
|||
|
print('1')
|
|||
|
init_redis('82.156.3.152', 6379, 0, '1508181008')
|
|||
|
print('2')
|
|||
|
vec = r.lrange(key,0,num-1)
|
|||
|
print('3')
|
|||
|
if len(vec)>0:
|
|||
|
print(vec)
|
|||
|
return vec
|
|||
|
|
|||
|
|
|||
|
class SetMac(object):
|
|||
|
"""
|
|||
|
修改 本地连接 mac地址
|
|||
|
"""
|
|||
|
|
|||
|
def __init__(self):
|
|||
|
# regex to MAC address like 00-00-00-00-00-00 or 00:00:00:00:00:00 or
|
|||
|
# 000000000000
|
|||
|
self.MAC_ADDRESS_RE = re.compile(r"""
|
|||
|
([0-9A-F]{1,2})[:-]?
|
|||
|
([0-9A-F]{1,2})[:-]?
|
|||
|
([0-9A-F]{1,2})[:-]?
|
|||
|
([0-9A-F]{1,2})[:-]?
|
|||
|
([0-9A-F]{1,2})[:-]?
|
|||
|
([0-9A-F]{1,2})
|
|||
|
""", re.I | re.VERBOSE) # re.I: case-insensitive matching. re.VERBOSE: just look nicer.
|
|||
|
|
|||
|
self.WIN_REGISTRY_PATH = "SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}"
|
|||
|
|
|||
|
|
|||
|
def is_admin(self):
|
|||
|
"""
|
|||
|
is user an admin?
|
|||
|
:return:
|
|||
|
"""
|
|||
|
if ctypes.windll.shell32.IsUserAnAdmin() == 0:
|
|||
|
print('Sorry! You should run this with administrative privileges if you want to change your MAC address.')
|
|||
|
sys.exit()
|
|||
|
else:
|
|||
|
print('admin')
|
|||
|
|
|||
|
def get_macinfos(self):
|
|||
|
"""
|
|||
|
查看所有mac信息
|
|||
|
:return:
|
|||
|
"""
|
|||
|
print('=' * 50)
|
|||
|
mac_info = subprocess.check_output('GETMAC /v /FO list', stderr=subprocess.STDOUT)
|
|||
|
mac_info = mac_info.decode('gbk')
|
|||
|
print('Your MAC address:\n', mac_info)
|
|||
|
# 想要匹配的连接名
|
|||
|
target_connection_name = "WLAN 2"
|
|||
|
|
|||
|
# 构建正则表达式模式
|
|||
|
pattern = re.compile(
|
|||
|
r"连接名:\s+" + re.escape(target_connection_name) + r".*?物理地址:\s+([0-9A-Fa-f-]+)",
|
|||
|
re.DOTALL
|
|||
|
)
|
|||
|
# 执行搜索
|
|||
|
match = pattern.search(mac_info).group(1)
|
|||
|
return match
|
|||
|
def get_target_device(self):
|
|||
|
"""
|
|||
|
返回 本地连接 网络适配器
|
|||
|
:return:
|
|||
|
"""
|
|||
|
mac_info = subprocess.check_output('GETMAC /v /FO list', stderr=subprocess.STDOUT)
|
|||
|
mac_info = mac_info.decode('gbk')
|
|||
|
print(mac_info)
|
|||
|
search = re.search(r'(WLAN 2)\s+网络适配器: (.+)\s+物理地址:', mac_info)
|
|||
|
print(search)
|
|||
|
|
|||
|
target_name, target_device = (search.group(1), search.group(2).strip()) if search else ('', '')
|
|||
|
if not all([target_name, target_device]):
|
|||
|
print('Cannot find the target device')
|
|||
|
sys.exit()
|
|||
|
|
|||
|
print(target_name, target_device)
|
|||
|
return target_device
|
|||
|
|
|||
|
def get_network_adapter_info(self):
|
|||
|
"""
|
|||
|
获取网络适配器信息
|
|||
|
re:
|
|||
|
{'16': 'Realtek RTL8821CE 802.11ac PCIe Adapter #2'}
|
|||
|
"""
|
|||
|
try:
|
|||
|
# 执行 wmic 命令并获取输出
|
|||
|
cmd = "wmic path win32_networkadapter get index, name"
|
|||
|
output = subprocess.check_output(cmd, universal_newlines=True)
|
|||
|
# 按换行符分割输出内容
|
|||
|
lines = output.strip().split('\n')
|
|||
|
# 去除每行前后的空白字符
|
|||
|
lines = [line.strip() for line in lines]
|
|||
|
# 过滤掉空行
|
|||
|
non_empty_lines = [line for line in lines if line]
|
|||
|
result = {}
|
|||
|
# 从第二行开始遍历
|
|||
|
for line in non_empty_lines[1:]:
|
|||
|
# 查找第一个空白字符的位置
|
|||
|
first_space_index = line.find(' ')
|
|||
|
# 提取 index
|
|||
|
index = line[:first_space_index].strip()
|
|||
|
# 提取 name
|
|||
|
name = line[first_space_index:].strip()
|
|||
|
result[index] = name
|
|||
|
return result
|
|||
|
except subprocess.CalledProcessError as e:
|
|||
|
print(f"命令执行失败,错误代码: {e.returncode}")
|
|||
|
print(e.output)
|
|||
|
except Exception as e:
|
|||
|
print(f"发生异常: {e}")
|
|||
|
return []
|
|||
|
|
|||
|
def set_mac_address(self, target_device, new_mac):
|
|||
|
"""
|
|||
|
设置新mac地址
|
|||
|
:param target_device: 本地连接 网络适配器
|
|||
|
:param new_mac: 新mac地址
|
|||
|
:return:
|
|||
|
"""
|
|||
|
|
|||
|
if not self.MAC_ADDRESS_RE.match(new_mac):
|
|||
|
print('Please input a correct MAC address')
|
|||
|
return
|
|||
|
|
|||
|
# Locate adapter's registry and update network address (mac)
|
|||
|
reg_hdl = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
|||
|
key = winreg.OpenKey(reg_hdl, self.WIN_REGISTRY_PATH)
|
|||
|
info = winreg.QueryInfoKey(key)
|
|||
|
|
|||
|
# Find adapter key based on sub keys
|
|||
|
adapter_key = None
|
|||
|
adapter_path = None
|
|||
|
target_index = -1
|
|||
|
|
|||
|
adapter_dict = self.get_network_adapter_info()
|
|||
|
|
|||
|
for index in range(info[0]):
|
|||
|
subkey = winreg.EnumKey(key, index)
|
|||
|
path = self.WIN_REGISTRY_PATH + "\\" + subkey
|
|||
|
|
|||
|
if subkey == 'Properties':
|
|||
|
break
|
|||
|
|
|||
|
# Check for adapter match for appropriate interface
|
|||
|
new_key = winreg.OpenKey(reg_hdl, path)
|
|||
|
try:
|
|||
|
adapterIndex = str(int(subkey))
|
|||
|
adapterName = adapter_dict.get(adapterIndex)
|
|||
|
adapterDesc = winreg.QueryValueEx(new_key, "DriverDesc")
|
|||
|
if adapterName == target_device:
|
|||
|
adapter_path = path
|
|||
|
target_index = adapterIndex
|
|||
|
break
|
|||
|
else:
|
|||
|
winreg.CloseKey(new_key)
|
|||
|
except (WindowsError) as err:
|
|||
|
if err.errno == 2: # register value not found, ok to ignore
|
|||
|
pass
|
|||
|
else:
|
|||
|
raise err
|
|||
|
|
|||
|
if adapter_path is None:
|
|||
|
print('Device not found.')
|
|||
|
winreg.CloseKey(key)
|
|||
|
winreg.CloseKey(reg_hdl)
|
|||
|
return
|
|||
|
|
|||
|
# Registry path found update mac addr
|
|||
|
adapter_key = winreg.OpenKey(reg_hdl, adapter_path, 0, winreg.KEY_WRITE)
|
|||
|
# winreg.SetValueEx(adapter_key, "NetworkAddress", 0, winreg.REG_SZ, -----new_mac)
|
|||
|
winreg.SetValueEx(adapter_key, "NetworkAddress", 0, winreg.REG_SZ, new_mac)
|
|||
|
winreg.CloseKey(adapter_key)
|
|||
|
winreg.CloseKey(key)
|
|||
|
winreg.CloseKey(reg_hdl)
|
|||
|
|
|||
|
# Adapter must be restarted in order for change to take affect
|
|||
|
# print 'Now you should restart your netsh'
|
|||
|
self.restart_adapter(target_index, target_device)
|
|||
|
|
|||
|
def restart_adapter(self, target_index, target_device):
|
|||
|
"""
|
|||
|
Disables and then re-enables device interface
|
|||
|
"""
|
|||
|
if platform.release() == 'XP':
|
|||
|
# description, adapter_name, address, current_address = find_interface(device)
|
|||
|
cmd = "devcon hwids =net"
|
|||
|
try:
|
|||
|
result = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
|||
|
except FileNotFoundError:
|
|||
|
raise
|
|||
|
query = '(' + target_device + '\r\n\s*.*:\r\n\s*)PCI\\\\(([A-Z]|[0-9]|_|&)*)'
|
|||
|
query = query.encode('ascii')
|
|||
|
match = re.search(query, result)
|
|||
|
cmd = 'devcon restart "PCI\\' + str(match.group(2).decode('ascii')) + '"'
|
|||
|
subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
|||
|
else:
|
|||
|
cmd = "wmic path win32_networkadapter where index=" + str(target_index) + " call disable"
|
|||
|
subprocess.check_output(cmd)
|
|||
|
cmd = "wmic path win32_networkadapter where index=" + str(target_index) + " call enable"
|
|||
|
subprocess.check_output(cmd)
|
|||
|
|
|||
|
def run(self):
|
|||
|
self.is_admin()
|
|||
|
mac = self.get_macinfos()
|
|||
|
warnings.filterwarnings('ignore')
|
|||
|
scripts_path = os.path.dirname(os.path.realpath(__file__))
|
|||
|
root_path = scripts_path[:scripts_path.find(project_name) + len(project_name)]
|
|||
|
log = Logger(f'{root_path}/logs', '18242094506')
|
|||
|
log.warn('MAC 重置之前:'+mac)
|
|||
|
target_device = self.get_target_device()
|
|||
|
# self.set_mac_address(target_device, '82E82CF2158F')
|
|||
|
# self.set_mac_address(target_device, '28CDC4CBFDCF')
|
|||
|
self.set_mac_address(target_device, self.randomMAC())
|
|||
|
|
|||
|
mac_new = self.get_macinfos()
|
|||
|
warnings.filterwarnings('ignore')
|
|||
|
scripts_path = os.path.dirname(os.path.realpath(__file__))
|
|||
|
root_path = scripts_path[:scripts_path.find(project_name) + len(project_name)]
|
|||
|
log = Logger(f'{root_path}/logs', '18242094506')
|
|||
|
log.warn('MAC 重置之后:'+mac_new)
|
|||
|
# 28-CD-C4-CB-FD-CF
|
|||
|
|
|||
|
def randomMAC(self):
|
|||
|
# mac = [ 0x52, 0x54, 0x00,
|
|||
|
# random.randint(0x00, 0x7f),
|
|||
|
# random.randint(0x00, 0xff),
|
|||
|
# random.randint(0x00, 0xff) ]
|
|||
|
#
|
|||
|
# while True:
|
|||
|
# one_mac = ''.join(map(lambda x: "%02x" % x, mac)).upper()
|
|||
|
# used_mac = redis_lget('Mac', -1)
|
|||
|
# used_mac = [i.decode() for i in used_mac]
|
|||
|
#
|
|||
|
# if one_mac not in used_mac:
|
|||
|
# r.rpush('Mac',one_mac)
|
|||
|
# break
|
|||
|
while True:
|
|||
|
first_byte = 0x02
|
|||
|
# 从 UUIDv4 获取后 5 个字节,随机性极高
|
|||
|
random_bytes = uuid.uuid4().bytes
|
|||
|
mac_bytes = [first_byte] + list(random_bytes[1:6])
|
|||
|
one_mac = ''.join(f'{b:02X}' for b in mac_bytes)
|
|||
|
used_mac = redis_lget('Mac', -1)
|
|||
|
used_mac = [i.decode() for i in used_mac]
|
|||
|
|
|||
|
if one_mac not in used_mac:
|
|||
|
r.rpush('Mac', one_mac)
|
|||
|
break
|
|||
|
print('1111111', one_mac)
|
|||
|
return one_mac
|
|||
|
|
|||
|
|
|||
|
def get_host_ip():
|
|||
|
"""
|
|||
|
查询本机ip地址
|
|||
|
:return:
|
|||
|
"""
|
|||
|
s = uuid.UUID(int=uuid.getnode()).hex[-12:]
|
|||
|
return s
|
|||
|
|
|||
|
def get_auth_type(url):
|
|||
|
time1 = int(time.time() * 1000)
|
|||
|
url = f'http://192.168.100.1/login.cgi?_={time1}'
|
|||
|
response = requests.get(url)
|
|||
|
if 'WWW-Authenticate' in response.headers:
|
|||
|
return response.headers['WWW-Authenticate']
|
|||
|
return None
|
|||
|
|
|||
|
|
|||
|
def parse_auth_params(auth_header):
|
|||
|
params = {}
|
|||
|
for param in auth_header.split(','):
|
|||
|
key, value = param.strip().split('=', 1)
|
|||
|
params[key] = value.strip('"')
|
|||
|
return params
|
|||
|
|
|||
|
|
|||
|
def hex_md5(data):
|
|||
|
return md5(data.encode()).hexdigest()
|
|||
|
|
|||
|
|
|||
|
def generate_cnonce():
|
|||
|
rand = random.randint(0, 100000)
|
|||
|
date = int(time.time() * 1000)
|
|||
|
salt = f"{rand}{date}"
|
|||
|
tmp = hex_md5(salt)
|
|||
|
return tmp[:16]
|
|||
|
|
|||
|
|
|||
|
def compute_digest_response(username, password, realm, nonce, uri, qop, cnonce, nc):
|
|||
|
ha1 = hex_md5(f"{username}:{realm}:{password}")
|
|||
|
ha2 = hex_md5(f"GET:{uri}")
|
|||
|
response = hex_md5(f"{ha1}:{nonce}:{nc}:{cnonce}:{qop}:{ha2}")
|
|||
|
return response
|
|||
|
|
|||
|
|
|||
|
def do_login(username, password, base_url):
|
|||
|
url = f"{base_url}/login.cgi"
|
|||
|
auth_header = get_auth_type(url)
|
|||
|
if not auth_header:
|
|||
|
print("Failed to get authentication parameters")
|
|||
|
return False
|
|||
|
|
|||
|
auth_params = parse_auth_params(auth_header)
|
|||
|
realm = auth_params.get('Digest realm')
|
|||
|
nonce = auth_params.get('nonce')
|
|||
|
qop = auth_params.get('qop')
|
|||
|
|
|||
|
if not all([realm, nonce, qop]):
|
|||
|
print("Missing required authentication parameters")
|
|||
|
return False
|
|||
|
|
|||
|
cnonce = generate_cnonce()
|
|||
|
nc = '00000001'
|
|||
|
uri = '/cgi/protected.cgi'
|
|||
|
response = compute_digest_response(username, password, realm, nonce, uri, qop, cnonce, nc)
|
|||
|
|
|||
|
headers = {
|
|||
|
'Authorization': f'Digest username="{username}", realm="{realm}", nonce="{nonce}", uri="{uri}", response="{response}", qop={qop}, nc={nc}, cnonce="{cnonce}"'
|
|||
|
}
|
|||
|
|
|||
|
login_url = f"{base_url}/login.cgi?Action=Digest&username={username}&realm={realm}&nonce={nonce}&response={response}&qop={qop}&cnonce={cnonce}&nc={nc}&temp=asr"
|
|||
|
response = requests.get(login_url, headers=headers)
|
|||
|
uri = "/cgi/xml_action.cgi"
|
|||
|
response = compute_digest_response(username, password, realm, nonce, uri, qop, cnonce, nc)
|
|||
|
|
|||
|
headers = {
|
|||
|
'Authorization': f'Digest username="{username}", realm="{realm}", nonce="{nonce}", uri="{uri}", response="{response}", qop={qop}, nc={nc}, cnonce="{cnonce}"'
|
|||
|
}
|
|||
|
|
|||
|
reset_url = 'http://192.168.100.1/xml_action.cgi?method=get&module=duster&file=reset'
|
|||
|
|
|||
|
response = requests.get(reset_url, headers=headers)
|
|||
|
print(response.content)
|
|||
|
if response.status_code == 200:
|
|||
|
print("Login successful")
|
|||
|
return True
|
|||
|
else:
|
|||
|
print(f"Login failed: {response.status_code} {response.reason}")
|
|||
|
return False
|
|||
|
def get_ip():
|
|||
|
"""
|
|||
|
查询本机ip地址
|
|||
|
:return:
|
|||
|
"""
|
|||
|
try:
|
|||
|
requests.get('https://checkip.amazonaws.com').text.strip()
|
|||
|
except:
|
|||
|
return '60.162.69.53'
|
|||
|
|
|||
|
def reset_proxy_to_default():
|
|||
|
"""恢复系统代理为默认(关闭)"""
|
|||
|
key = winreg.OpenKey(
|
|||
|
winreg.HKEY_CURRENT_USER,
|
|||
|
r"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
|
|||
|
0, winreg.KEY_WRITE
|
|||
|
)
|
|||
|
try:
|
|||
|
winreg.SetValueEx(key, "ProxyEnable", 0, winreg.REG_DWORD, 0)
|
|||
|
winreg.SetValueEx(key, "ProxyServer", 0, winreg.REG_SZ, "")
|
|||
|
winreg.SetValueEx(key, "ProxyOverride", 0, winreg.REG_SZ, "<local>")
|
|||
|
finally:
|
|||
|
winreg.CloseKey(key)
|
|||
|
internet_set_option = ctypes.windll.Wininet.InternetSetOptionW
|
|||
|
internet_set_option(0, 37, 0, 0)
|
|||
|
internet_set_option(0, 39, 0, 0)
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
def reset_wlan():
|
|||
|
warnings.filterwarnings('ignore')
|
|||
|
scripts_path = os.path.dirname(os.path.realpath(__file__))
|
|||
|
root_path = scripts_path[:scripts_path.find(project_name) + len(project_name)]
|
|||
|
log = Logger(f'{root_path}/logs', '18242094506')
|
|||
|
log.warn('===================')
|
|||
|
log.warn('ip 重置前:'+get_ip())
|
|||
|
count = 0
|
|||
|
while True:
|
|||
|
base_url = "http://192.168.100.1"
|
|||
|
username = "admin"
|
|||
|
password = "admin"
|
|||
|
do_login(username, password, base_url)
|
|||
|
time.sleep(1)
|
|||
|
t1 = time.time()
|
|||
|
while True:
|
|||
|
try:
|
|||
|
requests.get('https://www.baidu.com')
|
|||
|
print('网络重连成功,共耗时',time.time()-t1,'s')
|
|||
|
time.sleep(3)
|
|||
|
print('变更后IP',get_ip())
|
|||
|
warnings.filterwarnings('ignore')
|
|||
|
scripts_path = os.path.dirname(os.path.realpath(__file__))
|
|||
|
root_path = scripts_path[:scripts_path.find(project_name) + len(project_name)]
|
|||
|
log = Logger(f'{root_path}/logs', '18242094506')
|
|||
|
log.warn('ip 重置后:'+ get_ip())
|
|||
|
|
|||
|
break
|
|||
|
except:
|
|||
|
pass
|
|||
|
one_ip = get_ip()
|
|||
|
used_IP = redis_lget('IP', -1)
|
|||
|
used_IP = [i.decode() for i in used_IP]
|
|||
|
print('!!!!!!one_ip:', one_ip)
|
|||
|
print('!!!!!!used_IP:',used_IP)
|
|||
|
if one_ip not in used_IP:
|
|||
|
r.rpush('IP',one_ip)
|
|||
|
print('!!!!入库了 成功rpush')
|
|||
|
break
|
|||
|
|
|||
|
|
|||
|
else:
|
|||
|
#IP 重复 记录log
|
|||
|
warnings.filterwarnings('ignore')
|
|||
|
scripts_path = os.path.dirname(os.path.realpath(__file__))
|
|||
|
root_path = scripts_path[:scripts_path.find(project_name) + len(project_name)]
|
|||
|
log = Logger(f'{root_path}/logs', '18242094506')
|
|||
|
log.warn('IP not reset')
|
|||
|
if count ==3:
|
|||
|
send_email('重启IP出现问题', one_ip)
|
|||
|
exit()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
if __name__ == '__main__':
|
|||
|
print(get_host_ip())
|