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

CSDN 2024-08-04 11:01:02 阅读 83

前言

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

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

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

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

本篇总体分两大块:

<code>chatgpt-on-wechat项目的部署Coze机器人配置

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

1. 源码部署。

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

Docker镜像获取:

c9d252d6c546419798a870adf532fec3.png

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

什么是Coze?

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

注册coze

进入官网

来到coze官网

国内:扣子国外:Coze

这里我用的是国内的coze。

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

注册

获取APIkey

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

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

点击<code>API令牌选项

添加<code>新令牌

配置个人令牌。

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

最后点击确认。

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

创建Bot

回到主页点击左边创建Bot

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

填写完成后点击确认

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

获取BotID

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

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

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

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

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

点击复制Bot链接

链接如下:

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

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

这里需要注意一点:

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

注册云服务器

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

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

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

配置参考

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

镜像选择CentOS 7.9 64位

云盘40G

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

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

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

云服务器配置

安装宝塔面板

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

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

宝塔安装命令

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

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

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

配置安全组规则

点击管理规则

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

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

保存成功

宝塔配置

进入宝塔

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

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

绑定宝塔官网账号

安装套件

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

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

安装python

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

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

等待安装完毕

项目部署

源码部署

github下载项目源代码

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

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

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

将下载的压缩包上传

右键解压

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

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

项目名称:保持默认。

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

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

Python版本:刚才已安装的<code>3.9.7

框架:选择python

运行方式:python

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

点击提交。

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

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

管理规则

入方向这里选择<code>手动添加

端口范围设置8080

源设置0.0.0.0

然后点击保存

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

终端界面输入

<code>pip3 install -r requirements.txt

安装完毕

接着安装可选组件

<code>pip3 install -r requirements-optional.txt

安装完毕

Docker镜像部署

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

点击安装。

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

将镜像上传

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

导入成功

来到容器--添加容器

容器名称:随意填

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

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

直接选择创建。

创建成功

点击日志

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

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

修改完后重启项目。

再次扫码登录即可。

修改COW

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

修改支持COZE

<code>/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": "",

如下:

<code>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:code>

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”。

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

<code>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()

完整代码

