使用文字识别构建的微信群聊天机器人

2023-11-01 09:10

本文主要是介绍使用文字识别构建的微信群聊天机器人,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 摘要
  • 实现思路
    • 实现代码
    • 遇到问题
      • pymouse报错缺少windows和win32api
      • module 'PIL.Image' has no attribute 'ANTIALIAS'
      • 获取openai api key
      • pip install CnOcr提示需要c++编译环境
    • 总结

摘要

本文实现了一种使用文字识别的方法来构建的微信群聊机器人,调用openai来实现智能聊天。众所周知,微信机器人的项目现在由于网页版微信被ban,大部分都已经不可用了。我的常用微信号和小号都无法登录网页版微信,于是寻找能够通过pc端来实现微信机器人的方法,找到诸如wechaty等项目。这些项目通过dll注入的方式调用微信客户端的内部方法来接受和发送消息。但可惜的是这些项目都是收费的,本身这就是个伪需求,只是做着玩的,要花钱就不太值当了。既然找不到现成的框架,那么试试自己能不能实现吧!本身我也没学过逆向,想要破解微信来实现dll注入对我来说太过困难。而且就算凭我的三脚猫功夫能实现需求,估计不超过一周就能被封号😥。只能想个更安全的方式了,就是使用模拟操作来直接操作微信客户端,当然这种方式实现的功能有着非常大的局限性,不过只是做着玩的话也勉强可以用。

实现思路

使用python来实现,有着强大的第三方库的支持,避免自己造轮子。

  1. 我们首先需要能够监听到群里发送的消息。使用pyautogui库的screenshot()方法对屏幕进行截图。
  2. 之后要从截图中获得最后一条消息框的位置,并对消息框进行截图。可知消息框的左边界是固定的位置(只要你不缩放微信界面)。那就从这条线最底下往上找rgb为(255,255,255)白色的坐标。就可以获得消息框左下角坐标。在从该坐标分别向上找和向右找白色的部分,就可以得到消息框的大小了。
    获得消息框3. 获得消息框的截图后,对其使用文字识别吧。一开始使用了tesseract来进行文字识别,但其中文的识别不出正确文字,不知到是我配置有问题吗?当把图片放大8倍的时候好歹能识别出几个字了😑。总之又换成了CnOcr来进行文字识别了,虽然有时候也识别错误,但也足够用了。
    识别图片
    识别文字
    4. 获得新消息后,调用openai的api来获得要回复的文本。在用pyperclipcopy()方法复制文字进剪切板,pyautogui来实现ctrl+v粘贴,alt+s发送消息。

实现代码

废话不多说了,这个代码是花了两天写的,仍有很多不足,但小心点使用还是可以的。

