Python实现微信发送文件实例

新建Python文件:wx_file.py,代码如下

# -*- coding: utf-8 -*-
# @Author : CxiuM
# @Time   : 2023-07-06 10:12
# @Name   : wx_operation.py

"""微信群发消息"""

import os
import time
import subprocess

import requests
import uiautomation as auto
from copy import deepcopy
from typing import Iterable


class WxOperation:
    """
    微信群发消息的类。
    ...
    Attributes:
    ----------
    wx_window: auto.WindowControl
        微信控制窗口
    input_edit: wx_window.EditControl
        聊天界面输入框编辑控制窗口
    search_edit: wx_window.EditControl
        搜索输入框编辑控制窗口
    Methods:
    -------
    __goto_chat_box(name):
        跳转到 指定好友窗口
    __send_text(*msgs):
        发送文本。
    __send_file(*filepath):
        发送文件
    get_friend_list(tag, num):
        可指定tag,获取好友num页的好友数量
    send_msg(*names, msgs, file_paths)
        单个或批量发送文本和文件
    """

    def __init__(self):
        auto.SendKeys(text='{Alt}{Ctrl}z')  # 快捷键唤醒微信
        self.wx_window = auto.WindowControl(Name='微信', ClassName='WeChatMainWndForPC')
        assert self.wx_window.Exists(), "窗口不存在"
        self.input_edit = self.wx_window.EditControl(Name='输入')
        self.search_edit = self.wx_window.EditControl(Name='搜索')

    def __goto_chat_box(self, name: str) -> None:
        """
        跳转到指定 name好友的聊天窗口。
        Args:
            name(str): 必选参数,好友名称
        Returns:
            None
        """
        assert name, "无法跳转到名字为空的聊天窗口"
        self.wx_window.SendKeys(text='{Ctrl}f', waitTime=0.2)
        self.wx_window.SendKeys(text='{Ctrl}a', waitTime=0.1)
        self.wx_window.SendKey(key=auto.SpecialKeyNames['DELETE'])
        auto.SetClipboardText(text=name)
        self.wx_window.SendKeys(text='{Ctrl}v', waitTime=0.1)
        self.wx_window.SendKey(key=auto.SpecialKeyNames['ENTER'], waitTime=0.2)

    def __send_text(self, *msgs) -> None:
        """
        发送文本.
        Args:
            *msgs(Iterable or str): 必选参数,为发送的文本
        Returns:
            None
        """
        for msg in msgs:
            assert msg, "发送的文本内容为空"
            self.input_edit.SendKeys(text='{Ctrl}a', waitTime=0.1)
            self.input_edit.SendKey(key=auto.SpecialKeyNames['DELETE'])
            # self.input_edit.SendKeys(text=msg, waitTime=0.1) # 一个个字符插入,不建议使用该方法
            # 设置到剪切板再黏贴到输入框
            auto.SetClipboardText(text=msg)
            self.input_edit.SendKeys(text='{Ctrl}v', waitTime=0.1)
            self.wx_window.SendKey(key=auto.SpecialKeyNames['ENTER'], waitTime=0.2)

    def __send_file(self, *file_paths) -> None:
        """
        发送文件.
        Args:
            *file_paths(Iterable or str): 必选参数,为文件的路径
        Returns:
            None
        """
        all_path = str()
        for path in file_paths:
            full_path = os.path.abspath(path=path)
            assert os.path.exists(full_path), f"{full_path} 文件路径有误"
            all_path += "'" + full_path + "',"
        args = ['powershell', f'Get-Item {all_path[:-1]} | Set-Clipboard']
        # 去除console 弹窗
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags = subprocess.CREATE_NEW_CONSOLE | subprocess.STARTF_USESHOWWINDOW
        startupinfo.wShowWindow = subprocess.SW_HIDE
        subprocess.Popen(args=args, startupinfo=startupinfo)
        time.sleep(0.5)
        self.input_edit.SendKeys(text='{Ctrl}v', waitTime=0.2)
        self.wx_window.SendKey(key=auto.SpecialKeyNames['ENTER'], waitTime=0.2)

    def get_friend_list(self, tag: str = None, num: int = 10) -> list:
        """
        获取微信好友名称.
        Args:
            tag(str): 可选参数,如不指定,则获取所有好友
            num(int): 可选参数,如不指定,只获取10页好友
        Returns:
            list
        """

        def click_tag():
            """点击标签"""
            contacts_management_window.ButtonControl(Name="标签").Click()

        # 点击 通讯录管理
        self.wx_window.ButtonControl(Name="通讯录").Click()
        self.wx_window.ListControl(Name="联系人").ButtonControl(Name="通讯录管理").Click()
        contacts_management_window = auto.GetForegroundControl()  # 切换到通讯录管理,相当于切换到弹出来的页面
        # contacts_management_window.ButtonControl(Name='最大化').Click()

        if tag:
            click_tag()  # 点击标签
            contacts_management_window.PaneControl(Name=tag).Click()
            time.sleep(0.3)
            click_tag()  # 关闭标签
        # 获取滑动模式
        scroll = contacts_management_window.ListControl().GetScrollPattern()
        # assert scroll, "没有可滑动对象"
        name_list = list()
        if not scroll:
            for name_node in contacts_management_window.ListControl().GetChildren():  # 获取当前页面的 列表 -> 子节点
                nick_name = name_node.TextControl().Name  # 用户名
                remark_name = name_node.ButtonControl(foundIndex=2).Name  # 用户备注名,索引1会错位,索引2是备注名,索引3是标签名
                name_list.append(remark_name if remark_name else nick_name)
        else:
            rate: int = int(float(102000 / num))  # 根据输入的num计算滑动的步长
            for pct in range(0, 102000, rate):  # range不支持float,不导入numpy库,采取迂回这的方式
                # 每次滑动一点点,-1代表不用滑动
                scroll.SetScrollPercent(horizontalPercent=-1, verticalPercent=pct / 100000)
                for name_node in contacts_management_window.ListControl().GetChildren():  # 获取当前页面的 列表 -> 子节点
                    nick_name = name_node.TextControl().Name  # 用户名
                    remark_name = name_node.ButtonControl(foundIndex=2).Name  # 用户备注名,索引1会错位,索引2是备注名,索引3是标签名
                    name_list.append(remark_name if remark_name else nick_name)
        contacts_management_window.SendKey(auto.SpecialKeyNames['ESC'])  # 结束时候关闭 "通讯录管理" 窗口
        return list(set(name_list))  # 简单去重,但是存在误判(如果存在同名的好友

    def get_group_chat_list(self) -> list:
        """获取群聊通讯录中的用户名称"""
        name_list = list()
        auto.ButtonControl(Name='聊天信息').Click()
        time.sleep(0.5)
        chat_members_win = self.wx_window.ListControl(Name='聊天成员')
        if not chat_members_win.Exists():
            return list()
        self.wx_window.ButtonControl(Name='查看更多').Click()
        for item in chat_members_win.GetChildren():
            name_list.append(item.ButtonControl().Name)
        return name_list

    def get_chat_records(self, page: int = 1) -> list:
        """
        获取聊天列表的聊天记录.
        Args:
            page(int): 可选参数,如不指定,只获取1页聊天记录
        Returns:
            list
        """
        chat_records = list()

        def extract_msg() -> None:
            all_msgs = self.wx_window.ListControl(Name="消息").GetChildren()
            for msg_node in all_msgs:
                msg = msg_node.Name
                if not msg:
                    continue
                if msg_node.PaneControl().Name:
                    chat_records.append({'type': 'Time', 'name': 'System', 'msg': msg_node.PaneControl().Name})
                    continue
                if msg in ['以下为新消息', '查看更多消息', '该类型文件可能存在安全风险,建议先检查文件安全性后再打开。', '已撤回']:
                    chat_records.append({'type': 'System', 'name': 'System', 'msg': msg})
                    continue
                if '撤回了一条消息' in msg or '尝试撤回上一条消息' in msg:
                    chat_records.append(
                        {'type': 'Other', 'name': ''.join(msg.split(' ')[:-1]), 'msg': msg.split(' ')[-1]})
                    continue
                if msg in ['发出红包,请在手机上查看', '收到红包,请在手机上查看', '你发送了一次转账收款提醒,请在手机上查看', '你收到了一次转账收款提醒,请在手机上查看']:
                    chat_records.append({'type': 'RedEnvelope', 'name': 'System', 'msg': msg})
                    continue
                if '领取了你的红包' in msg:
                    _ = msg.split('领取了你的红包')
                    chat_records.append({'type': 'RedEnvelope', 'name': _[0], 'msg': _[1]})
                    continue
                name = msg_node.ButtonControl(foundIndex=1).Name
                if msg == '[文件]':
                    file_name = msg_node.PaneControl().TextControl(foundIndex=1).Name
                    size = msg_node.PaneControl().TextControl(foundIndex=2).Name
                    chat_records.append(
                        {'type': 'File', 'name': name, 'msg': f'size: {size}  ---  file_name: {file_name}'})
                    continue
                if msg == '微信转账':
                    operation = msg_node.PaneControl().TextControl(foundIndex=2).Name
                    amount = msg_node.PaneControl().TextControl(foundIndex=3).Name
                    chat_records.append(
                        {'type': 'RedEnvelope', 'name': name, 'msg': msg + f'    {operation}    ' + amount})
                    continue
                if '引用' in msg and '的消息' in msg:
                    chat_records.append({'type': 'Cited', 'name': name, 'msg': msg})
                    continue
                if msg == '[聊天记录]':
                    if not name:
                        name = msg_node.ButtonControl(foundIndex=2).Name
                chat_records.append({'type': 'Content', 'name': name, 'msg': msg})

        for _ in range(page):
            self.wx_window.WheelUp(wheelTimes=15)
        extract_msg()
        return chat_records

    def send_msg(self, *names, msgs, file_paths, add_remark_name=False) -> None:
        """
        发送消息,可同时发送文本和文件(至少选一项
        Args:
            *names (str or Iterable):必选参数,接收消息的好友名称,可以群发,也可以单发
            msgs (list): 可选参数,发送的文本消息
            file_paths (Iterable):可选参数,发送的文件路径
            add_remark_name(bool): 可选参数,是否添加备注名称发送
        Returns:
            None
        """
        assert names, "用户名列表为空"
        assert any([msgs, file_paths]), "没有发送任何消息"
        assert not isinstance(msgs, str), "文本必须为可迭代且非字符串类型"
        assert not isinstance(file_paths, str), "文件路径必须为可迭代且非字符串类型"
        for name in names:
            self.__goto_chat_box(name=name)
            if msgs:
                if add_remark_name:
                    new_msgs = deepcopy(msgs)
                    new_msgs.insert(0, name)
                    self.__send_text(*new_msgs)
                else:
                    self.__send_text(*msgs)
            if file_paths:
                self.__send_file(*file_paths)


