├── images ├── demo.png ├── flow.png └── qrcode.png ├── LICENSE ├── HTTP.md └── README.md /images/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chatrbot/chatbot/HEAD/images/demo.png -------------------------------------------------------------------------------- /images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chatrbot/chatbot/HEAD/images/flow.png -------------------------------------------------------------------------------- /images/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chatrbot/chatbot/HEAD/images/qrcode.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 chatrbot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /HTTP.md: -------------------------------------------------------------------------------- 1 | ## HTTP 接口 2 | 3 | > 服务器地址为 demo 里的 server 地址 4 | > 所有接口都为 POST,参数为 json 格式放在 body 中,token 拼接在 url 上 5 | > HEADER:Content-Type:application/json; charset=utf-8 6 | > 例子: http://服务器地址/api/v1/chat/sendText?token=your_token 7 | 8 | ## 发送消息类型 9 | 10 | ### 发送文本消息 11 | 12 | URI: /api/v1/chat/sendText 13 | METHOD:`POST` 14 | 参数: 15 | toUser string // 接收人 username 16 | content string // 发送的文本内容 17 | atList []string // 被@人的 username 数组,内容中也需要包含@昵称+空格+内容,例如@小明 你好 18 | 19 | ### 发送图片消息 20 | 21 | URI: /api/v1/chat/sendPic 22 | METHOD:`POST` 23 | 参数: 24 | toUser string // 接收人 username 25 | imgUrl string // 图片的网络地址 26 | 27 | ### 发送表情/动图消息 28 | 29 | URI: /api/v1/chat/sendEmoji 30 | METHOD:`POST` 31 | 参数: 32 | toUser string // 接收人 username 33 | gifUrl string 非必须 // 图片的网络地址 34 | emojiMd5 string 非必须 // 接收到 xml 中的 md5 字段值 35 | emojiTotalLen string 非必须 // 接收到 xml 中的 len 字段值 36 | 37 | > 不建议直接使用 gifUrl 参数来直接发送动图 有一定几率失败 38 | > 最好的方式还是通过接收到的表情消息 xml 中解析出 md5 和 len 来发送,这时候 gifUrl 可为空 39 | 40 | ### 发送视频消息 41 | 42 | URI: /api/v1/chat/sendVideo 43 | METHOD:`POST` 44 | 参数: 45 | toUser string // 接收人 46 | videoUrl string // 视频地址 47 | videoThumbUrl // 视频缩略图地址 48 | 49 | ### 发送语音消息 50 | 51 | URI: /api/v1/chat/sendVoice 52 | METHOD:`POST` 53 | 参数: 54 | toUser string // 接收人 username 55 | silkUrl string // 音频文件地址,silk 格式 56 | 57 | ### 发送小程序消息 58 | 59 | > 下面所有提到的参数可以在收到的小程序类型消息中获取到,整体是一个 xml 需要自行提取 60 | 61 | URI: /api/v1/chat/sendSmallApp 62 | METHOD:`POST` 63 | 参数: 64 | toUser string // 接收人 username 65 | thumbUrl // 缩略图地址 66 | title // 标题 67 | des // 描述 68 | url // 地址 69 | sourceUserName // 来源用户名 70 | sourceDisplayName // 来源显示名 71 | username // 用户名 72 | appid // 小程序 APPID 73 | type // 类型 74 | version // 版本 75 | iconUrl // 图标地址 76 | pagePath // 启动页 77 | 78 | --- 79 | 80 | ## 下载消息资源 81 | 82 | ### 下载消息中的图片 83 | 84 | URI: /api/v1/chat/downloadImage 85 | METHOD:`POST` 86 | 参数: 87 | xml string // 收到的消息 xml 88 | 89 | ### 下载消息中的视频 90 | 91 | URI: /api/v1/chat/downloadVideo 92 | METHOD:`POST` 93 | 参数: 94 | xml string // 收到的消息 xml 95 | 96 | ### 下载消息中的音频 97 | 98 | URI: /api/v1/chat/downloadVoice 99 | METHOD:`POST` 100 | 参数: 101 | xml string // 收到的消息 xml 102 | newMsgId string // 消息 id 103 | 104 | --- 105 | 106 | ## 群内操作 107 | 108 | ### 踢出群成员(机器人必须为管理员身份) 109 | 110 | URI: /api/v1/chatroom/delChatRoomMember 111 | METHOD:`POST` 112 | 参数: 113 | chatroom string // 相关群 id 114 | memberList []string // 批量踢出的成员 id 115 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChatBot 聊天机器人 2 | 3 | 一个可编程的微信机器人 4 | 你可以通过该机器人接入一些有趣的 API 来实现一些好玩的应用,例如快递查询、毒鸡汤、斗图等,也可以把自己人接入自己的服务中,作为告警、定时通知等服务. 5 | 6 | ## ⚠️ 关于群内机器人注意事项 ⚠️ 7 | 8 | 每个人只能将机器人拉入一个群,多的机器人会自动退群.如果需要增加上限可加入交流群联系我. 9 | 10 | ## 目录 11 | 12 | - [项目介绍](#项目介绍) 13 | - [常见问题](#常见问题) 14 | - [接入流程](#接入流程) 15 | - [示例项目](#示例项目) 16 | - [自定义开发](#自定义开发) 17 | - [入群交流](#联系方式) 18 | 19 | ## 项目介绍 20 | 21 | ### Feature: 22 | 23 | - 个人 24 | - [x] 机器人接收文本、图片、表情、视频、音频、小程序消息 25 | - 群聊 26 | - [x] 机器人接收文本、图片、表情、视频、音频、小程序消息 27 | - [x] 机器人被拉入群内事件 28 | - [x] 机器人被踢出群事件 29 | - [x] 群内成员新增事件 30 | - [x] 群内成员退出/被踢事件 31 | - [x] 机器人踢人接口 32 | - 功能 33 | - [ ] 命令:获取自己的微信号 34 | 35 | 类似 Server 酱,但是是通过个人微信实现.说通俗点其实是一个微信消息转发机器人,会把对应的消息转发到你的程序,并且提供了一定的发送接口能够让你命令机器人发送消息给你或者绑定的微信群. 36 | 37 | --- 38 | 39 | 主要流程图如下: 40 | 41 | ![流程图](./images/flow.png) 42 | 43 | > 图上蓝色部分都是你需要做的内容 44 | 45 | 主要流程: 46 | 47 | 1. 程序启动后建立一个 websocket 的连接,用于转发你发给机器人的消息和相关的群消息. 48 | 为什么使用 websocket 而不是 http 是因为如果是 http 回调的方式很多个人网络没有公网 ip,还需要内网穿透的方式来调试开发,过于繁琐. 49 | 2. 项目提供了一系列的消息发送接口(例如常用的文本\图片\语音\视频等类型的消息),可以根据`1`中收到的消息做相应的逻辑处理.当然也可以直接使用相关接口发送消息,可以用于系统告警等,具体操作可以点击[这里](#常见问题) 50 | 51 | ## 常见问题 52 | 53 | 1. 我是一个新手,该如何开始? 54 | 可以添加底部的机器人激活获取 token,然后根据下载 demo,根据 demo 中的 README 项目直接尝试运行看效果,慢慢修改成自己的逻辑. 55 | 2. 我只想让机器人发送消息给我,不想要启动一堆 websocket 之类的进程. 56 | 从目前的流程来看是可以实现的,但是在此之前仍然需要通过 websocket 的推送获取一次自己的 wxid,具体可以激活后通过执行一次 demo 项目,然后发送一条消息给机器人,终端就会打印出收到的消息,其中`fromUser`字段就是你自己的 wxid.之后直接调用相应的 HTTP 发送消息接口,填入该 wxid 即可. 57 | 3. websocket 的服务端地址是多少? 58 | `http://118.25.84.114:18881/ws?token={your_token}` 59 | 60 | ## 接入流程 61 | 62 | 1. 添加机器人为好友(机器人二维码在底部),一般在 1-2 分钟后会自动通过 63 | 2. 根据提示操作,可以发送`#帮助#`来获取具体指令,激活机器人,获取你自己的 token 64 | 3. 编写代码,连接机器人的 WebSocket Server 用于接收推送消息 65 | 4. 编写 Http Client 的代码,用于请求机器人接口,让机器人发送消息给你 66 | 67 | ## 示例项目 68 | 69 | - [ChatBot-Go](https://github.com/chatrbot/chatbot-go) 70 | - [ChatBot-Node](https://github.com/chatrbot/chatbot-node) 71 | - [ChatBot-PHP](https://github.com/chatrbot/chatbot-php)(暂未更新) 72 | 维护精力有限,Python 版本应该要滞后一些.Java 需要大佬帮一把了,欢迎提交 PR 73 | 74 | 可以进入各个项目查看具体的 README 介绍,不要忘了在启动项目前获得一个属于你自己的 token([如何获取 token](#接入流程)) 75 | 76 | ## 自定义开发 77 | 78 | 程序开发的主要逻辑其实就是上面提到的主要流程中的部分. 79 | 主要处理的就 2 个事情: 80 | 81 | 1. 接收 websocket 的消息 82 | 2. 调用 http 接口让机器人发送消息 83 | 84 | ### 建立 websocket 连接 85 | 86 | 这里拿 TypeScript 的伪代码举例: 87 | 88 | ```typescript 89 | this.ws = new WebSocket( 'ws://' + this.host + '/ws?token=' + this.token) 90 | this.ws.on('open', () => { 91 | console.log('连接websocket成功') 92 | this.heartBeatTimer = setInterval(() => { 93 | this.ws?.send('ping') 94 | }, 10000) 95 | } 96 | ``` 97 | 98 | - 需要注意的是在连接建立之后需要发送一个 10s/次的心跳包,内容为字符串的"ping"即可,消息类型也为 Text. 99 | 服务端会做心跳超时检测,阈值是 20s/次,也就是说至少 20s 内有一次心跳. 100 | - 如果自己用其他语言实现,例如 java 或者 python 记得做好断线重连(golang 和 ts 可以看[示例项目](#示例项目)中的例子),防止服务端更新重启导致的掉线. 101 | 102 | ### websocket 消息处理 103 | 104 | 目前机器人转发的消息类型主要分为两大类: 105 | 106 | - 普通的消息,例如文字\图片\视频等 107 | - 事件类型,主要是群内事件,例如群内有新成员加入,有人推群等. 108 | - [事件列表](#Feature) 109 | - [HTTP 接口](./HTTP.md) 110 | 111 | 整个消息处理流程目前是以"插件"的思想来完成,一条消息可以经过多个插件来处理. 112 | 如果你是用`typescirpt`实现,可以查看相关[demo](https://github.com/chatrbot/chatbot-node/tree/main/src/plugins)中的例子,需要实现插件的抽象类,其实也就一个`do`方法: 113 | 114 | ```typescript 115 | export abstract class Plugin { 116 | protected bot: ChatBot 117 | protected name: string 118 | 119 | protected constructor(name: string, bot: ChatBot) { 120 | this.name = name 121 | this.bot = bot 122 | } 123 | abstract do(msg: PushMessage, rawData?: string): void 124 | } 125 | ``` 126 | 127 | 其中的`PushMessage`就是程序会收到的消息,里面包含了具体的消息内容,发送人,接收人等信息,在我们调用发送接口的时候可能会用到. 128 | 129 | 这里分别举个收发消息和处理事件的例子: 130 | 机器人在收到`@小明 踢`这样的消息时,会进行踢人操作,并且在群内发送一条消息告知操作结果. 131 | 下面代码中`data.fromUser`就是`PushMessage`中这条消息/事件的发送人,其实也就是我们要发送消息的对象,原路返回的意思.如果是个人就是他的 wxId,如果是微信群则是群的 id. 132 | 133 | ```typescript 134 | export class GroupPlugin extends Plugin { 135 | constructor(bot: ChatBot) { 136 | super("group", bot) 137 | } 138 | 139 | do(msg: PushMessage) { 140 | //踢人操作要求机器人必须为群管理员 141 | if (msg.msgType === PushMsgType.Message) { 142 | const data = msg.data as UserMessage 143 | const keyword = "踢" 144 | /** 145 | * 必须是文本消息 146 | * 必须是群内消息 147 | * 必须匹配到触发关键字 148 | */ 149 | if ( 150 | data.msgType === MsgType.MsgTypeText && 151 | IsGroupMessage(data.fromUser) && 152 | SplitAtContent(data.groupContent) === keyword 153 | ) { 154 | if (IsGroupMember(data.groupMemberRole)) { 155 | this.bot 156 | .sendText( 157 | data.fromUser, 158 | "@" + 159 | data.groupMemberNickname + 160 | " 你不是管理员,休想命令我", 161 | [data.groupMember] 162 | ) 163 | .catch((err) => { 164 | console.log(err) 165 | }) 166 | return 167 | } 168 | this.bot 169 | .delGroupMembers(data.fromUser, data.atList) 170 | .then(() => { 171 | this.bot 172 | .sendText( 173 | data.fromUser, 174 | "@" + data.groupMemberNickname + " 搞定了老板", 175 | [data.groupMember] 176 | ) 177 | .catch((err) => console.log(err)) 178 | }) 179 | } 180 | if (data.msgType === MsgType.MsgTypeText) { 181 | this.bot.sendText(data.fromUser, "你好") 182 | } 183 | } 184 | } 185 | } 186 | ``` 187 | 188 | 完成插件内的逻辑后在主进程中调用即可,可以点击[这里](https://github.com/chatrbot/chatbot-node/blob/main/src/app.ts)查看调用例子. 189 | 整个链路涉及到的核心数据在下面注释,每个版本的 demo 中也基本注释详尽,还有问题可以加群交流. 190 | 191 | ### 核心数据结构注释 192 | 193 | ```javascript 194 | //所有消息的包装 195 | { 196 | //消息类型,1000为转发的聊天消息,1001为群内事件消息 197 | "msgType": 10000, 198 | //data在msgType不同的时候会有不同的数据结构,目前会有转发消息和群事件消息两种 199 | "data": {} 200 | } 201 | 202 | //机器人转发的消息,包括文本、图片、视频等格式 203 | { 204 | //下载语音的时候会用到 205 | "newMsgId":0, 206 | //发送人,这个也会是你发送消息的对象,即接口toUser字段的值就是这个 207 | "fromUser":"test", 208 | //消息中@了谁 209 | "atList":[], 210 | "createTime":0, 211 | //群内消息会变成"xxx发送了一个图片"这样的简短说明 212 | "pushContent":"", 213 | //机器人id 214 | "clientUserName":"test", 215 | //消息接收人id 216 | "toUser":"test", 217 | //暂时无用 218 | "imgBuf":"", 219 | //消息类型,具体看demo中的注释 220 | "msgType":1, 221 | //具体的消息内容,如果是其他格式的会是xml 222 | //如果是群内消息会变成 发言人id:消息内容 这样的格式 223 | "content":"test", 224 | //暂时无用 225 | "msgSource":"", 226 | // ====================================== 227 | // 下面这个三个字段只有群内消息有用 228 | // 方便开发者调用,省去来自己解析content这一步 229 | // ====================================== 230 | //谁@了机器人 231 | "whoAtBot":"", 232 | //发言人id 233 | "groupMember":"", 234 | //发言内容 235 | "groupContent":"" 236 | } 237 | 238 | //群内事件 239 | { 240 | //具体的事件id,这里100003只是举个例子,是成员离开事件 241 | "event":100003, 242 | //事件的中文描述 243 | "EventText":"群成员离开", 244 | //发生事件的群基本信息 245 | "group":{ 246 | //群id 247 | "groupUserName":"", 248 | //群名称 249 | "groupNickName":"", 250 | //群头像 251 | "groupHeadImg":"" 252 | }, 253 | //发生事件的群成员信息 254 | "members":[ 255 | { 256 | //群成员id 257 | "userName":"", 258 | //群成员昵称 259 | "nickName":"", 260 | //群成员头像 261 | "headImg":"" 262 | } 263 | ] 264 | } 265 | ``` 266 | 267 | ## 联系方式 268 | 269 | 1. 请先添加机器人为好友,等待 1-2 分钟后会自动通过 270 | 2. 对机器人发送命令(记得带上#号): #交流群# 即可获取邀请链接 271 | 272 | ![qrcode](./images/qrcode.png) 273 | --------------------------------------------------------------------------------