wkdxz 发表于 2022-8-12 16:13

python模拟键鼠,发送微信消息,支持命令行调用,可作为模块使用

本帖最后由 wkdxz 于 2022-8-13 10:05 编辑

一直以来都是使用AHK来操作微信发消息的,最近看到@yuupuu 的帖子,看到python控制微信也比较稳定,而且写代码更简单,于是也参照原贴写了一个。
因为是在Excel里面使用代码,所以就做了参数调用,可以使用命令行运行。
为避免以后重复书写,就做成了一个简陋的模块,可以直接import调用。
命令行不方便传入多行文字,于是增加了直接发送剪贴板的功能。
因为是使用剪贴板来复制粘贴输入文字和文件,所以增加了还原剪贴板数据的功能。


2022-08-13更新:不再强制修改微信窗口大小和位置,使用相对坐标点击。



代码功能是东拼西凑的,不够精简和完善,喜欢的朋友可以拿去用。
这里的0.25是鼠标点击的延迟 0.25秒,如果电脑反应慢,可以增加这个数值。
def click(self, x, y, timeout=0.25):


除了主代码,另外两个是示例文件,喜欢的朋友可以下载来用。




主代码:wx_sender.py
from pyperclip import copy, paste
from time import sleep
import ctypes
import pyautogui
import sys
import win32clipboard
import win32con
import win32gui


class DROPFILES(ctypes.Structure):
    _fields_ = [
      ("pFiles", ctypes.c_uint),
      ("x", ctypes.c_long),
      ("y", ctypes.c_long),
      ("fNC", ctypes.c_int),
      ("fWide", ctypes.c_bool),
    ]


def restore_clipboard(func):#自动还原剪贴板
    def wrapper(*args, **kwargs):
      clip_bak = paste()# 备份
      result = func(*args, **kwargs)
      copy(clip_bak)#还原
      return result

    return wrapper


class WxMsg:
    def __handle(self):
      return win32gui.FindWindow('WeChatMainWndForPC', '微信')

    def __pos_and_size(self):
      win_x, win_y, win_right_down_x, win_right_down_y = win32gui.GetWindowRect(
            self.__handle())
      win_width = win_right_down_x - win_x
      win_height = win_right_down_y - win_y
      return win_x, win_y, win_width, win_height

    #复制文件到剪贴板 代码来源:https://www.cnblogs.com/love-DanDan/p/15900159.html
    def __copy_file(self, files):
      pDropFiles = DROPFILES()
      pDropFiles.pFiles = ctypes.sizeof(DROPFILES)
      pDropFiles.fWide = True
      mdata = bytes(pDropFiles)
      files = ("\0".join(files)).replace("/", "\\")
      data = files.encode("U16") + b"\0\0"
      win32clipboard.OpenClipboard()
      try:
            win32clipboard.EmptyClipboard()
            win32clipboard.SetClipboardData(win32clipboard.CF_HDROP,
                                          mdata + data)
      finally:
            win32clipboard.CloseClipboard()

    #打开窗口代码来源: 【yuupuu】 https://www.52pojie.cn/thread-1673429-1-1.html
    def __activate_wx_window(self):
      if handle := self.__handle():
            win_x, win_y, win_width, win_height = self.__pos_and_size()
            win32gui.ShowWindow(handle, win32con.SW_SHOWMINIMIZED)
            win32gui.ShowWindow(handle, win32con.SW_SHOWNORMAL)
            win32gui.ShowWindow(handle, win32con.SW_SHOW)
            win32gui.SetWindowPos(handle, win32con.HWND_TOP, win_x, win_y,
                                  win_width, win_height,
                                  win32con.SWP_SHOWWINDOW)
            win32gui.SetForegroundWindow(handle)
      else:
            print('微信未登录')
            exit()

    #复制文字,并粘贴
    @restore_clipboard
    def __wx_input(self, txt):
      copy(txt)
      pyautogui.hotkey('ctrl', 'v')

    #延时点击坐标
    #默认使用相对坐标点击 (实际点击位置:窗口坐标+相对坐标)
    def __click(self, x, y, timeout=0.25, relative=True):#默认使用相对坐标点击
      sleep(timeout)
      win_x, win_y = self.__pos_and_size()[:2]
      if relative:
            pyautogui.click(win_x + x, win_y + y)#使用相对坐标点击
      else:
            pyautogui.click(x, y)

      #把消息发送出去
    def __send_msg_out(self):
      sleep(0.4)
      pyautogui.hotkey('alt', 's')

    #搜索联系人
    def search(self, wxid):
      self.__activate_wx_window()
      self.__click(21, 145)# 通讯录
      self.__click(143, 39)# 搜索框
      self.__wx_input(wxid)# 搜索微信id
      self.__click(155, 120, 0.6)# 点第一个搜索结果 要多停留一会儿

      #点输入框,从微信窗口右下角:x-50,y-63,使用绝对坐标点击
      win_right_down_x, win_right_down_y = win32gui.GetWindowRect(
            self.__handle())[-2:]#微信窗口右下角的坐标
      self.__click(win_right_down_x - 50, win_right_down_y - 63, 0.2, False)

    #发送文字消息
    @restore_clipboard
    def send_txt(self, txt, send_out=True):
      copy(txt)# 粘贴文本内容
      pyautogui.hotkey('ctrl', 'v')# 粘贴复制的内容
      if send_out:
            self.__send_msg_out()

    #发送文件,传入参数为文件路径
    @restore_clipboard
    def send_file(self, file_list, send_out=True):
      if isinstance(file_list, str):
            file_list =
      self.__copy_file(file_list)
      pyautogui.hotkey('ctrl', 'v')# 粘贴复制的内容
      if send_out:
            self.__send_msg_out()

    #直接发送剪贴板内容
    def send_clipboard(self, send_out=True):
      if paste():
            pyautogui.hotkey('ctrl', 'v')# 粘贴复制的内容
            if send_out:
                self.__send_msg_out()
      else:
            print('剪贴板为空')
            exit()