再建一个Python文件为:mytest.py,代码如下:

from wx_file import WxOperation

wx = WxOperation()
# 发送文本和文件
wx.send_msg('文件传输助手', msgs=["Hello"], file_paths=["F:\Test.txt"])
wx.send_msg('文件传输助手', msgs=["world"], file_paths=["F:\Test.txt"])

但是有时第一次发送不成功,第二次成功。

注意:微信最新版本3.9.5以上代码会发送文件不成功,原因是找不到控件。把电脑版本微信降到上一个版本3.9.2发送成功。

 

仅用于学习交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/35222.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ElasticSearch】DSL查询语法

文章目录 1、DSL查询分类2、DSL基本语法3、全文检索查询4、精确查询5、地理查询6、复合查询--相关性打分算法7、复合查询之Function Score Query8、复合查询之BooleanQuery 1、DSL查询分类 Elasticsearch提供了基于JSON的DSL(Domain Specific Language)…

Java版本工程项目管理系统源码

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下: 首页 工作台:待办工作、消息通知、预警信息,点击可进入相应的列表 项目进度图表:选择(总体或单个)项目显示…

Librosa库——语音识别,语音音色识别训练及应用

很多同学以为语音识别是非常难的,其实并不然,起初我也是这么认为,但后来发现语音识别是最简单的,因为同学们可能不知道Python有一个音频处理库Librosa,这个库非常的强大,可以进行音频处理、频谱表示、幅度转…