import pyautogui
from pynput.keyboard import Key, Controller
from pyperclip import copy,paste
from PIL import Image, ImageChops
import time
import io
from cnocr import CnOcr
from queue import Queue
import openai
import threading
import logging"""向队列中添加元素,并保持队列元素的个数。args:queue:队列limit:限制队列元素的个数item:要添加的元素
"""
def queue_limit_put(queue,limit,item):queue.put(item)if queue.qsize()>limit:queue.get()def send_msg_on_wechat(message,response):send_msg=message+"\n--------\n"+responsecopy(send_msg)pyautogui.hotkey('ctrl', 'v')pyautogui.hotkey('alt','s')class WeChatRobot:def __init__(self) -> None:self.snap_position=[300,60,980,825]#截图框的位置:left,top,length,heightself.dialog_left=340#消息框的左边界self.ocr=CnOcr()self.message_list=Queue()#新消息队列self.robot_name="%小马"#机器人的唤醒名字self.using_name=True#True时要带机器人名字的消息才会回复,False所有消息都回复self.snap_error_check=True"""对屏幕区域进行截图args:x,y:左上点的坐标m:长度n:高度"""def snap(self,x,y,m,n):try:im=pyautogui.screenshot(region=(x,y,m,n))self.snap_error_check=Trueexcept Exception as e:if self.snap_error_check:self.snap_error_check=False#防止发送太多的崩溃消息send_msg_on_wechat('我崩溃啦!','截图失败了!')return im#对比两张图片相似度def compare_image(self,screenshot1,screenshot2):image1=screenshot1image2=screenshot2diff=ImageChops.difference(image1,image2)diff_image = diff.convert('RGBA')diff_count = diff.getbbox()if diff_count is None:#与上一张截图相同return Falsesimilarity = 1 - ((diff_count[2]-diff_count[0]) * (diff_count[3]-diff_count[1])) / (image1.width * image1.height)if __debug__:print(f"相似度:{similarity}")if similarity>=0.95:#相似度大于0.95判定为相同,低于0.95判定为有新消息return Falsereturn True#寻找对话框的坐下点坐标def find_dialog_leftbott(self,im):x=80y=self.snap_position[3]-1while y>0:if im.getpixel((x,y))==(255,255,255):return (x,y)else:y=y-1return (-1,-1)#寻找对话框的长与高,(x,y)为左下点坐标def find_dialog_lenhei(self,im,x,y):right=xtop=ywhile right<self.snap_position[2]:if im.getpixel((right,y))!=(255,255,255):breakright=right+1while top>0:if im.getpixel((x,top))!=(255,255,255):breaktop=top-1if right==self.snap_position[2] or top==0:return (-1,-1)return (right-x,y-top)# def test_ai_reply(self):#     time.sleep(1)#     for i in range(2):#         self.message_list.put(f"你是谁")def run(self):privous_screenshot=Nonetime.sleep(5)#等我切换到微信页面while True:#对微信界面进行截图作为原始图im=self.snap(self.snap_position[0],self.snap_position[1],self.snap_position[2],self.snap_position[3])#寻找消息框并截图(dialog_x,dialog_y)=self.find_dialog_leftbott(im)if dialog_x==-1 or dialog_y==-1:logger.warning("查找对话框位置失败!")if __debug__:print(f"dialog_x,dialog_y:{dialog_x,dialog_y}")(dialog_len,dialog_hei)=self.find_dialog_lenhei(im,dialog_x,dialog_y)if dialog_len==-1 or dialog_hei==-1:logger.warning("查找对话框长宽失败!")if __debug__:print(f"dialog_len,dialog_hei{dialog_len,dialog_hei}")#根据获得的参数截取对话框的图片dialog_image=im.crop((dialog_x,dialog_y-dialog_hei,dialog_x+dialog_len,dialog_y))hasNews=Falseif privous_screenshot is not None:#与上一张截图进行对比,相似度是否在阈值内#此处只对比了消息框的内容,会导致用户连续发送相同的内容不会认为其是新消息,暂时没解决办法hasNews=self.compare_image(privous_screenshot,dialog_image)if hasNews:#使用cnocr进行识别,部分文字仍识别不正确,但正确率仍可以接受result=self.ocr.ocr(dialog_image)res_text=""for element in result:#拼接识别的文字res_text=res_text+element.get('text')logger.info(f"识别新消息:{res_text}")if self.using_name:if res_text.startswith(self.robot_name):res_text=res_text.replace(self.robot_name,"")#识别出机器人名字,然后从消息中去除机器人名字self.message_list.put(res_text)else:self.message_list.put(res_text)privous_screenshot=dialog_imagetime.sleep(0.5)
class AI_reply:def __init__(self,queue) -> None:self.queue=queueself.api_key='abcdefghijklmnopqrstuvwxyz'#换成你的openai api keyopenai.api_key=self.api_keyopenai.proxy = 'http://127.0.0.1:1234'#设置下代理,否则连不上网络self.model='gpt-3.5-turbo'self.temperature=0.7self.max_tokens=200#设置200回复字数上限,担心我的额度啊self.n=1self.msg_context=Queue()#保存的上下文self.msg_context_limit=5#上下文大小,影响ai能记住几句之前的内容self.keyborad=Controller()# def test_api(self):#     messages = [{ "role": "user", "content": '欢迎使用openai!' }]#     model = 'gpt-3.5-turbo'#     response = openai.ChatCompletion.create(#         model=model,#         messages=messages,#         temperature=self.temperature,#         n=self.n#     )#     queue_limit_put(self.msg_context,self.msg_context_limit,response.choices[0].message)#     if __debug__:#         print(f'\033[31m收到的数据:{response.choices[0].message.content}\033[0m')#制作消息放入上下文中def create_message(self,content):message={"role":"user","content":content}queue_limit_put(self.msg_context,self.msg_context_limit,message)return self.msg_context#请求api,获得返回结果def get_response(self,message):try:response=openai.ChatCompletion.create(model=self.model,messages=message,temperature=self.temperature,max_tokens=self.max_tokens)except openai.APIConnectionError  as e:logger.error(e)return ('error','网络又抽风了,这也是没办法的事。')queue_limit_put(self.msg_context,self.msg_context_limit,response.choices[0].message)#将返回的结果放入上下文中,让ai能记住说了什么logger.info(f"从openai获得回复:{response.choices[0].message.content}")return ('success',response.choices[0].message.content)def run(self):while True:logger.info("等待新消息。")msg=self.queue.get()logger.info(f"获得新消息,正在请求openai:{msg}")self.create_message(msg)logger.info(f"token:{list(self.msg_context.queue)}")(statue,res)=self.get_response(list(self.msg_context.queue))if statue=='success':self.send_msg_on_wechat(msg,res)elif statue=='error':self.send_msg_on_wechat('链接不上openai了',res)logger=logging.getLogger('robot_logger')
logger.setLevel("INFO")
file_handler = logging.FileHandler("C:\\Users\\22308\\Documents\\python file\\log\\robot.txt")
file_handler.setLevel("INFO")
fmt =logging.Formatter('%(filename)s-%(lineno)d-%(asctime)s-%(levelname)s-%(message)s')
file_handler.setFormatter(fmt)
logger.addHandler(file_handler)
logger.info("开始运行!")
robot=WeChatRobot()
ai=AI_reply(robot.message_list)
logger.info("初始化完成")
#创建线程监控是否有新消息,然后请求api
thread = threading.Thread(target=ai.run)
thread.start()
robot.run()
thread.join()
logger.info("运行结束!")

