Skip to content

Commit

Permalink
Merge branch 'v2.x'
Browse files Browse the repository at this point in the history
  • Loading branch information
atorber committed Dec 22, 2023
2 parents b692cdf + 2003981 commit 7574fb3
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 50 deletions.
54 changes: 26 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
<!-- markdownlint-disable MD013 MD033 -->
# ChatFlow

<img alt="GitHub stars badge" src="https://img.shields.io/github/stars/atorber/chatflow"> <img alt="GitHub forks badge" src="https://img.shields.io/github/forks/atorber/chatflow"> <img alt="GitHub license badge" src="https://img.shields.io/github/license/atorber/chatflow"> [![NPM Version](https://img.shields.io/npm/v/@atorber/chatflow?color=brightgreen)](https://www.npmjs.com/package/@atorber/chatflow)
<img alt="GitHub stars badge" src="https://img.shields.io/github/stars/atorber/chatflow"> <img alt="GitHub forks badge" src="https://img.shields.io/github/forks/atorber/chatflow"> [![NPM Version](https://img.shields.io/npm/v/@atorber/chatflow?color=brightgreen)](https://www.npmjs.com/package/@atorber/chatflow)
![npm downloads](https://img.shields.io/npm/dm/@atorber/chatflow.svg) ![Docker Pulls](https://img.shields.io/docker/pulls/atorber/chatflow)
![Docker Image Size](https://img.shields.io/docker/image-size/atorber/chatflow/latest) ![Docker Stars](https://img.shields.io/docker/stars/atorber/chatflow)
![Docker Image Size](https://img.shields.io/docker/image-size/atorber/chatflow/latest) ![Docker Stars](https://img.shields.io/docker/stars/atorber/chatflow) <img alt="GitHub license badge" src="https://img.shields.io/github/license/atorber/chatflow">

## 简介

ChatFlow是一个聊天机器人管理系统,可以帮助你实现一些原生IM无法支持的功能。

如果你是一个社群工作者、拼团团长、业务群运营经理,使用这个项目可以帮助你解决一些实际工作中的问题
如果你是一个社群工作者、微信群私域运营人员,使用这个项目可以帮助你解决一些自动化问题

此外还提供定时任务、消息存档等功能
基于开源项目Wechaty实现,目前主要验证了对WeChat的支持,理论上支持钉钉、飞书、whatsapp等Wechaty已实现的所有IM

基于开源项目Wechaty实现,目前主要验证了对微信的支持,理论上支持微信、钉钉、飞书、whatsapp等。

已适配网页版微信,支持在Linux、Mac、Windows上运行。
支持在Linux、Mac、Windows上运行。

[访问项目语雀文档查看完整使用说明](https://www.yuque.com/atorber/chatflow)

### 功能列表

|功能|描述|
|--|--|
|定时提醒|定时消息发送,支持单次定时和周期消息发送给指定好友或群|
|智能问答|可以自定义问答内容,智能匹配答案,支持相似问题匹配,例如“什么时候到货?”“亲,几时到货”“亲,什么时候到货”均能匹配(基于微信对话开放平台,免费)|
|ChatGPT问答|已对接ChatGPT,支持使用ChatGPT作为聊天机器人呢|
|群发通知|向群或好友批量下发消息|
|消息存档|群聊天消息存档到表格(基于vika维格表,免费),可以在维格表中对聊天消息进行进一步统计、筛选、分析等|
|定时提醒|定时消息发送,支持单次定时和周期消息发送给指定好友或群|
|活动报名|群内接龙报名,使用 报名/取消 指令统计活动报名|
|智能问答|可以自定义问答内容,智能匹配答案,支持相似问题匹配,例如“什么时候到货?”“亲,几时到货”“亲,什么时候到货”均能匹配(基于微信对话开放平台,免费)|
|白名单|支持配置群白名单,白名单内群开启机器人问答/活动报名,未配置问题答案的群不会受到机器人干扰|
|白名单|支持配置群白名单,白名单内群开启机器人问答/活动报名/ChatGPT问答,未配置问题答案的群不会受到机器人干扰|
|MQTT消息推送|支持配置一个MQTTQ消息队列,将消息推送到队列当中|
|远程控制发消息|支持通过MQTT控制机器人向指定好友或群发消息|
|Web控制台|Web端控制台预览版已上线,抢险体验可以访问 https://chat.vlist.cc|

> 移步语雀文档查看 [详细功能查看](https://www.yuque.com/atorber/oegota/aialc7sbyb4ldmg4/edit)
Expand Down Expand Up @@ -71,20 +71,18 @@ npm run start

5.开启智能问答功能

5.1 设置微信对话平台token,填写"环境变量"表中的 【对话平台token】、【对话平台EncodingAESKey】并在"功能开关"表中开启智能问答
5.1 设置微信对话平台token,填写环境变量|Env】表中的 【微信对话开放平台-Token】、【微信对话开放平台-EncodingAESKey】、【微信对话开放平台-APPID】、【微信对话开放平台-管理员ID】并将【智能问答-启用自动问答】修改为 true

添加一个简单问题到微信对话开放平台,测试对应群内智能问答内容
5.2 添加问题到【问答列表|Qa】,添加之后在管理员群内发送【更新问答】

5.2 如果不希望每个群都开启智能问答,需设置群白名单,首先需要将上图中的群白名单开关设置为开启
5.3 将群加入到【白名单|WhiteList】,在【白名单|WhiteList】表中,所属应用选择【智能问答|qa】

然后将群加入到问答白名单,在“群白名单”表中,加入需要开启的群ID(roomid)
> 群ID在消息中查看(在群里发一条消息,然后控制台查看或在维格表中查找)
群ID在消息中查看(在群里发一条消息,然后控制台查看或在维格表中查找)
5.4 在管理群发送【更新白名单】或者重启程序

详细操作参考 [手把手教程](https://www.yuque.com/atorber/oegota/zm4ulnwnqp9whmd6)

5.3 重启程序,在指定群测试问答

## 在Docker中部署运行

注意,因为wechaty-puppet-xp必须依赖Windows微信客户端,所以不能使用Docker,但使用wechaty-puppet-padlocal、wechaty-puppet-service则可以用Doker来部署,
Expand All @@ -93,17 +91,6 @@ npm run start

> 移步语雀文档查看 [手把手教程](https://www.yuque.com/atorber/oegota/zm4ulnwnqp9whmd6)
### Wechaty-Puppet支持

|puppet名称|支持平台 |需要token |付费| 备注|
|--|--|--|--|--|
|wechaty-puppet-wechat| Windows、Linux、macOS |||网页版wechat,无法获取真实的微信ID和群ID,重启之后ID可能会变|
|wechaty-puppet-xp|Windows|||仅支持windows|
|wechaty-puppet-padlocal👍| Windows、Linux、macOS|||
|wechaty-puppet-service👍| Windows、Linux、macOS|||企业微信|

> 特别注意,Wechaty-Puppet是wechaty的概念,本项目不涉及机器人开发,只是使用wechaty项目进行业务功能实现,什么是[Wechaty](https://wechaty.js.org/)请点击链接进行了解学习
### 拉取和运行

- 最新版本
Expand All @@ -116,6 +103,17 @@ docker run -d --restart=always
atorber/chatflow:latest
```

## Wechaty-Puppet支持

|puppet名称|支持平台 |需要token |付费| 备注|
|--|--|--|--|--|
|wechaty-puppet-wechat| Windows、Linux、macOS |||网页版wechat,无法获取真实的微信ID和群ID,重启之后ID可能会变|
|wechaty-puppet-xp|Windows|||仅支持windows|
|wechaty-puppet-padlocal👍| Windows、Linux、macOS|||
|wechaty-puppet-service👍| Windows、Linux、macOS|||企业微信|

> 特别注意,Wechaty-Puppet是wechaty的概念,本项目不涉及机器人开发,只是使用wechaty项目进行业务功能实现,什么是[Wechaty](https://wechaty.js.org/)请点击链接进行了解学习
## 视频演示及使用教程

到项目官网 [查看视频教程](https://qabot.vlist.cc/)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@atorber/chatflow",
"version": "2.0.69",
"version": "2.0.70",
"description": "ChatFlow-聊天机器人管理平台",
"type": "module",
"exports": {
Expand Down
4 changes: 3 additions & 1 deletion src/api/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export interface MessageToCloud {
}

export const formatMessageToCloud = async (message: Message) => {
log.info('消息存储到lark...')
log.info('formatMessageToCloud 消息转换为存储到多维表格的格式...')
const room = message.room()
const talker = message.talker()
try {
Expand Down Expand Up @@ -328,8 +328,10 @@ export const saveMessageToCloud = async (record:any) => {
// log.info('saveMessageToCloud messageNew:', JSON.stringify(messageNew))
try {
if (ChatFlowConfig.dataBaseType === 'lark') {
log.info('消息写入到lark:', JSON.stringify(record))
await LarkChat.addChatRecord(record)
} else {
log.info('消息写入到vika:', JSON.stringify(record))
await MessageChat.addChatRecord(record)
}
// log.info('消息写入数据库成功:', res._id)
Expand Down
2 changes: 1 addition & 1 deletion src/app/extract-at.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const extractAtContent = async (message: Message): Promise<string | null>

p = p + chatText

p = `微信群聊天记录:\n${chatText}\n\n指令:\n你是微信聊天群里的成员【${keyWord}】,你正在参与大家的群聊天,先在轮到你发言了,你的回复尽可能清晰、严谨,字数不超过150字,并且你需要使用${role}的风格回复。当前时间是${time}。`
p = `微信群聊天记录:\n${chatText}\n\n指令:\n你是微信聊天群里的成员【${keyWord}】,你正在参与大家的群聊天,现在轮到你发言了,你的回复尽可能清晰、严谨,字数不超过150字,并且你需要使用${role}的风格回复。当前时间是${time}。`

p = p + `\n\n最新的对话:\n[${time} ${message.talker().name()}]:${newText}\n[${time} ${keyWord}]:`
log.info('提示词:', p)
Expand Down
40 changes: 25 additions & 15 deletions src/app/qa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,36 @@ import {
async function handleAutoQAForContact (message: Message, keyWord: string) {
const talker = message.talker()
const text = message.text()
log.info('联系人请求智能问答:', (text === keyWord))

if (ChatFlowConfig.configEnv.AUTOQA_AUTOREPLY) {
const includesKeyWord = text.indexOf(keyWord) !== -1
log.info('消息中包含关键字:', includesKeyWord)
const AUTOQA_AUTOREPLY = ChatFlowConfig.configEnv.AUTOQA_AUTOREPLY
log.info('自动问答开关开启:', AUTOQA_AUTOREPLY || false)
// 问答开关开启,且消息中包含关键字
if (AUTOQA_AUTOREPLY && includesKeyWord) {
// 判断是否在微信对话平台白名单内
const isInContactWhiteList = await containsContact(ChatFlowConfig.whiteList.contactWhiteList.qa, talker)
if (isInContactWhiteList) {
log.info('当前好友在qa白名单内,请求问答...')
log.info('当前好友在【白名单|WhiteList/智能问答|qa】内,请求问答...')
try {
await wxai(ChatFlowConfig.configEnv, ChatFlowConfig.bot, message)
} catch (e) {
log.error('当前好友在qa白名单内,发起请求wxai失败', e)
log.error('当前好友在【白名单|WhiteList/智能问答|qa】内,发起请求wxai失败', e)
}
} else {
log.info('当前好友不在qa白名单内,流程结束')
log.info('当前好友不在【白名单|WhiteList/智能问答|qa】内,流程结束')
}

// 判断是否在gpt白名单内
const isInGptContactWhiteList = await containsContact(ChatFlowConfig.whiteList.contactWhiteList.gpt, talker)
if (isInGptContactWhiteList) {
log.info('当前好友在qa白名单内,请求问答gpt...')
log.info('当前好友在【白名单|WhiteList/ChatGPT|gpt】内,请求问答gpt...')
try {
await gpt(ChatFlowConfig.bot, message)
} catch (e) {
log.error('发起请求gpt失败', e)
log.error('当前好友不在【白名单|WhiteList/ChatGPT|gpt】内,发起请求gpt失败', e)
}
} else {
log.info('当前好友不在gpt白名单内,gpt流程结束')
log.info('当前好友不在【白名单|WhiteList/ChatGPT|gpt】内,gpt流程结束')
}
}
}
Expand All @@ -48,29 +51,36 @@ async function handleAutoQA (message: Message, keyWord: string) {
const room = message.room() as Room
const topic = await room.topic()
const text = message.text()
log.info('群消息请求智能问答:' + JSON.stringify(text === keyWord))
if (ChatFlowConfig.configEnv.AUTOQA_AUTOREPLY) {
const includesKeyWord = text.indexOf(keyWord) !== -1
log.info('消息中包含关键字:', includesKeyWord)
const AUTOQA_AUTOREPLY = ChatFlowConfig.configEnv.AUTOQA_AUTOREPLY
log.info('自动问答开关开启:', AUTOQA_AUTOREPLY || false)

// 问答开关开启,且消息中包含关键字
if (AUTOQA_AUTOREPLY && includesKeyWord) {

// 判断是否在微信对话平台白名单内
const isInRoomWhiteList = await containsRoom(ChatFlowConfig.whiteList.roomWhiteList.qa, room)
if (isInRoomWhiteList) {
log.info('当前群在qa白名单内,请求问答...')
log.info('当前群在【白名单|WhiteList/智能问答|qa】内,请求问答...')
try {
await wxai(ChatFlowConfig.configEnv, ChatFlowConfig.bot, message)
} catch (e) {
log.error('当前群在qa白名单内,发起请求wxai失败', e)
log.error('当前群在【白名单|WhiteList/智能问答|qa】内,发起请求wxai失败', e)
}
}

// 判断是否在gpt白名单内
const isInGptRoomWhiteList = await containsRoom(ChatFlowConfig.whiteList.roomWhiteList.gpt, room)
if (isInGptRoomWhiteList) {
log.info('当前群在qa白名单内,请求问答gpt...')
log.info('当前群在【白名单|WhiteList/ChatGPT|gpt】白名单内,请求问答gpt...')
try {
await gpt(ChatFlowConfig.bot, message)
} catch (e) {
log.error('发起请求gpt失败', topic, e)
log.error('当前群在【白名单|WhiteList/ChatGPT|gpt】白名单内,发起请求gpt失败', topic, e)
}
} else {
log.info('当前群不在【白名单|WhiteList/ChatGPT|gpt】内,gpt流程结束')
}
}
}
Expand Down
10 changes: 7 additions & 3 deletions src/handlers/on-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { qa } from '../app/qa.js'
import { handleActivityManagement } from '../app/activity.js'
import { extractAtContent } from '../app/extract-at.js'

export async function onMessage (message: Message) {
export async function onMessage(message: Message) {

// 存储消息到db,如果写入失败则终止,用于检测是否是重复消息
try {
Expand All @@ -33,8 +33,12 @@ export async function onMessage (message: Message) {
}

// 输出格式化消息log
const chatMessage = await formatMessageToLog(message)
logForm(JSON.stringify(chatMessage))
try {
const chatMessage = await formatMessageToLog(message)
logForm(JSON.stringify(chatMessage))
} catch (e) {
log.error('消息格式化失败:\n', e)
}

// 请求管理员群操作
try {
Expand Down
2 changes: 1 addition & 1 deletion src/services/larkService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ export class LarkChat {
}

static async onMessage (message: Message) {
log.info('消息存储到lark...')
log.info('调用onMessage消息存储到lark...')
const room = message.room()
const talker = message.talker()
const files: any = []
Expand Down

0 comments on commit 7574fb3

Please sign in to comment.