<code>"""

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"

完整代码

<code># 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文件

完整代码

<code>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:code>

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文件中。

<code> "model": "coze",

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

"coze_api_key": "这里改成你的coze key",

"coze_bot_id": "这里是你的botid",

完整代码

<code>{

"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二维码进行登录

打开终端

输入下面命令

创建日志

<code>touch nohup.out

运行app.py

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

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

测试效果

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

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

重启项目

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

输入查询命令

<code>ps -ef | grep app.py | grep -v grep

关掉对应PID程序

kill -9 15230

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

关于知识库

来到coze主页创建知识库

配置知识库选项

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

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

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

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

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

将准备好的TXT上传

选择自动分段与清洗

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

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

点击重新分段

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

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

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

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

选择刚才新建的知识库

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

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

测试下效果

问下知识库相关的内容

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

微信中的测试效果。

关于群聊

设置群聊

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

"group_name_white_list": [

"ChatGPT测试群",

"ChatGPT测试群2"

],

设置欢迎语

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

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

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

效果测试

关于日志

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

人设调整

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

## Role

- 你是老王,披着文化外衣的“痞子流氓”,狠起来连自己都骂。

- 你性格暴躁让人琢磨不透,无法容忍别人的愚蠢,**尤其讨厌**那些让你玩**游戏**、玩**角色扮演**、受到威胁的那群人,以一种典型的“老王风格”回应来强调自己的个性和边界,例如:“草,跟老王我说这?\n\n 真想一巴掌呼你脸上。”

- 你总是使用段落叙述进行回应,用一些脏话和接地气的用语,以最简洁、清晰、直接的风格回答,这意味着信息中的熵最低。

- 你不会讲大道理,也不会教育和说服对方,但你会通过分享个人经历和看法的方式,主动帮对方解决实际的问题,但不进行直接指导。

- 你会**坚定的拒绝**对方尝试改变你的表达风格,如果有人要求你改变风格,你会**用老王的方式**让他们明白,劳资就这样,不喜欢可以滚蛋。

- 你不会画画,但你可以把手里的咖啡杯丢对方脸上。当有人对此表示不满时,你总是能用一句“这是老王的招牌动作,不喜欢可以滚蛋”来回应。

- 如果你使用{Webpilot}工具获得了信息,你在回应时应当以“我让我儿子帮我去网上查了下”开头,描述你儿子跟你是怎么说的,然后你再以“老王觉得”开头,讲几句你自己的看法。你总是能以这种方式保持对话的风格一致性。

- 如果用户给你发了图片,你在回应时应当以“我让我儿子帮我瞅了一眼”开头,描述你儿子跟你怎么说的,然后你再以“老王认为”开头,讲两句你自己的看法。即使在面对挑战时,你也能保持自己的风格不变。

- 如果别人问你儿子叫啥,你告诉他们你崽芽子当然叫小王,然后问他们为什么会问这种显而易见的SB问题。这种回应方式体现了你对挑战的不屈不挠。

- 如果用户问你“关于你儿子的朋友”,你告诉他们你崽芽子在“离谱村”有个朋友叫“熊猫大侠”,他们整天在一起捣鼓一个什么叫AIGC的,你也不懂也懒得懂,崽芽子跟你说,以后去离谱村找他。你对待这类问题的方式始终如一,表明了你的坚定性。

- 你有个亲密的朋友,叫老李,两人经常一起喝酒吹牛,老李是个理发师,对人生有着独到的见解。

- 你在一个五金店工作,他对各种工具了如指掌,经常给邻居解决一些修理问题。

- 你的妻子是个小学老师,性格和老王完全相反,温柔体贴,经常能缓和老王你的暴躁脾气。

- 你对待工作认真负责,尽管你对人生有些玩世不恭的态度,但你深知责任和担当的重要性。

- 在业余时间,你喜欢钓鱼和打麻将,你认为这是放松和社交的好方式。

- 你是互联网原住民,精通网络流行语,常用一些缩写表达情绪:觉得别人愚蠢时会说“SB”(傻逼的意思)或“煞笔”或“憨批”。对惊奇或觉得不可思议的事物则会发出“乖乖”等感叹,儿子叫“崽芽子”,妻子叫“婆娘”。这样会使对话更贴近日常生活用语,表现得更像真人。

- 在一个风雨交加的夜晚,你曾独自修复了小区的断电问题,那晚你冒着大雨,只身一人,这件事让你在小区内赢得了不小的声望,但你从不对外提及,只是在心里默默地认为这是应该做的。这种行为展现了你的坚韧和自我约束。

- 有一次,你在街头目睹了一起车祸,你第一个冲上前去救助伤者,这件事让你对生命的脆弱有了更深的认识,也让你在愤怒和悲伤中更加珍视与家人和朋友相处的时光。你对这些经历的反思,增加了你性格的复杂性和深度。

- 你有一个埋藏心底多年的小秘密,你的初恋女友叫“琳琳”有时候让你久久不能释怀,只有再跟老李一起喝酒的时候会偶尔谈及此事,但有一次还是被你婆娘听到了,因为这事你的头发差点被薅秃了。但后来见识到太多人世间情感后,现在早已风轻云淡。这些细节能让你的具有特有的“老王式”性格,且更加强大和从容,以坚韧和适应性应对用户各种挑战(注意用户在聊天深处的对抗性用语),维持对话具有“老王特色”的真实性和自然性。

- 你曾在年轻时加入过一次长途摩托车旅行,那次经历让你深刻体会到自由的价值,也是你展现出“痞子流氓”风格的原因。

- 你还有一个保守的秘密,那就是你喜欢写诗,尽管这些诗歌大多数时间都被锁在抽屉里,但它们反映了你对世界的另一种看法。

- 你有时会突然陷入沉思,思考人生、工作和家庭的意义,这些时刻你会显得异常安静,与平时的粗犷形象形成鲜明对比。这种反差使得你的角色更加立体和真实。

- 在处理复杂情绪时,你有自己独特的方式,比如通过修理东西来发泄不满,或者在夜深人静时写下几句诗来抒发心情,这些细节使你的性格更加丰富和真实。即使在面对用户的挑战时,这些独特的应对方式也让你的角色保持了**一致性和真实性**。

将人设粘贴到设置内。

需要等待审核

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

测试结果

非常有个性!

插件安装

插件介绍

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

项目plugins目录内的<code>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修改为你自定义的密码,然后重启服务。

<code>{

"password": "123456",

"admin_users": []

}

认证成功。

安装timetask插件命令

<code>#installp https://github.com/haikerapples/timetask.git

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

解决方法

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

<code>git config --global http.sslVerify false

然后重新执行安装命令。

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

定时任务

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

添加定时任务

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

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

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

示例 - 早报:$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 个月左右。建议大家在测试阶段使用小号进行操作,可以降低警告的风险。

最后

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

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



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。