遇到问题

写一下遇到的一些小问题吧。

pymouse报错缺少windows和win32api

虽然最后也没用到这个库,但遇到了就写一下吧。
缺少windows:

pip install PyUserinput

缺少win32api:

pip install pypiwin32

module ‘PIL.Image’ has no attribute ‘ANTIALIAS’

在pillow的10.0.0版本中,ANTIALIAS方法被删除了,改为10.0版本许可的参数即可。

  • 方案一,修改ddddocr的_init_.py文件,将其中的ANTIALIAS替换为新方法:

    image = image.resize((int(image.size[0] * (64 / image.size[1])), 64), Image.LANCZOS).convert('L')
    
  • 方案二,降级Pillow的版本

    pip uninstall -y Pillow

    pip install Pillow==9.5.0

获取openai api key

在openai的官网上注册好后,到API文档中激活api key,每个月有5美刀的额度,持续三个月。不幸的是我的key不知道怎么回事已经申请过了,现在已经过期了😅。只好去买一个账号了,好在也不贵。

pip install CnOcr提示需要c++编译环境

找解决方法时有说安装anaconda可以,安装visual cpp build tool的。但总有地方卡住我。最后还是直接安装了virtual sudio,下了6个G😅。

总结

除了接入chatgpt以外,其实还可以使用一些固定指令来实现天气,咨询等内容,有了框架后这些也好写了。现在这些内容放到相亲相爱一家人里装个逼足够了。当然如果大家有更好用的技术或者其他建议的话欢迎告诉我。最后放一下我老婆。
老婆!

这篇关于使用文字识别构建的微信群聊天机器人的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/321954