if __name__ == '__main__':
    arg_count = len(sys.argv)#传入的参数个数

    if arg_count not in {1, 2, 3, 4}:
      print('参数个数不是2或3,看看是不是传入了含空格的文件路径')
    else:
      wx = WxMsg()#实例化对象

      # 3个参数发文件
      if arg_count == 4:# 传入3个参数(微信id,消息内容,文件路径)
            _, wxid, msg_content, msg_type = sys.argv
            if msg_type.lower() == 'f':
                wx.search(wxid)#搜索微信号
                wx.send_file(msg_content)# 发送1个文件
            else:
                print(f'第3参数【{msg_type}】错误,必须是“f”\n可以检查下是不是传入了含空格的文件路径')

      # 2个参数发文字
      elif len(sys.argv) == 3:# 传入2个参数(微信id,文字消息内容)
            _, wxid, msg_content = sys.argv
            wx.search(wxid)#搜索微信号
            wx.send_txt(msg_content)

      # 1个参数发剪贴板内容
      elif len(sys.argv) == 2:# 传入1个参数(微信id)
            _, wxid = sys.argv
            wx.search(wxid)#搜索微信号
            wx.send_clipboard()

      ################################ 不传入参数,直接在python里运行,可以发送多行文本或者多个文件
      elif arg_count == 1:
            wxid = '小号'#要发送的微信号
            wx.search(wxid)#搜索微信号

            # 发送1条消息
            wx.send_txt('测试1条消息')

            # # 发送1个文件
            # wx.send_file(r'C:\Windows\write.exe')

            # # 发送多个文件,传入[文件路径列表]
            # sleep(1)
            # wx.send_file()

            # # 发送多条文字消息
            # sleep(1)
            # for i in range(5):
            #   wx.send_txt(f'发送第{i+1}条文字消息')
            #   sleep(1)

            # #粘贴消息,不发送出去
            # wx.send_txt('此消息不会发送出去,因为添加了第二参数 False', False)

使用Python可以轻松调用
from wx_sender import WxMsg
from time import sleep

wx = WxMsg()#实例化对象

wxid = '小号'#要发送的微信号
wx.search(wxid)#搜索微信号

# 发送剪贴板内容
wx.send_clipboard()

# 发送1条消息
wx.send_txt('测试单条消息')

# 发送1个文件
wx.send_file(r'C:\Windows\write.exe')

# 发送多个文件,传入[文件路径列表]
sleep(1)
wx.send_file()

# 发送多条文字消息
sleep(1)
for i in range(5):
    wx.send_txt(f'发送第{i+1}条文字消息')
    sleep(1)

#粘贴消息,不发送出去
wx.send_txt('此消息不会发送出去,因为添加了第二参数 False', False)



使用命令行调用
@echo off & pushd %~dp0

set wxid=小号

::发送文字消息
.\wx_sender.py %wxid% 测试消息


echo 按任意键测试发送文件&pause>nul

::发送文件,在后面加上参数f
.\wx_sender.py %wxid% "C:\Windows\write.exe" f


echo 按任意键测试发送剪贴板&pause>nul

::发送剪贴板内容
.\wx_sender.py %wxid%

wkdxz 发表于 2022-8-12 17:22

本帖最后由 wkdxz 于 2022-8-12 17:35 编辑

放羊的狼 发表于 2022-8-12 17:01
看楼主说的是在Excel中使用,那...sendkeys不一样能搞定吗,前阵子写过一个目前一直在用,貌似比较稳定,文 ...
正好VBA,python和AHK 我都在用。
vba别的不说,就延时一点就很卡,操作过程一直转圈,容易崩溃,可能是我技术不行或者WPS软件的问题。
AHK可以使用相对坐标点击,不用把微信窗口固定大小,放到左上角,但代码太不严谨,可参照的示例也没有python丰富。
python虽然要把微信窗口固定大小,放到左上角,但代码写起来很灵活,也有大堆的库可以调用。

后两者都可以使用cmd调用。所以,使用AHK或python书写,我还可以使用AHK快捷键来搜索联系人和发送微信消息。

A小虎 发表于 2022-8-12 16:23

太棒了,这个要被套娃了

miaogong 发表于 2022-8-12 16:38

太棒了,研究研究

cdsgg 发表于 2022-8-12 16:41

这种坐标的 如果不是绝对坐标的 估计到时候可能会出错 玩wx这种东西还是得靠hook

JiaoDaoNiu 发表于 2022-8-12 16:45

这个有啥好的应用场景吗{:1_904:}

aa702429162 发表于 2022-8-12 16:54

JiaoDaoNiu 发表于 2022-8-12 16:45
这个有啥好的应用场景吗

微信啊,已经没有api了,用这个模拟发消息很不错

放羊的狼 发表于 2022-8-12 17:01

看楼主说的是在Excel中使用,那...sendkeys不一样能搞定吗,前阵子写过一个目前一直在用,貌似比较稳定,文本、@成员、文件都可以通过剪贴板处理。前几天测试过ahk并不稳定(可能是我比较菜,毕竟只了解了两三天),python不了解,但看你代码也是模拟按键,貌似绕路绕得有点远。

edmin 发表于 2022-8-12 18:47

怎么运行呀

-白衬衫- 发表于 2022-8-12 19:28

这个要一直在前台运行吧?
页: [1] 2 3
查看完整版本: python模拟键鼠,发送微信消息,支持命令行调用,可作为模块使用