271 lines
7.3 KiB
Python
271 lines
7.3 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
"""
|
|||
|
|
微信自动发送祝福语脚本
|
|||
|
|
功能:
|
|||
|
|
1. 从API获取已选联系人的搜索姓名和祝福语
|
|||
|
|
2. 自动搜索联系人、发送祝福语
|
|||
|
|
3. 发送完成后更新前端状态
|
|||
|
|
4. 按 F9 停止运行
|
|||
|
|
"""
|
|||
|
|
import os
|
|||
|
|
import sys
|
|||
|
|
import time
|
|||
|
|
import json
|
|||
|
|
import threading
|
|||
|
|
import requests
|
|||
|
|
import pyautogui
|
|||
|
|
import pyperclip
|
|||
|
|
from ctypes import windll, c_long
|
|||
|
|
|
|||
|
|
# 坐标配置
|
|||
|
|
SEARCH_BOX_X, SEARCH_BOX_Y = 1933, 39
|
|||
|
|
SEARCH_RESULT_X, SEARCH_RESULT_Y = 2009, 100
|
|||
|
|
CHAT_INPUT_X, CHAT_INPUT_Y = 2160, 1173
|
|||
|
|
|
|||
|
|
# API配置
|
|||
|
|
API_BASE = 'http://localhost:5000/api'
|
|||
|
|
|
|||
|
|
# 全局停止标志
|
|||
|
|
stop_flag = False
|
|||
|
|
stop_lock = threading.Lock()
|
|||
|
|
|
|||
|
|
def get_selected_contacts():
|
|||
|
|
"""获取已选择的联系人"""
|
|||
|
|
try:
|
|||
|
|
response = requests.get(f'{API_BASE}/contacts?page=1&page_size=1000', timeout=10)
|
|||
|
|
data = response.json()
|
|||
|
|
selected = [c for c in data.get('contacts', []) if c.get('selected')]
|
|||
|
|
return selected
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"获取联系人失败: {e}")
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
def update_contact_sent(contact_id):
|
|||
|
|
"""更新联系人发送状态"""
|
|||
|
|
try:
|
|||
|
|
requests.put(
|
|||
|
|
f'{API_BASE}/contacts/{contact_id}',
|
|||
|
|
json={'selected': False},
|
|||
|
|
timeout=10
|
|||
|
|
)
|
|||
|
|
return True
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"更新状态失败: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def check_stop():
|
|||
|
|
"""检查是否需要停止"""
|
|||
|
|
with stop_lock:
|
|||
|
|
return stop_flag
|
|||
|
|
|
|||
|
|
def set_stop():
|
|||
|
|
"""设置停止标志"""
|
|||
|
|
with stop_lock:
|
|||
|
|
global stop_flag
|
|||
|
|
stop_flag = True
|
|||
|
|
print("\n收到停止信号,正在停止...")
|
|||
|
|
|
|||
|
|
def get_current_key():
|
|||
|
|
"""获取当前按下的键"""
|
|||
|
|
try:
|
|||
|
|
# 使用windll获取按键状态
|
|||
|
|
# F9 = 0x78 (120)
|
|||
|
|
if windll.user32.GetAsyncKeyState(0x78) & 0x8000:
|
|||
|
|
return 'F9'
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def wait_for_key_press():
|
|||
|
|
"""监听F9按键"""
|
|||
|
|
global stop_flag
|
|||
|
|
import keyboard
|
|||
|
|
try:
|
|||
|
|
keyboard.add_hotkey('F9', set_stop)
|
|||
|
|
print("提示: 按 F9 可随时停止运行")
|
|||
|
|
except ImportError:
|
|||
|
|
print("警告: keyboard库未安装,无法使用F9停止功能")
|
|||
|
|
print("请运行: pip install keyboard")
|
|||
|
|
|
|||
|
|
def send_blessing_to_contact(search_name, blessing):
|
|||
|
|
"""发送祝福语给单个联系人"""
|
|||
|
|
if check_stop():
|
|||
|
|
return False, "已停止"
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 1. 点击搜索框
|
|||
|
|
print(f" → 点击搜索框 ({SEARCH_BOX_X}, {SEARCH_BOX_Y})")
|
|||
|
|
pyautogui.moveTo(SEARCH_BOX_X, SEARCH_BOX_Y, duration=0.3)
|
|||
|
|
pyautogui.click()
|
|||
|
|
time.sleep(0.5)
|
|||
|
|
|
|||
|
|
if check_stop():
|
|||
|
|
return False, "已停止"
|
|||
|
|
|
|||
|
|
# 2. 清空搜索框并输入名字
|
|||
|
|
print(f" → 输入名字: {search_name}")
|
|||
|
|
pyautogui.hotkey('ctrl', 'a')
|
|||
|
|
time.sleep(0.2)
|
|||
|
|
pyperclip.copy(search_name)
|
|||
|
|
pyautogui.hotkey('ctrl', 'v')
|
|||
|
|
time.sleep(1)
|
|||
|
|
|
|||
|
|
if check_stop():
|
|||
|
|
return False, "已停止"
|
|||
|
|
|
|||
|
|
# 3. 点击搜索结果
|
|||
|
|
print(f" → 点击搜索结果 ({SEARCH_RESULT_X}, {SEARCH_RESULT_Y})")
|
|||
|
|
pyautogui.moveTo(SEARCH_RESULT_X, SEARCH_RESULT_Y, duration=0.3)
|
|||
|
|
pyautogui.click()
|
|||
|
|
time.sleep(1)
|
|||
|
|
|
|||
|
|
if check_stop():
|
|||
|
|
return False, "已停止"
|
|||
|
|
|
|||
|
|
# 4. 点击聊天输入框
|
|||
|
|
print(f" → 点击聊天输入框 ({CHAT_INPUT_X}, {CHAT_INPUT_Y})")
|
|||
|
|
pyautogui.moveTo(CHAT_INPUT_X, CHAT_INPUT_Y, duration=0.3)
|
|||
|
|
pyautogui.click()
|
|||
|
|
time.sleep(0.5)
|
|||
|
|
|
|||
|
|
if check_stop():
|
|||
|
|
return False, "已停止"
|
|||
|
|
|
|||
|
|
# 5. 输入祝福语
|
|||
|
|
print(f" → 输入祝福语...")
|
|||
|
|
pyperclip.copy(blessing)
|
|||
|
|
pyautogui.hotkey('ctrl', 'v')
|
|||
|
|
time.sleep(0.5)
|
|||
|
|
|
|||
|
|
if check_stop():
|
|||
|
|
return False, "已停止"
|
|||
|
|
|
|||
|
|
# 6. 发送
|
|||
|
|
print(f" → 发送消息")
|
|||
|
|
pyautogui.press('enter')
|
|||
|
|
time.sleep(0.5)
|
|||
|
|
|
|||
|
|
return True, "发送成功"
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
return False, f"发送失败: {str(e)}"
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
global stop_flag
|
|||
|
|
|
|||
|
|
print("=" * 60)
|
|||
|
|
print("微信自动发送祝福语脚本")
|
|||
|
|
print("=" * 60)
|
|||
|
|
print()
|
|||
|
|
print("配置信息:")
|
|||
|
|
print(f" 搜索框坐标: ({SEARCH_BOX_X}, {SEARCH_BOX_Y})")
|
|||
|
|
print(f" 搜索结果坐标: ({SEARCH_RESULT_X}, {SEARCH_RESULT_Y})")
|
|||
|
|
print(f" 聊天输入框坐标: ({CHAT_INPUT_X}, {CHAT_INPUT_Y})")
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
# 尝试导入keyboard库用于F9停止
|
|||
|
|
try:
|
|||
|
|
import keyboard
|
|||
|
|
keyboard.add_hotkey('F9', set_stop)
|
|||
|
|
print("✓ F9 停止功能已启用")
|
|||
|
|
except ImportError:
|
|||
|
|
print("✗ keyboard库未安装,F9停止功能不可用")
|
|||
|
|
print(" 请运行: pip install keyboard")
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"✗ keyboard库加载失败: {e}")
|
|||
|
|
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
# 获取已选联系人
|
|||
|
|
print("正在获取已选联系人...")
|
|||
|
|
contacts = get_selected_contacts()
|
|||
|
|
|
|||
|
|
if not contacts:
|
|||
|
|
print("没有找到已选择的联系人!")
|
|||
|
|
print("请在前端页面勾选要发送的联系人,然后重试。")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
print(f"找到 {len(contacts)} 个已选联系人:")
|
|||
|
|
print("-" * 60)
|
|||
|
|
for i, c in enumerate(contacts, 1):
|
|||
|
|
print(f" {i}. {c.get('name', '未知')} (搜索名: {c.get('search_name', '')})")
|
|||
|
|
print("-" * 60)
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
# 确认开始
|
|||
|
|
print("准备开始发送祝福语...")
|
|||
|
|
print("请在 5 秒内切换到微信窗口!")
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
# 等待用户准备
|
|||
|
|
for i in range(5, 0, -1):
|
|||
|
|
if check_stop():
|
|||
|
|
print("已取消运行")
|
|||
|
|
return
|
|||
|
|
print(f" {i}...")
|
|||
|
|
time.sleep(1)
|
|||
|
|
|
|||
|
|
print()
|
|||
|
|
print("开始发送祝福语...")
|
|||
|
|
print("=" * 60)
|
|||
|
|
|
|||
|
|
success_count = 0
|
|||
|
|
fail_count = 0
|
|||
|
|
skip_count = 0
|
|||
|
|
|
|||
|
|
for i, contact in enumerate(contacts, 1):
|
|||
|
|
if check_stop():
|
|||
|
|
print("\n用户停止运行")
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
name = contact.get('name', '未知')
|
|||
|
|
search_name = contact.get('search_name') or contact.get('name', '')
|
|||
|
|
blessing = contact.get('blessing', '')
|
|||
|
|
contact_id = contact.get('id')
|
|||
|
|
|
|||
|
|
print(f"\n[{i}/{len(contacts)}] 发送给: {name}")
|
|||
|
|
print(f" 搜索名: {search_name}")
|
|||
|
|
if len(blessing) > 50:
|
|||
|
|
print(f" 祝福语: {blessing[:50]}...")
|
|||
|
|
else:
|
|||
|
|
print(f" 祝福语: {blessing}")
|
|||
|
|
|
|||
|
|
# 检查祝福语是否为空
|
|||
|
|
if not blessing:
|
|||
|
|
print(f" ⚠ 跳过: 祝福语为空")
|
|||
|
|
skip_count += 1
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# 发送祝福语
|
|||
|
|
success, message = send_blessing_to_contact(search_name, blessing)
|
|||
|
|
|
|||
|
|
if success:
|
|||
|
|
print(f" ✓ 发送成功")
|
|||
|
|
success_count += 1
|
|||
|
|
|
|||
|
|
# 更新前端状态
|
|||
|
|
if update_contact_sent(contact_id):
|
|||
|
|
print(f" ✓ 已更新前端状态")
|
|||
|
|
else:
|
|||
|
|
print(f" ✗ 更新前端状态失败")
|
|||
|
|
|
|||
|
|
# 发送间隔
|
|||
|
|
time.sleep(1)
|
|||
|
|
else:
|
|||
|
|
print(f" ✗ {message}")
|
|||
|
|
fail_count += 1
|
|||
|
|
if not check_stop():
|
|||
|
|
time.sleep(2)
|
|||
|
|
|
|||
|
|
print()
|
|||
|
|
print("=" * 60)
|
|||
|
|
print("发送完成!")
|
|||
|
|
print("=" * 60)
|
|||
|
|
print(f" 成功: {success_count}")
|
|||
|
|
print(f" 失败: {fail_count}")
|
|||
|
|
print(f" 跳过: {skip_count}")
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
if __name__ == '__main__':
|
|||
|
|
main()
|