相关文章

使用Python将PDF表格自动提取并写入Word文档表格

《使用Python将PDF表格自动提取并写入Word文档表格》在实际办公与数据处理场景中,PDF文件里的表格往往无法直接复制到Word中,本文将介绍如何使用Python从PDF文件中提取表格数据,并将... 目录引言1. 加载 PDF 文件并准备 Word 文档2. 提取 PDF 表格并创建 Word 表格

使用Python实现局域网远程监控电脑屏幕的方法

《使用Python实现局域网远程监控电脑屏幕的方法》文章介绍了两种使用Python在局域网内实现远程监控电脑屏幕的方法,方法一使用mss和socket,方法二使用PyAutoGUI和Flask,每种方... 目录方法一:使用mss和socket实现屏幕共享服务端(被监控端)客户端(监控端)方法二:使用PyA

Python使用Matplotlib和Seaborn绘制常用图表的技巧

《Python使用Matplotlib和Seaborn绘制常用图表的技巧》Python作为数据科学领域的明星语言,拥有强大且丰富的可视化库,其中最著名的莫过于Matplotlib和Seaborn,本篇... 目录1. 引言:数据可视化的力量2. 前置知识与环境准备2.1. 必备知识2.2. 安装所需库2.3

Python数据验证神器Pydantic库的使用和实践中的避坑指南

《Python数据验证神器Pydantic库的使用和实践中的避坑指南》Pydantic是一个用于数据验证和设置的库,可以显著简化API接口开发,文章通过一个实际案例,展示了Pydantic如何在生产环... 目录1️⃣ 崩溃时刻:当你的API接口又双叒崩了!2️⃣ 神兵天降:3行代码解决验证难题3️⃣ 深度

Linux内核定时器使用及说明

《Linux内核定时器使用及说明》文章详细介绍了Linux内核定时器的特性、核心数据结构、时间相关转换函数以及操作API,通过示例展示了如何编写和使用定时器,包括按键消抖的应用... 目录1.linux内核定时器特征2.Linux内核定时器核心数据结构3.Linux内核时间相关转换函数4.Linux内核定时

python中的flask_sqlalchemy的使用及示例详解

《python中的flask_sqlalchemy的使用及示例详解》文章主要介绍了在使用SQLAlchemy创建模型实例时,通过元类动态创建实例的方式,并说明了如何在实例化时执行__init__方法,... 目录@orm.reconstructorSQLAlchemy的回滚关联其他模型数据库基本操作将数据添

Spring配置扩展之JavaConfig的使用小结

《Spring配置扩展之JavaConfig的使用小结》JavaConfig是Spring框架中基于纯Java代码的配置方式,用于替代传统的XML配置,通过注解(如@Bean)定义Spring容器的组... 目录JavaConfig 的概念什么是JavaConfig?为什么使用 JavaConfig?Jav

Java使用Spire.Doc for Java实现Word自动化插入图片

《Java使用Spire.DocforJava实现Word自动化插入图片》在日常工作中,Word文档是不可或缺的工具,而图片作为信息传达的重要载体,其在文档中的插入与布局显得尤为关键,下面我们就来... 目录1. Spire.Doc for Java库介绍与安装2. 使用特定的环绕方式插入图片3. 在指定位

Springboot3 ResponseEntity 完全使用案例

《Springboot3ResponseEntity完全使用案例》ResponseEntity是SpringBoot中控制HTTP响应的核心工具——它能让你精准定义响应状态码、响应头、响应体,相比... 目录Spring Boot 3 ResponseEntity 完全使用教程前置准备1. 项目基础依赖(M

Java使用Spire.Barcode for Java实现条形码生成与识别

《Java使用Spire.BarcodeforJava实现条形码生成与识别》在现代商业和技术领域,条形码无处不在,本教程将引导您深入了解如何在您的Java项目中利用Spire.Barcodefor... 目录1. Spire.Barcode for Java 简介与环境配置2. 使用 Spire.Barco