成为海王的日子——我做了一个微信自动聊天的工具

4,872 阅读3分钟

一直幻想着能够成为一个海王,于是做了一个微信自动聊天的工具。

测试微信版本:wechat 3.9.12.17

采用技术:

  • Bmob后端云(AI对话和存储需要自动聊天的人和prompt)
  • uiautomation
  • pyautogui

开发语言:

  • python,conda环境下运行

最终的效果大家可以看B站:www.bilibili.com/video/BV1yK…

一、获取微信对话内容

这里采用了网上的一些开源项目进行修改,写了一个自己的WeChat控制类,采用的是uiautomation和pyautogui组件进行UI的控制,模拟人的操作。

WeChat控制类的内容比较多,为了方便阅读,这里只呈现一部分,有需要的朋友可以联系我获取。

class WeChat:
    def __init__(self, path, locale="zh-CN"):
        # 微信打开路径
        self.path = path

        # 用于复制内容到剪切板
        self.app = QApplication([])

        self.lc = WeChatLocale(locale)

    # 鼠标移动到控件上
    def move(self,element):
        x, y = element.GetPosition()
        auto.SetCursorPos(x, y)

    # 鼠标快速点击控件
    def click(self,element):
        x, y = element.GetPosition()
        auto.Click(x, y)

    # 鼠标右键点击控件
    def right_click(self,element):
        x, y = element.GetPosition()
        auto.RightClick(x, y)


    # 鼠标快速点击两下控件
    def double_click(self,element):
        x, y = element.GetPosition()
        auto.SetCursorPos(x, y)
        element.DoubleClick()


    # 打开微信客户端
    def open_wechat(self):
        subprocess.Popen(self.path)

    # 搜寻微信客户端控件
    def get_wechat(self):
        return auto.WindowControl(Depth=1, Name=self.lc.weixin)

    # 防止微信长时间挂机导致掉线
    def prevent_offline(self):
        self.open_wechat()
        self.get_wechat()

        search_box = auto.EditControl(Depth=8, Name=self.lc.search)
        self.click(search_box)

    # 搜索指定用户
    def get_contact(self, name):
        self.open_wechat()
        self.get_wechat()

        search_box = auto.EditControl(Depth=8, Name=self.lc.search)
        self.click(search_box)

        pyperclip.copy(name)
        auto.SendKeys("{Ctrl}v")

        # 等待客户端搜索联系人
        time.sleep(0.3)
        search_box.SendKeys("{enter}")

    # 鼠标移动到发送按钮处点击发送消息
    def press_enter(self):
        # 获取发送按钮
        send_button = auto.ButtonControl(Depth=15, Name=self.lc.send)
        self.click(send_button)
        
    # 检测微信发新消息的用户
    def check_new_user(self):
        self.open_wechat()
        self.get_wechat()

        users = []

        # 获取左侧聊天按钮
        chat_btn = auto.ButtonControl(Name=self.lc.chats)
        self.double_click(chat_btn)

        # 持续点击聊天按钮,直到获取完全部新消息
        item = auto.ListItemControl(Depth=10)
        prev_name = item.ButtonControl().Name
        while True:
            # 判断该联系人是否有新消息
            pane_control = item.PaneControl()
            if len(pane_control.GetChildren()) == 3:
                users.append(item.ButtonControl().Name)

            self.click(item)

            # 跳转到下一个新消息
            self.double_click(chat_btn)
            item = auto.ListItemControl(Depth=10)

            # 已经完成遍历,退出循环
            if prev_name == item.ButtonControl().Name:
                break

            prev_name = item.ButtonControl().Name

        return users

二、获取需要自动聊天的人和对应的prompt

这部分信息我存储在Bmob后端云上面,对应的表结构(表名为:autochat,创建的字段为:nameprompt)和测试的内容如下:

image.png

获取信息的时候,我采用了子线程的方式,每隔300秒获取一次新的需要自动对话的微信和对应的prompt,代码如下:

# Bmob对象
bmob = Bmob(config['bmob_appid'], config['bmob_secret'])

# 存储从Bmob后端云获取到的自动对话的微信名和对应的prompt
name_prompts = {}

# 从Bmob后端云获取自动聊天的微信名和prompt
def get_user_prompt():
    users = bmob.findObjects('autochat')
    name_prompts.clear()
    for user in users:
        name_prompts[user.name] = user.prompt

# 每隔5分钟获取一次要自动聊天微信名称和对应的prompt
def run_with_interval():
    while True:
        get_user_prompt()
        time.sleep(300)

if __name__ == "__main__":
    t = threading.Thread(target=run_with_interval)
    t.start()

三、组装对话上下文和自动对话

这里主要用到了WeChat类的获取新对话人名字获取某个人历史聊天记录的方法,和Bmob后端云的AI功能,具体代码如下:

# 执行自动聊天功能
def main():
    wechat = WeChat(path=config['wechat_path'])
    # 创建一个锁
    lock = threading.Lock()

    while True:
        try:
            comtypes.CoInitialize()
            # 确保微信操作的线程安全
            with lock:
                wechat.click(auto.ButtonControl(Name=wechat.lc.facorites))
                users = wechat.check_new_user()
                if len(users) <= 0:
                    time.sleep(5)
                    print("暂时没有新消息")
                    continue

                for user in users:
                    if user not in name_prompts.keys():
                        time.sleep(5)
                        print(f"{user}不在需要自动问答的名单上")
                        continue
                    else:
                        # 获取聊天记录,30这个数字可以调整,越大的话,AI会越了解你,但消耗的token也越多
                        msgList = wechat.get_dialogs(user, 30)
                        if len(msgList) <= 0:
                            continue
                        
                        # 组装上下文对话记录
                        ai = []
                        ai.append({"content": name_prompts[user], "role": "system"})
                        for msg in msgList:
                            chatmsg = msg[2].replace('[动画表情]','')
                            if chatmsg=='':
                                continue

                            if msg[1] == user:
                                ai.append({"content": chatmsg, "role": "user"})
                            else:
                                ai.append({"content": chatmsg, "role": "assistant"})

                        bmob.connectAI()
                        to = bmob.chat2(ai)
                        bmob.closeAI()
                        print('ai:'+to)

                        if to != "":
                            wechat.send_msg(user, to)
        except Exception as e:
            print(e)