CSS(持续更新!~)

二: 进阶: 只打算起到装饰作用的图片就建议就背景图片 块级标签就是:独占一行的标签(比如div)并且可以加宽加高 行内元素:就是不会独占一行的标签(比如a,span等等,不可以…

TensorFlow项目练手(二)——猫狗熊猫的分类任务

项目介绍 通过猫狗熊猫图片来对图片进行识别,分类出猫狗熊猫的概率,文章会分成两部分,从基础网络模型->利用卷积网络经典模型Vgg。 基础网络模型 基础的网络模型主要是用全连接层来分类,比较经典的方法,也是祖先…

MinGW编译OpenCV 过程记录

1.下载源码opencv-3.4.10.zip ,可以在OpenCV官网下载Releases - OpenCV 解压缩如下: 2.下载Mingw64工具,需要支持posix 并设置系统环境目录,下载的文件名x86_64-8.1.0-release-posix-sjlj-rt_v6-rev0.7z (可以在网上找) 3.使用Cmake工具构建…

微信小程序个人中心展示样式(2)

这是之前的详细的看这里 因为这是好多年前写的了,好多人私信我代码有问题。正好今天有时间简单的还原下代码 话不多说先看图(图片样式自己搞奥~~~~我也好久没弄了这就是个参考demo) 以下是一个使用微信小程序开发的个人中心展示详情的示例: 在微信开发…

基于PyQt5的桌面图像调试仿真平台开发(10)色彩矩阵

系列文章目录 基于PyQt5的桌面图像调试仿真平台开发(1)环境搭建 基于PyQt5的桌面图像调试仿真平台开发(2)UI设计和控件绑定 基于PyQt5的桌面图像调试仿真平台开发(3)黑电平处理 基于PyQt5的桌面图像调试仿真平台开发(4)白平衡处理 基于PyQt5的桌面图像调试仿真平台开发(5)…

解决问题:通配符的匹配很全面, 但无法找到元素 ‘context:component-scan‘ 的声明~

异常描述如下&#xff1a; 产生异常原因&#xff1a; 因为在配置文件中没有找到<context:component-scan />元素的声明&#xff0c;解决办法&#xff1a;将XML配置文件中的声明改为下述代码&#xff1a; <beans xmlns"http://www.springframework.org/schema/b…

01 | 一条 SQL 查询语句是如何执行的?

以下内容出自 《MySQL 实战 45 讲》 一条 SQL 查询语句是如何执行的&#xff1f; 下面是 MySQL 的基本架构示意图&#xff0c;从中可以清楚地看到 SQL 语句在 MySQL 的各个功能模块中的执行过程。 大体来说&#xff0c;MySQL 可以分为 Server 层和存储引擎层两部分。 Server …

Python如何批量将图片以超链接的形式插入Excel

【研发背景】 在日常办公中&#xff0c;我们经常需要将图片插入进Excel中&#xff0c;但是如果插入的图片太多的话&#xff0c;就会导致Excel的文件内存越来越大&#xff0c;但是如果我直插入图片的路径&#xff0c;或者只是更改某一列的数据设置为超链接&#xff0c;这样的话&…

Spring底层核心架构

Spring底层核心架构 相关的配置类 1. user类 package com.zhouyu.service;import org.springframework.stereotype.Component;public class User { }2. AppConfig类 package com.zhouyu;import org.springframework.context.annotation.*; import org.springframework.sched…

open*w*r*t +dnspod ddns动态解析ipv6 远程控制移动内网路由器

1.修改openw*r*t web https管理端口为8443 修改ipv6 https 监听端口list listen_https [::]:8443 cd /etc/config/vi uhttpdvi /etc/config/uhttpdconfig uhttpd mainlist listen_http 0.0.0.0:80list listen_http [::]:80list listen_https 0.0.0.0:443list listen_https [:…

前端Vue一款基于canvas的精美商品海报生成组件 根据个性化数据生成商品海报图 长按保存图片

前端Vue一款基于canvas的精美商品海报生成组件 根据个性化数据生成商品海报图 长按保存图片&#xff0c;下载完整代码请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13326 效果图如下: # cc-beautyPoster #### 使用方法 使用方法 <!-- pos…

Java虚拟机(JVM)、垃圾回收器

一、Java简介 1、Java开发及运行版本 JRE(Java Runtime Environment&#xff0c;运行环境) 所有的程序都要在JRE下才能够运行。包括JVM和Java核心类库和支持文件。JDK(Java Development Kit&#xff0c;开发工具包) 用来编译、调试Java程序的开发工具包。包括Java工具(javac/…

【Redis】3、Redis 作为缓存(Redis中的穿透、雪崩、击穿、工具类)

目录 一、什么是缓存二、给业务添加缓存&#xff08;减少数据库访问次数&#xff09;三、给店铺类型查询业务添加缓存(1) 使用 String 类型(2) 使用 List 类型 四、缓存的更新策略(1) 主动更新(2) 最佳实现方案(3) 给查询商铺的缓存添加超时剔除和主动更新的策略① 存缓存&…

泰迪智能科技基于产业技能生态链学生学徒制的双创工作室--促进学生高质量就业

据悉&#xff0c;6月28日&#xff0c;广东省人力资源和社会保障厅在广东岭南现代技师学院举行广东省“产教评”技能生态链建设对接活动。该活动以“新培养、新就业、新动能”为主题&#xff0c;总结推广“产教评”技能人才培养新模式&#xff0c;推行“岗位培养”学徒就业新形式…

Tomcat多实例部署

1、关闭防火墙&#xff0c;将安装 Tomcat 所需软件包传到/opt目录下2、安装好 JDK3、设置JDK环境变量4、安装 tomcat5、配置 tomcat 环境变量6、修改 tomcat2 中的 server.xml 文件&#xff0c;要求各 tomcat 实例配置不能有重复的端口号7、修改各 tomcat 实例中的 startup.sh …

更便捷的人体三维模型制作方法

人体三维模型是一种以计算机辅助设计技术为基础的创新工具&#xff0c;它在医学、生物学、运动学等领域具有广泛的应用价值。这种模型通过将人体的形态、结构与功能等要素进行数字化处理和计算&#xff0c;能够以立体图像的形式展现出来。它可以精确地模拟人体的各种部位&#…

【C】数据在内存中的存储

前言 > 在内存中&#xff0c;整型和浮点型存储的方式是不同的&#xff0c;从内存中读取的方式也是有所差异的&#xff0c;这篇文章主要介绍整型和浮点型在内存中存储的方式。 整型在内存中的存储 计算机中有符号数有3种表示方式&#xff1a; 原码&#xff1a;直接将二进制按…