赞
踩
前阵子,我分享了把大模型接入微信机器人的教程(http://t.csdnimg.cn/XBZyb),该教程基于 chatgpt-on-wechat
项目,其中用到了 LinkAI、智谱 AI 这两个平台的模型。在后续的测试中,发现存在一些问题,比如智谱 AI 知识库的调用存在问题,有时击中率很低。
于是我停用了这个机器人,并寻找新的解决办法。直到发现了 coze(字节推出的 AI 机器人和智能体创建平台),便迫不及待地进行接入尝试。结果发现 coze 非常好用,也很符合我的使用场景。
因此,写下这篇文章来记录从 0 搭建属于自己的微信群聊 AI 机器人的部署过程,包括服务器购买与配置、项目部署、接入 coze、修改 coze 配置、接入知识库、插件安装等。
原本计划将此篇文章分为上下两篇,最后还是决定直接完成整篇,篇幅虽长,但操作相对简单,跟着操作基本没问题。
本篇总体分两大块:
chatgpt-on-wechat
项目的部署chatgpt-on-wechat
项目的部署有两种方式:
1. 源码部署。
2. 我已制作了docker镜像,可一键部署。
Docker镜像获取:
公众号回复(注意大小写):ChatonWeChat
Coze 是一个面向所有人的下一代 AI 应用程序和聊天机器人开发平台。它允许用户轻松创建各种聊天机器人,无论用户是否具有编程经验。用户可以将自己的聊天机器人部署和发布到不同的社交平台,例如豆包、飞书、微信等。Coze 提供了丰富的功能,包括角色和提示、插件、知识库、开场对话、预览和调试、工作流等,用户还可以创建自己的插件。此外,Coze 还有一个 Bot 商店,展示各种功能的开源机器人,供用户浏览和学习。
来到coze官网
这里我用的是国内的coze。
如果你的业务是海外的,建议用国外的coze,这里不过多讲述。
我们需要获取APIKey调用coze大模型接口。
注册好后点击主页下方的扣子API
点击API令牌选项
添加新令牌
配置个人令牌。
最后点击确认。
创建成功,这里生成的令牌需要保存好!后面会用到,点击复制按钮复制令牌。
回到主页点击左边创建Bot
工作空间
选择默认的PersonalBot名称
填写你自己定义的Bot功能介绍
随便填填写完成后点击确认
创建成功,进入配置界面。
先不做详细配置,直接点击发布。
开场白设置直接跳过,我们不需要。
发布记录这里随意填,我填写的是版本号。
选择发布平台:这里勾选扣子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
安装完成,将红框里的链接以及账号密码保存下来。
注意看!这里提示需要开放对应的端口号。我们需要去阿里云安全组里配置对应的端口号。
点击管理规则
点击手动添加(这里入方向有很多的端口号是因为我这个服务器是之前就买的,然后闲置下来的,入方向的端口号是之前加上的,这里无需跟我一样添加)
这里添加8080
、13695
端口。8080
端口是我们后面要部署项目所用到的端口号,是固定的。13695
是宝塔面板访问需要开通的端口号,这里每个人生成的端口号不一样。注意你的端口号!
保存成功
安全组设置完毕后,进入到刚才保存的宝塔链接,选择外网面板地址,复制到浏览器中打开。
进入后需要登录,填入刚才生成的username和password
绑定宝塔官网账号
首次进入后会推荐安装套件,这里选择LNMP
然后这个界面不要关闭,耐心等待安装完成
点击网站--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服务的。
点击安装。
安装完毕,来到宝塔的/www/wwwroot
根目录。
将镜像上传
导入镜像,路径选择刚才上传的路径/www/wwwroot
。
导入成功
来到容器--添加容器
容器名称:随意填
镜像:选择刚才导入的镜像
端口:8080(记得检查服务器的安全组规则)
直接选择创建。
创建成功
点击日志
可以看到项目已经正常运行并生成了微信登录二维码,这时不要急着去扫,还需要修改下项目配置。
到这里为止,使用docker镜像部署项目已经完成了,接下来跟着修改COW配置即可。这里红框区域简单框选出需要修改的地方,具体修改的含义在下面会有讲到。
修改完后重启项目。
再次扫码登录即可。
这里说下,如果你是docker镜像部署的,修改支持COZE这一步直接省略,无需修改。直接到修改配置这步去修改config.json
/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 周期 时间 事件
- 事件-拓展功能:默认已支持早报、搜索、点歌
-
- 示例 - 早报:$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 个月左右。建议大家在测试阶段使用小号进行操作,可以降低警告的风险。
恭喜!看到这儿的你,想必能够顺利部署项目并在微信上运行啦!要是在中间遇到了问题,欢迎下方留言跟我交流。
制作不易,如果本文对您有帮助,还请点个免费的赞或在看!感谢您的阅读!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。