import os
import time
import threading
import ctypes
import sys
from PIL import Image, ImageFile, UnidentifiedImageError
ImageFile.LOAD_TRUNCATED_IMAGES = True
import cv2
import uiautomation as auto
import win32clipboard
import win32con
from ctypes import *
import tempfile
# ===== 权限提升函数 =====
def elevate_privileges():
"""确保程序以管理员权限运行"""
try:
if windll.shell32.IsUserAnAdmin() == 0:
windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)
sys.exit(0)
return True
except Exception as e:
print(f"权限提升失败: {e}")
return False
# ===== 配置参数 =====
wx_groupName = '星海预警'
wx_context = '星系警告!!!---疯子出品,必数精品'
conVal = 2
path = 'D:/EVE_A_Eye-main/EVE_A_Eye'
devices = {
'QO2UL': [
'127.0.0.1:5555',
False
],
}
gameSendPosition = {
'第二频道': '38 117',
'第三频道': '38 170',
'第四频道': '38 223',
'第五频道': '38 278',
'第六频道': '38 332',
'第七频道': '38 382'
}
sendTo = gameSendPosition['第三频道']
mutex = threading.Lock()
# ===== 剪贴板操作强化 =====
class DROPFILES(Structure):
"""文件拖放操作的剪切板数据结构"""
_fields_ = [
("pFiles", c_uint), # 偏移量
("x", c_long), # 鼠标X坐标
("y", c_long), # 鼠标Y坐标
("fNC", c_int), # 非客户端区域标志
("fWide", c_bool) # Unicode路径标志
]
def safe_set_clipboard(img_path, max_retries=3):
"""线程安全的剪贴板文件操作"""
for attempt in range(max_retries):
try:
# 验证文件存在性
if not os.path.exists(img_path):
print(f"图片不存在: {img_path}")
return False
# 创建DROPFILES结构
pDrop = DROPFILES()
pDrop.pFiles = sizeof(DROPFILES)
pDrop.fWide = True # 启用Unicode
# 构造完整数据结构
file_bytes = (os.path.abspath(img_path) + '\0').encode('utf-16le')
data = bytes(pDrop) + file_bytes
# 安全设置剪贴板
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardData(win32clipboard.CF_HDROP, data)
win32clipboard.CloseClipboard()
return True
except Exception as e:
print(f'剪贴板设置失败(尝试 {attempt+1}/{max_retries}): {e}')
time.sleep(0.5)
return False
def activate_wechat():
"""激活并置顶微信窗口(通过搜索群聊名称)"""
max_retries = 3
for attempt in range(max_retries):
try:
# 唤醒微信
auto.SendKeys('{Ctrl}{Alt}w')
time.sleep(1) # 等待微信主窗口出现
# 获取微信主窗口
wechat_main = auto.WindowControl(ClassName='WeChatMainWndForPC', Depth=1)
if wechat_main.Exists(5):
wechat_main.SetTopmost(True)
# 查找搜索框
search_edit = wechat_main.EditControl(Name='搜索', Depth=12)
if search_edit.Exists(5):
# 清空搜索框并输入群聊名称
search_edit.Click()
time.sleep(0.2)
search_edit.SendKeys('{Ctrl}a}{Del}')
time.sleep(0.2)
search_edit.SendKeys(wx_groupName)
time.sleep(1) # 等待搜索结果
# 选择第一个搜索结果(群聊)
search_result = wechat_main.ListItemControl(Name=wx_groupName, Depth=1, foundIndex=1)
if search_result.Exists(5):
search_result.DoubleClick() # 双击打开群聊
time.sleep(0.5)
# 获取群聊窗口(注意:群聊窗口的Name是群聊名称)
group_window = auto.WindowControl(ClassName='ChatWnd', Name=wx_groupName)
if group_window.Exists(5):
group_window.SetActive()
group_window.SetTopmost(True)
return True
else:
print(f"第{attempt+1}次尝试:未找到群聊: {wx_groupName}")
else:
print(f"第{attempt+1}次尝试:未找到搜索框")
else:
print(f"第{attempt+1}次尝试:微信主窗口未找到")
except Exception as e:
print(f"激活微信窗口异常: {e}")
time.sleep(1)
return False
def send_msg(content, msg_type=1):
"""
发送消息到微信
msg_type:
1=文本,
2=位图,
3=文件路径(推荐)
"""
if not activate_wechat():
return False
# 在群聊窗口中查找输入框
group_window = auto.WindowControl(ClassName='ChatWnd', Name=wx_groupName)
if not group_window.Exists(3):
print("群聊窗口未找到")
return False
# 尝试定位输入框
edit = group_window.EditControl(Depth=9, ClassName='Edit', foundIndex=3)
if not edit.Exists(3):
# 尝试其他方式
edits = group_window.GetChildren(className='Edit')
if len(edits) >= 3: # 通常有多个编辑框,输入框是第三个
edit = edits[2]
elif len(edits) > 0:
edit = edits[-1]
else:
print("未找到输入框")
return False
try:
if msg_type == 1: # 文本
auto.SetClipboardText(content)
time.sleep(0.5)
elif msg_type == 2: # 位图
auto.SetClipboardBitmap(auto.Bitmap.FromFile(content))
time.sleep(0.5)
elif msg_type == 3: # 文件路径
if not safe_set_clipboard(content):
return False
time.sleep(0.5)
# 粘贴并发送
edit.SendKeys('{Ctrl}v', waitTime=100)
time.sleep(0.5)
edit.SendKeys("{Enter}", waitTime=100)
return True
except Exception as e:
print(f"消息发送失败: {e}")
return False
# ===== 图像处理函数 =====
def screenc(filename, num):
"""执行ADB截图"""
os.system(f'adb -s {devices[filename][0]} exec-out screencap -p > {path}/{filename}_{num}.png')
def crop(x1, y1, x2, y2, scFileName, svFileName):
"""图像裁剪"""
try:
img = Image.open(scFileName)
re = img.crop((x1, y1, x2, y2))
re.save(svFileName)
img.close()
return True
except Exception as e:
print(f"图像裁剪失败: {e}")
return False
def LoadImage(img1, img2):
"""加载图像为灰度图"""
i1 = cv2.imread(img1, 0)
i2 = cv2.imread(img2, 0)
return i1, i2
def IF_Img_I(src, mp):
"""
图像相似度检测
返回: (是否不同, 相似度分数)
"""
try:
res = cv2.matchTemplate(src, mp, cv2.TM_CCOEFF_NORMED)
_, mac_v, _, _ = cv2.minMaxLoc(res)
# 相似度阈值公式: $T_s = 0.99 - \frac{1}{resolution}$
return mac_v < 0.98, mac_v
except Exception as e:
print(f"图像匹配失败: {e}")
return False, 0.0
# ===== 警报发送函数 =====
def SendGameMassage(tag):
"""在游戏内发送警报消息"""
str1 = f'adb -s {devices[tag][0]} '
taps = [
(211, 478), # 打开聊天
tuple(map(int, sendTo.split())), # 选择频道
(266, 520), # 点击输入框
(870, 511), # 点击发送
(68, 292), # 确认操作1
(250, 350), # 确认操作2
(250, 433), # 确认操作3
(344, 190), # 关闭弹窗
(342, 512) # 返回主界面
]
for tap in taps:
os.system(str1 + f'shell input tap {tap[0]} {tap[1]}')
time.sleep(0.2)
def SendWeChat(tag, num):
"""向微信发送警报"""
img_path = f'{path}/{tag}_{num}.png'
mutex.acquire()
# 发送图片(使用文件路径模式)
send_result = send_msg(img_path, msg_type=3)
# 发送文本
if send_result:
context = f"{tag}{wx_context}"
send_msg(context, msg_type=1)
print(f"警报发送成功: {tag}")
else:
print(f"警报发送失败: {tag}")
mutex.release()
# ===== 监听核心 =====
def ship_detection(tag):
"""舰船列表检测子线程"""
num = 0
while True:
screenc(tag, 1)
time.sleep(0.5)
crop(918, 44, 956, 153, f'{path}/{tag}_1.png', f'{path}/new_{tag}_list.png')
# 图像差异检测
i3, i4 = LoadImage(f"{path}/new_{tag}_list.png", f"{path}/tem/list.png")
list_status, list_mac_v = IF_Img_I(i3, i4)
# 二次验证机制
if list_mac_v < 0.10 and list_mac_v != 0.0:
if num < 1:
num += 1
time.sleep(2)
continue
print(f'{tag}检测到舰船列表变化 (相似度: {list_mac_v:.2f})')
SendWeChat(tag, 1)
i1, i2 = LoadImage(f"{path}/new_{tag}_playerList.png", f"{path}/old_{tag}_playerList.png")
cv2.imwrite(f'{path}/old_{tag}_playerList.png', i1)
time.sleep(40)
num = 0
def Listening(tag):
"""设备监听主循环"""
# 启动舰船检测线程
threading.Thread(target=ship_detection, args=(tag,), daemon=True).start()
# 主检测循环
while True:
screenc(tag, 2)
time.sleep(conVal + 0.35)
crop(774, 502, 956, 537, f'{path}/{tag}_2.png', f'{path}/new_{tag}_playerList.png')
# 加载并比较图像
i1, i2 = LoadImage(f"{path}/new_{tag}_playerList.png", f"{path}/old_{tag}_playerList.png")
list_status, list_mac_v = IF_Img_I(i1, i2)
# 错误检测: 相似度过低
if list_mac_v <= 0.01:
print(f'{tag}系统故障,相似度过低')
mutex.acquire()
send_msg(f"{tag}系统故障,相似度{list_mac_v:.4f},即将下线", msg_type=1)
mutex.release()
time.sleep(3)
os._exit(1)
# 检测到显著变化
if list_status:
print(f'{tag}检测到本地警报 (相似度: {list_mac_v:.2f})')
SendGameMassage(tag)
SendWeChat(tag, 2)
cv2.imwrite(f'{path}/old_{tag}_playerList.png', i1)
# ===== 初始化函数 =====
def Start():
"""初始化程序"""
# 重置图片缓存
with open(f'{path}/tem/list.png', 'rb') as sc1:
con = sc1.read()
for k in devices:
with open(f'{path}/new_{k}_list.png', 'wb') as f:
f.write(con)
with open(f'{path}/tem/playerList.png', 'rb') as sc:
con = sc.read()
for k in devices:
with open(f'{path}/old_{k}_playerList.png', 'wb') as f:
f.write(con)
with open(f'{path}/new_{k}_playerList.png', 'wb') as f:
f.write(con)
# 启动监听线程
for k in devices:
t = threading.Thread(target=Listening, args=(k,))
t.daemon = True
t.start()
print('预警系统已启动')
context = f"预警系统已上线,监测星系列表:\n{list(devices.keys())}"
mutex.acquire()
send_msg(context, msg_type=1)
mutex.release()
# ===== 主程序入口 =====
if __name__ == "__main__":
if elevate_privileges():
Start()
# 保持主线程运行
while True:
time.sleep(3600) # 每小时检查一次