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

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

相关文章

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

C++中使用vector存储并遍历数据的基本步骤

《C++中使用vector存储并遍历数据的基本步骤》C++标准模板库(STL)提供了多种容器类型,包括顺序容器、关联容器、无序关联容器和容器适配器,每种容器都有其特定的用途和特性,:本文主要介绍C... 目录(1)容器及简要描述‌php顺序容器‌‌关联容器‌‌无序关联容器‌(基于哈希表):‌容器适配器‌:(

使用Python实现高效的端口扫描器

《使用Python实现高效的端口扫描器》在网络安全领域,端口扫描是一项基本而重要的技能,通过端口扫描,可以发现目标主机上开放的服务和端口,这对于安全评估、渗透测试等有着不可忽视的作用,本文将介绍如何使... 目录1. 端口扫描的基本原理2. 使用python实现端口扫描2.1 安装必要的库2.2 编写端口扫

使用Python实现操作mongodb详解

《使用Python实现操作mongodb详解》这篇文章主要为大家详细介绍了使用Python实现操作mongodb的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、示例二、常用指令三、遇到的问题一、示例from pymongo import MongoClientf

SQL Server使用SELECT INTO实现表备份的代码示例

《SQLServer使用SELECTINTO实现表备份的代码示例》在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误,在SQLServer中,可以使用SELECTINT... 在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SE

使用Python合并 Excel单元格指定行列或单元格范围

《使用Python合并Excel单元格指定行列或单元格范围》合并Excel单元格是Excel数据处理和表格设计中的一项常用操作,本文将介绍如何通过Python合并Excel中的指定行列或单... 目录python Excel库安装Python合并Excel 中的指定行Python合并Excel 中的指定列P

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

golang1.23版本之前 Timer Reset方法无法正确使用

《golang1.23版本之前TimerReset方法无法正确使用》在Go1.23之前,使用`time.Reset`函数时需要先调用`Stop`并明确从timer的channel中抽取出东西,以避... 目录golang1.23 之前 Reset ​到底有什么问题golang1.23 之前到底应该如何正确的

详解Vue如何使用xlsx库导出Excel文件

《详解Vue如何使用xlsx库导出Excel文件》第三方库xlsx提供了强大的功能来处理Excel文件,它可以简化导出Excel文件这个过程,本文将为大家详细介绍一下它的具体使用,需要的小伙伴可以了解... 目录1. 安装依赖2. 创建vue组件3. 解释代码在Vue.js项目中导出Excel文件,使用第三

Linux alias的三种使用场景方式

《Linuxalias的三种使用场景方式》文章介绍了Linux中`alias`命令的三种使用场景:临时别名、用户级别别名和系统级别别名,临时别名仅在当前终端有效,用户级别别名在当前用户下所有终端有效... 目录linux alias三种使用场景一次性适用于当前用户全局生效,所有用户都可调用删除总结Linux