目录
前言
一、uiautomation是什么?
二、openai怎么使用?
三、使用步骤
1.前期准备
2.引入库
3.主程序
4.演示效果
总结
前言
人工ai对话技术最近火热,相信很多人会想到做一款微信聊天机器人,让自己那些对程序不是很了解的亲朋好友使用体验一下。
目前大多数的微信聊天机器人,都是基于itchat或者wxautoapi 来操作微信,原理是监听和操作微信的网页版端口,来达到短信收发的目的。但腾讯官方对于这种办法卡得很严,很多微信账号(包括我的)已经不能使用了,就算使用也会面临封禁的风险。
也有用pyautogui来实现的,不过这种基于图像识别的技术的可移植性不是太好,也很难去实现某些功能。
在本文中,利用uiautomation去操作PC端微信,利用官方的openai库获得AI自动回复,实现微信聊天机器人的功能。
此微信聊天机器人的具有以下功能:
1.可以回复多名好友,能自动检测未读消息并回复。
2.配置上下文联系数量和每天问题字数限制,并且每天凌晨可以刷新字数限制。
3.可以给好友配置VIP权限,具有更多的上下文联系数量和问题限制。
4.可以回复群消息,只有当群友@聊天机器人的时候,才会回复。
一、uiautomation是什么?
uiautomation是python的一个模块,封装了微软UIAutomation API,可以对windows的桌面进行控件的搜索、点击等一些列操作,因为完全是桌面级操作,不涉及API接口问题,所以不容易被针对封禁——至少在微信操作上是这样。
uiautomation的原github地址链接
中文说明链接
二、openai怎么使用?
openai是官方提供的python库,通过API接口就可以去获取ChatGPT的回答,值得注意的是如何去实现上下文功能,下面是一段演示代码:
# Note: you need to be using OpenAI Python v0.27.0 for the code below to workimport openaiopenai.ChatCompletion.create( model="gpt-3.5-turbo", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Who won the world series in 2020?"}, {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."}, {"role": "user", "content": "Where was it played?"} ])
在messages中,{"role": "system", "content": "You are a helpful assistant."}算是表头,系统消息有助于设置助手的行为。在上面的例子中,助手被指示“你是一个有帮助的助手”。通常,对话首先使用系统消息格式化,然后是交替的用户和助理消息。
{"role": "user", "content": "Who won the world series in 2020?"}就是第一次对话,"role": "user"表示是用户发起的,问的问题是"Who won the world series in 2020?"。
而{"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."}就是第一次的回答,"role": "assistant"表示是AI的,回答内容是"The Los Angeles Dodgers won the World Series in 2020."
{"role": "user", "content": "Where was it played?"}就是第二次回答了,这样往复就能实现上下文的联系。
需要注意的是,message的发送也占用token数量,openai的chat-3.5模型收费价格是0.002美元/K token,虽然每个账号有18美元的免费额度,但是不限制上下文数量,估计没多久你的额度就爆了。
三、使用步骤
1.前期准备
首先推荐一个pytharm的插件,可以使用中文变量,像我这种英文不好的,最讨厌的就是去想变量名了,有了这个插件会方便好多,还减少了注释。直接在插件搜索Chinese就行:
其次,需要你把urllib3的版本回退到1.25.11,因为使用openai需要挂代理,但是1.26版以上会报错,而1.25.11版本就没有这个问题了。打开cmd,输入以下命令:
pip uninstall urllib3pip install urllib3==1.25.11
2.引入库
import timefrom datetime import datetimeimport uiautomation as ui # 导入控件控制库import pyperclip # 导入剪切板相关库import pyautogui # 导入自动操作库import openai
3.主程序
openai.api_key = "" # 这里填入你自己的OpenAi的APIui.SetGlobalSearchTimeout(0) # 设置全局搜索超时info = {}'''用户配置,范例:{'张':{ '更新时间': '2023-04-01', '是否VIP': '是”, '最大Token数': 5000, '已使用Token数': 0, '上下文联系数量': 5, 'messages':[{'role': 'system', 'content': '张的对话'}] }}'''VipList = ['赵某', '王某', '智障'] # vip名单# 对用户创建一个专门的配置文件,用于存放权限以及对话上下文def 用户配置(name, group=False): global info # 第一步:检索配置中是否存在该人 if name in info: # 如果在配置列表中 if info[name]['更新时间'] != datetime.now().strftime('%y-%m-%d'): # 如果当前日期和更新时间不同,更新频率为1天 info[name]['更新时间'] = datetime.now().strftime('%y-%m-%d') # 刷新更新时间 info[name]['已使用Token数'] = 0 # 重置已使用Token数 info[name]['messages'] = [{'role': 'system', 'content': name + '的对话'}] # 重置messages else: # 不在配置列表中 info[name] = {} # 以用户名创建配置列表 info[name]['更新时间'] = datetime.now().strftime('%y-%m-%d') info[name]['messages'] = [{'role': 'system', 'content': name + '的对话'}] info[name]['已使用Token数'] = 0 # 第二步:判断一下用户是否为vip if name in VipList: # 如果用户为vip用户 info[name]['是否VIP'] = '是' info[name]['最大Token数'] = 5000 info[name]['上下文联系数量'] = 10 else: # 如果用户非vip用户 info[name]['是否VIP'] = '否' info[name]['最大Token数'] = 1000 info[name]['上下文联系数量'] = 1 # 第三步:判断一下用户是否为群聊用户 if group: info[name]['是否VIP'] = '否' info[name]['最大Token数'] = 100000 # 因为是在群聊中所有人一起使用,可以设适当设置大一些,谨慎使用! info[name]['上下文联系数量'] = 1 # 因为是在群聊中所有人一起使用,所以最好就设置成不联系上下文了def getOpenAiMsg(name, question): """ 此函数这是向OpenAi发送对话,并返回AI的回答 Args: name:用户的名称,用于读写对应的用户配置,str格式 question:对话的具体内容,str格式 Returns: answer:AI的回答,str格式 """ global info if question == "权限": # 如果用户输入“权限”, answer = name + "\n" \ + "更新时间: " + info[name]['更新时间'] + "\n" \ + "是否VIP: " + info[name]['是否VIP'] + "\n" \ + "上下文联系数量: " + str(info[name]['上下文联系数量']) + "\n" \ + "最大Token数: " + str(info[name]['最大Token数']) + "\n" \ + "已使用Token数: " + str(info[name]['已使用Token数']) return answer # 则返回用户配置信息,不会触发后续的OpenAi自动回复 if info[name]['已使用Token数'] >= info[name]['最大Token数']: # 查询是否超出配额 answer = '很抱歉,你今天使用量超出最大配额,发送“权限”查看您的账号情况' return answer else: if len(info[name]['messages']) >= 2 * info[name]['上下文联系数量'] + 1: # 如果上下文数量超出预设 # 删除第一组对话 info[name]['messages'].pop(1) info[name]['messages'].pop(1) if info[name]['上下文联系数量'] <= 1: # 如果上下文数量设置为1 info[name]['messages'] = [{'role': 'user', 'content': question}] # 直接设置一条即可 else: info[name]['messages'].append({'role': 'user', 'content': question}) # 增添对话信息 try: response = openai.ChatCompletion.create( model='gpt-3.5-turbo', messages=info[name]['messages'], temperature=0.2 ) answer = response.choices[0].message.content # 从返回值中提取回答 # 把回答添加到用户配置中的上下文,为了防止tokens爆炸,限制了字数 info[name]['messages'].append({'role': 'assistant', 'content': answer[:100]}) info[name]['已使用Token数'] = info[name]['已使用Token数'] + response.usage.total_tokens return answer except: answer = '很抱歉,服务器出错,请重试' return answerdef 普通用户回复(): """普通用户的回复""" wx = ui.WindowControl(Name="微信", searchDepth=1) # 绑定名为微信的主窗口控件 wx.SwitchToThisWindow() # 主窗口置前显示 hw = wx.ListControl(Name="会话") # 绑定对话列表 we = hw.EditControl(searchDepth=3) # 查找新对话 if we.Exists(0): 好友 = we.GetParentControl().GetParentControl() 姓名 = 好友.Name print("聊天对象:" + 姓名) we.Click(simulateMove=False) last_msg = wx.ListControl(Name="消息").GetChildren()[-1].Name # 获取最新消息 print("获得消息:" + last_msg) 用户配置(姓名) response = getOpenAiMsg(姓名, last_msg) # 将收到的信息转发给OpenAi print("你的回复:\n" + response) # print(info[姓名]) # 调试用,查看下用户配置 print( '已消耗标记:' + str(info[姓名]['已使用Token数']) + "\n--------------------------------------------------\n") # 输入回复信息 pyperclip.copy(response) # 复制信息 pyautogui.hotkey('ctrl', 'v') # 粘贴 pyautogui.press('enter') # 回车 # 隐藏对话 好友.RightClick() 右键菜单 = ui.MenuControl(ClassName="CMenuWnd") if 右键菜单.Exists(3, 0.1): 右键菜单.TextControl(Name="不显示聊天").Click()def 群聊用户回复(): """群聊用户的回复,请将群消息接收设置为消息免打扰""" wx = ui.WindowControl(Name="微信", searchDepth=1) # 绑定名为微信的主窗口控件 wx.SwitchToThisWindow() # 主窗口置前显示 hw = wx.ListControl(Name="会话") # 绑定名为对话列表 we = hw.EditControl(SubName="有人@我", searchDepth=5) # 查找新对话 if we.Exists(0): 群对象 = we.GetParentControl().GetParentControl().GetParentControl().GetParentControl() 群名 = 群对象.Name print("聊天群对象:" + 群名) we.Click(simulateMove=False) last_msg = wx.ListControl(Name="消息").GetChildren()[-1].Name # 获取最新消息 last_msg = last_msg[7:len(last_msg)] # 将@信息删除,注意这里要和你自身的微信名相匹配,尤其注意微信被@后会多一个‘\u2005’的字符 # print(wx.ListControl(Name="消息").GetChildren()[-1]) # 感兴趣的可以用这行命令去看下原始消息文本 # print(last_msg == '权限') # 建议先在群里发送”权限“两个字,用这行命令比对一下 print("获得群消息:" + last_msg) 用户配置(群名, True) response = getOpenAiMsg(群名, last_msg) # 将收到的信息转发给OpenAi print("你的回复:\n" + response) print( '已消耗标记:' + str(info[群名]['已使用Token数']) + "\n--------------------------------------------------\n") # 输入回复信息 pyperclip.copy(response) # 复制信息 pyautogui.hotkey('ctrl', 'v') # 粘贴 pyautogui.press('enter') # 回车 # 隐藏对话 群对象.RightClick() # 隐藏权限 右键菜单 = ui.MenuControl(ClassName="CMenuWnd") if 右键菜单.Exists(3, 0.1): 右键菜单.TextControl(Name="不显示聊天").Click()print("======================================\n开始\n======================================\n")while True: 普通用户回复() 群聊用户回复() # 记得把群消息接收设置为消息免打扰 time.sleep(0.2)
4.演示效果
好友对话,发送”权限“时不会触发chatgpt,而是查看用户对应权限
好友对话,可以看到能正确触发上下文联系
群聊,从成本考虑,群聊一般不设置上下文联系。
总结
本文仅仅简单介绍了uiautomation和openai的一些相关知识,以及相应的实现代码,后续考虑把这个程序直接部署在服务器上长期运行,可惜在阿里云、腾讯云等国内厂家产品中,可部署的境外云服务器并不便宜,境内服务器又需要挂代理,实际体验下来还是挺受网络波动影响,快的时候可以秒回,慢的时候要等十几秒钟。
最后,编程不易,文章也写的幸苦,为了踩坑颇花了一些钱,这里求个捐助,随便大家给多少~