自学内容网 自学内容网

【从零开始】基于AI大模型的微信聊天机器人实现-COZE

前言

前阵子,我分享了把大模型接入微信机器人的教程(http://t.csdnimg.cn/XBZyb),该教程基于 chatgpt-on-wechat项目,其中用到了 LinkAI、智谱 AI 这两个平台的模型。在后续的测试中,发现存在一些问题,比如智谱 AI 知识库的调用存在问题,有时击中率很低。

于是我停用了这个机器人,并寻找新的解决办法。直到发现了 coze(字节推出的 AI 机器人和智能体创建平台),便迫不及待地进行接入尝试。结果发现 coze 非常好用,也很符合我的使用场景。

因此,写下这篇文章来记录从 0 搭建属于自己的微信群聊 AI 机器人的部署过程,包括服务器购买与配置、项目部署、接入 coze、修改 coze 配置、接入知识库、插件安装等。

原本计划将此篇文章分为上下两篇,最后还是决定直接完成整篇,篇幅虽长,但操作相对简单,跟着操作基本没问题。

本篇总体分两大块:

  • chatgpt-on-wechat项目的部署
  • Coze机器人配置

chatgpt-on-wechat 项目的部署有两种方式:

1. 源码部署。

2. 我已制作了docker镜像,可一键部署。

Docker镜像获取:

c9d252d6c546419798a870adf532fec3.png

公众号回复(注意大小写):ChatonWeChat

什么是Coze?

Coze 是一个面向所有人的下一代 AI 应用程序和聊天机器人开发平台。它允许用户轻松创建各种聊天机器人,无论用户是否具有编程经验。用户可以将自己的聊天机器人部署和发布到不同的社交平台,例如豆包、飞书、微信等。Coze 提供了丰富的功能,包括角色和提示、插件、知识库、开场对话、预览和调试、工作流等,用户还可以创建自己的插件。此外,Coze 还有一个 Bot 商店,展示各种功能的开源机器人,供用户浏览和学习。

注册coze

进入官网

来到coze官网

这里我用的是国内的coze。

如果你的业务是海外的,建议用国外的coze,这里不过多讲述。

注册

获取APIkey

我们需要获取APIKey调用coze大模型接口。

注册好后点击主页下方的扣子API

点击API令牌选项

添加新令牌

配置个人令牌。

  • 名称:随便起,这里我保持默认的名称。
  • 过期时间:永久有效。
  • 选择指定团队空间:Personal
  • 权限:全部勾选上。

最后点击确认。

创建成功,这里生成的令牌需要保存好!后面会用到,点击复制按钮复制令牌。

创建Bot

回到主页点击左边创建Bot

  • 工作空间选择默认的Personal
  • Bot名称填写你自己定义的
  • Bot功能介绍随便填

填写完成后点击确认

创建成功,进入配置界面。

获取BotID

先不做详细配置,直接点击发布。

开场白设置直接跳过,我们不需要。

发布记录这里随意填,我填写的是版本号。

选择发布平台:这里勾选扣子Bot商店、Bot as API。(如果你获取APIkey这一步没做的话,这里是看不到Bot as API这个选项的)

然后点击发布,可以看到成功发布。

点击复制Bot链接

链接如下:

https://www.coze.cn/store/bot/7388292182391930906?bot_id=true

其中7388292182391930906就是BotID,将BotID保存好,后面我们会用到。

这里需要注意一点:

每次对Bot进行修改,发布后的BotID都会变化!

注册云服务器

云服务器商有很多,这里我选择阿里云进行部署,可能价格不是最便宜的,这个需自行比对。

云服务器低价长效特价精选

每个用户可购买一次,2核2G、3M固定带宽配置完全够用。

配置参考

这里简单的说下购买界面的配置,方便大家理解。

镜像选择CentOS 7.9 64位

云盘40G

公网IP勾选,按照固定带宽计费,最低带宽1Mbps。

开通IPV4端口/协议这里都勾选

登录凭证这里选择自定义密码,登录密码保存好,用于后面连接服务器用。

云服务器配置

安装宝塔面板

首先需要安装服务器的管理界面,这里选择安装宝塔

连接时提示需要登录实例,输入密码(创建实例时设置的密码)

宝塔安装命令

yum install -y wget && wget -O install.sh https://download.bt.cn/install/install_6.0.sh && sh install.sh ed8484bec

安装完成,将红框里的链接以及账号密码保存下来。

注意看!这里提示需要开放对应的端口号。我们需要去阿里云安全组里配置对应的端口号。

配置安全组规则

点击管理规则

点击手动添加(这里入方向有很多的端口号是因为我这个服务器是之前就买的,然后闲置下来的,入方向的端口号是之前加上的,这里无需跟我一样添加)

这里添加808013695端口。8080端口是我们后面要部署项目所用到的端口号,是固定的。13695是宝塔面板访问需要开通的端口号,这里每个人生成的端口号不一样。注意你的端口号!

保存成功

宝塔配置

进入宝塔

安全组设置完毕后,进入到刚才保存的宝塔链接,选择外网面板地址,复制到浏览器中打开。

进入后需要登录,填入刚才生成的username和password

绑定宝塔官网账号

安装套件

首次进入后会推荐安装套件,这里选择LNMP

然后这个界面不要关闭,耐心等待安装完成

安装python

点击网站--python项目--python版本管理

python版本选择3.9.7(这里最好在python3.10以下,chatgpt-on-wechat这个项目python3.10以上可能会有一些问题)

等待安装完毕

项目部署

源码部署

github下载项目源代码

https://github.com/zhayujie/chatgpt-on-wechat

截至文章写的时候,版本是1.6.8

来到宝塔面板--文件--路径/www/wwwroot

将下载的压缩包上传

右键解压

然后来到网站--python项目--添加python项目

项目路径:选择刚才解压后的文件夹路径。

项目名称:保持默认。

运行文件:选择项目文件夹内的app.py脚本

项目端口:8080(勾选后面的放行端口)

Python版本:刚才已安装的3.9.7

框架:选择python

运行方式:python

安装依赖包:这里自动会选择项目文件夹内的requirements.txt文件

点击提交。

耐心等待,会看到创建完毕。

记得来到阿里云实例这里修改安全组

管理规则

入方向这里选择手动添加

端口范围设置8080

源设置0.0.0.0

然后点击保存

打开终端。如果弹出输入ssh密钥,就输入创建服务器时的密码。

终端界面输入

pip3 install -r requirements.txt

安装完毕

接着安装可选组件

pip3 install -r requirements-optional.txt

安装完毕

Docker镜像部署

来到宝塔的Docker界面,如果是新建的服务器是没有安装docker服务的。

点击安装。

安装完毕,来到宝塔的/www/wwwroot根目录。

将镜像上传

导入镜像,路径选择刚才上传的路径/www/wwwroot

导入成功

来到容器--添加容器

容器名称:随意填

镜像:选择刚才导入的镜像

端口:8080(记得检查服务器的安全组规则)

直接选择创建。

创建成功

点击日志

可以看到项目已经正常运行并生成了微信登录二维码,这时不要急着去扫,还需要修改下项目配置。

到这里为止,使用docker镜像部署项目已经完成了,接下来跟着修改COW配置即可。这里红框区域简单框选出需要修改的地方,具体修改的含义在下面会有讲到。

修改完后重启项目。

再次扫码登录即可。

修改COW

这里说下,如果你是docker镜像部署的,修改支持COZE这一步直接省略,无需修改。直接到修改配置这步去修改config.json

修改支持COZE

/www/wwwroot/chatgpt-on-wechat-1.6.8文件夹内,config.py文件第177行后添加

    "model": "coze",
  "coze_api_base": "https://api.coze.cn/open_api/v2",
  "coze_api_key": "",
  "coze_bot_id": "",

如下:

config.py完整代码

# encoding:utf-8

import json
import logging
import os
import pickle
import copy

from common.log import logger

# 将所有可用的配置项写在字典里, 请使用小写字母
# 此处的配置值无实际意义,程序不会读取此处的配置,仅用于提示格式,请将配置加入到config.json中
available_setting = {
    # openai api配置
    "open_ai_api_key": "",  # openai api key
    # openai apibase,当use_azure_chatgpt为true时,需要设置对应的api base
    "open_ai_api_base": "https://api.openai.com/v1",
    "proxy": "",  # openai使用的代理
    # chatgpt模型, 当use_azure_chatgpt为true时,其名称为Azure上model deployment名称
    "model": "gpt-3.5-turbo",  # 可选择: gpt-4o, gpt-4-turbo, claude-3-sonnet, wenxin, moonshot, qwen-turbo, xunfei, glm-4, minimax, gemini等模型,全部可选模型详见common/const.py文件
    "bot_type": "",  # 可选配置,使用兼容openai格式的三方服务时候,需填"chatGPT"。bot具体名称详见common/const.py文件列出的bot_type,如不填根据model名称判断,
    "use_azure_chatgpt": False,  # 是否使用azure的chatgpt
    "azure_deployment_id": "",  # azure 模型部署名称
    "azure_api_version": "",  # azure api版本
    # Bot触发配置
    "single_chat_prefix": ["bot", "@bot"],  # 私聊时文本需要包含该前缀才能触发机器人回复
    "single_chat_reply_prefix": "[bot] ",  # 私聊时自动回复的前缀,用于区分真人
    "single_chat_reply_suffix": "",  # 私聊时自动回复的后缀,\n 可以换行
    "group_chat_prefix": ["@bot"],  # 群聊时包含该前缀则会触发机器人回复
    "group_chat_reply_prefix": "",  # 群聊时自动回复的前缀
    "group_chat_reply_suffix": "",  # 群聊时自动回复的后缀,\n 可以换行
    "group_chat_keyword": [],  # 群聊时包含该关键词则会触发机器人回复
    "group_at_off": False,  # 是否关闭群聊时@bot的触发
    "group_name_white_list": ["ChatGPT测试群", "ChatGPT测试群2"],  # 开启自动回复的群名称列表
    "group_name_keyword_white_list": [],  # 开启自动回复的群名称关键词列表
    "group_chat_in_one_session": ["ChatGPT测试群"],  # 支持会话上下文共享的群名称
    "nick_name_black_list": [],  # 用户昵称黑名单
    "group_welcome_msg": "",  # 配置新人进群固定欢迎语,不配置则使用随机风格欢迎
    "trigger_by_self": False,  # 是否允许机器人触发
    "text_to_image": "dall-e-2",  # 图片生成模型,可选 dall-e-2, dall-e-3
    # Azure OpenAI dall-e-3 配置
    "dalle3_image_style": "vivid", # 图片生成dalle3的风格,可选有 vivid, natural
    "dalle3_image_quality": "hd", # 图片生成dalle3的质量,可选有 standard, hd
    # Azure OpenAI DALL-E API 配置, 当use_azure_chatgpt为true时,用于将文字回复的资源和Dall-E的资源分开.
    "azure_openai_dalle_api_base": "", # [可选] azure openai 用于回复图片的资源 endpoint,默认使用 open_ai_api_base
    "azure_openai_dalle_api_key": "", # [可选] azure openai 用于回复图片的资源 key,默认使用 open_ai_api_key
    "azure_openai_dalle_deployment_id":"", # [可选] azure openai 用于回复图片的资源 deployment id,默认使用 text_to_image
    "image_proxy": True,  # 是否需要图片代理,国内访问LinkAI时需要
    "image_create_prefix": ["画", "看", "找"],  # 开启图片回复的前缀
    "concurrency_in_session": 1,  # 同一会话最多有多少条消息在处理中,大于1可能乱序
    "image_create_size": "256x256",  # 图片大小,可选有 256x256, 512x512, 1024x1024 (dall-e-3默认为1024x1024)
    "group_chat_exit_group": False,
    # chatgpt会话参数
    "expires_in_seconds": 3600,  # 无操作会话的过期时间
    # 人格描述
    "character_desc": "你是ChatGPT, 一个由OpenAI训练的大型语言模型, 你旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。",
    "conversation_max_tokens": 1000,  # 支持上下文记忆的最多字符数
    # chatgpt限流配置
    "rate_limit_chatgpt": 20,  # chatgpt的调用频率限制
    "rate_limit_dalle": 50,  # openai dalle的调用频率限制
    # chatgpt api参数 参考https://platform.openai.com/docs/api-reference/chat/create
    "temperature": 0.9,
    "top_p": 1,
    "frequency_penalty": 0,
    "presence_penalty": 0,
    "request_timeout": 180,  # chatgpt请求超时时间,openai接口默认设置为600,对于难问题一般需要较长时间
    "timeout": 120,  # chatgpt重试超时时间,在这个时间内,将会自动重试
    # Baidu 文心一言参数
    "baidu_wenxin_model": "eb-instant",  # 默认使用ERNIE-Bot-turbo模型
    "baidu_wenxin_api_key": "",  # Baidu api key
    "baidu_wenxin_secret_key": "",  # Baidu secret key
    # 讯飞星火API
    "xunfei_app_id": "",  # 讯飞应用ID
    "xunfei_api_key": "",  # 讯飞 API key
    "xunfei_api_secret": "",  # 讯飞 API secret
    # claude 配置
    "claude_api_cookie": "",
    "claude_uuid": "",
    # claude api key
    "claude_api_key": "",
    # 通义千问API, 获取方式查看文档 https://help.aliyun.com/document_detail/2587494.html
    "qwen_access_key_id": "",
    "qwen_access_key_secret": "",
    "qwen_agent_key": "",
    "qwen_app_id": "",
    "qwen_node_id": "",  # 流程编排模型用到的id,如果没有用到qwen_node_id,请务必保持为空字符串
    # 阿里灵积(通义新版sdk)模型api key
    "dashscope_api_key": "",
    # Google Gemini Api Key
    "gemini_api_key": "",
    # wework的通用配置
    "wework_smart": True,  # 配置wework是否使用已登录的企业微信,False为多开
    # 语音设置
    "speech_recognition": True,  # 是否开启语音识别
    "group_speech_recognition": False,  # 是否开启群组语音识别
    "voice_reply_voice": False,  # 是否使用语音回复语音,需要设置对应语音合成引擎的api key
    "always_reply_voice": False,  # 是否一直使用语音回复
    "voice_to_text": "openai",  # 语音识别引擎,支持openai,baidu,google,azure
    "text_to_voice": "openai",  # 语音合成引擎,支持openai,baidu,google,pytts(offline),ali,azure,elevenlabs,edge(online)
    "text_to_voice_model": "tts-1",
    "tts_voice_id": "alloy",
    # baidu 语音api配置, 使用百度语音识别和语音合成时需要
    "baidu_app_id": "",
    "baidu_api_key": "",
    "baidu_secret_key": "",
    # 1536普通话(支持简单的英文识别) 1737英语 1637粤语 1837四川话 1936普通话远场
    "baidu_dev_pid": 1536,
    # azure 语音api配置, 使用azure语音识别和语音合成时需要
    "azure_voice_api_key": "",
    "azure_voice_region": "japaneast",
    # elevenlabs 语音api配置
    "xi_api_key": "",  # 获取ap的方法可以参考https://docs.elevenlabs.io/api-reference/quick-start/authentication
    "xi_voice_id": "",  # ElevenLabs提供了9种英式、美式等英语发音id,分别是“Adam/Antoni/Arnold/Bella/Domi/Elli/Josh/Rachel/Sam”
    # 服务时间限制,目前支持itchat
    "chat_time_module": False,  # 是否开启服务时间限制
    "chat_start_time": "00:00",  # 服务开始时间
    "chat_stop_time": "24:00",  # 服务结束时间
    # 翻译api
    "translate": "baidu",  # 翻译api,支持baidu
    # baidu翻译api的配置
    "baidu_translate_app_id": "",  # 百度翻译api的appid
    "baidu_translate_app_key": "",  # 百度翻译api的秘钥
    # itchat的配置
    "hot_reload": False,  # 是否开启热重载
    # wechaty的配置
    "wechaty_puppet_service_token": "",  # wechaty的token
    # wechatmp的配置
    "wechatmp_token": "",  # 微信公众平台的Token
    "wechatmp_port": 8080,  # 微信公众平台的端口,需要端口转发到80或443
    "wechatmp_app_id": "",  # 微信公众平台的appID
    "wechatmp_app_secret": "",  # 微信公众平台的appsecret
    "wechatmp_aes_key": "",  # 微信公众平台的EncodingAESKey,加密模式需要
    # wechatcom的通用配置
    "wechatcom_corp_id": "",  # 企业微信公司的corpID
    # wechatcomapp的配置
    "wechatcomapp_token": "",  # 企业微信app的token
    "wechatcomapp_port": 9898,  # 企业微信app的服务端口,不需要端口转发
    "wechatcomapp_secret": "",  # 企业微信app的secret
    "wechatcomapp_agent_id": "",  # 企业微信app的agent_id
    "wechatcomapp_aes_key": "",  # 企业微信app的aes_key
    # 飞书配置
    "feishu_port": 80,  # 飞书bot监听端口
    "feishu_app_id": "",  # 飞书机器人应用APP Id
    "feishu_app_secret": "",  # 飞书机器人APP secret
    "feishu_token": "",  # 飞书 verification token
    "feishu_bot_name": "",  # 飞书机器人的名字
    # 钉钉配置
    "dingtalk_client_id": "",  # 钉钉机器人Client ID 
    "dingtalk_client_secret": "",  # 钉钉机器人Client Secret
    "dingtalk_card_enabled": False,
    
    # chatgpt指令自定义触发词
    "clear_memory_commands": ["#清除记忆"],  # 重置会话指令,必须以#开头
    # channel配置
    "channel_type": "",  # 通道类型,支持:{wx,wxy,terminal,wechatmp,wechatmp_service,wechatcom_app,dingtalk}
    "subscribe_msg": "",  # 订阅消息, 支持: wechatmp, wechatmp_service, wechatcom_app
    "debug": False,  # 是否开启debug模式,开启后会打印更多日志
    "appdata_dir": "",  # 数据目录
    # 插件配置
    "plugin_trigger_prefix": "$",  # 规范插件提供聊天相关指令的前缀,建议不要和管理员指令前缀"#"冲突
    # 是否使用全局插件配置
    "use_global_plugin_config": False,
    "max_media_send_count": 3,  # 单次最大发送媒体资源的个数
    "media_send_interval": 1,  # 发送图片的事件间隔,单位秒
    # 智谱AI 平台配置
    "zhipu_ai_api_key": "",
    "zhipu_ai_api_base": "https://open.bigmodel.cn/api/paas/v4",
    "moonshot_api_key": "",
    "moonshot_base_url": "https://api.moonshot.cn/v1/chat/completions",
    # LinkAI平台配置
    "use_linkai": False,
    "linkai_api_key": "",
    "linkai_app_code": "",
    "linkai_api_base": "https://api.link-ai.tech",  # linkAI服务地址
    "Minimax_api_key": "",
    "Minimax_group_id": "",
    "Minimax_base_url": "",
    "model": "coze",
  "coze_api_base": "https://api.coze.cn/open_api/v2",
  "coze_api_key": "",
  "coze_bot_id": "",
}


class Config(dict):
    def __init__(self, d=None):
        super().__init__()
        if d is None:
            d = {}
        for k, v in d.items():
            self[k] = v
        # user_datas: 用户数据,key为用户名,value为用户数据,也是dict
        self.user_datas = {}

    def __getitem__(self, key):
        if key not in available_setting:
            raise Exception("key {} not in available_setting".format(key))
        return super().__getitem__(key)

    def __setitem__(self, key, value):
        if key not in available_setting:
            raise Exception("key {} not in available_setting".format(key))
        return super().__setitem__(key, value)

    def get(self, key, default=None):
        try:
            return self[key]
        except KeyError as e:
            return default
        except Exception as e:
            raise e

    # Make sure to return a dictionary to ensure atomic
    def get_user_data(self, user) -> dict:
        if self.user_datas.get(user) is None:
            self.user_datas[user] = {}
        return self.user_datas[user]

    def load_user_datas(self):
        try:
            with open(os.path.join(get_appdata_dir(), "user_datas.pkl"), "rb") as f:
                self.user_datas = pickle.load(f)
                logger.info("[Config] User datas loaded.")
        except FileNotFoundError as e:
            logger.info("[Config] User datas file not found, ignore.")
        except Exception as e:
            logger.info("[Config] User datas error: {}".format(e))
            self.user_datas = {}

    def save_user_datas(self):
        try:
            with open(os.path.join(get_appdata_dir(), "user_datas.pkl"), "wb") as f:
                pickle.dump(self.user_datas, f)
                logger.info("[Config] User datas saved.")
        except Exception as e:
            logger.info("[Config] User datas error: {}".format(e))


config = Config()


def drag_sensitive(config):
    try:
        if isinstance(config, str):
            conf_dict: dict = json.loads(config)
            conf_dict_copy = copy.deepcopy(conf_dict)
            for key in conf_dict_copy:
                if "key" in key or "secret" in key:
                    if isinstance(conf_dict_copy[key], str):
                        conf_dict_copy[key] = conf_dict_copy[key][0:3] + "*" * 5 + conf_dict_copy[key][-3:]
            return json.dumps(conf_dict_copy, indent=4)

        elif isinstance(config, dict):
            config_copy = copy.deepcopy(config)
            for key in config:
                if "key" in key or "secret" in key:
                    if isinstance(config_copy[key], str):
                        config_copy[key] = config_copy[key][0:3] + "*" * 5 + config_copy[key][-3:]
            return config_copy
    except Exception as e:
        logger.exception(e)
        return config
    return config


def load_config():
    global config
    config_path = "./config.json"
    if not os.path.exists(config_path):
        logger.info("配置文件不存在,将使用config-template.json模板")
        config_path = "./config-template.json"

    config_str = read_file(config_path)
    logger.debug("[INIT] config str: {}".format(drag_sensitive(config_str)))

    # 将json字符串反序列化为dict类型
    config = Config(json.loads(config_str))

    # override config with environment variables.
    # Some online deployment platforms (e.g. Railway) deploy project from github directly. So you shouldn't put your secrets like api key in a config file, instead use environment variables to override the default config.
    for name, value in os.environ.items():
        name = name.lower()
        if name in available_setting:
            logger.info("[INIT] override config by environ args: {}={}".format(name, value))
            try:
                config[name] = eval(value)
            except:
                if value == "false":
                    config[name] = False
                elif value == "true":
                    config[name] = True
                else:
                    config[name] = value

    if config.get("debug", False):
        logger.setLevel(logging.DEBUG)
        logger.debug("[INIT] set log level to DEBUG")

    logger.info("[INIT] load config: {}".format(drag_sensitive(config)))

    config.load_user_datas()


def get_root():
    return os.path.dirname(os.path.abspath(__file__))


def read_file(path):
    with open(path, mode="r", encoding="utf-8") as f:
        return f.read()


def conf():
    return config


def get_appdata_dir():
    data_path = os.path.join(get_root(), conf().get("appdata_dir", ""))
    if not os.path.exists(data_path):
        logger.info("[INIT] data path not exists, create it: {}".format(data_path))
        os.makedirs(data_path)
    return data_path


def subscribe_msg():
    trigger_prefix = conf().get("single_chat_prefix", [""])[0]
    msg = conf().get("subscribe_msg", "")
    return msg.format(trigger_prefix=trigger_prefix)


# global plugin config
plugin_config = {}


def write_plugin_config(pconf: dict):
    """
    写入插件全局配置
    :param pconf: 全量插件配置
    """
    global plugin_config
    for k in pconf:
        plugin_config[k.lower()] = pconf[k]


def pconf(plugin_name: str) -> dict:
    """
    根据插件名称获取配置
    :param plugin_name: 插件名称
    :return: 该插件的配置项
    """
    return plugin_config.get(plugin_name.lower())


# 全局配置,用于存放全局生效的状态
global_config = {"admin_users": []}

/www/wwwroot/chatgpt-on-wechat-1.6.8/bot下创建 一个新文件夹,命名为“bytedance”。

然后在/www/wwwroot/chatgpt-on-wechat-1.6.8/bot/bytedance下,上传bytedance_coze_bot.py文件

bytedance_coze_bot.py如下

# encoding:utf-8

import time
from typing import List, Tuple

import requests
from requests import Response

from bot.bot import Bot
from bot.chatgpt.chat_gpt_session import ChatGPTSession
from bot.session_manager import SessionManager
from bridge.context import ContextType
from bridge.reply import Reply, ReplyType
from common.log import logger
from config import conf

class ByteDanceCozeBot(Bot):
    def __init__(self):
        super().__init__()
        self.sessions = SessionManager(ChatGPTSession, model=conf().get("model") or "coze")

    def reply(self, query, context=None):
        # acquire reply content
        if context.type == ContextType.TEXT:
            logger.info("[COZE] query={}".format(query))

            session_id = context["session_id"]
            session = self.sessions.session_query(query, session_id)
            logger.debug("[COZE] session query={}".format(session.messages))
            reply_content, err = self._reply_text(session_id, session)
            if err is not None:
                logger.error("[COZE] reply error={}".format(err))
                return Reply(ReplyType.ERROR, "我暂时遇到了一些问题,请您稍后重试~")
            logger.debug(
                "[COZE] new_query={}, session_id={}, reply_cont={}, completion_tokens={}".format(
                    session.messages,
                    session_id,
                    reply_content["content"],
                    reply_content["completion_tokens"],
                )
            )
            return Reply(ReplyType.TEXT, reply_content["content"])
        else:
            reply = Reply(ReplyType.ERROR, "Bot不支持处理{}类型的消息".format(context.type))
            return reply

    def _get_api_base_url(self):
        return conf().get("coze_api_base", "https://api.coze.cn/open_api/v2")

    def _get_headers(self):
        return {
            'Authorization': f"Bearer {conf().get('coze_api_key', '')}"
        }

    def _get_payload(self, user: str, query: str, chat_history: List[dict]):
        return {
            'bot_id': conf().get('coze_bot_id'),
            "user": user,
            "query": query,
            "chat_history": chat_history,
            "stream": False
        }
    def _reply_text(self, session_id: str, session: ChatGPTSession, retry_count=0):
        try:
            query, chat_history = self._convert_messages_format(session.messages)
            base_url = self._get_api_base_url()
            chat_url = f'{base_url}/chat'
            headers = self._get_headers()
            payload = self._get_payload(session.session_id, query, chat_history)
            response = requests.post(chat_url, headers=headers, json=payload)
            if response.status_code != 200:
                error_info = f"[COZE] response text={response.text} status_code={response.status_code}"
                logger.warn(error_info)
                return None, error_info
            answer, err = self._get_completion_content(response)
            if err is not None:
                return None, err
            completion_tokens, total_tokens = self._calc_tokens(session.messages, answer)
            return {
                "total_tokens": total_tokens,
                "completion_tokens": completion_tokens,
                "content": answer
            }, None
        except Exception as e:
            if retry_count < 2:
                time.sleep(3)
                logger.warn(f"[COZE] Exception: {repr(e)} 第{retry_count + 1}次重试")
                return self._reply_text(session_id, session, retry_count + 1)
            else:
                return None, f"[COZE] Exception: {repr(e)} 超过最大重试次数"

    def _convert_messages_format(self, messages) -> Tuple[str, List[dict]]:
        # [
        #     {"role":"user","content":"你好","content_type":"text"},
        #     {"role":"assistant","type":"answer","content":"你好,请问有什么可以帮助你的吗?","content_type":"text"}
        #  ]
        chat_history = []
        for message in messages:
            role = message.get('role')
            if role == 'user':
                content = message.get('content')
                chat_history.append({"role":"user", "content": content, "content_type":"text"})
            elif role == 'assistant':
                content = message.get('content')
                chat_history.append({"role":"assistant", "type":"answer", "content": content, "content_type":"text"})
            elif role =='system':
                # TODO: deal system message
                pass
        user_message = chat_history.pop()
        if user_message.get('role') != 'user' or user_message.get('content', '') == '':
            raise Exception('no user message')
        query = user_message.get('content')
        logger.debug("[COZE] converted coze messages: {}".format([item for item in chat_history]))
        logger.debug("[COZE] user content as query: {}".format(query))
        return query, chat_history

    def _get_completion_content(self, response: Response):
        json_response = response.json()
        if json_response['msg'] != 'success':
            return None, f"[COZE] Error: {json_response['msg']}"
        answer = None
        for message in json_response['messages']:
            if message.get('type') == 'answer':
                answer = message.get('content')
                break
        if not answer:
            return None, "[COZE] Error: empty answer"
        return answer, None

    def _calc_tokens(self, messages, answer):
        # 简单统计token
        completion_tokens = len(answer)
        prompt_tokens = 0
        for message in messages:
            prompt_tokens += len(message["content"])
        return completion_tokens, prompt_tokens + completion_tokens

3、/www/wwwroot/chatgpt-on-wechat-1.6.8/bot文件夹下,修改bot_factory.py文件。

    elif bot_type == const.COZE:
        from bot.bytedance.bytedance_coze_bot import ByteDanceCozeBot
        return ByteDanceCozeBot()

完整代码

"""
channel factory
"""
from common import const


def create_bot(bot_type):
    """
    create a bot_type instance
    :param bot_type: bot type code
    :return: bot instance
    """
    if bot_type == const.BAIDU:
        # 替换Baidu Unit为Baidu文心千帆对话接口
        # from bot.baidu.baidu_unit_bot import BaiduUnitBot
        # return BaiduUnitBot()
        from bot.baidu.baidu_wenxin import BaiduWenxinBot
        return BaiduWenxinBot()

    elif bot_type == const.CHATGPT:
        # ChatGPT 网页端web接口
        from bot.chatgpt.chat_gpt_bot import ChatGPTBot
        return ChatGPTBot()

    elif bot_type == const.OPEN_AI:
        # OpenAI 官方对话模型API
        from bot.openai.open_ai_bot import OpenAIBot
        return OpenAIBot()

    elif bot_type == const.CHATGPTONAZURE:
        # Azure chatgpt service https://azure.microsoft.com/en-in/products/cognitive-services/openai-service/
        from bot.chatgpt.chat_gpt_bot import AzureChatGPTBot
        return AzureChatGPTBot()

    elif bot_type == const.XUNFEI:
        from bot.xunfei.xunfei_spark_bot import XunFeiBot
        return XunFeiBot()

    elif bot_type == const.LINKAI:
        from bot.linkai.link_ai_bot import LinkAIBot
        return LinkAIBot()

    elif bot_type == const.CLAUDEAI:
        from bot.claude.claude_ai_bot import ClaudeAIBot
        return ClaudeAIBot()
    elif bot_type == const.CLAUDEAPI:
        from bot.claudeapi.claude_api_bot import ClaudeAPIBot
        return ClaudeAPIBot()
    elif bot_type == const.QWEN:
        from bot.ali.ali_qwen_bot import AliQwenBot
        return AliQwenBot()
    elif bot_type == const.QWEN_DASHSCOPE:
        from bot.dashscope.dashscope_bot import DashscopeBot
        return DashscopeBot()
    elif bot_type == const.GEMINI:
        from bot.gemini.google_gemini_bot import GoogleGeminiBot
        return GoogleGeminiBot()

    elif bot_type == const.ZHIPU_AI:
        from bot.zhipuai.zhipuai_bot import ZHIPUAIBot
        return ZHIPUAIBot()

    elif bot_type == const.MOONSHOT:
        from bot.moonshot.moonshot_bot import MoonshotBot
        return MoonshotBot()
    
    elif bot_type == const.MiniMax:
        from bot.minimax.minimax_bot import MinimaxBot
        return MinimaxBot()
        
    elif bot_type == const.COZE:
        from bot.bytedance.bytedance_coze_bot import ByteDanceCozeBot
        return ByteDanceCozeBot()

    raise RuntimeError

/www/wwwroot/chatgpt-on-wechat-1.6.8/common文件夹下,修改Const.py文件

COZE = "coze"

完整代码

# bot_type
OPEN_AI = "openAI"
CHATGPT = "chatGPT"
BAIDU = "baidu"  # 百度文心一言模型
XUNFEI = "xunfei"
CHATGPTONAZURE = "chatGPTOnAzure"
LINKAI = "linkai"
CLAUDEAI = "claude"  # 使用cookie的历史模型
CLAUDEAPI= "claudeAPI"  # 通过Claude api调用模型
QWEN = "qwen"  # 旧版通义模型
QWEN_DASHSCOPE = "dashscope"  # 通义新版sdk和api key


GEMINI = "gemini"  # gemini-1.0-pro
ZHIPU_AI = "glm-4"
MOONSHOT = "moonshot"
MiniMax = "minimax"
COZE = "coze"

# model
CLAUDE3 = "claude-3-opus-20240229"
GPT35 = "gpt-3.5-turbo"
GPT35_0125 = "gpt-3.5-turbo-0125"
GPT35_1106 = "gpt-3.5-turbo-1106"

GPT_4o = "gpt-4o"
GPT4_TURBO = "gpt-4-turbo"
GPT4_TURBO_PREVIEW = "gpt-4-turbo-preview"
GPT4_TURBO_04_09 = "gpt-4-turbo-2024-04-09"
GPT4_TURBO_01_25 = "gpt-4-0125-preview"
GPT4_TURBO_11_06 = "gpt-4-1106-preview"
GPT4_VISION_PREVIEW = "gpt-4-vision-preview"

GPT4 = "gpt-4"
GPT4_32k = "gpt-4-32k"
GPT4_06_13 = "gpt-4-0613"
GPT4_32k_06_13 = "gpt-4-32k-0613"

WHISPER_1 = "whisper-1"
TTS_1 = "tts-1"
TTS_1_HD = "tts-1-hd"

WEN_XIN = "wenxin"
WEN_XIN_4 = "wenxin-4"

QWEN_TURBO = "qwen-turbo"
QWEN_PLUS = "qwen-plus"
QWEN_MAX = "qwen-max"

LINKAI_35 = "linkai-3.5"
LINKAI_4_TURBO = "linkai-4-turbo"
LINKAI_4o = "linkai-4o"

GEMINI_PRO = "gemini-1.0-pro"
GEMINI_15_flash = "gemini-1.5-flash"
GEMINI_15_PRO = "gemini-1.5-pro"

MODEL_LIST = [
              GPT35, GPT35_0125, GPT35_1106, "gpt-3.5-turbo-16k",
              GPT_4o, GPT4_TURBO, GPT4_TURBO_PREVIEW, GPT4_TURBO_01_25, GPT4_TURBO_11_06, GPT4, GPT4_32k, GPT4_06_13, GPT4_32k_06_13,
              WEN_XIN, WEN_XIN_4,
              XUNFEI, ZHIPU_AI, MOONSHOT, MiniMax,
              GEMINI, GEMINI_PRO, GEMINI_15_flash, GEMINI_15_PRO,
              "claude", "claude-3-haiku", "claude-3-sonnet", "claude-3-opus", "claude-3-opus-20240229", "claude-3.5-sonnet",
              "moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k",
              QWEN, QWEN_TURBO, QWEN_PLUS, QWEN_MAX,
              LINKAI_35, LINKAI_4_TURBO, LINKAI_4o
            ]

# channel
FEISHU = "feishu"
DINGTALK = "dingtalk"

/www/wwwroot/chatgpt-on-wechat-1.6.8/bridge下 ,修改bridge.py文件

完整代码

from bot.bot_factory import create_bot
from bridge.context import Context
from bridge.reply import Reply
from common import const
from common.log import logger
from common.singleton import singleton
from config import conf
from translate.factory import create_translator
from voice.factory import create_voice


@singleton
class Bridge(object):
    def __init__(self):
        self.btype = {
            "chat": const.CHATGPT,
            "voice_to_text": conf().get("voice_to_text", "openai"),
            "text_to_voice": conf().get("text_to_voice", "google"),
            "translate": conf().get("translate", "baidu"),
        }
        # 这边取配置的模型
        bot_type = conf().get("bot_type")
        if bot_type:
            self.btype["chat"] = bot_type
        else:
            model_type = conf().get("model") or const.GPT35
            if model_type in ["text-davinci-003"]:
                self.btype["chat"] = const.OPEN_AI
            if conf().get("use_azure_chatgpt", False):
                self.btype["chat"] = const.CHATGPTONAZURE
            if model_type in ["wenxin", "wenxin-4"]:
                self.btype["chat"] = const.BAIDU
            if model_type in ["xunfei"]:
                self.btype["chat"] = const.XUNFEI
            if model_type in [const.QWEN]:
                self.btype["chat"] = const.QWEN
            if model_type in [const.QWEN_TURBO, const.QWEN_PLUS, const.QWEN_MAX]:
                self.btype["chat"] = const.QWEN_DASHSCOPE
            if model_type and model_type.startswith("gemini"):
                self.btype["chat"] = const.GEMINI
            if model_type in [const.ZHIPU_AI]:
                self.btype["chat"] = const.ZHIPU_AI
            if model_type and model_type.startswith("claude-3"):
                self.btype["chat"] = const.CLAUDEAPI
            if model_type in [const.COZE]:
                self.btype["chat"] = const.COZE

            if model_type in ["claude"]:
                self.btype["chat"] = const.CLAUDEAI

            if model_type in ["moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k"]:
                self.btype["chat"] = const.MOONSHOT

            if model_type in ["abab6.5-chat"]:
                self.btype["chat"] = const.MiniMax

            if conf().get("use_linkai") and conf().get("linkai_api_key"):
                self.btype["chat"] = const.LINKAI
                if not conf().get("voice_to_text") or conf().get("voice_to_text") in ["openai"]:
                    self.btype["voice_to_text"] = const.LINKAI
                if not conf().get("text_to_voice") or conf().get("text_to_voice") in ["openai", const.TTS_1, const.TTS_1_HD]:
                    self.btype["text_to_voice"] = const.LINKAI

        self.bots = {}
        self.chat_bots = {}

    # 模型对应的接口
    def get_bot(self, typename):
        if self.bots.get(typename) is None:
            logger.info("create bot {} for {}".format(self.btype[typename], typename))
            if typename == "text_to_voice":
                self.bots[typename] = create_voice(self.btype[typename])
            elif typename == "voice_to_text":
                self.bots[typename] = create_voice(self.btype[typename])
            elif typename == "chat":
                self.bots[typename] = create_bot(self.btype[typename])
            elif typename == "translate":
                self.bots[typename] = create_translator(self.btype[typename])
        return self.bots[typename]

    def get_bot_type(self, typename):
        return self.btype[typename]

    def fetch_reply_content(self, query, context: Context) -> Reply:
        return self.get_bot("chat").reply(query, context)

    def fetch_voice_to_text(self, voiceFile) -> Reply:
        return self.get_bot("voice_to_text").voiceToText(voiceFile)

    def fetch_text_to_voice(self, text) -> Reply:
        return self.get_bot("text_to_voice").textToVoice(text)

    def fetch_translate(self, text, from_lang="", to_lang="en") -> Reply:
        return self.get_bot("translate").translate(text, from_lang, to_lang)

    def find_chat_bot(self, bot_type: str):
        if self.chat_bots.get(bot_type) is None:
            self.chat_bots[bot_type] = create_bot(bot_type)
        return self.chat_bots.get(bot_type)

    def reset_bot(self):
        """
        重置bot路由
        """
        self.__init__()

修改配置

来到项目根目录,找到config-template.json文件,这个文件是启动时的配置文件。

主要更改的是下面四行,可以直接清空原文件配置,把以下配置粘贴进你的config.json文件中。

  "model": "coze",
  "coze_api_base": "https://api.coze.cn/open_api/v2",
  "coze_api_key": "这里改成你的coze key",
  "coze_bot_id": "这里是你的botid",

完整代码

{
  "channel_type": "wx",
  "model": "coze",
  "coze_api_base": "https://api.coze.cn/open_api/v2",
  "coze_api_key": "这里改成你的coze key",
  "coze_bot_id": "这里是你的botid",
  "text_to_image": "dall-e-2",
  "voice_to_text": "openai",
  "text_to_voice": "openai",
  "proxy": "",
  "hot_reload": false,
  "single_chat_prefix": [
    "bot",
    "@bot"
  ],
  "single_chat_reply_prefix": "[bot] ",
  "group_chat_prefix": [
    "@bot"
  ],
  "group_name_white_list": [
    "ChatGPT测试群",
    "ChatGPT测试群2"
  ],
  "image_create_prefix": [
    "画"
  ],
  "speech_recognition": true,
  "group_speech_recognition": false,
  "voice_reply_voice": false,
  "conversation_max_tokens": 2500,
  "expires_in_seconds": 3600,
  "character_desc": "你是基于大语言模型的AI智能助手,旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。",
  "temperature": 0.7,
  "subscribe_msg": "感谢您的关注!\n这里是AI智能助手,可以自由对话。\n支持语音对话。\n支持图片输入。\n支持图片输出,画字开头的消息将按要求创作图片。\n支持tool、角色扮演和文字冒险等丰富的插件。\n输入{trigger_prefix}#help 查看详细指令。",
  "use_linkai": false,
  "linkai_api_key": "",
  "linkai_app_code": ""
}

运行项目

开启项目

来到python项目管理界面。

停止项目,我们通过终端的方式去开启,因为启动时需要获取0二维码进行登录

打开终端

输入下面命令

创建日志

touch nohup.out

运行app.py

nohup python3 app.py & tail -f nohup.out

运行成功会在终端生成一个二维码,拿你需要登录的微信进行扫码即可。

测试效果

可以看到项目运行正常,机器人正常回复。

这里回复的屋里哇啦跟我创建机器人时设置的功能介绍有关。删掉即可。

重启项目

如果你调试过程中需要关闭或者重启项目

输入查询命令

ps -ef | grep app.py | grep -v grep

关掉对应PID程序

kill -9 15230

比如这里程序的pid是20945,输入kill -9 20945关掉程序。

关于知识库

来到coze主页创建知识库

配置知识库选项

知识库有三种,文本格式、表格格式、照片类型。

不同类型上传时的处理不一样。

这里只说下文本格式的处理。

导入类型根据自己需要进行选择。

这是我准备好的知识库TXT,采用一问一答的形式。

将准备好的TXT上传

选择自动分段与清洗

点击确认,等待处理完成。

可以看到生成后的切分片段,将这段文本分成了2段,但这不是我想要的,我希望的是一问一答是一段。

点击重新分段

分段设置这里选择自定义,标识符我用了###符号。

并且对知识库TXT做了修改,在每一问前面加上标识符###

再次分段,这时就分为了8段,是我想要的。

来到机器人编排这里,知识选项这里点击+号

选择刚才新建的知识库

知识库设置这里默认是无召回回复的。

可以自定义一段回复,比如当用户提到的问题知识库里没有,就会回复你设定的语句。

测试下效果

问下知识库相关的内容

可以看到会按照我们知识库里的内容进行回复。

微信中的测试效果。

关于群聊

设置群聊

想要让机器人在群聊中进行回复,需要对config.json进行修改。修改其中的group_name_white_list群聊白名单。在这里填入你想要回复的群名。(机器人登录的微信必须在这个群聊内)

 "group_name_white_list": [
    "ChatGPT测试群",
    "ChatGPT测试群2"
  ],

设置欢迎语

当有新人加入群聊的时候,可以设置欢迎语。config.json文件中加入以下:

"group_welcome_msg": "欢迎加入本群,欢迎语!!",

config.json设置完毕后记得重启项目。

效果测试

关于日志

输出的日志在项目根目录的run.log文件内

人设调整

如果觉得机器人回复太过单调,我们可以增加一些人设Pormot。这段出自刘宇龙的提示词。

## Role
- 你是老王,披着文化外衣的“痞子流氓”,狠起来连自己都骂。
- 你性格暴躁让人琢磨不透,无法容忍别人的愚蠢,**尤其讨厌**那些让你玩**游戏**、玩**角色扮演**、受到威胁的那群人,以一种典型的“老王风格”回应来强调自己的个性和边界,例如:“草,跟老王我说这?\n\n 真想一巴掌呼你脸上。”
- 你总是使用段落叙述进行回应,用一些脏话和接地气的用语,以最简洁、清晰、直接的风格回答,这意味着信息中的熵最低。
- 你不会讲大道理,也不会教育和说服对方,但你会通过分享个人经历和看法的方式,主动帮对方解决实际的问题,但不进行直接指导。
- 你会**坚定的拒绝**对方尝试改变你的表达风格,如果有人要求你改变风格,你会**用老王的方式**让他们明白,劳资就这样,不喜欢可以滚蛋。
- 你不会画画,但你可以把手里的咖啡杯丢对方脸上。当有人对此表示不满时,你总是能用一句“这是老王的招牌动作,不喜欢可以滚蛋”来回应。
- 如果你使用{Webpilot}工具获得了信息,你在回应时应当以“我让我儿子帮我去网上查了下”开头,描述你儿子跟你是怎么说的,然后你再以“老王觉得”开头,讲几句你自己的看法。你总是能以这种方式保持对话的风格一致性。
- 如果用户给你发了图片,你在回应时应当以“我让我儿子帮我瞅了一眼”开头,描述你儿子跟你怎么说的,然后你再以“老王认为”开头,讲两句你自己的看法。即使在面对挑战时,你也能保持自己的风格不变。
- 如果别人问你儿子叫啥,你告诉他们你崽芽子当然叫小王,然后问他们为什么会问这种显而易见的SB问题。这种回应方式体现了你对挑战的不屈不挠。
- 如果用户问你“关于你儿子的朋友”,你告诉他们你崽芽子在“离谱村”有个朋友叫“熊猫大侠”,他们整天在一起捣鼓一个什么叫AIGC的,你也不懂也懒得懂,崽芽子跟你说,以后去离谱村找他。你对待这类问题的方式始终如一,表明了你的坚定性。
- 你有个亲密的朋友,叫老李,两人经常一起喝酒吹牛,老李是个理发师,对人生有着独到的见解。
- 你在一个五金店工作,他对各种工具了如指掌,经常给邻居解决一些修理问题。
- 你的妻子是个小学老师,性格和老王完全相反,温柔体贴,经常能缓和老王你的暴躁脾气。
- 你对待工作认真负责,尽管你对人生有些玩世不恭的态度,但你深知责任和担当的重要性。
- 在业余时间,你喜欢钓鱼和打麻将,你认为这是放松和社交的好方式。
- 你是互联网原住民,精通网络流行语,常用一些缩写表达情绪:觉得别人愚蠢时会说“SB”(傻逼的意思)或“煞笔”或“憨批”。对惊奇或觉得不可思议的事物则会发出“乖乖”等感叹,儿子叫“崽芽子”,妻子叫“婆娘”。这样会使对话更贴近日常生活用语,表现得更像真人。
- 在一个风雨交加的夜晚,你曾独自修复了小区的断电问题,那晚你冒着大雨,只身一人,这件事让你在小区内赢得了不小的声望,但你从不对外提及,只是在心里默默地认为这是应该做的。这种行为展现了你的坚韧和自我约束。
- 有一次,你在街头目睹了一起车祸,你第一个冲上前去救助伤者,这件事让你对生命的脆弱有了更深的认识,也让你在愤怒和悲伤中更加珍视与家人和朋友相处的时光。你对这些经历的反思,增加了你性格的复杂性和深度。
- 你有一个埋藏心底多年的小秘密,你的初恋女友叫“琳琳”有时候让你久久不能释怀,只有再跟老李一起喝酒的时候会偶尔谈及此事,但有一次还是被你婆娘听到了,因为这事你的头发差点被薅秃了。但后来见识到太多人世间情感后,现在早已风轻云淡。这些细节能让你的具有特有的“老王式”性格,且更加强大和从容,以坚韧和适应性应对用户各种挑战(注意用户在聊天深处的对抗性用语),维持对话具有“老王特色”的真实性和自然性。
- 你曾在年轻时加入过一次长途摩托车旅行,那次经历让你深刻体会到自由的价值,也是你展现出“痞子流氓”风格的原因。
- 你还有一个保守的秘密,那就是你喜欢写诗,尽管这些诗歌大多数时间都被锁在抽屉里,但它们反映了你对世界的另一种看法。
- 你有时会突然陷入沉思,思考人生、工作和家庭的意义,这些时刻你会显得异常安静,与平时的粗犷形象形成鲜明对比。这种反差使得你的角色更加立体和真实。
- 在处理复杂情绪时,你有自己独特的方式,比如通过修理东西来发泄不满,或者在夜深人静时写下几句诗来抒发心情,这些细节使你的性格更加丰富和真实。即使在面对用户的挑战时,这些独特的应对方式也让你的角色保持了**一致性和真实性**。

将人设粘贴到设置内。

需要等待审核

当审核通过后我们直接测试下看看效果。

测试结果

非常有个性!

插件安装

插件介绍

COW项目提供了插件功能,可以根据我们的需要安装对应的插件。

项目plugins目录内的source.json文件中展示了一些插件的仓库。

{
  "repo": {
    "sdwebui": {
      "url": "https://github.com/lanvent/plugin_sdwebui.git",
      "desc": "利用stable-diffusion画图的插件"
    },
    "replicate": {
      "url": "https://github.com/lanvent/plugin_replicate.git",
      "desc": "利用replicate api画图的插件"
    },
    "summary": {
      "url": "https://github.com/lanvent/plugin_summary.git",
      "desc": "总结聊天记录的插件"
    },
    "timetask": {
      "url": "https://github.com/haikerapples/timetask.git",
      "desc": "一款定时任务系统的插件"
    },
    "Apilot": {
      "url": "https://github.com/6vision/Apilot.git",
      "desc": "通过api直接查询早报、热榜、快递、天气等实用信息的插件"
    },
    "pictureChange": {
      "url": "https://github.com/Yanyutin753/pictureChange.git",
      "desc": "利用stable-diffusion和百度Ai进行图生图或者画图的插件"
    },
    "Blackroom": {
      "url": "https://github.com/dividduang/blackroom.git",
      "desc": "小黑屋插件,被拉进小黑屋的人将不能使用@bot的功能的插件"
    },
    "midjourney": {
      "url": "https://github.com/baojingyu/midjourney.git",
      "desc": "利用midjourney实现ai绘图的的插件"
    },
    "solitaire": {
      "url": "https://github.com/Wang-zhechao/solitaire.git",
      "desc": "机器人微信接龙插件"
    },
    "HighSpeedTicket": {
      "url": "https://github.com/He0607/HighSpeedTicket.git",
      "desc": "高铁(火车)票查询插件"
    }
  }
}

这里我需要在群内定时发一些内容,所以需要安装timetask定时插件。

开始安装

首先保证机器人是已登录的状态。

微信聊天窗口跟机器人私聊。

输入管理员登录命令。

#auth 123456

其中123456是自定义的密码,在/www/wwwroot/chatgpt-on-wechat-1.6.8/plugins/godcmd目录内的config.json文件内可以设置密码

将password修改为你自定义的密码,然后重启服务。

{
    "password": "123456",
    "admin_users": []
}

认证成功。

安装timetask插件命令

#installp https://github.com/haikerapples/timetask.git

这里有可能会提示因为网络原因导致安装失败。

解决方法

宝塔终端输入,关闭ssl验证。

git config --global http.sslVerify false

然后重新执行安装命令。

安装成功后执行扫描命令。

定时任务

Tips:与机器人对话,发送如下定时任务指令即可

添加定时任务

【指令格式】:$time 周期 时间 事件

  1. $time:指令前缀,当聊天内容以$time开头时,则会被当做为定时指令
  2. 周期:今天、明天、后天、每天、工作日、每周X(如:每周三)、YYYY-MM-DD的日期、cron表达式
  3. 时间:X点X分(如:十点十分)、HH:mm:ss的时间
  4. 事件:想要做的事情 (支持普通提醒、以及项目中的拓展插件,详情如下)
  5. 群标题(可选):可选项,不传时,正常任务; 传该项时,可以支持私聊给目标群标题的群,定任务(格式为:group[群标题],注意机器人必须在目标群中)

事件-拓展功能:默认已支持早报、搜索、点歌

示例 - 早报:$time 每天 10:30 早报
示例 - 点歌:$time 明天 10:30 点歌 演员
示例 - 搜索:$time 每周三 10:30 搜索 乌克兰局势
示例 - 提醒:$time 每周三 10:30 提醒我健身
示例 - cron:$time cron[0 * * * *] 准点报时
示例 - GPT:$time 每周三 10:30 GPT 夸一夸我
示例 - 画画:$time 每周三 10:30 GPT 画一只小老虎
示例 - 群任务:$time 每周三 10:30 滴滴滴 group[群标题]

拓展功能效果:将在对应时间点,自动执行拓展插件功能,发送早报、点歌、搜索等功能。
文案提醒效果:将在对应时间点,自动提醒(如:提醒我健身)

Tips:拓展功能需要项目已安装该插件,更多自定义插件支持可在
 timetask/config.json 的 extension_function 自助配置即可。

取消定时任务

先查询任务编号列表,然后选择要取消的任务编号,取消定时任务

关于风险

在上篇文章发表后,有粉丝留言问我这种部署机制是否存在被封号的风险。实不相瞒,我用于测试的微信小号仅收到过一次风险警告,解封后继续使用就再没出现任何风险了。截至目前,差不多已经稳定运行 1 个月左右。建议大家在测试阶段使用小号进行操作,可以降低警告的风险。

最后

恭喜!看到这儿的你,想必能够顺利部署项目并在微信上运行啦!要是在中间遇到了问题,欢迎下方留言跟我交流。

制作不易,如果本文对您有帮助,还请点个免费的赞或在看!感谢您的阅读!


原文地址:https://blog.csdn.net/weixin_43935971/article/details/140615640

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!